Bug 1277338 - Part 9: Move the servo/rust-mozjs crate providing bindings to SpiderMonkey to js/rust; r=sfink
authorNick Fitzgerald <fitzgen@gmail.com>
Tue, 05 Sep 2017 09:26:21 -0700
changeset 659199 8b984dd3cda3c236e3085791499cbeeae92cad08
parent 659198 ec49322e332fc2893e54f9fbd2c55a160e621875
child 659200 81ef74814f555845e8ce4336b65374c2a1743e03
push id78047
push userbmo:francesco.lodolo@gmail.com
push dateTue, 05 Sep 2017 17:25:17 +0000
reviewerssfink
bugs1277338
milestone57.0a1
Bug 1277338 - Part 9: Move the servo/rust-mozjs crate providing bindings to SpiderMonkey to js/rust; r=sfink
js/rust/.gitignore
js/rust/CMakeLists.txt
js/rust/Cargo.lock
js/rust/Cargo.toml
js/rust/README.md
js/rust/build.rs
js/rust/etc/wrapper.hpp
js/rust/src/ac.rs
js/rust/src/conversions.rs
js/rust/src/error.rs
js/rust/src/glue.rs
js/rust/src/heap.rs
js/rust/src/jsapi.rs
js/rust/src/jsglue.cpp
js/rust/src/jsval.rs
js/rust/src/lib.rs
js/rust/src/panic.rs
js/rust/src/rust.rs
js/rust/src/sc.rs
js/rust/src/typedarray.rs
js/rust/tests/callback.rs
js/rust/tests/enumerate.rs
js/rust/tests/evaluate.rs
js/rust/tests/panic.rs
js/rust/tests/rooting.rs
js/rust/tests/runtime.rs
js/rust/tests/stack_limit.rs
js/rust/tests/typedarray.rs
js/rust/tests/vec_conversion.rs
js/src/Cargo.lock
js/src/Cargo.toml
new file mode 100644
--- /dev/null
+++ b/js/rust/.gitignore
@@ -0,0 +1,1 @@
+target/
new file mode 100644
--- /dev/null
+++ b/js/rust/CMakeLists.txt
@@ -0,0 +1,35 @@
+project(rust-mozjs)
+cmake_minimum_required(VERSION 2.6)
+
+set(DUMMY ${CMAKE_BUILD_TYPE})
+
+set(SOURCES
+  src/jsglue.cpp
+  )
+
+include_directories($ENV{DEP_MOZJS_OUTDIR}/dist/include)
+
+if(MSVC)
+  if(NOT "$ENV{CARGO_FEATURE_DEBUGMOZJS}" STREQUAL "")
+    add_definitions(-MDd -Od -DDEBUG -D_DEBUG)
+  else()
+    add_definitions(-MD)
+  endif()
+  add_definitions(-FI$ENV{DEP_MOZJS_OUTDIR}/js/src/js-confdefs.h)
+  add_definitions(-DWIN32)
+  add_definitions(-Zi -GR-)
+else()
+  if(NOT "$ENV{CARGO_FEATURE_DEBUGMOZJS}" STREQUAL "")
+    add_definitions(-g -O0 -DDEBUG -D_DEBUG)
+  endif()
+  if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    add_definitions(-Wno-c++0x-extensions -Wno-return-type-c-linkage -Wno-invalid-offsetof)
+  endif()
+  add_definitions(-fPIC -fno-rtti)
+  add_definitions(-std=c++11 -DJS_NO_JSVAL_JSID_STRUCT_TYPES)
+  add_definitions(-include $ENV{DEP_MOZJS_OUTDIR}/js/src/js-confdefs.h)
+endif()
+
+add_library(jsglue STATIC ${SOURCES})
+install(TARGETS jsglue ARCHIVE DESTINATION lib)
+
new file mode 100644
--- /dev/null
+++ b/js/rust/Cargo.lock
@@ -0,0 +1,494 @@
+[root]
+name = "js"
+version = "0.1.4"
+dependencies = [
+ "bindgen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mozjs_sys 0.0.0",
+ "num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "aster"
+version = "0.41.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bitflags"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bitflags"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cexpr"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "clang-sys"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libloading 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "clap"
+version = "2.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cmake"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gcc"
+version = "0.3.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "glob"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "heapsize"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "kernel32-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lazy_static"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libloading"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "log"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "memchr"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "mozjs_sys"
+version = "0.0.0"
+dependencies = [
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "nom"
+version = "1.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num-traits"
+version = "0.1.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num_cpus"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "peeking_take_while"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "quasi"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quasi_codegen"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "rustc-serialize"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "strsim"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "syntex"
+version = "0.58.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syntex_errors"
+version = "0.58.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syntex_pos"
+version = "0.58.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syntex_syntax"
+version = "0.58.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "term"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "term_size"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thread-id"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "thread_local"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unreachable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "utf8-ranges"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "vec_map"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "which"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-build"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
+"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
+"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
+"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
+"checksum bindgen 0.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "33024f55a754d920637461adf87fb485702a69bdf7ac1d307b7e18da93bae505"
+"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
+"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
+"checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
+"checksum cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c47d456a36ebf0536a6705c83c1cbbcb9255fbc1d905a6ded104f479268a29"
+"checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a"
+"checksum clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771"
+"checksum cmake 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ebbb35d3dc9cd09497168f33de1acb79b265d350ab0ac34133b98f8509af1f"
+"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
+"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a"
+"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
+"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
+"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
+"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
+"checksum libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "38f5c2b18a287cf78b4097db62e20f43cace381dc76ae5c0a3073067f78b7ddc"
+"checksum libloading 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "be99f814beb3e9503a786a592c909692bb6d4fc5a695f6ed7987223acfbd5194"
+"checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
+"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
+"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
+"checksum nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
+"checksum num-traits 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "1708c0628602a98b52fad936cf3edb9a107af06e52e49fdf0707e884456a6af6"
+"checksum num_cpus 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83df569ffd47dbf87f36bead512632f89e90882b8e7a14286d0471daf6b72de9"
+"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
+"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
+"checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4"
+"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
+"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
+"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
+"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
+"checksum syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8f5e3aaa79319573d19938ea38d068056b826db9883a5d47f86c1cecc688f0e"
+"checksum syntex_errors 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "867cc5c2d7140ae7eaad2ae9e8bf39cb18a67ca651b7834f88d46ca98faadb9c"
+"checksum syntex_pos 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13ad4762fe52abc9f4008e85c4fb1b1fe3aa91ccb99ff4826a439c7c598e1047"
+"checksum syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e4dbae163dd98989464c23dd503161b338790640e11537686f2ef0f25c791"
+"checksum term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d168af3930b369cfe245132550579d47dfd873d69470755a19c2c6568dbbd989"
+"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
+"checksum textwrap 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f86300c3e7416ee233abd7cda890c492007a3980f941f79185c753a701257167"
+"checksum thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8df7875b676fddfadffd96deea3b1124e5ede707d4884248931077518cf1f773"
+"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7"
+"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"
+"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
+"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
+"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
+"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
+"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
+"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
+"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+"checksum which 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d238435618c0f298d2d75596c2d4fa7d4ea469c0c1c3ff824737ed50ad5ab61c"
+"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
+"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
new file mode 100644
--- /dev/null
+++ b/js/rust/Cargo.toml
@@ -0,0 +1,50 @@
+[package]
+name = "js"
+version = "0.1.4"
+authors = ["The Servo Project Developers"]
+build = "build.rs"
+license = "MPL-2.0"
+
+[build-dependencies]
+env_logger = "0.4"
+log = "0.3"
+bindgen = "0.30.0"
+cmake = "0.1"
+glob = "0.2.11"
+
+[[test]]
+name = "callback"
+[[test]]
+name = "enumerate"
+[[test]]
+name = "evaluate"
+[[test]]
+name = "panic"
+[[test]]
+name = "rooting"
+[[test]]
+name = "runtime"
+[[test]]
+name = "typedarray"
+[[test]]
+name = "stack_limit"
+[[test]]
+name = "vec_conversion"
+
+[lib]
+doctest = false
+
+[features]
+debugmozjs = ['mozjs_sys/debugmozjs']
+promises = ['mozjs_sys/promises']
+nonzero = []
+
+[dependencies.mozjs_sys]
+path = "../src"
+
+[dependencies]
+lazy_static = "0.2.1"
+libc = "0.2"
+log = "0.3"
+heapsize = "0.4"
+num-traits = "0.1.32"
new file mode 100644
--- /dev/null
+++ b/js/rust/README.md
@@ -0,0 +1,46 @@
+# The `js` Crate: Rust Bindings to SpiderMonkey
+
+[User Documentation](http://doc.servo.org/js/)
+
+## Building
+
+To build a release version of SpiderMonkey and the Rust code with optimizations
+enabled:
+
+```
+$ cargo build --release
+```
+
+To build with SpiderMonkey's DEBUG checks and assertions:
+
+```
+$ cargo build --features debugmozjs
+```
+
+Raw FFI bindings to JSAPI are machine generated with
+[`rust-lang-nursery/rust-bindgen`][bindgen], and requires libclang >= 3.9. See
+`./build.rs` for details.
+
+[bindgen]: https://github.com/rust-lang-nursery/rust-bindgen
+
+## Cargo Features
+
+* `debugmozjs`: Create a DEBUG build of SpiderMonkey with many extra assertions
+  enabled. This is decoupled from whether the crate and its Rust code is built
+  in debug or release mode.
+
+* `promises`: Enable SpiderMonkey native promises.
+
+* `nonzero`: Leverage the unstable `NonZero` type. Requires nightly Rust.
+
+## Testing
+
+Make sure to test both with and without the `debugmozjs` feature because various
+structures have different sizes and get passed through functions differently at
+the ABI level! At minimum, you should test with `debugmozjs` to get extra
+assertions and checking.
+
+```
+$ cargo test
+$ cargo test --features debugmozjs
+```
new file mode 100644
--- /dev/null
+++ b/js/rust/build.rs
@@ -0,0 +1,468 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+extern crate bindgen;
+extern crate cmake;
+extern crate glob;
+
+extern crate log;
+extern crate env_logger;
+
+use std::env;
+use std::path;
+
+fn main() {
+    log::set_logger(|max_log_level| {
+        use env_logger::Logger;
+        let env_logger = Logger::new();
+        max_log_level.set(env_logger.filter());
+        Box::new(env_logger)
+    }).expect("Failed to set logger.");
+
+    build_jsapi_bindings();
+    build_jsglue_cpp();
+}
+
+/// Build the ./src/jsglue.cpp file containing C++ glue methods built on top of
+/// JSAPI.
+fn build_jsglue_cpp() {
+    let dst = cmake::Config::new(".").build();
+
+    println!("cargo:rustc-link-search=native={}/lib", dst.display());
+    println!("cargo:rustc-link-lib=static=jsglue");
+    println!("cargo:rerun-if-changed=src/jsglue.cpp");
+}
+
+/// Find the public include directory within our mozjs-sys crate dependency.
+fn get_mozjs_include_dir() -> path::PathBuf {
+    let out_dir = env::var("OUT_DIR")
+        .expect("cargo should invoke us with the OUT_DIR env var set");
+
+    let mut target_build_dir = path::PathBuf::from(out_dir);
+    target_build_dir.push("../../");
+
+    let mut include_dir_glob = target_build_dir.display().to_string();
+    include_dir_glob.push_str("mozjs_sys-*/out/dist/include");
+
+    let entries = glob::glob(&include_dir_glob)
+        .expect("Should find entries for mozjs-sys include dir");
+
+    for entry in entries {
+        if let Ok(path) = entry {
+            return path.canonicalize()
+                .expect("Should canonicalize include path");
+        }
+    }
+
+    panic!("Should have found either a mozjs_sys in target/debug or in target/release");
+}
+
+/// Invoke bindgen on the JSAPI headers to produce raw FFI bindings for use from
+/// Rust.
+///
+/// To add or remove which functions, types, and variables get bindings
+/// generated, see the `const` configuration variables below.
+fn build_jsapi_bindings() {
+    let mut builder = bindgen::builder()
+        .rust_target(bindgen::RustTarget::Stable_1_19)
+        .header("./etc/wrapper.hpp")
+        .raw_line("pub use self::root::*;")
+        .enable_cxx_namespaces();
+
+    if cfg!(feature = "debugmozjs") {
+        builder = builder
+            .clang_arg("-DJS_GC_ZEAL")
+            .clang_arg("-DDEBUG")
+            .clang_arg("-DJS_DEBUG");
+    }
+
+    let include_dir = get_mozjs_include_dir();
+    let include_dir = include_dir.to_str()
+        .expect("Path to mozjs include dir should be utf-8");
+    builder = builder.clang_arg("-I");
+    builder = builder.clang_arg(include_dir);
+
+    for ty in UNSAFE_IMPL_SYNC_TYPES {
+        builder = builder.raw_line(format!("unsafe impl Sync for {} {{}}", ty));
+    }
+
+    for extra in EXTRA_CLANG_FLAGS {
+        builder = builder.clang_arg(*extra);
+    }
+
+    for ty in WHITELIST_TYPES {
+        builder = builder.whitelisted_type(ty);
+    }
+
+    for var in WHITELIST_VARS {
+        builder = builder.whitelisted_var(var);
+    }
+
+    for func in WHITELIST_FUNCTIONS {
+        builder = builder.whitelisted_function(func);
+    }
+
+    for ty in OPAQUE_TYPES {
+        builder = builder.opaque_type(ty);
+    }
+
+    for ty in BLACKLIST_TYPES {
+        builder = builder.hide_type(ty);
+    }
+
+    let bindings = builder.generate()
+        .expect("Should generate JSAPI bindings OK");
+
+    let out = path::PathBuf::from(env::var("OUT_DIR").unwrap());
+
+    if cfg!(feature = "debugmozjs") {
+        bindings.write_to_file(out.join("jsapi_debug.rs"))
+            .expect("Should write bindings to file OK");
+    } else {
+        bindings.write_to_file(out.join("jsapi.rs"))
+            .expect("Should write bindings to file OK");
+    }
+
+    println!("cargo:rerun-if-changed=etc/wrapper.hpp");
+}
+
+/// JSAPI types for which we should implement `Sync`.
+const UNSAFE_IMPL_SYNC_TYPES: &'static [&'static str] = &[
+    "JSClass",
+    "JSFunctionSpec",
+    "JSNativeWrapper",
+    "JSPropertySpec",
+    "JSTypedMethodJitInfo",
+];
+
+/// Flags passed through bindgen directly to Clang.
+const EXTRA_CLANG_FLAGS: &'static [&'static str] = &[
+    "-x", "c++",
+    "-std=c++14",
+    "-DRUST_BINDGEN",
+];
+
+/// Types which we want to generate bindings for (and every other type they
+/// transitively use).
+const WHITELIST_TYPES: &'static [&'static str] = &[
+    "JS::AutoCheckCannotGC",
+    "JS::AutoIdVector",
+    "JS::AutoObjectVector",
+    "JS::CallArgs",
+    "js::Class",
+    "JS::CompartmentOptions",
+    "JS::ContextOptions",
+    "js::DOMCallbacks",
+    "js::DOMProxyShadowsResult",
+    "js::ESClass",
+    "JS::ForOfIterator",
+    "JS::Handle",
+    "JS::HandleId",
+    "JS::HandleObject",
+    "JS::HandleString",
+    "JS::HandleValue",
+    "JS::HandleValueArray",
+    "JS::IsAcceptableThis",
+    "JSAutoCompartment",
+    "JSAutoStructuredCloneBuffer",
+    "JSClass",
+    "JSClassOps",
+    "JSContext",
+    "JSErrNum",
+    "JSErrorCallback",
+    "JSErrorFormatString",
+    "JSErrorReport",
+    "JSExnType",
+    "JSFlatString",
+    "JSFunction",
+    "JSFunctionSpec",
+    "JS::GCDescription",
+    "JSGCInvocationKind",
+    "JSGCMode",
+    "JSGCParamKey",
+    "JS::GCProgress",
+    "JSGCStatus",
+    "JSJitCompilerOption",
+    "JSJitGetterCallArgs",
+    "JSJitMethodCallArgs",
+    "JSJitSetterCallArgs",
+    "JSNativeWrapper",
+    "JSPropertySpec",
+    "JSProtoKey",
+    "JSObject",
+    "JSString",
+    "JSStructuredCloneReader",
+    "JSStructuredCloneWriter",
+    "JSScript",
+    "JSType",
+    "JSTypedMethodJitInfo",
+    "JSValueTag",
+    "JSValueType",
+    "JSVersion",
+    "JSWrapObjectCallbacks",
+    "jsid",
+    "JS::Latin1Char",
+    "JS::detail::MaybeWrapped",
+    "JS::MutableHandle",
+    "JS::MutableHandleObject",
+    "JS::MutableHandleValue",
+    "JS::NativeImpl",
+    "js::ObjectOps",
+    "JS::ObjectOpResult",
+    "JS::PromiseState",
+    "JS::PropertyDescriptor",
+    "JS::Rooted",
+    "JS::RootedObject",
+    "JS::RootingContext",
+    "JS::RootKind",
+    "js::Scalar::Type",
+    "JS::ServoSizes",
+    "js::shadow::Object",
+    "js::shadow::ObjectGroup",
+    "JS::SourceBufferHolder",
+    "JSStructuredCloneCallbacks",
+    "JS::Symbol",
+    "JS::SymbolCode",
+    "JS::TraceKind",
+    "JS::TransferableOwnership",
+    "JS::Value",
+    "JS::WarningReporter",
+    "JS::shadow::Zone",
+    "JS::Zone",
+];
+
+/// Global variables we want to generate bindings to.
+const WHITELIST_VARS: &'static [&'static str] = &[
+    "JS_STRUCTURED_CLONE_VERSION",
+    "JSCLASS_.*",
+    "JSFUN_.*",
+    "JSID_VOID",
+    "JSITER_.*",
+    "JSPROP_.*",
+    "JS::FalseHandleValue",
+    "JS::NullHandleValue",
+    "JS::TrueHandleValue",
+    "JS::UndefinedHandleValue",
+];
+
+/// Functions we want to generate bindings to.
+const WHITELIST_FUNCTIONS: &'static [&'static str] = &[
+    "INTERNED_STRING_TO_JSID",
+    "JS_AddExtraGCRootsTracer",
+    "JS_AddInterruptCallback",
+    "JS::AddPromiseReactions",
+    "js::AddRawValueRoot",
+    "JS_AlreadyHasOwnPropertyById",
+    "JS_AtomizeAndPinString",
+    "js::AssertSameCompartment",
+    "JS::Call",
+    "JS_CallFunctionValue",
+    "JS::CallOriginalPromiseThen",
+    "JS::CallOriginalPromiseResolve",
+    "JS::CallOriginalPromiseReject",
+    "JS::CompileFunction",
+    "JS::Construct",
+    "JS::ContextOptionsRef",
+    "JS_CopyPropertiesFrom",
+    "JS::CurrentGlobalOrNull",
+    "JS_DeletePropertyById",
+    "js::detail::IsWindowSlow",
+    "JS::Evaluate",
+    "JS_ForwardGetPropertyTo",
+    "JS_ForwardSetPropertyTo",
+    "JS::GCTraceKindToAscii",
+    "js::GetArrayBufferLengthAndData",
+    "js::GetArrayBufferViewLengthAndData",
+    "JS_GetErrorPrototype",
+    "js::GetFunctionNativeReserved",
+    "JS_GetFunctionPrototype",
+    "js::GetGlobalForObjectCrossCompartment",
+    "JS_GetIteratorPrototype",
+    "js::GetObjectProto",
+    "JS_GetObjectPrototype",
+    "JS_GetObjectRuntime",
+    "JS_GetOwnPropertyDescriptorById",
+    "JS::GetPromiseState",
+    "JS_GetPropertyDescriptorById",
+    "js::GetPropertyKeys",
+    "JS_GetPrototype",
+    "JS_GetRuntime",
+    "js::GetStaticPrototype",
+    "JS_HasOwnPropertyById",
+    "JS_HasProperty",
+    "JS_HasPropertyById",
+    "JS::HeapObjectPostBarrier",
+    "JS::HeapValuePostBarrier",
+    "JS_InitializePropertiesFromCompatibleNativeObject",
+    "JS::InitSelfHostedCode",
+    "JS::IsConstructor",
+    "JS::IsPromiseObject",
+    "JS_BeginRequest",
+    "JS_ClearPendingException",
+    "JS_DefineElement",
+    "JS_DefineFunction",
+    "JS_DefineFunctions",
+    "JS_DefineProperties",
+    "JS_DefineProperty",
+    "JS_DefinePropertyById",
+    "JS_DefineUCProperty",
+    "JS::detail::InitWithFailureDiagnostic",
+    "JS_DestroyContext",
+    "JS::DisableIncrementalGC",
+    "js::Dump.*",
+    "JS_EncodeStringToUTF8",
+    "JS_EndRequest",
+    "JS_EnterCompartment",
+    "JS_EnumerateStandardClasses",
+    "JS_ErrorFromException",
+    "JS_FireOnNewGlobalObject",
+    "JS_GC",
+    "JS_GetArrayBufferData",
+    "JS_GetArrayBufferViewType",
+    "JS_GetFloat32ArrayData",
+    "JS_GetFloat64ArrayData",
+    "JS_GetFunctionObject",
+    "JS_GetGCParameter",
+    "JS_GetInt16ArrayData",
+    "JS_GetInt32ArrayData",
+    "JS_GetInt8ArrayData",
+    "JS_GetLatin1StringCharsAndLength",
+    "JS_GetParentRuntime",
+    "JS_GetPendingException",
+    "JS_GetProperty",
+    "JS_GetPropertyById",
+    "js::GetPropertyKeys",
+    "JS_GetPrototype",
+    "JS_GetReservedSlot",
+    "JS::GetScriptedCallerGlobal",
+    "JS_GetTwoByteStringCharsAndLength",
+    "JS_GetUint16ArrayData",
+    "JS_GetUint32ArrayData",
+    "JS_GetUint8ArrayData",
+    "JS_GetUint8ClampedArrayData",
+    "JS::GetWellKnownSymbol",
+    "JS_GlobalObjectTraceHook",
+    "JS::HideScriptedCaller",
+    "JS_InitStandardClasses",
+    "JS_IsArrayObject",
+    "JS_IsExceptionPending",
+    "JS_IsGlobalObject",
+    "JS::IsCallable",
+    "JS_LeaveCompartment",
+    "JS_LinkConstructorAndPrototype",
+    "JS_MayResolveStandardClass",
+    "JS_NewArrayBuffer",
+    "JS_NewArrayObject",
+    "JS_NewContext",
+    "JS_NewFloat32Array",
+    "JS_NewFloat64Array",
+    "JS_NewFunction",
+    "js::NewFunctionWithReserved",
+    "JS_NewGlobalObject",
+    "JS_NewInt16Array",
+    "JS_NewInt32Array",
+    "JS_NewInt8Array",
+    "JS_NewObject",
+    "JS_NewObjectWithGivenProto",
+    "JS_NewObjectWithoutMetadata",
+    "JS_NewObjectWithUniqueType",
+    "JS_NewPlainObject",
+    "JS::NewPromiseObject",
+    "JS_NewStringCopyN",
+    "JS_NewUCStringCopyN",
+    "JS_NewUint16Array",
+    "JS_NewUint32Array",
+    "JS_NewUint8Array",
+    "JS_NewUint8ClampedArray",
+    "js::ObjectClassName",
+    "JS_ObjectIsDate",
+    "JS_ParseJSON",
+    "JS_ReadBytes",
+    "JS_ReadStructuredClone",
+    "JS_ReadUint32Pair",
+    "js::RemoveRawValueRoot",
+    "JS_ReportErrorASCII",
+    "JS_ReportErrorNumberUTF8",
+    "JS_RequestInterruptCallback",
+    "JS_ResolveStandardClass",
+    "JS_SameValue",
+    "js::SetDOMCallbacks",
+    "js::SetDOMProxyInformation",
+    "JS::SetEnqueuePromiseJobCallback",
+    "js::SetFunctionNativeReserved",
+    "JS_SetGCCallback",
+    "JS::SetGCSliceCallback",
+    "JS_SetGCParameter",
+    "JS_SetGCZeal",
+    "JS::SetGetIncumbentGlobalCallback",
+    "JS_SetGlobalJitCompilerOption",
+    "JS_SetImmutablePrototype",
+    "JS_SetNativeStackQuota",
+    "JS_SetOffthreadIonCompilationEnabled",
+    "JS_SetParallelParsingEnabled",
+    "JS_SetPendingException",
+    "js::SetPreserveWrapperCallback",
+    "JS_SetPrototype",
+    "js::SetWindowProxy",
+    "js::SetWindowProxyClass",
+    "JS_SetProperty",
+    "JS_SetReservedSlot",
+    "JS_SetWrapObjectCallbacks",
+    "JS_ShutDown",
+    "JS_SplicePrototype",
+    "JS_StrictPropertyStub",
+    "JS_StringEqualsAscii",
+    "JS_StringHasLatin1Chars",
+    "JS_WrapObject",
+    "JS_WrapValue",
+    "JS_WriteBytes",
+    "JS_WriteStructuredClone",
+    "JS_WriteUint32Pair",
+    "JS::ResolvePromise",
+    "JS::RejectPromise",
+    "JS::SetWarningReporter",
+    "js::ToBooleanSlow",
+    "js::ToInt32Slow",
+    "js::ToInt64Slow",
+    "js::ToNumberSlow",
+    "js::ToStringSlow",
+    "js::ToUint16Slow",
+    "js::ToUint32Slow",
+    "js::ToUint64Slow",
+    "JS_TransplantObject",
+    "js::detail::ToWindowProxyIfWindowSlow",
+    "JS::UnhideScriptedCaller",
+    "js::UnwrapArrayBuffer",
+    "js::UnwrapArrayBufferView",
+    "js::UnwrapFloat32Array",
+    "js::UnwrapFloat64Array",
+    "js::UnwrapInt16Array",
+    "js::UnwrapInt32Array",
+    "js::UnwrapInt8Array",
+    "js::UnwrapUint16Array",
+    "js::UnwrapUint32Array",
+    "js::UnwrapUint8Array",
+    "js::UnwrapUint8ClampedArray",
+];
+
+/// Types that should be treated as an opaque blob of bytes whenever they show
+/// up within a whitelisted type.
+///
+/// These are types which are too tricky for bindgen to handle, and/or use C++
+/// features that don't have an equivalent in rust, such as partial template
+/// specialization.
+const OPAQUE_TYPES: &'static [&'static str] = &[
+    "JS::ReadOnlyCompileOptions",
+    "mozilla::BufferList",
+    "mozilla::UniquePtr.*",
+    "JS::Rooted<JS::Auto.*Vector.*>",
+];
+
+/// Types for which we should NEVER generate bindings, even if it is used within
+/// a type or function signature that we are generating bindings for.
+const BLACKLIST_TYPES: &'static [&'static str] = &[
+    // We provide our own definition because we need to express trait bounds in
+    // the definition of the struct to make our Drop implementation correct.
+    "JS::Heap",
+];
new file mode 100644
--- /dev/null
+++ b/js/rust/etc/wrapper.hpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdint.h>
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+
+typedef uint32_t HashNumber;
+
+#include "jsfriendapi.h"
+#include "js/Conversions.h"
+#include "js/Initialization.h"
+#include "js/MemoryMetrics.h"
+
+// Replacements for types that are too difficult for rust-bindgen.
+
+/// <div rustbindgen replaces="JS::detail::MaybeWrapped" />
+template <typename T>
+using replaces_MaybeWrapped = T;
new file mode 100644
--- /dev/null
+++ b/js/rust/src/ac.rs
@@ -0,0 +1,56 @@
+use jsapi::root::*;
+#[cfg(feature = "debugmozjs")]
+use std::ptr;
+
+#[derive(Debug)]
+pub struct AutoCompartment(JSAutoCompartment);
+
+impl AutoCompartment {
+    #[cfg(feature = "debugmozjs")]
+    pub unsafe fn with_obj(cx: *mut JSContext,
+                           target: *mut JSObject)
+                           -> AutoCompartment
+    {
+        let mut notifier = mozilla::detail::GuardObjectNotifier {
+            mStatementDone: ptr::null_mut(),
+        };
+
+        AutoCompartment(
+            JSAutoCompartment::new(
+                cx,
+                target,
+                &mut notifier as *mut _))
+    }
+
+    #[cfg(not(feature = "debugmozjs"))]
+    pub unsafe fn with_obj(cx: *mut JSContext,
+                           target: *mut JSObject)
+                           -> AutoCompartment
+    {
+        AutoCompartment(JSAutoCompartment::new(cx, target))
+    }
+
+    #[cfg(feature = "debugmozjs")]
+    pub unsafe fn with_script(cx: *mut JSContext,
+                              target: *mut JSScript)
+                              -> AutoCompartment
+    {
+        let mut notifier = mozilla::detail::GuardObjectNotifier {
+            mStatementDone: ptr::null_mut(),
+        };
+
+        AutoCompartment(
+            JSAutoCompartment::new1(
+                cx,
+                target,
+                &mut notifier as *mut _))
+    }
+
+    #[cfg(not(feature = "debugmozjs"))]
+    pub unsafe fn with_script(cx: *mut JSContext,
+                              target: *mut JSScript)
+                              -> AutoCompartment
+    {
+        AutoCompartment(JSAutoCompartment::new1(cx, target))
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/conversions.rs
@@ -0,0 +1,679 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Conversions of Rust values to and from `JSVal`.
+//!
+//! | IDL type                | Type                             |
+//! |-------------------------|----------------------------------|
+//! | any                     | `JSVal`                          |
+//! | boolean                 | `bool`                           |
+//! | byte                    | `i8`                             |
+//! | octet                   | `u8`                             |
+//! | short                   | `i16`                            |
+//! | unsigned short          | `u16`                            |
+//! | long                    | `i32`                            |
+//! | unsigned long           | `u32`                            |
+//! | long long               | `i64`                            |
+//! | unsigned long long      | `u64`                            |
+//! | unrestricted float      | `f32`                            |
+//! | float                   | `Finite<f32>`                    |
+//! | unrestricted double     | `f64`                            |
+//! | double                  | `Finite<f64>`                    |
+//! | USVString               | `String`                         |
+//! | object                  | `*mut JSObject`                  |
+//! | nullable types          | `Option<T>`                      |
+//! | sequences               | `Vec<T>`                         |
+
+#![deny(missing_docs)]
+
+#[cfg(feature = "nonzero")]
+use core::nonzero::NonZero;
+
+use error::throw_type_error;
+use glue::RUST_JS_NumberValue;
+use heap::Heap;
+use jsapi::root::*;
+use jsval::{BooleanValue, Int32Value, NullValue, UInt32Value, UndefinedValue};
+use jsval::{ObjectValue, ObjectOrNullValue, StringValue};
+use rust::{ToBoolean, ToInt32, ToInt64, ToNumber, ToUint16, ToUint32, ToUint64};
+use rust::{ToString, maybe_wrap_object_or_null_value, maybe_wrap_value};
+use libc;
+use num_traits::{Bounded, Zero};
+use std::borrow::Cow;
+use std::rc::Rc;
+use std::{ptr, slice};
+
+trait As<O>: Copy {
+    fn cast(self) -> O;
+}
+
+macro_rules! impl_as {
+    ($I:ty, $O:ty) => (
+        impl As<$O> for $I {
+            fn cast(self) -> $O {
+                self as $O
+            }
+        }
+    )
+}
+
+impl_as!(f64, u8);
+impl_as!(f64, u16);
+impl_as!(f64, u32);
+impl_as!(f64, u64);
+impl_as!(f64, i8);
+impl_as!(f64, i16);
+impl_as!(f64, i32);
+impl_as!(f64, i64);
+
+impl_as!(u8, f64);
+impl_as!(u16, f64);
+impl_as!(u32, f64);
+impl_as!(u64, f64);
+impl_as!(i8, f64);
+impl_as!(i16, f64);
+impl_as!(i32, f64);
+impl_as!(i64, f64);
+
+impl_as!(i32, i8);
+impl_as!(i32, u8);
+impl_as!(i32, i16);
+impl_as!(u16, u16);
+impl_as!(i32, i32);
+impl_as!(u32, u32);
+impl_as!(i64, i64);
+impl_as!(u64, u64);
+
+/// A trait to convert Rust types to `JSVal`s.
+pub trait ToJSValConvertible {
+    /// Convert `self` to a `JSVal`. JSAPI failure causes a panic.
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue);
+}
+
+/// An enum to better support enums through FromJSValConvertible::from_jsval.
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum ConversionResult<T> {
+    /// Everything went fine.
+    Success(T),
+    /// Conversion failed, without a pending exception.
+    Failure(Cow<'static, str>),
+}
+
+impl<T> ConversionResult<T> {
+    /// Returns Some(value) if it is `ConversionResult::Success`.
+    pub fn get_success_value(&self) -> Option<&T> {
+        match *self {
+            ConversionResult::Success(ref v) => Some(v),
+            _ => None,
+        }
+    }
+}
+
+/// A trait to convert `JSVal`s to Rust types.
+pub trait FromJSValConvertible: Sized {
+    /// Optional configurable behaviour switch; use () for no configuration.
+    type Config;
+    /// Convert `val` to type `Self`.
+    /// Optional configuration of type `T` can be passed as the `option`
+    /// argument.
+    /// If it returns `Err(())`, a JSAPI exception is pending.
+    /// If it returns `Ok(Failure(reason))`, there is no pending JSAPI exception.
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: Self::Config)
+                         -> Result<ConversionResult<Self>, ()>;
+}
+
+/// Behavior for converting out-of-range integers.
+#[derive(PartialEq, Eq, Clone)]
+pub enum ConversionBehavior {
+    /// Wrap into the integer's range.
+    Default,
+    /// Throw an exception.
+    EnforceRange,
+    /// Clamp into the integer's range.
+    Clamp,
+}
+
+/// Try to cast the number to a smaller type, but
+/// if it doesn't fit, it will return an error.
+unsafe fn enforce_range<D>(cx: *mut JSContext, d: f64) -> Result<ConversionResult<D>, ()>
+    where D: Bounded + As<f64>,
+          f64: As<D>
+{
+    if d.is_infinite() {
+        throw_type_error(cx, "value out of range in an EnforceRange argument");
+        return Err(());
+    }
+
+    let rounded = d.round();
+    if D::min_value().cast() <= rounded && rounded <= D::max_value().cast() {
+        Ok(ConversionResult::Success(rounded.cast()))
+    } else {
+        throw_type_error(cx, "value out of range in an EnforceRange argument");
+        Err(())
+    }
+}
+
+/// Try to cast the number to a smaller type, but if it doesn't fit,
+/// round it to the MAX or MIN of the source type before casting it to
+/// the destination type.
+fn clamp_to<D>(d: f64) -> D
+    where D: Bounded + As<f64> + Zero,
+          f64: As<D>
+{
+    if d.is_nan() {
+        D::zero()
+    } else if d > D::max_value().cast() {
+        D::max_value()
+    } else if d < D::min_value().cast() {
+        D::min_value()
+    } else {
+        d.cast()
+    }
+}
+
+// https://heycam.github.io/webidl/#es-void
+impl ToJSValConvertible for () {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(UndefinedValue());
+    }
+}
+
+impl FromJSValConvertible for JS::HandleValue {
+    type Config = ();
+    #[inline]
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         value: JS::HandleValue,
+                         _option: ())
+                         -> Result<ConversionResult<JS::HandleValue>, ()> {
+        if value.is_object() {
+            js::AssertSameCompartment(cx, value.to_object());
+        }
+        Ok(ConversionResult::Success(value))
+    }
+}
+
+impl FromJSValConvertible for JS::Value {
+    type Config = ();
+    unsafe fn from_jsval(_cx: *mut JSContext,
+                         value: JS::HandleValue,
+                         _option: ())
+                         -> Result<ConversionResult<JS::Value>, ()> {
+        Ok(ConversionResult::Success(value.get()))
+    }
+}
+
+impl FromJSValConvertible for Heap<JS::Value> {
+    type Config = ();
+    unsafe fn from_jsval(_cx: *mut JSContext,
+                         value: JS::HandleValue,
+                         _option: ())
+                         -> Result<ConversionResult<Self>, ()> {
+        Ok(ConversionResult::Success(Heap::<JS::Value>::new(value.get())))
+    }
+}
+
+impl ToJSValConvertible for JS::Value {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(*self);
+        maybe_wrap_value(cx, rval);
+    }
+}
+
+impl ToJSValConvertible for JS::HandleValue {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(self.get());
+        maybe_wrap_value(cx, rval);
+    }
+}
+
+impl ToJSValConvertible for Heap<JS::Value> {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(self.get());
+        maybe_wrap_value(cx, rval);
+    }
+}
+
+#[inline]
+unsafe fn convert_int_from_jsval<T, M>(cx: *mut JSContext, value: JS::HandleValue,
+                                       option: ConversionBehavior,
+                                       convert_fn: unsafe fn(*mut JSContext, JS::HandleValue) -> Result<M, ()>)
+                                       -> Result<ConversionResult<T>, ()>
+    where T: Bounded + Zero + As<f64>,
+          M: Zero + As<T>,
+          f64: As<T>
+{
+    match option {
+        ConversionBehavior::Default => Ok(ConversionResult::Success(try!(convert_fn(cx, value)).cast())),
+        ConversionBehavior::EnforceRange => enforce_range(cx, try!(ToNumber(cx, value))),
+        ConversionBehavior::Clamp => Ok(ConversionResult::Success(clamp_to(try!(ToNumber(cx, value))))),
+    }
+}
+
+// https://heycam.github.io/webidl/#es-boolean
+impl ToJSValConvertible for bool {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(BooleanValue(*self));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-boolean
+impl FromJSValConvertible for bool {
+    type Config = ();
+    unsafe fn from_jsval(_cx: *mut JSContext, val: JS::HandleValue, _option: ()) -> Result<ConversionResult<bool>, ()> {
+        Ok(ToBoolean(val)).map(ConversionResult::Success)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-byte
+impl ToJSValConvertible for i8 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(Int32Value(*self as i32));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-byte
+impl FromJSValConvertible for i8 {
+    type Config = ConversionBehavior;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: ConversionBehavior)
+                         -> Result<ConversionResult<i8>, ()> {
+        convert_int_from_jsval(cx, val, option, ToInt32)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-octet
+impl ToJSValConvertible for u8 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(Int32Value(*self as i32));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-octet
+impl FromJSValConvertible for u8 {
+    type Config = ConversionBehavior;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: ConversionBehavior)
+                         -> Result<ConversionResult<u8>, ()> {
+        convert_int_from_jsval(cx, val, option, ToInt32)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-short
+impl ToJSValConvertible for i16 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(Int32Value(*self as i32));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-short
+impl FromJSValConvertible for i16 {
+    type Config = ConversionBehavior;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: ConversionBehavior)
+                         -> Result<ConversionResult<i16>, ()> {
+        convert_int_from_jsval(cx, val, option, ToInt32)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-short
+impl ToJSValConvertible for u16 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(Int32Value(*self as i32));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-short
+impl FromJSValConvertible for u16 {
+    type Config = ConversionBehavior;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: ConversionBehavior)
+                         -> Result<ConversionResult<u16>, ()> {
+        convert_int_from_jsval(cx, val, option, ToUint16)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-long
+impl ToJSValConvertible for i32 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(Int32Value(*self));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-long
+impl FromJSValConvertible for i32 {
+    type Config = ConversionBehavior;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: ConversionBehavior)
+                         -> Result<ConversionResult<i32>, ()> {
+        convert_int_from_jsval(cx, val, option, ToInt32)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-long
+impl ToJSValConvertible for u32 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(UInt32Value(*self));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-long
+impl FromJSValConvertible for u32 {
+    type Config = ConversionBehavior;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: ConversionBehavior)
+                         -> Result<ConversionResult<u32>, ()> {
+        convert_int_from_jsval(cx, val, option, ToUint32)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-long-long
+impl ToJSValConvertible for i64 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(RUST_JS_NumberValue(*self as f64));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-long-long
+impl FromJSValConvertible for i64 {
+    type Config = ConversionBehavior;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: ConversionBehavior)
+                         -> Result<ConversionResult<i64>, ()> {
+        convert_int_from_jsval(cx, val, option, ToInt64)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-long-long
+impl ToJSValConvertible for u64 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(RUST_JS_NumberValue(*self as f64));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-long-long
+impl FromJSValConvertible for u64 {
+    type Config = ConversionBehavior;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         val: JS::HandleValue,
+                         option: ConversionBehavior)
+                         -> Result<ConversionResult<u64>, ()> {
+        convert_int_from_jsval(cx, val, option, ToUint64)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-float
+impl ToJSValConvertible for f32 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(RUST_JS_NumberValue(*self as f64));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-float
+impl FromJSValConvertible for f32 {
+    type Config = ();
+    unsafe fn from_jsval(cx: *mut JSContext, val: JS::HandleValue, _option: ()) -> Result<ConversionResult<f32>, ()> {
+        let result = ToNumber(cx, val);
+        result.map(|f| f as f32).map(ConversionResult::Success)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-double
+impl ToJSValConvertible for f64 {
+    #[inline]
+    unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(RUST_JS_NumberValue(*self));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-double
+impl FromJSValConvertible for f64 {
+    type Config = ();
+    unsafe fn from_jsval(cx: *mut JSContext, val: JS::HandleValue, _option: ()) -> Result<ConversionResult<f64>, ()> {
+        ToNumber(cx, val).map(ConversionResult::Success)
+    }
+}
+
+/// Converts a `JSString`, encoded in "Latin1" (i.e. U+0000-U+00FF encoded as 0x00-0xFF) into a
+/// `String`.
+pub unsafe fn latin1_to_string(cx: *mut JSContext, s: *mut JSString) -> String {
+    assert!(JS_StringHasLatin1Chars(s));
+
+    let mut length = 0;
+    let chars = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s, &mut length);
+    assert!(!chars.is_null());
+
+    let chars = slice::from_raw_parts(chars, length as usize);
+    let mut s = String::with_capacity(length as usize);
+    s.extend(chars.iter().map(|&c| c as char));
+    s
+}
+
+// https://heycam.github.io/webidl/#es-USVString
+impl ToJSValConvertible for str {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        let mut string_utf16: Vec<u16> = Vec::with_capacity(self.len());
+        string_utf16.extend(self.encode_utf16());
+        let jsstr = JS_NewUCStringCopyN(cx,
+                                        string_utf16.as_ptr(),
+                                        string_utf16.len() as libc::size_t);
+        if jsstr.is_null() {
+            panic!("JS_NewUCStringCopyN failed");
+        }
+        rval.set(StringValue(&*jsstr));
+    }
+}
+
+// https://heycam.github.io/webidl/#es-USVString
+impl ToJSValConvertible for String {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        (**self).to_jsval(cx, rval);
+    }
+}
+
+// https://heycam.github.io/webidl/#es-USVString
+impl FromJSValConvertible for String {
+    type Config = ();
+    unsafe fn from_jsval(cx: *mut JSContext, value: JS::HandleValue, _: ()) -> Result<ConversionResult<String>, ()> {
+        let jsstr = ToString(cx, value);
+        if jsstr.is_null() {
+            debug!("ToString failed");
+            return Err(());
+        }
+        if JS_StringHasLatin1Chars(jsstr) {
+            return Ok(latin1_to_string(cx, jsstr)).map(ConversionResult::Success);
+        }
+
+        let mut length = 0;
+        let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length);
+        assert!(!chars.is_null());
+        let char_vec = slice::from_raw_parts(chars, length as usize);
+        Ok(String::from_utf16_lossy(char_vec)).map(ConversionResult::Success)
+    }
+}
+
+impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        match self {
+            &Some(ref value) => value.to_jsval(cx, rval),
+            &None => rval.set(NullValue()),
+        }
+    }
+}
+
+impl<T: ToJSValConvertible> ToJSValConvertible for Rc<T> {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        (**self).to_jsval(cx, rval)
+    }
+}
+
+impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> {
+    type Config = T::Config;
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         value: JS::HandleValue,
+                         option: T::Config)
+                         -> Result<ConversionResult<Option<T>>, ()> {
+        if value.get().is_null_or_undefined() {
+            Ok(ConversionResult::Success(None))
+        } else {
+            Ok(match try!(FromJSValConvertible::from_jsval(cx, value, option)) {
+                ConversionResult::Success(v) => ConversionResult::Success(Some(v)),
+                ConversionResult::Failure(v) => ConversionResult::Failure(v),
+            })
+        }
+    }
+}
+
+// https://heycam.github.io/webidl/#es-sequence
+impl<T: ToJSValConvertible> ToJSValConvertible for Vec<T> {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rooted!(in(cx) let js_array = JS_NewArrayObject1(cx, self.len() as libc::size_t));
+        assert!(!js_array.handle().is_null());
+
+        rooted!(in(cx) let mut val = UndefinedValue());
+        for (index, obj) in self.iter().enumerate() {
+            obj.to_jsval(cx, val.handle_mut());
+
+            assert!(JS_DefineElement(
+                cx,
+                js_array.handle(),
+                index as u32,
+                val.handle(),
+                JSPROP_ENUMERATE as _
+            ));
+        }
+
+        rval.set(ObjectValue(js_array.handle().get()));
+    }
+}
+
+/// Rooting guard for the iterator field of ForOfIterator.
+/// Behaves like RootedGuard (roots on creation, unroots on drop),
+/// but borrows and allows access to the whole ForOfIterator, so
+/// that methods on ForOfIterator can still be used through it.
+struct ForOfIteratorGuard<'a> {
+    root: &'a mut JS::ForOfIterator
+}
+
+impl<'a> ForOfIteratorGuard<'a> {
+    fn new(cx: *mut JSContext, root: &'a mut JS::ForOfIterator) -> Self {
+        unsafe {
+            root.iterator.register_with_root_lists(cx);
+        }
+        ForOfIteratorGuard {
+            root: root
+        }
+    }
+}
+
+impl<'a> Drop for ForOfIteratorGuard<'a> {
+    fn drop(&mut self) {
+        unsafe {
+            self.root.iterator.remove_from_root_stack();
+        }
+    }
+}
+
+impl<C: Clone, T: FromJSValConvertible<Config=C>> FromJSValConvertible for Vec<T> {
+    type Config = C;
+
+    unsafe fn from_jsval(cx: *mut JSContext,
+                         value: JS::HandleValue,
+                         option: C)
+                         -> Result<ConversionResult<Vec<T>>, ()> {
+        let mut iterator = JS::ForOfIterator {
+            cx_: cx,
+            iterator: JS::RootedObject::new_unrooted(),
+            index: ::std::u32::MAX, // NOT_ARRAY
+        };
+        let iterator = ForOfIteratorGuard::new(cx, &mut iterator);
+        let iterator = &mut *iterator.root;
+
+        if !iterator.init(value, JS::ForOfIterator_NonIterableBehavior::AllowNonIterable) {
+            return Err(())
+        }
+
+        if iterator.iterator.ptr.is_null() {
+            return Ok(ConversionResult::Failure("Value is not iterable".into()));
+        }
+
+        let mut ret = vec![];
+
+        loop {
+            let mut done = false;
+            rooted!(in(cx) let mut val = UndefinedValue());
+            if !iterator.next(val.handle_mut(), &mut done) {
+                return Err(())
+            }
+
+            if done {
+                break;
+            }
+
+            ret.push(match try!(T::from_jsval(cx, val.handle(), option.clone())) {
+                ConversionResult::Success(v) => v,
+                ConversionResult::Failure(e) => return Ok(ConversionResult::Failure(e)),
+            });
+        }
+
+        Ok(ret).map(ConversionResult::Success)
+    }
+}
+
+// https://heycam.github.io/webidl/#es-object
+impl ToJSValConvertible for *mut JSObject {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(ObjectOrNullValue(*self));
+        maybe_wrap_object_or_null_value(cx, rval);
+    }
+}
+
+// https://heycam.github.io/webidl/#es-object
+#[cfg(feature = "nonzero")]
+impl ToJSValConvertible for NonZero<*mut JSObject> {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        use rust::maybe_wrap_object_value;
+        rval.set(ObjectValue(self.get()));
+        maybe_wrap_object_value(cx, rval);
+    }
+}
+
+// https://heycam.github.io/webidl/#es-object
+impl ToJSValConvertible for Heap<*mut JSObject> {
+    #[inline]
+    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+        rval.set(ObjectOrNullValue(self.get()));
+        maybe_wrap_object_or_null_value(cx, rval);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/error.rs
@@ -0,0 +1,73 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Functions to throw JavaScript exceptions from Rust.
+
+#![deny(missing_docs)]
+
+use jsapi::root::*;
+use libc;
+use std::ffi::CString;
+use std::{mem, os, ptr};
+
+/// Format string used to throw javascript errors.
+static ERROR_FORMAT_STRING_STRING: [libc::c_char; 4] = [
+    '{' as libc::c_char,
+    '0' as libc::c_char,
+    '}' as libc::c_char,
+    0 as libc::c_char,
+];
+
+/// Format string struct used to throw `TypeError`s.
+static mut TYPE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
+    name: b"RUSTMSG_TYPE_ERROR\0" as *const _ as *const libc::c_char,
+    format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
+    argCount: 1,
+    exnType: JSExnType::JSEXN_TYPEERR as i16,
+};
+
+/// Format string struct used to throw `RangeError`s.
+static mut RANGE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
+    name: b"RUSTMSG_RANGE_ERROR\0" as *const _ as *const libc::c_char,
+    format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
+    argCount: 1,
+    exnType: JSExnType::JSEXN_RANGEERR as i16,
+};
+
+/// Callback used to throw javascript errors.
+/// See throw_js_error for info about error_number.
+unsafe extern "C" fn get_error_message(_user_ref: *mut os::raw::c_void,
+                                       error_number: libc::c_uint)
+                                       -> *const JSErrorFormatString {
+    let num: JSExnType = mem::transmute(error_number);
+    match num {
+        JSExnType::JSEXN_TYPEERR => &TYPE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
+        JSExnType::JSEXN_RANGEERR => &RANGE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
+        _ => panic!("Bad js error number given to get_error_message: {}",
+                    error_number),
+    }
+}
+
+/// Helper fn to throw a javascript error with the given message and number.
+/// Reuse the jsapi error codes to distinguish the error_number
+/// passed back to the get_error_message callback.
+/// c_uint is u32, so this cast is safe, as is casting to/from i32 from there.
+unsafe fn throw_js_error(cx: *mut JSContext, error: &str, error_number: u32) {
+    let error = CString::new(error).unwrap();
+    JS_ReportErrorNumberUTF8(cx,
+                             Some(get_error_message),
+                             ptr::null_mut(),
+                             error_number,
+                             error.as_ptr());
+}
+
+/// Throw a `TypeError` with the given message.
+pub unsafe fn throw_type_error(cx: *mut JSContext, error: &str) {
+    throw_js_error(cx, error, JSExnType::JSEXN_TYPEERR as u32);
+}
+
+/// Throw a `RangeError` with the given message.
+pub unsafe fn throw_range_error(cx: *mut JSContext, error: &str) {
+    throw_js_error(cx, error, JSExnType::JSEXN_RANGEERR as u32);
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/glue.rs
@@ -0,0 +1,348 @@
+use jsapi::root::*;
+use heap::Heap;
+use std::os::raw::c_void;
+
+
+pub enum Action { }
+unsafe impl Sync for ProxyTraps {}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct ProxyTraps {
+    pub enter: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                          proxy: JS::HandleObject,
+                                                          id: JS::HandleId,
+                                                          action: Action,
+                                                          bp: *mut bool)
+                                                          -> bool>,
+    pub getOwnPropertyDescriptor:
+        ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                   proxy: JS::HandleObject,
+                                                   id: JS::HandleId,
+                                                   desc: JS::MutableHandle<JS::PropertyDescriptor>)
+                                                   -> bool>,
+    pub defineProperty:
+        ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                   proxy: JS::HandleObject,
+                                                   id: JS::HandleId,
+                                                   desc: JS::Handle<JS::PropertyDescriptor>,
+                                                   result: *mut JS::ObjectOpResult)
+                                                   -> bool>,
+    pub ownPropertyKeys: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                                    proxy: JS::HandleObject,
+                                                                    props: *mut JS::AutoIdVector)
+                                                                    -> bool>,
+    pub delete_: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                            proxy: JS::HandleObject,
+                                                            id: JS::HandleId,
+                                                            result: *mut JS::ObjectOpResult)
+                                                            -> bool>,
+    pub enumerate: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                              proxy: JS::HandleObject,
+                                                              objp: JS::MutableHandleObject)
+                                                              -> bool>,
+    pub getPrototypeIfOrdinary:
+        ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                   proxy: JS::HandleObject,
+                                                   isOrdinary: *mut bool,
+                                                   protop: JS::MutableHandleObject)
+                                                   -> bool>,
+    pub preventExtensions:
+        ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                   proxy: JS::HandleObject,
+                                                   result: *mut JS::ObjectOpResult)
+                                                   -> bool>,
+    pub isExtensible: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                                 proxy: JS::HandleObject,
+                                                                 succeeded: *mut bool)
+                                                                 -> bool>,
+    pub has: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                        proxy: JS::HandleObject,
+                                                        id: JS::HandleId,
+                                                        bp: *mut bool)
+                                                        -> bool>,
+    pub get: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                        proxy: JS::HandleObject,
+                                                        receiver: JS::HandleValue,
+                                                        id: JS::HandleId,
+                                                        vp: JS::MutableHandleValue)
+                                                        -> bool>,
+    pub set: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                        proxy: JS::HandleObject,
+                                                        id: JS::HandleId,
+                                                        v: JS::HandleValue,
+                                                        receiver: JS::HandleValue,
+                                                        result: *mut JS::ObjectOpResult)
+                                                        -> bool>,
+    pub call: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                         proxy: JS::HandleObject,
+                                                         args: *const JS::CallArgs)
+                                                         -> bool>,
+    pub construct: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                              proxy: JS::HandleObject,
+                                                              args: *const JS::CallArgs)
+                                                              -> bool>,
+    pub getPropertyDescriptor:
+        ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                   proxy: JS::HandleObject,
+                                                   id: JS::HandleId,
+                                                   desc: JS::MutableHandle<JS::PropertyDescriptor>)
+                                                   -> bool>,
+    pub hasOwn: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                           proxy: JS::HandleObject,
+                                                           id: JS::HandleId,
+                                                           bp: *mut bool)
+                                                           -> bool>,
+    pub getOwnEnumerablePropertyKeys:
+        ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                   proxy: JS::HandleObject,
+                                                   props: *mut JS::AutoIdVector)
+                                                   -> bool>,
+    pub nativeCall: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                               test: JS::IsAcceptableThis,
+                                                               _impl: JS::NativeImpl,
+                                                               args: JS::CallArgs)
+                                                               -> bool>,
+    pub hasInstance: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                                proxy: JS::HandleObject,
+                                                                v: JS::MutableHandleValue,
+                                                                bp: *mut bool)
+                                                                -> bool>,
+    pub objectClassIs: ::std::option::Option<unsafe extern "C" fn(obj: JS::HandleObject,
+                                                                  classValue: js::ESClass,
+                                                                  cx: *mut JSContext)
+                                                                  -> bool>,
+    pub className: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                              proxy: JS::HandleObject)
+                                                              -> *const i8>,
+    pub fun_toString: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                                 proxy: JS::HandleObject,
+                                                                 indent: u32)
+                                                                 -> *mut JSString>,
+    pub boxedValue_unbox: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                                     proxy: JS::HandleObject,
+                                                                     vp: JS::MutableHandleValue)
+                                                                     -> bool>,
+    pub defaultValue: ::std::option::Option<unsafe extern "C" fn(cx: *mut JSContext,
+                                                                 obj: JS::HandleObject,
+                                                                 hint: JSType,
+                                                                 vp: JS::MutableHandleValue)
+                                                                 -> bool>,
+    pub trace:
+        ::std::option::Option<unsafe extern "C" fn(trc: *mut JSTracer, proxy: *mut JSObject)>,
+    pub finalize:
+        ::std::option::Option<unsafe extern "C" fn(fop: *mut JSFreeOp, proxy: *mut JSObject)>,
+    pub objectMoved:
+        ::std::option::Option<unsafe extern "C" fn(proxy: *mut JSObject, old: *const JSObject)>,
+    pub isCallable: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
+    pub isConstructor: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
+}
+impl ::std::default::Default for ProxyTraps {
+    fn default() -> ProxyTraps {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct WrapperProxyHandler {
+    pub mTraps: ProxyTraps,
+}
+impl ::std::default::Default for WrapperProxyHandler {
+    fn default() -> WrapperProxyHandler {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct ForwardingProxyHandler {
+    pub mTraps: ProxyTraps,
+    pub mExtra: *const ::libc::c_void,
+}
+impl ::std::default::Default for ForwardingProxyHandler {
+    fn default() -> ForwardingProxyHandler {
+        unsafe { ::std::mem::zeroed() }
+    }
+}
+
+extern "C" {
+    pub fn InvokeGetOwnPropertyDescriptor(handler: *const ::libc::c_void,
+                                          cx: *mut JSContext,
+                                          proxy: JS::HandleObject,
+                                          id: JS::HandleId,
+                                          desc: JS::MutableHandle<JS::PropertyDescriptor>)
+                                          -> bool;
+    pub fn InvokeHasOwn(handler: *const ::libc::c_void,
+                        cx: *mut JSContext,
+                        proxy: JS::HandleObject,
+                        id: JS::HandleId,
+                        bp: *mut bool)
+                        -> bool;
+    pub fn RUST_JS_NumberValue(d: f64) -> JS::Value;
+    pub fn RUST_FUNCTION_VALUE_TO_JITINFO(v: JS::Value) -> *const JSJitInfo;
+    pub fn CreateCallArgsFromVp(argc: u32, v: *mut JS::Value) -> JS::CallArgs;
+    pub fn CallJitGetterOp(info: *const JSJitInfo,
+                           cx: *mut JSContext,
+                           thisObj: JS::HandleObject,
+                           specializedThis: *mut ::libc::c_void,
+                           argc: u32,
+                           vp: *mut JS::Value)
+                           -> bool;
+    pub fn CallJitSetterOp(info: *const JSJitInfo,
+                           cx: *mut JSContext,
+                           thisObj: JS::HandleObject,
+                           specializedThis: *mut ::libc::c_void,
+                           argc: u32,
+                           vp: *mut JS::Value)
+                           -> bool;
+    pub fn CallJitMethodOp(info: *const JSJitInfo,
+                           cx: *mut JSContext,
+                           thisObj: JS::HandleObject,
+                           specializedThis: *mut ::libc::c_void,
+                           argc: u32,
+                           vp: *mut JS::Value)
+                           -> bool;
+    pub fn CreateProxyHandler(aTraps: *const ProxyTraps,
+                              aExtra: *const ::libc::c_void)
+                              -> *const ::libc::c_void;
+    pub fn CreateWrapperProxyHandler(aTraps: *const ProxyTraps) -> *const ::libc::c_void;
+    pub fn CreateRustJSPrincipal(origin: *const ::libc::c_void,
+                                  destroy: Option<unsafe extern "C" fn
+                                                  (principal: *mut JSPrincipals)>,
+                                  write: Option<unsafe extern "C" fn
+                                                (cx: *mut JSContext,
+                                                 writer: *mut JSStructuredCloneWriter)
+                                                 -> bool>)
+-> *mut JSPrincipals;
+    pub fn GetPrincipalOrigin(principal: *const JSPrincipals) -> *const ::libc::c_void;
+    pub fn GetCrossCompartmentWrapper() -> *const ::libc::c_void;
+    pub fn GetSecurityWrapper() -> *const ::libc::c_void;
+    pub fn NewCompileOptions(aCx: *mut JSContext,
+                             aFile: *const ::libc::c_char,
+                             aLine: u32)
+                             -> *mut JS::ReadOnlyCompileOptions;
+    pub fn DeleteCompileOptions(aOpts: *mut JS::ReadOnlyCompileOptions);
+    pub fn NewProxyObject(aCx: *mut JSContext,
+                          aHandler: *const ::libc::c_void,
+                          aPriv: JS::HandleValue,
+                          proto: *mut JSObject,
+                          parent: *mut JSObject,
+                          call: *mut JSObject,
+                          construct: *mut JSObject)
+                          -> *mut JSObject;
+    pub fn WrapperNew(aCx: *mut JSContext,
+                      aObj: JS::HandleObject,
+                      aHandler: *const ::libc::c_void,
+                      aClass: *const JSClass,
+                      aSingleton: bool)
+                      -> *mut JSObject;
+    pub fn NewWindowProxy(aCx: *mut JSContext,
+                          aObj: JS::HandleObject,
+                          aHandler: *const ::libc::c_void)
+                          -> *mut JSObject;
+    pub fn GetWindowProxyClass() -> *const js::Class;
+    pub fn GetProxyPrivate(obj: *mut JSObject) -> JS::Value;
+    pub fn SetProxyPrivate(obj: *mut JSObject, private: *const JS::Value);
+    pub fn GetProxyReservedSlot(obj: *mut JSObject, slot: u32) -> JS::Value;
+    pub fn SetProxyReservedSlot(obj: *mut JSObject, slot: u32, val: *const JS::Value);
+    pub fn RUST_JSID_IS_INT(id: JS::HandleId) -> bool;
+    pub fn RUST_JSID_TO_INT(id: JS::HandleId) -> i32;
+    pub fn int_to_jsid(i: i32) -> jsid;
+    pub fn RUST_JSID_IS_STRING(id: JS::HandleId) -> bool;
+    pub fn RUST_JSID_TO_STRING(id: JS::HandleId) -> *mut JSString;
+    pub fn RUST_SYMBOL_TO_JSID(sym: *mut JS::Symbol) -> jsid;
+    pub fn RUST_SET_JITINFO(func: *mut JSFunction, info: *const JSJitInfo);
+    pub fn RUST_INTERNED_STRING_TO_JSID(cx: *mut JSContext, str: *mut JSString) -> jsid;
+    pub fn RUST_js_GetErrorMessage(userRef: *mut ::libc::c_void,
+                                   errorNumber: u32)
+                                   -> *const JSErrorFormatString;
+    pub fn IsProxyHandlerFamily(obj: *mut JSObject) -> u8;
+    pub fn GetProxyHandlerExtra(obj: *mut JSObject) -> *const ::libc::c_void;
+    pub fn GetProxyHandler(obj: *mut JSObject) -> *const ::libc::c_void;
+    pub fn ReportError(aCx: *mut JSContext, aError: *const i8);
+    pub fn IsWrapper(obj: *mut JSObject) -> bool;
+    pub fn UnwrapObject(obj: *mut JSObject, stopAtOuter: u8) -> *mut JSObject;
+    pub fn UncheckedUnwrapObject(obj: *mut JSObject, stopAtOuter: u8) -> *mut JSObject;
+    pub fn CreateAutoIdVector(cx: *mut JSContext) -> *mut JS::AutoIdVector;
+    pub fn AppendToAutoIdVector(v: *mut JS::AutoIdVector, id: jsid) -> bool;
+    pub fn SliceAutoIdVector(v: *const JS::AutoIdVector, length: *mut usize) -> *const jsid;
+    pub fn DestroyAutoIdVector(v: *mut JS::AutoIdVector);
+    pub fn CreateAutoObjectVector(aCx: *mut JSContext) -> *mut JS::AutoObjectVector;
+    pub fn AppendToAutoObjectVector(v: *mut JS::AutoObjectVector, obj: *mut JSObject) -> bool;
+    pub fn DeleteAutoObjectVector(v: *mut JS::AutoObjectVector);
+    pub fn CollectServoSizes(rt: *mut JSRuntime, sizes: *mut JS::ServoSizes) -> bool;
+    pub fn CallIdTracer(trc: *mut JSTracer, idp: *mut Heap<jsid>, name: *const ::libc::c_char);
+    pub fn CallValueTracer(trc: *mut JSTracer,
+                           valuep: *mut Heap<JS::Value>,
+                           name: *const ::libc::c_char);
+    pub fn CallObjectTracer(trc: *mut JSTracer,
+                            objp: *mut Heap<*mut JSObject>,
+                            name: *const ::libc::c_char);
+    pub fn CallStringTracer(trc: *mut JSTracer,
+                            strp: *mut Heap<*mut JSString>,
+                            name: *const ::libc::c_char);
+    pub fn CallScriptTracer(trc: *mut JSTracer,
+                            scriptp: *mut Heap<*mut JSScript>,
+                            name: *const ::libc::c_char);
+    pub fn CallFunctionTracer(trc: *mut JSTracer,
+                              funp: *mut Heap<*mut JSFunction>,
+                              name: *const ::libc::c_char);
+    pub fn CallUnbarrieredObjectTracer(trc: *mut JSTracer,
+                                       objp: *mut *mut JSObject,
+                                       name: *const ::libc::c_char);
+    pub fn GetProxyHandlerFamily() -> *const c_void;
+
+    pub fn GetInt8ArrayLengthAndData(obj: *mut JSObject,
+                                     length: *mut u32,
+                                     isSharedMemory: *mut bool,
+                                     data: *mut *mut i8);
+    pub fn GetUint8ArrayLengthAndData(obj: *mut JSObject,
+                                      length: *mut u32,
+                                      isSharedMemory: *mut bool,
+                                      data: *mut *mut u8);
+    pub fn GetUint8ClampedArrayLengthAndData(obj: *mut JSObject,
+                                             length: *mut u32,
+                                             isSharedMemory: *mut bool,
+                                             data: *mut *mut u8);
+    pub fn GetInt16ArrayLengthAndData(obj: *mut JSObject,
+                                      length: *mut u32,
+                                      isSharedMemory: *mut bool,
+                                      data: *mut *mut i16);
+    pub fn GetUint16ArrayLengthAndData(obj: *mut JSObject,
+                                       length: *mut u32,
+                                       isSharedMemory: *mut bool,
+                                       data: *mut *mut u16);
+    pub fn GetInt32ArrayLengthAndData(obj: *mut JSObject,
+                                      length: *mut u32,
+                                      isSharedMemory: *mut bool,
+                                      data: *mut *mut i32);
+    pub fn GetUint32ArrayLengthAndData(obj: *mut JSObject,
+                                       length: *mut u32,
+                                       isSharedMemory: *mut bool,
+                                       data: *mut *mut u32);
+    pub fn GetFloat32ArrayLengthAndData(obj: *mut JSObject,
+                                        length: *mut u32,
+                                        isSharedMemory: *mut bool,
+                                        data: *mut *mut f32);
+    pub fn GetFloat64ArrayLengthAndData(obj: *mut JSObject,
+                                        length: *mut u32,
+                                        isSharedMemory: *mut bool,
+                                        data: *mut *mut f64);
+
+    pub fn NewJSAutoStructuredCloneBuffer(scope: JS::StructuredCloneScope,
+                                          callbacks: *const JSStructuredCloneCallbacks)
+                                          -> *mut JSAutoStructuredCloneBuffer;
+    pub fn DeleteJSAutoStructuredCloneBuffer(buf: *mut JSAutoStructuredCloneBuffer);
+    pub fn GetLengthOfJSStructuredCloneData(data: *mut JSStructuredCloneData) -> usize;
+    pub fn CopyJSStructuredCloneData(src: *mut JSStructuredCloneData, dest: *mut u8);
+    pub fn WriteBytesToJSStructuredCloneData(src: *const u8,
+                                             len: usize,
+                                             dest: *mut JSStructuredCloneData)
+                                             -> bool;
+
+    pub fn IsDebugBuild() -> bool;
+}
+
+#[test]
+fn jsglue_cpp_configured_correctly() {
+    assert_eq!(cfg!(feature = "debugmozjs"), unsafe { IsDebugBuild() });
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/heap.rs
@@ -0,0 +1,172 @@
+use glue;
+use heapsize::HeapSizeOf;
+use jsapi::root::*;
+use rust::GCMethods;
+use std::cell::UnsafeCell;
+use std::ptr;
+
+/// Types that can be traced.
+///
+/// This trait is unsafe; if it is implemented incorrectly, the GC may end up
+/// collecting objects that are still reachable.
+pub unsafe trait Trace {
+    unsafe fn trace(&self, trc: *mut JSTracer);
+}
+
+/**
+ * The Heap<T> class is a heap-stored reference to a JS GC thing. All members of
+ * heap classes that refer to GC things should use Heap<T> (or possibly
+ * TenuredHeap<T>, described below).
+ *
+ * Heap<T> is an abstraction that hides some of the complexity required to
+ * maintain GC invariants for the contained reference. It uses operator
+ * overloading to provide a normal pointer interface, but notifies the GC every
+ * time the value it contains is updated. This is necessary for generational GC,
+ * which keeps track of all pointers into the nursery.
+ *
+ * Heap<T> instances must be traced when their containing object is traced to
+ * keep the pointed-to GC thing alive.
+ *
+ * Heap<T> objects should only be used on the heap. GC references stored on the
+ * C/C++ stack must use Rooted/Handle/MutableHandle instead.
+ *
+ * Type T must be a public GC pointer type.
+ */
+#[repr(C)]
+#[derive(Debug)]
+pub struct Heap<T: GCMethods + Copy> {
+    ptr: UnsafeCell<T>,
+}
+
+impl<T: GCMethods + Copy> Heap<T> {
+    pub fn new(v: T) -> Heap<T>
+        where Heap<T>: Default
+    {
+        let ptr = Heap::default();
+        ptr.set(v);
+        ptr
+    }
+
+    pub fn set(&self, v: T) {
+        unsafe {
+            let ptr = self.ptr.get();
+            let prev = *ptr;
+            *ptr = v;
+            T::post_barrier(ptr, prev, v);
+        }
+    }
+
+    pub fn get(&self) -> T {
+        unsafe {
+            *self.ptr.get()
+        }
+    }
+
+    pub unsafe fn get_unsafe(&self) -> *mut T {
+        self.ptr.get()
+    }
+
+    pub fn handle(&self) -> JS::Handle<T> {
+        unsafe {
+            JS::Handle::from_marked_location(self.ptr.get() as *const _)
+        }
+    }
+
+    pub fn handle_mut(&self) -> JS::MutableHandle<T> {
+        unsafe {
+            JS::MutableHandle::from_marked_location(self.ptr.get())
+        }
+    }
+}
+
+impl<T: GCMethods + Copy> Clone for Heap<T>
+    where Heap<T>: Default
+{
+    fn clone(&self) -> Self {
+        Heap::new(self.get())
+    }
+}
+
+impl<T: GCMethods + Copy + PartialEq> PartialEq for Heap<T> {
+    fn eq(&self, other: &Self) -> bool {
+        self.get() == other.get()
+    }
+}
+
+impl<T> Default for Heap<*mut T>
+    where *mut T: GCMethods + Copy
+{
+    fn default() -> Heap<*mut T> {
+        Heap {
+            ptr: UnsafeCell::new(ptr::null_mut())
+        }
+    }
+}
+
+impl Default for Heap<JS::Value> {
+    fn default() -> Heap<JS::Value> {
+        Heap {
+            ptr: UnsafeCell::new(JS::Value::default())
+        }
+    }
+}
+
+impl<T: GCMethods + Copy> Drop for Heap<T> {
+    fn drop(&mut self) {
+        unsafe {
+            let prev = self.ptr.get();
+            T::post_barrier(prev, *prev, T::initial());
+        }
+    }
+}
+
+// Creates a C string literal `$str`.
+macro_rules! c_str {
+    ($str:expr) => {
+        concat!($str, "\0").as_ptr() as *const ::std::os::raw::c_char
+    }
+}
+
+unsafe impl Trace for Heap<*mut JSFunction> {
+    unsafe fn trace(&self, trc: *mut JSTracer) {
+        glue::CallFunctionTracer(trc, self as *const _ as *mut Self, c_str!("function"));
+    }
+}
+
+unsafe impl Trace for Heap<*mut JSObject> {
+    unsafe fn trace(&self, trc: *mut JSTracer) {
+        glue::CallObjectTracer(trc, self as *const _ as *mut Self, c_str!("object"));
+    }
+}
+
+unsafe impl Trace for Heap<*mut JSScript> {
+    unsafe fn trace(&self, trc: *mut JSTracer) {
+        glue::CallScriptTracer(trc, self as *const _ as *mut Self, c_str!("script"));
+    }
+}
+
+unsafe impl Trace for Heap<*mut JSString> {
+    unsafe fn trace(&self, trc: *mut JSTracer) {
+        glue::CallStringTracer(trc, self as *const _ as *mut Self, c_str!("string"));
+    }
+}
+
+unsafe impl Trace for Heap<JS::Value> {
+    unsafe fn trace(&self, trc: *mut JSTracer) {
+        glue::CallValueTracer(trc, self as *const _ as *mut Self, c_str!("value"));
+    }
+}
+
+unsafe impl Trace for Heap<jsid> {
+    unsafe fn trace(&self, trc: *mut JSTracer) {
+        glue::CallIdTracer(trc, self as *const _ as *mut Self, c_str!("id"));
+    }
+}
+
+// This is measured properly by the heap measurement implemented in
+// SpiderMonkey.
+impl<T: Copy + GCMethods> HeapSizeOf for Heap<T> {
+    fn heap_size_of_children(&self) -> usize {
+        0
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/jsapi.rs
@@ -0,0 +1,5 @@
+#[cfg(feature = "debugmozjs")]
+include!(concat!(env!("OUT_DIR"), "/jsapi_debug.rs"));
+
+#[cfg(not(feature = "debugmozjs"))]
+include!(concat!(env!("OUT_DIR"), "/jsapi.rs"));
new file mode 100644
--- /dev/null
+++ b/js/rust/src/jsglue.cpp
@@ -0,0 +1,940 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+#include "js-config.h"
+
+#ifdef JS_DEBUG
+// A hack for MFBT. Guard objects need this to work.
+#define DEBUG 1
+#endif
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/Proxy.h"
+#include "js/Class.h"
+#include "jswrapper.h"
+#include "js/MemoryMetrics.h"
+#include "js/Principals.h"
+#include "assert.h"
+
+struct ProxyTraps {
+    bool (*enter)(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
+                  js::BaseProxyHandler::Action action, bool *bp);
+
+    bool (*getOwnPropertyDescriptor)(JSContext *cx, JS::HandleObject proxy,
+                                     JS::HandleId id,
+                                     JS::MutableHandle<JS::PropertyDescriptor> desc);
+    bool (*defineProperty)(JSContext *cx, JS::HandleObject proxy,
+                           JS::HandleId id,
+                           JS::Handle<JS::PropertyDescriptor> desc,
+                           JS::ObjectOpResult &result);
+    bool (*ownPropertyKeys)(JSContext *cx, JS::HandleObject proxy,
+                            JS::AutoIdVector &props);
+    bool (*delete_)(JSContext *cx, JS::HandleObject proxy,
+                    JS::HandleId id, JS::ObjectOpResult &result);
+
+    JSObject* (*enumerate)(JSContext *cx, JS::HandleObject proxy);
+
+    bool (*getPrototypeIfOrdinary)(JSContext *cx, JS::HandleObject proxy,
+                                   bool *isOrdinary, JS::MutableHandleObject protop);
+    // getPrototype
+    // setPrototype
+    // setImmutablePrototype
+
+    bool (*preventExtensions)(JSContext *cx, JS::HandleObject proxy,
+                              JS::ObjectOpResult &result);
+
+    bool (*isExtensible)(JSContext *cx, JS::HandleObject proxy, bool *succeeded);
+
+    bool (*has)(JSContext *cx, JS::HandleObject proxy,
+                JS::HandleId id, bool *bp);
+    bool (*get)(JSContext *cx, JS::HandleObject proxy, JS::HandleValue receiver,
+                JS::HandleId id, JS::MutableHandleValue vp);
+    bool (*set)(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
+                JS::HandleValue v, JS::HandleValue receiver,
+                JS::ObjectOpResult &result);
+
+    bool (*call)(JSContext *cx, JS::HandleObject proxy,
+                 const JS::CallArgs &args);
+    bool (*construct)(JSContext *cx, JS::HandleObject proxy,
+                      const JS::CallArgs &args);
+
+    bool (*getPropertyDescriptor)(JSContext *cx, JS::HandleObject proxy,
+                                  JS::HandleId id,
+                                  JS::MutableHandle<JS::PropertyDescriptor> desc);
+    bool (*hasOwn)(JSContext *cx, JS::HandleObject proxy,
+                   JS::HandleId id, bool *bp);
+    bool (*getOwnEnumerablePropertyKeys)(JSContext *cx, JS::HandleObject proxy,
+                                         JS::AutoIdVector &props);
+    bool (*nativeCall)(JSContext *cx, JS::IsAcceptableThis test,
+                       JS::NativeImpl impl, JS::CallArgs args);
+    bool (*hasInstance)(JSContext *cx, JS::HandleObject proxy,
+                        JS::MutableHandleValue v, bool *bp);
+    bool (*objectClassIs)(JS::HandleObject obj, js::ESClass classValue,
+                          JSContext *cx);
+    const char *(*className)(JSContext *cx, JS::HandleObject proxy);
+    JSString* (*fun_toString)(JSContext *cx, JS::HandleObject proxy,
+                              bool isToString);
+    //bool (*regexp_toShared)(JSContext *cx, JS::HandleObject proxy, RegExpGuard *g);
+    bool (*boxedValue_unbox)(JSContext *cx, JS::HandleObject proxy,
+                             JS::MutableHandleValue vp);
+    bool (*defaultValue)(JSContext *cx, JS::HandleObject obj, JSType hint, JS::MutableHandleValue vp);
+    void (*trace)(JSTracer *trc, JSObject *proxy);
+    void (*finalize)(JSFreeOp *fop, JSObject *proxy);
+    void (*objectMoved)(JSObject *proxy, const JSObject *old);
+
+    bool (*isCallable)(JSObject *obj);
+    bool (*isConstructor)(JSObject *obj);
+
+    // watch
+    // unwatch
+    // getElements
+
+    // weakmapKeyDelegate
+    // isScripted
+};
+
+static int HandlerFamily;
+
+#define DEFER_TO_TRAP_OR_BASE_CLASS(_base)                                      \
+                                                                                \
+    /* Standard internal methods. */                                            \
+    virtual JSObject* enumerate(JSContext *cx,                                  \
+                                JS::HandleObject proxy) const override          \
+    {                                                                           \
+        return mTraps.enumerate                                                 \
+               ? mTraps.enumerate(cx, proxy)                                    \
+               : _base::enumerate(cx, proxy);                                   \
+    }                                                                           \
+                                                                                \
+    virtual bool has(JSContext* cx, JS::HandleObject proxy,                     \
+                     JS::HandleId id, bool *bp) const override                  \
+    {                                                                           \
+        return mTraps.has                                                       \
+               ? mTraps.has(cx, proxy, id, bp)                                  \
+               : _base::has(cx, proxy, id, bp);                                 \
+    }                                                                           \
+                                                                                \
+    virtual bool get(JSContext* cx, JS::HandleObject proxy,                     \
+                     JS::HandleValue receiver,                                  \
+                     JS::HandleId id, JS::MutableHandleValue vp) const override \
+    {                                                                           \
+        return mTraps.get                                                       \
+               ? mTraps.get(cx, proxy, receiver, id, vp)                        \
+               : _base::get(cx, proxy, receiver, id, vp);                       \
+    }                                                                           \
+                                                                                \
+    virtual bool set(JSContext* cx, JS::HandleObject proxy,                     \
+                     JS::HandleId id, JS::HandleValue v,                        \
+                     JS::HandleValue receiver,                                  \
+                     JS::ObjectOpResult &result) const override                 \
+    {                                                                           \
+        return mTraps.set                                                       \
+               ? mTraps.set(cx, proxy, id, v, receiver, result)                 \
+               : _base::set(cx, proxy, id, v, receiver, result);                \
+    }                                                                           \
+                                                                                \
+    virtual bool call(JSContext* cx, JS::HandleObject proxy,                    \
+                      const JS::CallArgs &args) const override                  \
+    {                                                                           \
+        return mTraps.call                                                      \
+               ? mTraps.call(cx, proxy, args)                                   \
+               : _base::call(cx, proxy, args);                                  \
+    }                                                                           \
+                                                                                \
+    virtual bool construct(JSContext* cx, JS::HandleObject proxy,               \
+                           const JS::CallArgs &args) const override             \
+    {                                                                           \
+        return mTraps.construct                                                 \
+               ? mTraps.construct(cx, proxy, args)                              \
+               : _base::construct(cx, proxy, args);                             \
+    }                                                                           \
+                                                                                \
+    /* Spidermonkey extensions. */                                              \
+    virtual bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, \
+                        bool* bp) const override                                \
+    {                                                                           \
+        return mTraps.hasOwn                                                    \
+               ? mTraps.hasOwn(cx, proxy, id, bp)                               \
+               : _base::hasOwn(cx, proxy, id, bp);                              \
+    }                                                                           \
+                                                                                \
+    virtual bool getOwnEnumerablePropertyKeys(JSContext* cx,                    \
+                                              JS::HandleObject proxy,           \
+                                              JS::AutoIdVector &props) const override \
+    {                                                                           \
+        return mTraps.getOwnEnumerablePropertyKeys                              \
+               ? mTraps.getOwnEnumerablePropertyKeys(cx, proxy, props)          \
+               : _base::getOwnEnumerablePropertyKeys(cx, proxy, props);         \
+    }                                                                           \
+                                                                                \
+    virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test,           \
+                            JS::NativeImpl impl,                                \
+                            const JS::CallArgs& args) const override            \
+    {                                                                           \
+        return mTraps.nativeCall                                                \
+               ? mTraps.nativeCall(cx, test, impl, args)                        \
+               : _base::nativeCall(cx, test, impl, args);                       \
+    }                                                                           \
+                                                                                \
+    virtual bool hasInstance(JSContext* cx, JS::HandleObject proxy,             \
+                             JS::MutableHandleValue v, bool* bp) const override \
+    {                                                                           \
+        return mTraps.hasInstance                                               \
+               ? mTraps.hasInstance(cx, proxy, v, bp)                           \
+               : _base::hasInstance(cx, proxy, v, bp);                          \
+    }                                                                           \
+                                                                                \
+    virtual const char *className(JSContext *cx, JS::HandleObject proxy) const override\
+    {                                                                           \
+        return mTraps.className                                                 \
+               ? mTraps.className(cx, proxy)                                    \
+               : _base::className(cx, proxy);                                   \
+    }                                                                           \
+                                                                                \
+    virtual JSString* fun_toString(JSContext* cx, JS::HandleObject proxy,       \
+                                   bool isToString) const override              \
+    {                                                                           \
+        return mTraps.fun_toString                                              \
+               ? mTraps.fun_toString(cx, proxy, isToString)                     \
+               : _base::fun_toString(cx, proxy, isToString);                    \
+    }                                                                           \
+                                                                                \
+    virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy,        \
+                                  JS::MutableHandleValue vp) const override     \
+    {                                                                           \
+        return mTraps.boxedValue_unbox                                          \
+               ? mTraps.boxedValue_unbox(cx, proxy, vp)                         \
+               : _base::boxedValue_unbox(cx, proxy, vp);                        \
+    }                                                                           \
+                                                                                \
+    virtual void trace(JSTracer* trc, JSObject* proxy) const override           \
+    {                                                                           \
+        mTraps.trace                                                            \
+        ? mTraps.trace(trc, proxy)                                              \
+        : _base::trace(trc, proxy);                                             \
+    }                                                                           \
+                                                                                \
+    virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override        \
+    {                                                                           \
+        mTraps.finalize                                                         \
+        ? mTraps.finalize(fop, proxy)                                           \
+        : _base::finalize(fop, proxy);                                          \
+    }                                                                           \
+                                                                                \
+    virtual void objectMoved(JSObject* proxy,                                   \
+                             const JSObject *old) const override                \
+    {                                                                           \
+        mTraps.objectMoved                                                      \
+        ? mTraps.objectMoved(proxy, old)                                        \
+        : _base::objectMoved(proxy, old);                                       \
+    }                                                                           \
+                                                                                \
+    virtual bool isCallable(JSObject* obj) const override                       \
+    {                                                                           \
+        return mTraps.isCallable                                                \
+               ? mTraps.isCallable(obj)                                         \
+               : _base::isCallable(obj);                                        \
+    }                                                                           \
+                                                                                \
+    virtual bool isConstructor(JSObject* obj) const override                    \
+    {                                                                           \
+        return mTraps.isConstructor                                             \
+               ? mTraps.isConstructor(obj)                                      \
+               : _base::isConstructor(obj);                                     \
+    }
+
+class WrapperProxyHandler : public js::Wrapper
+{
+    ProxyTraps mTraps;
+  public:
+    WrapperProxyHandler(const ProxyTraps& aTraps)
+    : js::Wrapper(0), mTraps(aTraps) {}
+
+    virtual bool finalizeInBackground(const JS::Value& priv) const override
+    {
+        return false;
+    }
+
+    DEFER_TO_TRAP_OR_BASE_CLASS(js::Wrapper)
+
+    virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy,
+                                          JS::HandleId id,
+                                          JS::MutableHandle<JS::PropertyDescriptor> desc) const override
+    {
+        return mTraps.getOwnPropertyDescriptor
+               ? mTraps.getOwnPropertyDescriptor(cx, proxy, id, desc)
+               : js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
+    }
+
+    virtual bool defineProperty(JSContext *cx,
+                                JS::HandleObject proxy, JS::HandleId id,
+                                JS::Handle<JS::PropertyDescriptor> desc,
+                                JS::ObjectOpResult &result) const override
+    {
+        return mTraps.defineProperty
+               ? mTraps.defineProperty(cx, proxy, id, desc, result)
+               : js::Wrapper::defineProperty(cx, proxy, id, desc, result);
+    }
+
+    virtual bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy,
+                                 JS::AutoIdVector &props) const override
+    {
+        return mTraps.ownPropertyKeys
+               ? mTraps.ownPropertyKeys(cx, proxy, props)
+               : js::Wrapper::ownPropertyKeys(cx, proxy, props);
+    }
+
+    virtual bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
+                         JS::ObjectOpResult &result) const override
+    {
+        return mTraps.delete_
+               ? mTraps.delete_(cx, proxy, id, result)
+               : js::Wrapper::delete_(cx, proxy, id, result);
+    }
+
+    virtual bool preventExtensions(JSContext *cx, JS::HandleObject proxy,
+                                   JS::ObjectOpResult &result) const override
+    {
+        return mTraps.preventExtensions
+               ? mTraps.preventExtensions(cx, proxy, result)
+               : js::Wrapper::preventExtensions(cx, proxy, result);
+    }
+
+    virtual bool isExtensible(JSContext *cx, JS::HandleObject proxy,
+                              bool *succeeded) const override
+    {
+        return mTraps.isExtensible
+               ? mTraps.isExtensible(cx, proxy, succeeded)
+               : js::Wrapper::isExtensible(cx, proxy, succeeded);
+    }
+
+    virtual bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy,
+                                       JS::HandleId id,
+                                       JS::MutableHandle<JS::PropertyDescriptor> desc) const override
+    {
+        return mTraps.getPropertyDescriptor
+               ? mTraps.getPropertyDescriptor(cx, proxy, id, desc)
+               : js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc);
+    }
+};
+
+class RustJSPrincipal : public JSPrincipals
+{
+    const void* origin; //box with origin in it
+    void (*destroyCallback)(JSPrincipals *principal);
+    bool (*writeCallback)(JSContext* cx, JSStructuredCloneWriter* writer);
+
+  public:
+    RustJSPrincipal(const void* origin,
+                     void (*destroy)(JSPrincipals *principal),
+                     bool (*write)(JSContext* cx, JSStructuredCloneWriter* writer))
+    : JSPrincipals() {
+      this->origin = origin;
+      this->destroyCallback = destroy;
+      this->writeCallback = write;
+    }
+
+    virtual const void* getOrigin() {
+      return origin;
+    }
+
+    virtual void destroy() {
+      if(this->destroyCallback)
+        this->destroyCallback(this);
+    }
+
+    bool write(JSContext* cx, JSStructuredCloneWriter* writer) {
+      return this->writeCallback
+             ? this->writeCallback(cx, writer)
+             : false;
+    }
+};
+
+class ForwardingProxyHandler : public js::BaseProxyHandler
+{
+    ProxyTraps mTraps;
+    const void* mExtra;
+  public:
+    ForwardingProxyHandler(const ProxyTraps& aTraps, const void* aExtra)
+    : js::BaseProxyHandler(&HandlerFamily), mTraps(aTraps), mExtra(aExtra) {}
+
+    const void* getExtra() const {
+        return mExtra;
+    }
+
+    virtual bool finalizeInBackground(const JS::Value& priv) const override
+    {
+        return false;
+    }
+
+    DEFER_TO_TRAP_OR_BASE_CLASS(BaseProxyHandler)
+
+    virtual bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy,
+                                          JS::HandleId id,
+                                          JS::MutableHandle<JS::PropertyDescriptor> desc) const override
+    {
+        return mTraps.getOwnPropertyDescriptor(cx, proxy, id, desc);
+    }
+
+    virtual bool defineProperty(JSContext *cx,
+                                JS::HandleObject proxy, JS::HandleId id,
+                                JS::Handle<JS::PropertyDescriptor> desc,
+                                JS::ObjectOpResult &result) const override
+    {
+        return mTraps.defineProperty(cx, proxy, id, desc, result);
+    }
+
+    virtual bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy,
+                                 JS::AutoIdVector &props) const override
+    {
+        return mTraps.ownPropertyKeys(cx, proxy, props);
+    }
+
+    virtual bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
+                         JS::ObjectOpResult &result) const override
+    {
+        return mTraps.delete_(cx, proxy, id, result);
+    }
+
+    virtual bool getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject proxy,
+                                        bool* isOrdinary,
+                                        JS::MutableHandleObject protop) const override
+    {
+        return mTraps.getPrototypeIfOrdinary(cx, proxy, isOrdinary, protop);
+    }
+
+    virtual bool preventExtensions(JSContext *cx, JS::HandleObject proxy,
+                                   JS::ObjectOpResult &result) const override
+    {
+        return mTraps.preventExtensions(cx, proxy, result);
+    }
+
+    virtual bool isExtensible(JSContext *cx, JS::HandleObject proxy,
+                              bool *succeeded) const override
+    {
+        return mTraps.isExtensible(cx, proxy, succeeded);
+    }
+
+    virtual bool getPropertyDescriptor(JSContext *cx, JS::HandleObject proxy,
+                                       JS::HandleId id,
+                                       JS::MutableHandle<JS::PropertyDescriptor> desc) const override
+    {
+        return mTraps.getPropertyDescriptor(cx, proxy, id, desc);
+    }
+};
+
+extern "C" {
+
+JSPrincipals*
+CreateRustJSPrincipal(const void* origin,
+                       void (*destroy)(JSPrincipals *principal),
+                       bool (*write)(JSContext* cx, JSStructuredCloneWriter *writer)){
+  return new RustJSPrincipal(origin, destroy, write);
+}
+
+const void*
+GetPrincipalOrigin(JSPrincipals* principal) {
+  return static_cast<RustJSPrincipal*>(principal)->getOrigin();
+}
+
+bool
+InvokeGetOwnPropertyDescriptor(
+        const void *handler,
+        JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
+        JS::MutableHandle<JS::PropertyDescriptor> desc)
+{
+    return static_cast<const ForwardingProxyHandler*>(handler)->
+        getOwnPropertyDescriptor(cx, proxy, id, desc);
+}
+
+bool
+InvokeHasOwn(
+       const void *handler,
+       JSContext *cx, JS::HandleObject proxy,
+       JS::HandleId id, bool *bp)
+{
+    return static_cast<const js::BaseProxyHandler*>(handler)->
+        hasOwn(cx, proxy, id, bp);
+}
+
+JS::Value
+RUST_JS_NumberValue(double d)
+{
+    return JS_NumberValue(d);
+}
+
+const JSJitInfo*
+RUST_FUNCTION_VALUE_TO_JITINFO(JS::Value v)
+{
+    return FUNCTION_VALUE_TO_JITINFO(v);
+}
+
+JS::CallArgs
+CreateCallArgsFromVp(unsigned argc, JS::Value* vp)
+{
+    return JS::CallArgsFromVp(argc, vp);
+}
+
+bool
+CallJitGetterOp(const JSJitInfo* info, JSContext* cx,
+                JS::HandleObject thisObj, void* specializedThis,
+                unsigned argc, JS::Value* vp)
+{
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    return info->getter(cx, thisObj, specializedThis, JSJitGetterCallArgs(args));
+}
+
+bool
+CallJitSetterOp(const JSJitInfo* info, JSContext* cx,
+                JS::HandleObject thisObj, void* specializedThis,
+                unsigned argc, JS::Value* vp)
+{
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    return info->setter(cx, thisObj, specializedThis, JSJitSetterCallArgs(args));
+}
+
+bool
+CallJitMethodOp(const JSJitInfo* info, JSContext* cx,
+                JS::HandleObject thisObj, void* specializedThis,
+                uint32_t argc, JS::Value* vp)
+{
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    return info->method(cx, thisObj, specializedThis, JSJitMethodCallArgs(args));
+}
+
+const void*
+CreateProxyHandler(const ProxyTraps* aTraps, const void* aExtra)
+{
+    return new ForwardingProxyHandler(*aTraps, aExtra);
+}
+
+const void*
+CreateWrapperProxyHandler(const ProxyTraps* aTraps)
+{
+    return new WrapperProxyHandler(*aTraps);
+}
+
+const void*
+GetCrossCompartmentWrapper()
+{
+    return &js::CrossCompartmentWrapper::singleton;
+}
+
+const void*
+GetSecurityWrapper()
+{
+  return &js::CrossCompartmentSecurityWrapper::singleton;
+}
+
+JS::ReadOnlyCompileOptions*
+NewCompileOptions(JSContext* aCx, const char* aFile, unsigned aLine)
+{
+    JS::OwningCompileOptions *opts = new JS::OwningCompileOptions(aCx);
+    opts->setFileAndLine(aCx, aFile, aLine);
+    opts->setVersion(JSVERSION_DEFAULT);
+    return opts;
+}
+
+void
+DeleteCompileOptions(JS::ReadOnlyCompileOptions *aOpts)
+{
+    delete static_cast<JS::OwningCompileOptions *>(aOpts);
+}
+
+JSObject*
+NewProxyObject(JSContext* aCx, const void* aHandler, JS::HandleValue aPriv,
+               JSObject* proto, JSObject* parent, JSObject* call,
+               JSObject* construct)
+{
+    js::ProxyOptions options;
+    return js::NewProxyObject(aCx, (js::BaseProxyHandler*)aHandler, aPriv, proto,
+                              options);
+}
+
+JSObject*
+WrapperNew(JSContext* aCx, JS::HandleObject aObj, const void* aHandler,
+           const JSClass* aClass, bool aSingleton)
+{
+    js::WrapperOptions options;
+    if (aClass) {
+        options.setClass(js::Valueify(aClass));
+    }
+    options.setSingleton(aSingleton);
+    return js::Wrapper::New(aCx, aObj, (const js::Wrapper*)aHandler, options);
+}
+
+void WindowProxyObjectMoved(JSObject*, const JSObject*)
+{
+    abort();
+}
+
+static const js::ClassExtension WindowProxyClassExtension = PROXY_MAKE_EXT(
+    WindowProxyObjectMoved
+);
+
+const js::Class WindowProxyClass = PROXY_CLASS_WITH_EXT(
+    "Proxy",
+    JSCLASS_HAS_RESERVED_SLOTS(1), /* additional class flags */
+    &WindowProxyClassExtension);
+
+const js::Class*
+GetWindowProxyClass()
+{
+    return &WindowProxyClass;
+}
+
+JS::Value
+GetProxyReservedSlot(JSObject* obj, uint32_t slot)
+{
+    return js::GetProxyReservedSlot(obj, slot);
+}
+
+void
+SetProxyReservedSlot(JSObject* obj, uint32_t slot, const JS::Value* val)
+{
+    js::SetProxyReservedSlot(obj, slot, *val);
+}
+
+JSObject*
+NewWindowProxy(JSContext* aCx, JS::HandleObject aObj, const void* aHandler)
+{
+    return WrapperNew(aCx, aObj, aHandler, Jsvalify(&WindowProxyClass), true);
+}
+
+JS::Value
+GetProxyPrivate(JSObject* obj)
+{
+    return js::GetProxyPrivate(obj);
+}
+
+void
+SetProxyPrivate(JSObject* obj, const JS::Value* expando)
+{
+    js::SetProxyPrivate(obj, *expando);
+}
+
+bool
+RUST_JSID_IS_INT(JS::HandleId id)
+{
+    return JSID_IS_INT(id);
+}
+
+jsid
+int_to_jsid(int32_t i)
+{
+    return INT_TO_JSID(i);
+}
+
+int32_t
+RUST_JSID_TO_INT(JS::HandleId id)
+{
+    return JSID_TO_INT(id);
+}
+
+bool
+RUST_JSID_IS_STRING(JS::HandleId id)
+{
+    return JSID_IS_STRING(id);
+}
+
+JSString*
+RUST_JSID_TO_STRING(JS::HandleId id)
+{
+    return JSID_TO_STRING(id);
+}
+
+jsid
+RUST_SYMBOL_TO_JSID(JS::Symbol* sym)
+{
+    return SYMBOL_TO_JSID(sym);
+}
+
+void
+RUST_SET_JITINFO(JSFunction* func, const JSJitInfo* info) {
+    SET_JITINFO(func, info);
+}
+
+jsid
+RUST_INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str) {
+    return INTERNED_STRING_TO_JSID(cx, str);
+}
+
+const JSErrorFormatString*
+RUST_js_GetErrorMessage(void* userRef, uint32_t errorNumber)
+{
+    return js::GetErrorMessage(userRef, errorNumber);
+}
+
+bool
+IsProxyHandlerFamily(JSObject* obj)
+{
+    auto family = js::GetProxyHandler(obj)->family();
+    return family == &HandlerFamily;
+}
+
+const void*
+GetProxyHandlerFamily()
+{
+    return &HandlerFamily;
+}
+
+const void*
+GetProxyHandlerExtra(JSObject* obj)
+{
+    const js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
+    assert(handler->family() == &HandlerFamily);
+    return static_cast<const ForwardingProxyHandler*>(handler)->getExtra();
+}
+
+const void*
+GetProxyHandler(JSObject* obj)
+{
+    const js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
+    assert(handler->family() == &HandlerFamily);
+    return handler;
+}
+
+void
+ReportError(JSContext* aCx, const char* aError)
+{
+#ifdef DEBUG
+    for (const char* p = aError; *p; ++p) {
+        assert(*p != '%');
+    }
+#endif
+    JS_ReportErrorASCII(aCx, "%s", aError);
+}
+
+bool
+IsWrapper(JSObject* obj)
+{
+    return js::IsWrapper(obj);
+}
+
+JSObject*
+UnwrapObject(JSObject* obj, bool stopAtOuter)
+{
+    return js::CheckedUnwrap(obj, stopAtOuter);
+}
+
+JSObject*
+UncheckedUnwrapObject(JSObject* obj, bool stopAtOuter)
+{
+    return js::UncheckedUnwrap(obj, stopAtOuter);
+}
+
+JS::AutoIdVector*
+CreateAutoIdVector(JSContext* cx)
+{
+    return new JS::AutoIdVector(cx);
+}
+
+bool
+AppendToAutoIdVector(JS::AutoIdVector* v, jsid id)
+{
+    return v->append(id);
+}
+
+const jsid*
+SliceAutoIdVector(const JS::AutoIdVector* v, size_t* length)
+{
+    *length = v->length();
+    return v->begin();
+}
+
+void
+DestroyAutoIdVector(JS::AutoIdVector* v)
+{
+    delete v;
+}
+
+JS::AutoObjectVector*
+CreateAutoObjectVector(JSContext* aCx)
+{
+    JS::AutoObjectVector* vec = new JS::AutoObjectVector(aCx);
+    return vec;
+}
+
+bool
+AppendToAutoObjectVector(JS::AutoObjectVector* v, JSObject* obj)
+{
+    return v->append(obj);
+}
+
+void
+DeleteAutoObjectVector(JS::AutoObjectVector* v)
+{
+    delete v;
+}
+
+#if defined(__linux__)
+ #include <malloc.h>
+#elif defined(__APPLE__)
+ #include <malloc/malloc.h>
+#elif defined(__MINGW32__) || defined(__MINGW64__)
+ // nothing needed here
+#elif defined(_MSC_VER)
+ // nothing needed here
+#else
+ #error "unsupported platform"
+#endif
+
+// SpiderMonkey-in-Rust currently uses system malloc, not jemalloc.
+static size_t MallocSizeOf(const void* aPtr)
+{
+#if defined(__linux__)
+    return malloc_usable_size((void*)aPtr);
+#elif defined(__APPLE__)
+    return malloc_size((void*)aPtr);
+#elif defined(__MINGW32__) || defined(__MINGW64__)
+    return _msize((void*)aPtr);
+#elif defined(_MSC_VER)
+    return _msize((void*)aPtr);
+#else
+    #error "unsupported platform"
+#endif
+}
+
+bool
+CollectServoSizes(JSContext* cx, JS::ServoSizes *sizes)
+{
+    mozilla::PodZero(sizes);
+    return JS::AddServoSizeOf(cx, MallocSizeOf,
+                              /* ObjectPrivateVisitor = */ nullptr, sizes);
+}
+
+void
+CallValueTracer(JSTracer* trc, JS::Heap<JS::Value>* valuep, const char* name)
+{
+    JS::TraceEdge(trc, valuep, name);
+}
+
+void
+CallIdTracer(JSTracer* trc, JS::Heap<jsid>* idp, const char* name)
+{
+    JS::TraceEdge(trc, idp, name);
+}
+
+void
+CallObjectTracer(JSTracer* trc, JS::Heap<JSObject*>* objp, const char* name)
+{
+    JS::TraceEdge(trc, objp, name);
+}
+
+void
+CallStringTracer(JSTracer* trc, JS::Heap<JSString*>* strp, const char* name)
+{
+    JS::TraceEdge(trc, strp, name);
+}
+
+void
+CallScriptTracer(JSTracer* trc, JS::Heap<JSScript*>* scriptp, const char* name)
+{
+    JS::TraceEdge(trc, scriptp, name);
+}
+
+void
+CallFunctionTracer(JSTracer* trc, JS::Heap<JSFunction*>* funp, const char* name)
+{
+    JS::TraceEdge(trc, funp, name);
+}
+
+void
+CallUnbarrieredObjectTracer(JSTracer* trc, JSObject** objp, const char* name)
+{
+    js::UnsafeTraceManuallyBarrieredEdge(trc, objp, name);
+}
+
+bool
+IsDebugBuild()
+{
+#ifdef JS_DEBUG
+    return true;
+#else
+    return false;
+#endif
+}
+
+#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type)                         \
+    void                                                                       \
+    Get ## Type ## ArrayLengthAndData(JSObject* obj, uint32_t* length,         \
+                                      bool* isSharedMemory, type** data)       \
+    {                                                                          \
+        js::Get ## Type ## ArrayLengthAndData(obj, length, isSharedMemory,     \
+                                              data);                           \
+    }
+
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double)
+
+#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR
+
+JSAutoStructuredCloneBuffer*
+NewJSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope,
+                               const JSStructuredCloneCallbacks* callbacks)
+{
+    return js_new<JSAutoStructuredCloneBuffer>(scope, callbacks, nullptr);
+}
+
+JSStructuredCloneData*
+DeleteJSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer* buf)
+{
+    js_delete(buf);
+}
+
+size_t
+GetLengthOfJSStructuredCloneData(JSStructuredCloneData* data)
+{
+    assert(data != nullptr);
+
+    size_t len = 0;
+
+    auto iter = data->Iter();
+    while (!iter.Done()) {
+        size_t len_of_this_segment = iter.RemainingInSegment();
+        len += len_of_this_segment;
+        iter.Advance(*data, len_of_this_segment);
+    }
+
+    return len;
+}
+
+void
+CopyJSStructuredCloneData(JSStructuredCloneData* src, uint8_t* dest)
+{
+    assert(src != nullptr);
+    assert(dest != nullptr);
+
+    size_t bytes_copied = 0;
+
+    auto iter = src->Iter();
+    while (!iter.Done()) {
+        size_t len_of_this_segment = iter.RemainingInSegment();
+        memcpy(dest + bytes_copied, iter.Data(), len_of_this_segment);
+        bytes_copied += len_of_this_segment;
+        iter.Advance(*src, len_of_this_segment);
+    }
+}
+
+bool
+WriteBytesToJSStructuredCloneData(const uint8_t* src, size_t len, JSStructuredCloneData* dest)
+{
+    assert(src != nullptr);
+    assert(dest != nullptr);
+
+    return dest->WriteBytes(reinterpret_cast<const char*>(src), len);
+}
+
+} // extern "C"
new file mode 100644
--- /dev/null
+++ b/js/rust/src/jsval.rs
@@ -0,0 +1,549 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+use jsapi::root::*;
+use libc::c_void;
+use std::mem;
+
+#[cfg(target_pointer_width = "64")]
+const JSVAL_TAG_SHIFT: usize = 47;
+
+#[cfg(target_pointer_width = "64")]
+const JSVAL_TAG_MAX_DOUBLE: u32 = 0x1FFF0u32;
+
+#[cfg(target_pointer_width = "32")]
+const JSVAL_TAG_CLEAR: u32 = 0xFFFFFF80;
+
+#[cfg(target_pointer_width = "64")]
+#[repr(u32)]
+#[allow(dead_code)]
+#[derive(Clone, Copy, Debug)]
+enum ValueTag {
+    INT32     = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_INT32 as u32),
+    UNDEFINED = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
+    STRING    = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_STRING as u32),
+    SYMBOL    = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
+    BOOLEAN   = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
+    MAGIC     = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_MAGIC as u32),
+    NULL      = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_NULL as u32),
+    OBJECT    = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_OBJECT as u32),
+}
+
+#[cfg(target_pointer_width = "32")]
+#[repr(u32)]
+#[allow(dead_code)]
+#[derive(Clone, Copy, Debug)]
+enum ValueTag {
+    PRIVATE   = 0,
+    INT32     = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_INT32 as u32),
+    UNDEFINED = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
+    STRING    = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_STRING as u32),
+    SYMBOL    = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
+    BOOLEAN   = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
+    MAGIC     = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_MAGIC as u32),
+    NULL      = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_NULL as u32),
+    OBJECT    = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_OBJECT as u32),
+}
+
+#[cfg(target_pointer_width = "64")]
+#[repr(u64)]
+#[allow(dead_code)]
+#[derive(Clone, Copy, Debug)]
+enum ValueShiftedTag {
+    MAX_DOUBLE = (((JSVAL_TAG_MAX_DOUBLE as u64) << JSVAL_TAG_SHIFT) | 0xFFFFFFFFu64),
+    INT32      = ((ValueTag::INT32 as u64)      << JSVAL_TAG_SHIFT),
+    UNDEFINED  = ((ValueTag::UNDEFINED as u64)  << JSVAL_TAG_SHIFT),
+    STRING     = ((ValueTag::STRING as u64)     << JSVAL_TAG_SHIFT),
+    SYMBOL     = ((ValueTag::SYMBOL as u64)     << JSVAL_TAG_SHIFT),
+    BOOLEAN    = ((ValueTag::BOOLEAN as u64)    << JSVAL_TAG_SHIFT),
+    MAGIC      = ((ValueTag::MAGIC as u64)      << JSVAL_TAG_SHIFT),
+    NULL       = ((ValueTag::NULL as u64)       << JSVAL_TAG_SHIFT),
+    OBJECT     = ((ValueTag::OBJECT as u64)     << JSVAL_TAG_SHIFT),
+}
+
+
+#[cfg(target_pointer_width = "64")]
+const JSVAL_PAYLOAD_MASK: u64 = 0x00007FFFFFFFFFFF;
+
+#[inline(always)]
+fn AsJSVal(val: u64) -> JS::Value {
+    JS::Value {
+        data: JS::Value_layout {
+            asBits: val,
+        }
+    }
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+fn BuildJSVal(tag: ValueTag, payload: u64) -> JS::Value {
+    AsJSVal(((tag as u32 as u64) << JSVAL_TAG_SHIFT) | payload)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+fn BuildJSVal(tag: ValueTag, payload: u64) -> JS::Value {
+    AsJSVal(((tag as u32 as u64) << 32) | payload)
+}
+
+#[inline(always)]
+pub fn NullValue() -> JS::Value {
+    BuildJSVal(ValueTag::NULL, 0)
+}
+
+#[inline(always)]
+pub fn UndefinedValue() -> JS::Value {
+    BuildJSVal(ValueTag::UNDEFINED, 0)
+}
+
+#[inline(always)]
+pub fn Int32Value(i: i32) -> JS::Value {
+    BuildJSVal(ValueTag::INT32, i as u32 as u64)
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+pub fn DoubleValue(f: f64) -> JS::Value {
+    let bits: u64 = unsafe { mem::transmute(f) };
+    assert!(bits <= ValueShiftedTag::MAX_DOUBLE as u64);
+    AsJSVal(bits)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn DoubleValue(f: f64) -> JS::Value {
+    let bits: u64 = unsafe { mem::transmute(f) };
+    let val = AsJSVal(bits);
+    assert!(val.is_double());
+    val
+}
+
+#[inline(always)]
+pub fn UInt32Value(ui: u32) -> JS::Value {
+    if ui > 0x7fffffff {
+        DoubleValue(ui as f64)
+    } else {
+        Int32Value(ui as i32)
+    }
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+pub fn StringValue(s: &JSString) -> JS::Value {
+    let bits = s as *const JSString as usize as u64;
+    assert!((bits >> JSVAL_TAG_SHIFT) == 0);
+    BuildJSVal(ValueTag::STRING, bits)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn StringValue(s: &JSString) -> JS::Value {
+    let bits = s as *const JSString as usize as u64;
+    BuildJSVal(ValueTag::STRING, bits)
+}
+
+#[inline(always)]
+pub fn BooleanValue(b: bool) -> JS::Value {
+    BuildJSVal(ValueTag::BOOLEAN, b as u64)
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+pub fn ObjectValue(o: *mut JSObject) -> JS::Value {
+    let bits = o as usize as u64;
+    assert!((bits >> JSVAL_TAG_SHIFT) == 0);
+    BuildJSVal(ValueTag::OBJECT, bits)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn ObjectValue(o: *mut JSObject) -> JS::Value {
+    let bits = o as usize as u64;
+    BuildJSVal(ValueTag::OBJECT, bits)
+}
+
+#[inline(always)]
+pub fn ObjectOrNullValue(o: *mut JSObject) -> JS::Value {
+    if o.is_null() {
+        NullValue()
+    } else {
+        ObjectValue(o)
+    }
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+pub fn PrivateValue(o: *const c_void) -> JS::Value {
+    let ptrBits = o as usize as u64;
+    assert!((ptrBits & 1) == 0);
+    AsJSVal(ptrBits >> 1)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn PrivateValue(o: *const c_void) -> JS::Value {
+    let ptrBits = o as usize as u64;
+    assert!((ptrBits & 1) == 0);
+    BuildJSVal(ValueTag::PRIVATE, ptrBits)
+}
+
+impl JS::Value {
+    #[inline(always)]
+    unsafe fn asBits(&self) -> u64 {
+        self.data.asBits
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_undefined(&self) -> bool {
+        unsafe {
+            self.asBits() == ValueShiftedTag::UNDEFINED as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_undefined(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) == ValueTag::UNDEFINED as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_null(&self) -> bool {
+        unsafe {
+            self.asBits() == ValueShiftedTag::NULL as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_null(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) == ValueTag::NULL as u64
+        }
+    }
+
+    #[inline(always)]
+    pub fn is_null_or_undefined(&self) -> bool {
+        self.is_null() || self.is_undefined()
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_boolean(&self) -> bool {
+        unsafe {
+            (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::BOOLEAN as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_boolean(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) == ValueTag::BOOLEAN as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_int32(&self) -> bool {
+        unsafe {
+            (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::INT32 as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_int32(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) == ValueTag::INT32 as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_double(&self) -> bool {
+        unsafe {
+            self.asBits() <= ValueShiftedTag::MAX_DOUBLE as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_double(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) <= JSVAL_TAG_CLEAR as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_number(&self) -> bool {
+        const JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET: u64 = ValueShiftedTag::UNDEFINED as u64;
+        unsafe {
+            self.asBits() < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_number(&self) -> bool {
+        const JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET: u64 = ValueTag::INT32 as u64;
+        unsafe {
+            (self.asBits() >> 32) <= JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_primitive(&self) -> bool {
+        const JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET: u64 = ValueShiftedTag::OBJECT as u64;
+        unsafe {
+            self.asBits() < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_primitive(&self) -> bool {
+        const JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET: u64 = ValueTag::OBJECT as u64;
+        unsafe {
+            (self.asBits() >> 32) < JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_string(&self) -> bool {
+        unsafe {
+            (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::STRING as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_string(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) == ValueTag::STRING as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_object(&self) -> bool {
+        unsafe {
+            assert!((self.asBits() >> JSVAL_TAG_SHIFT) <= ValueTag::OBJECT as u64);
+            self.asBits() >= ValueShiftedTag::OBJECT as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_object(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) == ValueTag::OBJECT as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_symbol(&self) -> bool {
+        unsafe {
+            self.asBits() == ValueShiftedTag::SYMBOL as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_symbol(&self) -> bool {
+        unsafe {
+            (self.asBits() >> 32) == ValueTag::SYMBOL as u64
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn to_boolean(&self) -> bool {
+        assert!(self.is_boolean());
+        unsafe {
+            (self.asBits() & JSVAL_PAYLOAD_MASK) != 0
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn to_boolean(&self) -> bool {
+        assert!(self.is_boolean());
+        unsafe {
+            (self.asBits() & 0x00000000FFFFFFFF) != 0
+        }
+    }
+
+    #[inline(always)]
+    pub fn to_int32(&self) -> i32 {
+        assert!(self.is_int32());
+        unsafe {
+            (self.asBits() & 0x00000000FFFFFFFF) as i32
+        }
+    }
+
+    #[inline(always)]
+    pub fn to_double(&self) -> f64 {
+        assert!(self.is_double());
+        unsafe { mem::transmute(self.asBits()) }
+    }
+
+    #[inline(always)]
+    pub fn to_number(&self) -> f64 {
+        assert!(self.is_number());
+        if self.is_double() {
+            self.to_double()
+        } else {
+            self.to_int32() as f64
+        }
+    }
+
+    #[inline(always)]
+    pub fn to_object(&self) -> *mut JSObject {
+        assert!(self.is_object());
+        self.to_object_or_null()
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn to_string(&self) -> *mut JSString {
+        assert!(self.is_string());
+        unsafe {
+            let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
+            ptrBits as usize as *mut JSString
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn to_string(&self) -> *mut JSString {
+        assert!(self.is_string());
+        unsafe {
+            let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
+            ptrBits as *mut JSString
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_object_or_null(&self) -> bool {
+        const JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET: u64 = ValueShiftedTag::NULL as u64;
+        unsafe {
+            assert!((self.asBits() >> JSVAL_TAG_SHIFT) <= ValueTag::OBJECT as u64);
+            self.asBits() >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_object_or_null(&self) -> bool {
+        const JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET: u64 = ValueTag::NULL as u64;
+        unsafe {
+            assert!((self.asBits() >> 32) <= ValueTag::OBJECT as u64);
+            (self.asBits() >> 32) >= JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn to_object_or_null(&self) -> *mut JSObject {
+        assert!(self.is_object_or_null());
+        unsafe {
+            let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
+            assert!((ptrBits & 0x7) == 0);
+            ptrBits as usize as *mut JSObject
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn to_object_or_null(&self) -> *mut JSObject {
+        assert!(self.is_object_or_null());
+        unsafe {
+            let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
+            ptrBits as *mut JSObject
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn to_private(&self) -> *const c_void {
+        assert!(self.is_double());
+        unsafe {
+            assert!((self.asBits() & 0x8000000000000000u64) == 0);
+            (self.asBits() << 1) as usize as *const c_void
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn to_private(&self) -> *const c_void {
+        unsafe {
+            let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
+            ptrBits as *const c_void
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn is_gcthing(&self) -> bool {
+        const JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET: u64 = ValueShiftedTag::STRING as u64;
+        unsafe {
+            self.asBits() >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn is_gcthing(&self) -> bool {
+        const JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET: u64 = ValueTag::STRING as u64;
+        unsafe {
+            (self.asBits() >> 32) >= JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "64")]
+    pub fn to_gcthing(&self) -> *mut c_void {
+        assert!(self.is_gcthing());
+        unsafe {
+            let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
+            assert!((ptrBits & 0x7) == 0);
+            ptrBits as *mut c_void
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(target_pointer_width = "32")]
+    pub fn to_gcthing(&self) -> *mut c_void {
+        assert!(self.is_gcthing());
+        unsafe {
+            let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
+            ptrBits as *mut c_void
+        }
+    }
+
+    #[inline(always)]
+    pub fn is_markable(&self) -> bool {
+        self.is_gcthing() && !self.is_null()
+    }
+
+    #[inline(always)]
+    pub fn trace_kind(&self) -> JS::TraceKind {
+        assert!(self.is_markable());
+        if self.is_object() {
+            JS::TraceKind::Object
+        } else {
+            JS::TraceKind::String
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/lib.rs
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![crate_name = "js"]
+#![crate_type = "rlib"]
+
+#![cfg_attr(feature = "nonzero", feature(nonzero))]
+
+#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case, improper_ctypes)]
+
+#[cfg(feature = "nonzero")]
+extern crate core;
+#[macro_use]
+extern crate heapsize;
+#[macro_use]
+extern crate lazy_static;
+extern crate libc;
+#[macro_use]
+extern crate log;
+#[allow(unused_extern_crates)]
+extern crate mozjs_sys;
+extern crate num_traits;
+
+#[macro_use]
+pub mod rust;
+
+pub mod ac;
+pub mod conversions;
+pub mod error;
+pub mod glue;
+pub mod heap;
+pub mod jsval;
+pub mod panic;
+pub mod sc;
+pub mod typedarray;
+
+pub mod jsapi;
+use self::jsapi::root::*;
+
+#[inline(always)]
+pub unsafe fn JS_ARGV(_cx: *mut JSContext, vp: *mut JS::Value) -> *mut JS::Value {
+    vp.offset(2)
+}
+
+known_heap_size!(0, JS::Value);
+
+impl JS::ObjectOpResult {
+    /// Set this ObjectOpResult to true and return true.
+    pub fn succeed(&mut self) -> bool {
+        self.code_ = JS::ObjectOpResult_SpecialCodes::OkCode as usize;
+        true
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/panic.rs
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::any::Any;
+use std::cell::RefCell;
+use std::panic::{UnwindSafe, catch_unwind, resume_unwind};
+
+thread_local!(static PANIC_RESULT: RefCell<Option<Box<Any + Send>>> = RefCell::new(None));
+
+/// If there is a pending panic, resume unwinding.
+pub fn maybe_resume_unwind() {
+    if let Some(error) = PANIC_RESULT.with(|result| result.borrow_mut().take()) {
+        resume_unwind(error);
+    }
+}
+
+/// Generic wrapper for JS engine callbacks panic-catching
+pub fn wrap_panic<F, R>(function: F, generic_return_type: R) -> R
+    where F: FnOnce() -> R + UnwindSafe
+{
+    let result = catch_unwind(function);
+    match result {
+        Ok(result) => result,
+        Err(error) => {
+            PANIC_RESULT.with(|result| {
+                assert!(result.borrow().is_none());
+                *result.borrow_mut() = Some(error);
+            });
+            generic_return_type
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/rust.rs
@@ -0,0 +1,1120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Rust wrappers around the raw JS apis
+
+use ac::AutoCompartment;
+use libc::c_uint;
+use heapsize::HeapSizeOf;
+use std::cell::{Cell, UnsafeCell};
+use std::char;
+use std::ffi;
+use std::ptr;
+use std::slice;
+use std::mem;
+use std::u32;
+use std::default::Default;
+use std::marker;
+use std::ops::{Deref, DerefMut};
+use std::sync::{Once, ONCE_INIT};
+use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
+use std::thread;
+use jsapi::root::*;
+use jsval::{self, UndefinedValue};
+use glue::{CreateAutoObjectVector, CreateCallArgsFromVp, AppendToAutoObjectVector, DeleteAutoObjectVector, IsDebugBuild};
+use glue::{CreateAutoIdVector, SliceAutoIdVector, DestroyAutoIdVector};
+use glue::{NewCompileOptions, DeleteCompileOptions};
+use panic;
+
+const DEFAULT_HEAPSIZE: u32 = 32_u32 * 1024_u32 * 1024_u32;
+
+// From Gecko:
+// Our "default" stack is what we use in configurations where we don't have a compelling reason to
+// do things differently. This is effectively 1MB on 64-bit platforms.
+const STACK_QUOTA: usize = 128 * 8 * 1024;
+
+// From Gecko:
+// The JS engine permits us to set different stack limits for system code,
+// trusted script, and untrusted script. We have tests that ensure that
+// we can always execute 10 "heavy" (eval+with) stack frames deeper in
+// privileged code. Our stack sizes vary greatly in different configurations,
+// so satisfying those tests requires some care. Manual measurements of the
+// number of heavy stack frames achievable gives us the following rough data,
+// ordered by the effective categories in which they are grouped in the
+// JS_SetNativeStackQuota call (which predates this analysis).
+//
+// (NB: These numbers may have drifted recently - see bug 938429)
+// OSX 64-bit Debug: 7MB stack, 636 stack frames => ~11.3k per stack frame
+// OSX64 Opt: 7MB stack, 2440 stack frames => ~3k per stack frame
+//
+// Linux 32-bit Debug: 2MB stack, 426 stack frames => ~4.8k per stack frame
+// Linux 64-bit Debug: 4MB stack, 455 stack frames => ~9.0k per stack frame
+//
+// Windows (Opt+Debug): 900K stack, 235 stack frames => ~3.4k per stack frame
+//
+// Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame
+// Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame
+//
+// We tune the trusted/untrusted quotas for each configuration to achieve our
+// invariants while attempting to minimize overhead. In contrast, our buffer
+// between system code and trusted script is a very unscientific 10k.
+const SYSTEM_CODE_BUFFER: usize = 10 * 1024;
+
+// Gecko's value on 64-bit.
+const TRUSTED_SCRIPT_BUFFER: usize = 8 * 12800;
+
+trait ToResult {
+    fn to_result(self) -> Result<(), ()>;
+}
+
+impl ToResult for bool {
+    fn to_result(self) -> Result<(), ()> {
+        if self {
+            Ok(())
+        } else {
+            Err(())
+        }
+    }
+}
+
+// ___________________________________________________________________________
+// friendly Rustic API to runtimes
+
+thread_local!(static CONTEXT: Cell<*mut JSContext> = Cell::new(ptr::null_mut()));
+
+lazy_static! {
+    static ref OUTSTANDING_RUNTIMES: AtomicUsize = AtomicUsize::new(0);
+    static ref SHUT_DOWN: AtomicBool = AtomicBool::new(false);
+}
+
+/// A wrapper for the `JSContext` structure in SpiderMonkey.
+pub struct Runtime {
+    cx: *mut JSContext,
+}
+
+impl Runtime {
+    /// Get the `JSContext` for this thread.
+    pub fn get() -> *mut JSContext {
+        let cx = CONTEXT.with(|context| {
+            context.get()
+        });
+        assert!(!cx.is_null());
+        cx
+    }
+
+    /// Creates a new `JSContext`.
+    pub fn new() -> Result<Runtime, ()> {
+        if SHUT_DOWN.load(Ordering::SeqCst) {
+            return Err(());
+        }
+
+        OUTSTANDING_RUNTIMES.fetch_add(1, Ordering::SeqCst);
+
+        unsafe {
+            #[derive(Debug)]
+            struct Parent(UnsafeCell<*mut JSContext>);
+            unsafe impl ::std::marker::Sync for Parent {}
+
+            impl Parent {
+                fn set(&self, val: *mut JSContext) {
+                    assert!(self.get().is_null());
+                    self.as_atomic().store(val, Ordering::SeqCst);
+                    assert_eq!(self.get(), val);
+                }
+
+                fn get(&self) -> *mut JSContext {
+                    self.as_atomic().load(Ordering::SeqCst)
+                }
+
+                fn as_atomic(&self) -> &AtomicPtr<JSContext> {
+                    unsafe {
+                        mem::transmute(&self.0)
+                    }
+                }
+            }
+
+            lazy_static! {
+                static ref PARENT: Parent = Parent(UnsafeCell::new(0 as *mut _));
+            }
+            static ONCE: Once = ONCE_INIT;
+
+            ONCE.call_once(|| {
+                // There is a 1:1 relationship between threads and JSContexts,
+                // so we must spawn a new thread for the parent context.
+                let _ = thread::spawn(move || {
+                    let is_debug_mozjs = cfg!(feature = "debugmozjs");
+                    let diagnostic = JS::detail::InitWithFailureDiagnostic(is_debug_mozjs);
+                    if !diagnostic.is_null() {
+                        panic!("JS::detail::InitWithFailureDiagnostic failed: {}",
+                               ffi::CStr::from_ptr(diagnostic).to_string_lossy());
+                    }
+
+                    let context = JS_NewContext(
+                        DEFAULT_HEAPSIZE, ChunkSize as u32, ptr::null_mut());
+                    assert!(!context.is_null());
+                    JS::InitSelfHostedCode(context);
+                    PARENT.set(context);
+
+                    loop {
+                        thread::sleep(::std::time::Duration::new(::std::u64::MAX, 0));
+                    }
+                });
+
+                while PARENT.get().is_null() {
+                    thread::yield_now();
+                }
+            });
+
+            assert_eq!(IsDebugBuild(), cfg!(feature = "debugmozjs"));
+
+            let js_context = JS_NewContext(DEFAULT_HEAPSIZE,
+                                           ChunkSize as u32,
+                                           JS_GetParentRuntime(PARENT.get()));
+            assert!(!js_context.is_null());
+
+            // Unconstrain the runtime's threshold on nominal heap size, to avoid
+            // triggering GC too often if operating continuously near an arbitrary
+            // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
+            // still in effect to cause periodical, and we hope hygienic,
+            // last-ditch GCs from within the GC's allocator.
+            JS_SetGCParameter(
+                js_context, JSGCParamKey::JSGC_MAX_BYTES, u32::MAX);
+
+            JS_SetNativeStackQuota(
+                js_context,
+                STACK_QUOTA,
+                STACK_QUOTA - SYSTEM_CODE_BUFFER,
+                STACK_QUOTA - SYSTEM_CODE_BUFFER - TRUSTED_SCRIPT_BUFFER);
+
+            let opts = JS::ContextOptionsRef(js_context);
+            (*opts).set_baseline_(true);
+            (*opts).set_ion_(true);
+            (*opts).set_nativeRegExp_(true);
+
+            CONTEXT.with(|context| {
+                assert!(context.get().is_null());
+                context.set(js_context);
+            });
+
+            JS::InitSelfHostedCode(js_context);
+
+            JS::SetWarningReporter(js_context, Some(report_warning));
+
+            JS_BeginRequest(js_context);
+
+            Ok(Runtime {
+                cx: js_context,
+            })
+        }
+    }
+
+    /// Returns the underlying `JSContext` object.
+    pub fn cx(&self) -> *mut JSContext {
+        self.cx
+    }
+
+    /// Returns the underlying `JSContext`'s `JSRuntime`.
+    pub fn rt(&self) -> *mut JSRuntime {
+        unsafe {
+            JS_GetRuntime(self.cx)
+        }
+    }
+
+    pub fn evaluate_script(&self, glob: JS::HandleObject, script: &str, filename: &str,
+                           line_num: u32, rval: JS::MutableHandleValue)
+                    -> Result<(),()> {
+        let script_utf16: Vec<u16> = script.encode_utf16().collect();
+        let filename_cstr = ffi::CString::new(filename.as_bytes()).unwrap();
+        debug!("Evaluating script from {} with content {}", filename, script);
+        // SpiderMonkey does not approve of null pointers.
+        let (ptr, len) = if script_utf16.len() == 0 {
+            static empty: &'static [u16] = &[];
+            (empty.as_ptr(), 0)
+        } else {
+            (script_utf16.as_ptr(), script_utf16.len() as c_uint)
+        };
+        assert!(!ptr.is_null());
+        unsafe {
+            let _ac = AutoCompartment::with_obj(self.cx(), glob.get());
+            let options = CompileOptionsWrapper::new(self.cx(), filename_cstr.as_ptr(), line_num);
+
+            if !JS::Evaluate2(self.cx(), options.ptr, ptr as *const u16, len as _, rval) {
+                debug!("...err!");
+                panic::maybe_resume_unwind();
+                Err(())
+            } else {
+                // we could return the script result but then we'd have
+                // to root it and so forth and, really, who cares?
+                debug!("...ok!");
+                Ok(())
+            }
+        }
+    }
+}
+
+impl Drop for Runtime {
+    fn drop(&mut self) {
+        unsafe {
+            JS_EndRequest(self.cx);
+            JS_DestroyContext(self.cx);
+
+            CONTEXT.with(|context| {
+                assert_eq!(context.get(), self.cx);
+                context.set(ptr::null_mut());
+            });
+
+            if OUTSTANDING_RUNTIMES.fetch_sub(1, Ordering::SeqCst) == 1 {
+                SHUT_DOWN.store(true, Ordering::SeqCst);
+                JS_ShutDown();
+            }
+        }
+    }
+}
+
+// This is measured through `glue::CollectServoSizes`.
+impl HeapSizeOf for Runtime {
+    fn heap_size_of_children(&self) -> usize {
+        0
+    }
+}
+
+// ___________________________________________________________________________
+// Rooting API for standard JS things
+
+pub trait RootKind {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind;
+}
+
+impl RootKind for *mut JSObject {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::Object }
+}
+
+impl RootKind for *mut JSFlatString {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::String }
+}
+
+impl RootKind for *mut JSFunction {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::Object }
+}
+
+impl RootKind for *mut JSString {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::String }
+}
+
+impl RootKind for *mut JS::Symbol {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::Symbol }
+}
+
+impl RootKind for *mut JSScript {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::Script }
+}
+
+impl RootKind for jsid {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::Id }
+}
+
+impl RootKind for JS::Value {
+    #[inline(always)]
+    fn rootKind() -> JS::RootKind { JS::RootKind::Value }
+}
+
+impl<T> JS::Rooted<T> {
+    pub fn new_unrooted() -> JS::Rooted<T>
+        where T: GCMethods,
+    {
+        JS::Rooted {
+            stack: ptr::null_mut(),
+            prev: ptr::null_mut(),
+            ptr: unsafe { T::initial() },
+            _phantom_0: marker::PhantomData,
+        }
+    }
+
+    unsafe fn get_rooting_context(cx: *mut JSContext) -> *mut JS::RootingContext {
+        mem::transmute(cx)
+    }
+
+    unsafe fn get_root_stack(cx: *mut JSContext)
+                             -> *mut *mut JS::Rooted<*mut ::std::os::raw::c_void>
+        where T: RootKind
+    {
+        let kind = T::rootKind() as usize;
+        let rooting_cx = Self::get_rooting_context(cx);
+        &mut rooting_cx.as_mut().unwrap().stackRoots_[kind] as *mut _ as *mut _
+    }
+
+    pub unsafe fn register_with_root_lists(&mut self, cx: *mut JSContext)
+        where T: RootKind
+    {
+        self.stack = Self::get_root_stack(cx);
+        let stack = self.stack.as_mut().unwrap();
+        self.prev = *stack as *mut _;
+
+        *stack = self as *mut _ as usize as _;
+    }
+
+    pub unsafe fn remove_from_root_stack(&mut self) {
+        assert!(*self.stack == self as *mut _ as usize as _);
+        *self.stack = self.prev;
+    }
+}
+
+/// Rust API for keeping a JS::Rooted value in the context's root stack.
+/// Example usage: `rooted!(in(cx) let x = UndefinedValue());`.
+/// `RootedGuard::new` also works, but the macro is preferred.
+pub struct RootedGuard<'a, T: 'a + RootKind + GCMethods> {
+    root: &'a mut JS::Rooted<T>
+}
+
+impl<'a, T: 'a + RootKind + GCMethods> RootedGuard<'a, T> {
+    pub fn new(cx: *mut JSContext, root: &'a mut JS::Rooted<T>, initial: T) -> Self {
+        root.ptr = initial;
+        unsafe {
+            root.register_with_root_lists(cx);
+        }
+        RootedGuard {
+            root: root
+        }
+    }
+
+    pub fn handle(&self) -> JS::Handle<T> {
+        unsafe {
+            JS::Handle::from_marked_location(&self.root.ptr)
+        }
+    }
+
+    pub fn handle_mut(&mut self) -> JS::MutableHandle<T> {
+        unsafe {
+            JS::MutableHandle::from_marked_location(&mut self.root.ptr)
+        }
+    }
+
+    pub fn get(&self) -> T where T: Copy {
+        self.root.ptr
+    }
+
+    pub fn set(&mut self, v: T) {
+        self.root.ptr = v;
+    }
+}
+
+impl<'a, T: 'a + RootKind + GCMethods> Deref for RootedGuard<'a, T> {
+    type Target = T;
+    fn deref(&self) -> &T {
+        &self.root.ptr
+    }
+}
+
+impl<'a, T: 'a + RootKind + GCMethods> DerefMut for RootedGuard<'a, T> {
+    fn deref_mut(&mut self) -> &mut T {
+        &mut self.root.ptr
+    }
+}
+
+impl<'a, T: 'a + RootKind + GCMethods> Drop for RootedGuard<'a, T> {
+    fn drop(&mut self) {
+        unsafe {
+            self.root.ptr = T::initial();
+            self.root.remove_from_root_stack();
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! rooted {
+    (in($cx:expr) let $name:ident = $init:expr) => {
+        let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
+        let $name = $crate::rust::RootedGuard::new($cx, &mut __root, $init);
+    };
+    (in($cx:expr) let mut $name:ident = $init:expr) => {
+        let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
+        let mut $name = $crate::rust::RootedGuard::new($cx, &mut __root, $init);
+    }
+}
+
+impl<T> JS::Handle<T> {
+    pub fn get(&self) -> T
+        where T: Copy
+    {
+        unsafe { *self.ptr }
+    }
+
+    pub unsafe fn from_marked_location(ptr: *const T) -> JS::Handle<T> {
+        JS::Handle {
+            ptr: mem::transmute(ptr),
+            _phantom_0: marker::PhantomData,
+        }
+    }
+}
+
+impl<T> Deref for JS::Handle<T> {
+    type Target = T;
+
+    fn deref<'a>(&'a self) -> &'a T {
+        unsafe { &*self.ptr }
+    }
+}
+
+impl<T> JS::MutableHandle<T> {
+    pub unsafe fn from_marked_location(ptr: *mut T) -> JS::MutableHandle<T> {
+        JS::MutableHandle {
+            ptr: ptr,
+            _phantom_0: marker::PhantomData,
+        }
+    }
+
+    pub fn handle(&self) -> JS::Handle<T> {
+        unsafe {
+            JS::Handle::from_marked_location(self.ptr as *const _)
+        }
+    }
+
+    pub fn get(&self) -> T
+        where T: Copy
+    {
+        unsafe { *self.ptr }
+    }
+
+    pub fn set(&self, v: T)
+        where T: Copy
+    {
+        unsafe { *self.ptr = v }
+    }
+}
+
+impl<T> Deref for JS::MutableHandle<T> {
+    type Target = T;
+
+    fn deref<'a>(&'a self) -> &'a T {
+        unsafe { &*self.ptr }
+    }
+}
+
+impl<T> DerefMut for JS::MutableHandle<T> {
+    fn deref_mut<'a>(&'a mut self) -> &'a mut T {
+        unsafe { &mut *self.ptr }
+    }
+}
+
+impl JS::HandleValue {
+    pub fn null() -> JS::HandleValue {
+        unsafe {
+            JS::NullHandleValue
+        }
+    }
+
+    pub fn undefined() -> JS::HandleValue {
+        unsafe {
+            JS::UndefinedHandleValue
+        }
+    }
+}
+
+impl JS::HandleValueArray {
+    pub fn new() -> JS::HandleValueArray {
+        JS::HandleValueArray {
+            length_: 0,
+            elements_: ptr::null(),
+        }
+    }
+
+    pub unsafe fn from_rooted_slice(values: &[JS::Value]) -> JS::HandleValueArray {
+        JS::HandleValueArray {
+            length_: values.len(),
+            elements_: values.as_ptr()
+        }
+    }
+}
+
+const ConstNullValue: *mut JSObject = 0 as *mut JSObject;
+
+impl JS::HandleObject {
+    pub fn null() -> JS::HandleObject {
+        unsafe {
+            JS::HandleObject::from_marked_location(&ConstNullValue)
+        }
+    }
+}
+
+impl Default for jsid {
+    fn default() -> jsid {
+        unsafe {
+            JSID_VOID
+        }
+    }
+}
+
+impl Default for JS::Value {
+    fn default() -> JS::Value { jsval::UndefinedValue() }
+}
+
+impl Default for JS::CompartmentOptions {
+    fn default() -> Self { unsafe { ::std::mem::zeroed() } }
+}
+
+const ChunkShift: usize = 20;
+const ChunkSize: usize = 1 << ChunkShift;
+
+#[cfg(target_pointer_width = "32")]
+const ChunkLocationOffset: usize = ChunkSize - 2 * 4 - 8;
+
+pub trait GCMethods {
+    unsafe fn initial() -> Self;
+    unsafe fn post_barrier(v: *mut Self, prev: Self, next: Self);
+}
+
+impl GCMethods for jsid {
+    unsafe fn initial() -> jsid { JSID_VOID }
+    unsafe fn post_barrier(_: *mut jsid, _: jsid, _: jsid) {}
+}
+
+impl GCMethods for *mut JSObject {
+    unsafe fn initial() -> *mut JSObject { ptr::null_mut() }
+    unsafe fn post_barrier(v: *mut *mut JSObject,
+                           prev: *mut JSObject, next: *mut JSObject) {
+        JS::HeapObjectPostBarrier(v, prev, next);
+    }
+}
+
+impl GCMethods for *mut JSString {
+    unsafe fn initial() -> *mut JSString { ptr::null_mut() }
+    unsafe fn post_barrier(_: *mut *mut JSString, _: *mut JSString, _: *mut JSString) {}
+}
+
+impl GCMethods for *mut JSScript {
+    unsafe fn initial() -> *mut JSScript { ptr::null_mut() }
+    unsafe fn post_barrier(_: *mut *mut JSScript, _: *mut JSScript, _: *mut JSScript) { }
+}
+
+impl GCMethods for *mut JSFunction {
+    unsafe fn initial() -> *mut JSFunction { ptr::null_mut() }
+    unsafe fn post_barrier(v: *mut *mut JSFunction,
+                           prev: *mut JSFunction, next: *mut JSFunction) {
+        JS::HeapObjectPostBarrier(mem::transmute(v),
+                                  mem::transmute(prev),
+                                  mem::transmute(next));
+    }
+}
+
+impl GCMethods for JS::Value {
+    unsafe fn initial() -> JS::Value { UndefinedValue() }
+    unsafe fn post_barrier(v: *mut JS::Value, prev: JS::Value, next: JS::Value) {
+        JS::HeapValuePostBarrier(v, &prev, &next);
+    }
+}
+
+// ___________________________________________________________________________
+// Implementations for various things in jsapi.rs
+
+impl Drop for JSAutoCompartment {
+    fn drop(&mut self) {
+        unsafe { JS_LeaveCompartment(self.cx_, self.oldCompartment_); }
+    }
+}
+
+impl JSJitMethodCallArgs {
+    #[inline]
+    pub fn get(&self, i: u32) -> JS::HandleValue {
+        unsafe {
+            if i < self._base.argc_ {
+                JS::HandleValue::from_marked_location(self._base.argv_.offset(i as isize))
+            } else {
+                JS::UndefinedHandleValue
+            }
+        }
+    }
+
+    #[inline]
+    pub fn index(&self, i: u32) -> JS::HandleValue {
+        assert!(i < self._base.argc_);
+        unsafe {
+            JS::HandleValue::from_marked_location(self._base.argv_.offset(i as isize))
+        }
+    }
+
+    #[inline]
+    pub fn index_mut(&self, i: u32) -> JS::MutableHandleValue {
+        assert!(i < self._base.argc_);
+        unsafe {
+            JS::MutableHandleValue::from_marked_location(self._base.argv_.offset(i as isize))
+        }
+    }
+
+    #[inline]
+    pub fn rval(&self) -> JS::MutableHandleValue {
+        unsafe {
+            JS::MutableHandleValue::from_marked_location(self._base.argv_.offset(-2))
+        }
+    }
+}
+
+// XXX need to hack up bindgen to convert this better so we don't have
+//     to duplicate so much code here
+impl JS::CallArgs {
+    #[inline]
+    pub unsafe fn from_vp(vp: *mut JS::Value, argc: u32) -> JS::CallArgs {
+        CreateCallArgsFromVp(argc, vp)
+    }
+
+    #[inline]
+    pub fn index(&self, i: u32) -> JS::HandleValue {
+        assert!(i < self._base.argc_);
+        unsafe {
+            JS::HandleValue::from_marked_location(self._base.argv_.offset(i as isize))
+        }
+    }
+
+    #[inline]
+    pub fn index_mut(&self, i: u32) -> JS::MutableHandleValue {
+        assert!(i < self._base.argc_);
+        unsafe {
+            JS::MutableHandleValue::from_marked_location(self._base.argv_.offset(i as isize))
+        }
+    }
+
+    #[inline]
+    pub fn get(&self, i: u32) -> JS::HandleValue {
+        unsafe {
+            if i < self._base.argc_ {
+                JS::HandleValue::from_marked_location(self._base.argv_.offset(i as isize))
+            } else {
+                JS::UndefinedHandleValue
+            }
+        }
+    }
+
+    #[inline]
+    pub fn rval(&self) -> JS::MutableHandleValue {
+        unsafe {
+            JS::MutableHandleValue::from_marked_location(self._base.argv_.offset(-2))
+        }
+    }
+
+    #[inline]
+    pub fn thisv(&self) -> JS::HandleValue {
+        unsafe {
+            JS::HandleValue::from_marked_location(self._base.argv_.offset(-1))
+        }
+    }
+
+    #[inline]
+    pub fn calleev(&self) -> JS::HandleValue {
+        unsafe {
+            JS::HandleValue::from_marked_location(self._base.argv_.offset(-2))
+        }
+    }
+
+    #[inline]
+    pub fn callee(&self) -> *mut JSObject {
+        self.calleev().to_object()
+    }
+
+    #[inline]
+    pub fn new_target(&self) -> JS::MutableHandleValue {
+        assert!(self._base.constructing_());
+        unsafe {
+            JS::MutableHandleValue::from_marked_location(
+                self._base.argv_.offset(self._base.argc_ as isize))
+        }
+    }
+}
+
+impl JSJitGetterCallArgs {
+    #[inline]
+    pub fn rval(&self) -> JS::MutableHandleValue {
+        self._base
+    }
+}
+
+impl JSJitSetterCallArgs {
+    #[inline]
+    pub fn get(&self, i: u32) -> JS::HandleValue {
+        assert!(i == 0);
+        self._base.handle()
+    }
+}
+
+// ___________________________________________________________________________
+// Wrappers around things in jsglue.cpp
+
+pub struct AutoObjectVectorWrapper {
+    pub ptr: *mut JS::AutoObjectVector
+}
+
+impl AutoObjectVectorWrapper {
+    pub fn new(cx: *mut JSContext) -> AutoObjectVectorWrapper {
+        AutoObjectVectorWrapper {
+            ptr: unsafe {
+                 CreateAutoObjectVector(cx)
+            }
+        }
+    }
+
+    pub fn append(&self, obj: *mut JSObject) -> bool {
+        unsafe {
+            AppendToAutoObjectVector(self.ptr, obj)
+        }
+    }
+}
+
+impl Drop for AutoObjectVectorWrapper {
+    fn drop(&mut self) {
+        unsafe { DeleteAutoObjectVector(self.ptr) }
+    }
+}
+
+pub struct CompileOptionsWrapper {
+    pub ptr: *mut JS::ReadOnlyCompileOptions
+}
+
+impl CompileOptionsWrapper {
+    pub fn new(cx: *mut JSContext, file: *const ::libc::c_char, line: c_uint) -> CompileOptionsWrapper {
+        CompileOptionsWrapper {
+            ptr: unsafe { NewCompileOptions(cx, file, line) }
+        }
+    }
+}
+
+impl Drop for CompileOptionsWrapper {
+    fn drop(&mut self) {
+        unsafe { DeleteCompileOptions(self.ptr) }
+    }
+}
+
+// ___________________________________________________________________________
+// Fast inline converters
+
+#[inline]
+pub unsafe fn ToBoolean(v: JS::HandleValue) -> bool {
+    let val = *v.ptr;
+
+    if val.is_boolean() {
+        return val.to_boolean();
+    }
+
+    if val.is_int32() {
+        return val.to_int32() != 0;
+    }
+
+    if val.is_null_or_undefined() {
+        return false;
+    }
+
+    if val.is_double() {
+        let d = val.to_double();
+        return !d.is_nan() && d != 0f64;
+    }
+
+    if val.is_symbol() {
+        return true;
+    }
+
+    js::ToBooleanSlow(v)
+}
+
+#[inline]
+pub unsafe fn ToNumber(cx: *mut JSContext, v: JS::HandleValue) -> Result<f64, ()> {
+    let val = *v.ptr;
+    if val.is_number() {
+        return Ok(val.to_number());
+    }
+
+    let mut out = Default::default();
+    if js::ToNumberSlow(cx, v, &mut out) {
+        Ok(out)
+    } else {
+        Err(())
+    }
+}
+
+#[inline]
+unsafe fn convert_from_int32<T: Default + Copy>(
+    cx: *mut JSContext,
+    v: JS::HandleValue,
+    conv_fn: unsafe extern "C" fn(*mut JSContext, JS::HandleValue, *mut T) -> bool)
+        -> Result<T, ()> {
+
+    let val = *v.ptr;
+    if val.is_int32() {
+        let intval: i64 = val.to_int32() as i64;
+        // TODO: do something better here that works on big endian
+        let intval = *(&intval as *const i64 as *const T);
+        return Ok(intval);
+    }
+
+    let mut out = Default::default();
+    if conv_fn(cx, v, &mut out) {
+        Ok(out)
+    } else {
+        Err(())
+    }
+}
+
+#[inline]
+pub unsafe fn ToInt32(cx: *mut JSContext, v: JS::HandleValue) -> Result<i32, ()> {
+    convert_from_int32::<i32>(cx, v, js::ToInt32Slow)
+}
+
+#[inline]
+pub unsafe fn ToUint32(cx: *mut JSContext, v: JS::HandleValue) -> Result<u32, ()> {
+    convert_from_int32::<u32>(cx, v, js::ToUint32Slow)
+}
+
+#[inline]
+pub unsafe fn ToUint16(cx: *mut JSContext, v: JS::HandleValue) -> Result<u16, ()> {
+    convert_from_int32::<u16>(cx, v, js::ToUint16Slow)
+}
+
+#[inline]
+pub unsafe fn ToInt64(cx: *mut JSContext, v: JS::HandleValue) -> Result<i64, ()> {
+    convert_from_int32::<i64>(cx, v, js::ToInt64Slow)
+}
+
+#[inline]
+pub unsafe fn ToUint64(cx: *mut JSContext, v: JS::HandleValue) -> Result<u64, ()> {
+    convert_from_int32::<u64>(cx, v, js::ToUint64Slow)
+}
+
+#[inline]
+pub unsafe fn ToString(cx: *mut JSContext, v: JS::HandleValue) -> *mut JSString {
+    let val = *v.ptr;
+    if val.is_string() {
+        return val.to_string();
+    }
+
+    js::ToStringSlow(cx, v)
+}
+
+pub unsafe extern fn report_warning(_cx: *mut JSContext, report: *mut JSErrorReport) {
+    fn latin1_to_string(bytes: &[u8]) -> String {
+        bytes.iter().map(|c| char::from_u32(*c as u32).unwrap()).collect()
+    }
+
+    let fnptr = (*report)._base.filename;
+    let fname = if !fnptr.is_null() {
+        let c_str = ffi::CStr::from_ptr(fnptr);
+        latin1_to_string(c_str.to_bytes())
+    } else {
+        "none".to_string()
+    };
+
+    let lineno = (*report)._base.lineno;
+    let column = (*report)._base.column;
+
+    let msg_ptr = (*report)._base.message_.data_ as *const u16;
+    let msg_len = (0usize..).find(|&i| *msg_ptr.offset(i as isize) == 0).unwrap();
+    let msg_slice = slice::from_raw_parts(msg_ptr, msg_len);
+    let msg = String::from_utf16_lossy(msg_slice);
+
+    warn!("Warning at {}:{}:{}: {}\n", fname, lineno, column, msg);
+}
+
+impl JSNativeWrapper {
+    fn is_zeroed(&self) -> bool {
+        let JSNativeWrapper { op, info } = *self;
+        op.is_none() && info.is_null()
+    }
+}
+
+pub struct IdVector(*mut JS::AutoIdVector);
+
+impl IdVector {
+    pub unsafe fn new(cx: *mut JSContext) -> IdVector {
+        let vector = CreateAutoIdVector(cx);
+        assert!(!vector.is_null());
+        IdVector(vector)
+    }
+
+    pub fn get(&self) -> *mut JS::AutoIdVector {
+        self.0
+    }
+}
+
+impl Drop for IdVector {
+    fn drop(&mut self) {
+        unsafe {
+            DestroyAutoIdVector(self.0)
+        }
+    }
+}
+
+impl Deref for IdVector {
+    type Target = [jsid];
+
+    fn deref(&self) -> &[jsid] {
+        unsafe {
+            let mut length = 0;
+            let pointer = SliceAutoIdVector(self.0 as *const _, &mut length);
+            slice::from_raw_parts(pointer, length)
+        }
+    }
+}
+
+/// Defines methods on `obj`. The last entry of `methods` must contain zeroed
+/// memory.
+///
+/// # Failures
+///
+/// Returns `Err` on JSAPI failure.
+///
+/// # Panics
+///
+/// Panics if the last entry of `methods` does not contain zeroed memory.
+///
+/// # Safety
+///
+/// - `cx` must be valid.
+/// - This function calls into unaudited C++ code.
+pub unsafe fn define_methods(cx: *mut JSContext, obj: JS::HandleObject,
+                             methods: &'static [JSFunctionSpec])
+                             -> Result<(), ()> {
+    assert!({
+        match methods.last() {
+            Some(&JSFunctionSpec { name, call, nargs, flags, selfHostedName }) => {
+                name.is_null() && call.is_zeroed() && nargs == 0 && flags == 0 &&
+                selfHostedName.is_null()
+            },
+            None => false,
+        }
+    });
+
+    JS_DefineFunctions(cx, obj, methods.as_ptr()).to_result()
+}
+
+/// Defines attributes on `obj`. The last entry of `properties` must contain
+/// zeroed memory.
+///
+/// # Failures
+///
+/// Returns `Err` on JSAPI failure.
+///
+/// # Panics
+///
+/// Panics if the last entry of `properties` does not contain zeroed memory.
+///
+/// # Safety
+///
+/// - `cx` must be valid.
+/// - This function calls into unaudited C++ code.
+pub unsafe fn define_properties(cx: *mut JSContext, obj: JS::HandleObject,
+                                properties: &'static [JSPropertySpec])
+                                -> Result<(), ()> {
+    assert!(!properties.is_empty());
+    assert!({
+        let spec = properties.last().unwrap();
+        let slice = slice::from_raw_parts(spec as *const _ as *const u8,
+                                          mem::size_of::<JSPropertySpec>());
+        slice.iter().all(|byte| *byte == 0)
+    });
+
+    JS_DefineProperties(cx, obj, properties.as_ptr()).to_result()
+}
+
+static SIMPLE_GLOBAL_CLASS_OPS: JSClassOps = JSClassOps {
+    addProperty: None,
+    delProperty: None,
+    enumerate: Some(JS_EnumerateStandardClasses),
+    newEnumerate: None,
+    resolve: Some(JS_ResolveStandardClass),
+    mayResolve: Some(JS_MayResolveStandardClass),
+    finalize: None,
+    call: None,
+    hasInstance: None,
+    construct: None,
+    trace: Some(JS_GlobalObjectTraceHook),
+};
+
+/// This is a simple `JSClass` for global objects, primarily intended for tests.
+pub static SIMPLE_GLOBAL_CLASS: JSClass = JSClass {
+    name: b"Global\0" as *const u8 as *const _,
+    flags: (JSCLASS_IS_GLOBAL | ((JSCLASS_GLOBAL_SLOT_COUNT & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT)) as u32,
+    cOps: &SIMPLE_GLOBAL_CLASS_OPS as *const JSClassOps,
+    reserved: [0 as *mut _; 3]
+};
+
+#[inline]
+unsafe fn get_object_group(obj: *mut JSObject) -> *mut js::shadow::ObjectGroup {
+    assert!(!obj.is_null());
+    let obj = obj as *mut js::shadow::Object;
+    (*obj).group
+}
+
+#[inline]
+pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass {
+    (*get_object_group(obj)).clasp as *const _
+}
+
+#[inline]
+pub unsafe fn get_object_compartment(obj: *mut JSObject) -> *mut JSCompartment {
+    (*get_object_group(obj)).compartment
+}
+
+#[inline]
+pub fn is_dom_class(class: &JSClass) -> bool {
+    class.flags & JSCLASS_IS_DOMJSCLASS != 0
+}
+
+#[inline]
+pub unsafe fn is_dom_object(obj: *mut JSObject) -> bool {
+    is_dom_class(&*get_object_class(obj))
+}
+
+#[inline]
+pub unsafe fn is_global(obj: *mut JSObject) -> bool {
+    (*get_object_class(obj)).flags & JSCLASS_IS_GLOBAL != 0
+}
+
+#[inline]
+pub unsafe fn is_window(obj: *mut JSObject) -> bool {
+    is_global(obj) && js::detail::IsWindowSlow(obj)
+}
+
+#[inline]
+pub unsafe fn try_to_outerize(rval: JS::MutableHandleValue) {
+    let obj = rval.to_object();
+    if is_window(obj) {
+        let obj = js::detail::ToWindowProxyIfWindowSlow(obj);
+        assert!(!obj.is_null());
+        rval.set(jsval::ObjectValue(&mut *obj));
+    }
+}
+
+#[inline]
+pub unsafe fn maybe_wrap_object_value(cx: *mut JSContext, rval: JS::MutableHandleValue) {
+    assert!(rval.is_object());
+
+    // There used to be inline checks if this out of line call was necessary or
+    // not here, but JSAPI no longer exposes a way to get a JSContext's
+    // compartment, and additionally JSContext is under a bunch of churn in
+    // JSAPI in general right now.
+
+    assert!(JS_WrapValue(cx, rval));
+}
+
+#[inline]
+pub unsafe fn maybe_wrap_object_or_null_value(
+        cx: *mut JSContext,
+        rval: JS::MutableHandleValue) {
+    assert!(rval.is_object_or_null());
+    if !rval.is_null() {
+        maybe_wrap_object_value(cx, rval);
+    }
+}
+
+#[inline]
+pub unsafe fn maybe_wrap_value(cx: *mut JSContext, rval: JS::MutableHandleValue) {
+    if rval.is_string() {
+        assert!(JS_WrapValue(cx, rval));
+    } else if rval.is_object() {
+        maybe_wrap_object_value(cx, rval);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/sc.rs
@@ -0,0 +1,102 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Nicer, Rust-y APIs for structured cloning.
+
+use glue;
+use jsapi;
+use rust::Runtime;
+use std::ptr;
+
+/// An RAII owned buffer for structured cloning into and out of.
+pub struct StructuredCloneBuffer {
+    raw: *mut jsapi::JSAutoStructuredCloneBuffer,
+}
+
+impl StructuredCloneBuffer {
+    /// Construct a new `StructuredCloneBuffer`.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the underlying JSAPI calls fail.
+    pub fn new(scope: jsapi::JS::StructuredCloneScope,
+               callbacks: &jsapi::JSStructuredCloneCallbacks)
+               -> StructuredCloneBuffer {
+        let raw = unsafe {
+            glue::NewJSAutoStructuredCloneBuffer(scope, callbacks)
+        };
+        assert!(!raw.is_null());
+        StructuredCloneBuffer {
+            raw: raw,
+        }
+    }
+
+    /// Get the raw `*mut JSStructuredCloneData` owned by this buffer.
+    pub fn data(&self) -> *mut jsapi::JSStructuredCloneData {
+        unsafe {
+            &mut (*self.raw).data_
+        }
+    }
+
+    /// Copy this buffer's data into a vec.
+    pub fn copy_to_vec(&self) -> Vec<u8> {
+        let len = unsafe {
+            glue::GetLengthOfJSStructuredCloneData(self.data())
+        };
+        let mut vec = Vec::with_capacity(len);
+        unsafe {
+            glue::CopyJSStructuredCloneData(self.data(), vec.as_mut_ptr());
+        }
+        vec
+    }
+
+    /// Read a JS value out of this buffer.
+    pub fn read(&mut self,
+                vp: jsapi::JS::MutableHandleValue,
+                callbacks: &jsapi::JSStructuredCloneCallbacks)
+                -> Result<(), ()> {
+        if unsafe {
+            (*self.raw).read(Runtime::get(), vp, callbacks, ptr::null_mut())
+        } {
+            Ok(())
+        } else {
+            Err(())
+        }
+    }
+
+    /// Write a JS value into this buffer.
+    pub fn write(&mut self,
+                 v: jsapi::JS::HandleValue,
+                 callbacks: &jsapi::JSStructuredCloneCallbacks)
+                 -> Result<(), ()> {
+        if unsafe {
+            (*self.raw).write(Runtime::get(), v, callbacks, ptr::null_mut())
+        } {
+            Ok(())
+        } else {
+            Err(())
+        }
+    }
+
+    /// Copy the given slice into this buffer.
+    pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ()> {
+        let len = bytes.len();
+        let src = bytes.as_ptr();
+        if unsafe {
+            glue::WriteBytesToJSStructuredCloneData(src, len, self.data())
+        } {
+            Ok(())
+        } else {
+            Err(())
+        }
+    }
+}
+
+impl Drop for StructuredCloneBuffer {
+    fn drop(&mut self) {
+        unsafe {
+            glue::DeleteJSAutoStructuredCloneBuffer(self.raw);
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/src/typedarray.rs
@@ -0,0 +1,301 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! High-level, safe bindings for JS typed array APIs. Allows creating new
+//! typed arrays or wrapping existing JS reflectors, and prevents reinterpreting
+//! existing buffers as different types except in well-defined cases.
+
+use glue::GetFloat32ArrayLengthAndData;
+use glue::GetFloat64ArrayLengthAndData;
+use glue::GetInt16ArrayLengthAndData;
+use glue::GetInt32ArrayLengthAndData;
+use glue::GetInt8ArrayLengthAndData;
+use glue::GetUint16ArrayLengthAndData;
+use glue::GetUint32ArrayLengthAndData;
+use glue::GetUint8ArrayLengthAndData;
+use glue::GetUint8ClampedArrayLengthAndData;
+use jsapi::*;
+use jsapi::js::*;
+use jsapi::JS::*;
+use rust::RootedGuard;
+use std::ptr;
+use std::slice;
+
+pub enum CreateWith<'a, T: 'a> {
+    Length(u32),
+    Slice(&'a [T]),
+}
+
+/// A rooted typed array.
+pub struct TypedArray<'a, T: 'a + TypedArrayElement> {
+    object: RootedGuard<'a, *mut JSObject>,
+    computed: Option<(*mut T::Element, u32)>,
+}
+
+impl<'a, T: TypedArrayElement> TypedArray<'a, T> {
+    /// Create a typed array representation that wraps an existing JS reflector.
+    /// This operation will fail if attempted on a JS object that does not match
+    /// the expected typed array details.
+    pub fn from(cx: *mut JSContext,
+                root: &'a mut Rooted<*mut JSObject>,
+                object: *mut JSObject)
+                -> Result<Self, ()> {
+        if object.is_null() {
+            return Err(());
+        }
+        unsafe {
+            let mut guard = RootedGuard::new(cx, root, object);
+            let unwrapped = T::unwrap_array(*guard);
+            if unwrapped.is_null() {
+                return Err(());
+            }
+
+            *guard = unwrapped;
+            Ok(TypedArray {
+                object: guard,
+                computed: None,
+            })
+        }
+    }
+
+    fn data(&mut self) -> (*mut T::Element, u32) {
+        if let Some(data) = self.computed {
+            return data;
+        }
+
+        let data = unsafe { T::length_and_data(*self.object) };
+        self.computed = Some(data);
+        data
+    }
+
+    /// # Unsafety
+    ///
+    /// The returned slice can be invalidated if the underlying typed array
+    /// is neutered.
+    pub unsafe fn as_slice(&mut self) -> &[T::Element] {
+        let (pointer, length) = self.data();
+        slice::from_raw_parts(pointer as *const T::Element, length as usize)
+    }
+
+    /// # Unsafety
+    ///
+    /// The returned slice can be invalidated if the underlying typed array
+    /// is neutered.
+    ///
+    /// The underlying `JSObject` can be aliased, which can lead to
+    /// Undefined Behavior due to mutable aliasing.
+    pub unsafe fn as_mut_slice(&mut self) -> &mut [T::Element] {
+        let (pointer, length) = self.data();
+        slice::from_raw_parts_mut(pointer, length as usize)
+    }
+}
+
+impl<'a, T: TypedArrayElementCreator + TypedArrayElement> TypedArray<'a, T> {
+    /// Create a new JS typed array, optionally providing initial data that will
+    /// be copied into the newly-allocated buffer. Returns the new JS reflector.
+    pub unsafe fn create(cx: *mut JSContext,
+                         with: CreateWith<T::Element>,
+                         result: MutableHandleObject)
+                         -> Result<(), ()> {
+        let length = match with {
+            CreateWith::Length(len) => len,
+            CreateWith::Slice(slice) => slice.len() as u32,
+        };
+
+        result.set(T::create_new(cx, length));
+        if result.get().is_null() {
+            return Err(());
+        }
+
+        if let CreateWith::Slice(data) = with {
+            TypedArray::<T>::update_raw(data, result.handle());
+        }
+
+        Ok(())
+    }
+
+    ///  Update an existed JS typed array
+    pub unsafe fn update(&mut self, data: &[T::Element]) {
+        TypedArray::<T>::update_raw(data, self.object.handle());
+    }
+
+    unsafe fn update_raw(data: &[T::Element], result: HandleObject) {
+        let (buf, length) = T::length_and_data(result.get());
+        assert!(data.len() <= length as usize);
+        ptr::copy_nonoverlapping(data.as_ptr(), buf, data.len());
+    }
+}
+
+/// Internal trait used to associate an element type with an underlying representation
+/// and various functions required to manipulate typed arrays of that element type.
+pub trait TypedArrayElement {
+    /// Underlying primitive representation of this element type.
+    type Element;
+    /// Unwrap a typed array JS reflector for this element type.
+    unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject;
+    /// Retrieve the length and data of a typed array's buffer for this element type.
+    unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, u32);
+}
+
+/// Internal trait for creating new typed arrays.
+pub trait TypedArrayElementCreator: TypedArrayElement {
+    /// Create a new typed array.
+    unsafe fn create_new(cx: *mut JSContext, length: u32) -> *mut JSObject;
+    /// Get the data.
+    unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element;
+}
+
+macro_rules! typed_array_element {
+    ($t: ident,
+     $element: ty,
+     $unwrap: ident,
+     $length_and_data: ident) => (
+        /// A kind of typed array.
+        pub struct $t;
+
+        impl TypedArrayElement for $t {
+            type Element = $element;
+            unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject {
+                $unwrap(obj)
+            }
+
+            unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, u32) {
+                let mut len = 0;
+                let mut shared = false;
+                let mut data = ptr::null_mut();
+                $length_and_data(obj, &mut len, &mut shared, &mut data);
+                assert!(!shared);
+                (data, len)
+            }
+        }
+    );
+
+    ($t: ident,
+     $element: ty,
+     $unwrap: ident,
+     $length_and_data: ident,
+     $create_new: ident,
+     $get_data: ident) => (
+        typed_array_element!($t, $element, $unwrap, $length_and_data);
+
+        impl TypedArrayElementCreator for $t {
+            unsafe fn create_new(cx: *mut JSContext, length: u32) -> *mut JSObject {
+                $create_new(cx, length)
+            }
+
+            unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element {
+                let mut shared = false;
+                let data = $get_data(obj, &mut shared, ptr::null_mut());
+                assert!(!shared);
+                data
+            }
+        }
+    );
+}
+
+typed_array_element!(Uint8,
+                     u8,
+                     UnwrapUint8Array,
+                     GetUint8ArrayLengthAndData,
+                     JS_NewUint8Array,
+                     JS_GetUint8ArrayData);
+typed_array_element!(Uint16,
+                     u16,
+                     UnwrapUint16Array,
+                     GetUint16ArrayLengthAndData,
+                     JS_NewUint16Array,
+                     JS_GetUint16ArrayData);
+typed_array_element!(Uint32,
+                     u32,
+                     UnwrapUint32Array,
+                     GetUint32ArrayLengthAndData,
+                     JS_NewUint32Array,
+                     JS_GetUint32ArrayData);
+typed_array_element!(Int8,
+                     i8,
+                     UnwrapInt8Array,
+                     GetInt8ArrayLengthAndData,
+                     JS_NewInt8Array,
+                     JS_GetInt8ArrayData);
+typed_array_element!(Int16,
+                     i16,
+                     UnwrapInt16Array,
+                     GetInt16ArrayLengthAndData,
+                     JS_NewInt16Array,
+                     JS_GetInt16ArrayData);
+typed_array_element!(Int32,
+                     i32,
+                     UnwrapInt32Array,
+                     GetInt32ArrayLengthAndData,
+                     JS_NewInt32Array,
+                     JS_GetInt32ArrayData);
+typed_array_element!(Float32,
+                     f32,
+                     UnwrapFloat32Array,
+                     GetFloat32ArrayLengthAndData,
+                     JS_NewFloat32Array,
+                     JS_GetFloat32ArrayData);
+typed_array_element!(Float64,
+                     f64,
+                     UnwrapFloat64Array,
+                     GetFloat64ArrayLengthAndData,
+                     JS_NewFloat64Array,
+                     JS_GetFloat64ArrayData);
+typed_array_element!(ClampedU8,
+                     u8,
+                     UnwrapUint8ClampedArray,
+                     GetUint8ClampedArrayLengthAndData,
+                     JS_NewUint8ClampedArray,
+                     JS_GetUint8ClampedArrayData);
+typed_array_element!(ArrayBufferU8,
+                     u8,
+                     UnwrapArrayBuffer,
+                     GetArrayBufferLengthAndData,
+                     JS_NewArrayBuffer,
+                     JS_GetArrayBufferData);
+typed_array_element!(ArrayBufferViewU8,
+                     u8,
+                     UnwrapArrayBufferView,
+                     GetArrayBufferViewLengthAndData);
+
+/// The Uint8ClampedArray type.
+pub type Uint8ClampedArray<'a> = TypedArray<'a, ClampedU8>;
+/// The Uint8Array type.
+pub type Uint8Array<'a> = TypedArray<'a, Uint8>;
+/// The Int8Array type.
+pub type Int8Array<'a> = TypedArray<'a, Int8>;
+/// The Uint16Array type.
+pub type Uint16Array<'a> = TypedArray<'a, Uint16>;
+/// The Int16Array type.
+pub type Int16Array<'a> = TypedArray<'a, Int16>;
+/// The Uint32Array type.
+pub type Uint32Array<'a> = TypedArray<'a, Uint32>;
+/// The Int32Array type.
+pub type Int32Array<'a> = TypedArray<'a, Int32>;
+/// The Float32Array type.
+pub type Float32Array<'a> = TypedArray<'a, Float32>;
+/// The Float64Array type.
+pub type Float64Array<'a> = TypedArray<'a, Float64>;
+/// The ArrayBuffer type.
+pub type ArrayBuffer<'a> = TypedArray<'a, ArrayBufferU8>;
+/// The ArrayBufferView type
+pub type ArrayBufferView<'a> = TypedArray<'a, ArrayBufferViewU8>;
+
+impl<'a> ArrayBufferView<'a> {
+    pub fn get_array_type(&self) -> Scalar::Type {
+        unsafe { JS_GetArrayBufferViewType(self.object.get()) }
+    }
+}
+
+#[macro_export]
+macro_rules! typedarray {
+    (in($cx:expr) let $name:ident : $ty:ident = $init:expr) => {
+        let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
+        let $name = $crate::typedarray::$ty::from($cx, &mut __root, $init);
+    };
+    (in($cx:expr) let mut $name:ident : $ty:ident = $init:expr) => {
+        let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
+        let mut $name = $crate::typedarray::$ty::from($cx, &mut __root, $init);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/callback.rs
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_use]
+extern crate js;
+extern crate libc;
+
+use js::ac::AutoCompartment;
+use js::jsapi::root::JS::CallArgs;
+use js::jsapi::root::JS::CompartmentOptions;
+use js::jsapi::root::JSContext;
+use js::jsapi::root::JS_DefineFunction;
+use js::jsapi::root::JS_EncodeStringToUTF8;
+use js::jsapi::root::JS_NewGlobalObject;
+use js::jsapi::root::JS_ReportErrorASCII;
+use js::jsapi::root::JS::OnNewGlobalHookOption;
+use js::jsapi::root::JS::Value;
+use js::jsval::UndefinedValue;
+use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
+
+use std::ffi::CStr;
+use std::ptr;
+use std::str;
+
+#[test]
+fn callback() {
+    let runtime = Runtime::new().unwrap();
+    let context = runtime.cx();
+    let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
+    let c_option = CompartmentOptions::default();
+
+    unsafe {
+        let global = JS_NewGlobalObject(context, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(), h_option, &c_option);
+        rooted!(in(context) let global_root = global);
+        let global = global_root.handle();
+        let _ac = AutoCompartment::with_obj(context, global.get());
+        let function = JS_DefineFunction(context, global, b"puts\0".as_ptr() as *const libc::c_char,
+                                         Some(puts), 1, 0);
+        assert!(!function.is_null());
+        let javascript = "puts('Test Iñtërnâtiônàlizætiøn ┬─┬ノ( º _ ºノ) ');";
+        rooted!(in(context) let mut rval = UndefinedValue());
+        let _ = runtime.evaluate_script(global, javascript, "test.js", 0, rval.handle_mut());
+    }
+}
+
+unsafe extern "C" fn puts(context: *mut JSContext, argc: u32, vp: *mut Value) -> bool {
+    let args = CallArgs::from_vp(vp, argc);
+
+    if args._base.argc_ != 1 {
+        JS_ReportErrorASCII(context, b"puts() requires exactly 1 argument\0".as_ptr() as *const libc::c_char);
+        return false;
+    }
+
+    let arg = args.get(0);
+    let js = js::rust::ToString(context, arg);
+    rooted!(in(context) let message_root = js);
+    let message = JS_EncodeStringToUTF8(context, message_root.handle());
+    let message = CStr::from_ptr(message);
+    println!("{}", str::from_utf8(message.to_bytes()).unwrap());
+
+    args.rval().set(UndefinedValue());
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/enumerate.rs
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_use]
+extern crate js;
+
+use js::glue::RUST_JSID_IS_STRING;
+use js::glue::RUST_JSID_TO_STRING;
+use js::jsapi::root::JS::CompartmentOptions;
+use js::jsapi::root::js::GetPropertyKeys;
+use js::jsapi::root::JSITER_OWNONLY;
+use js::jsapi::root::JS_NewGlobalObject;
+use js::jsapi::root::JS_StringEqualsAscii;
+use js::jsapi::root::JS::OnNewGlobalHookOption;
+use js::jsval::UndefinedValue;
+use js::rust::IdVector;
+use js::rust::Runtime;
+use js::rust::SIMPLE_GLOBAL_CLASS;
+use std::ptr;
+
+#[test]
+fn enumerate() {
+    let rt = Runtime::new().unwrap();
+    let cx = rt.cx();
+
+    unsafe {
+        rooted!(in(cx) let global =
+            JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
+                               OnNewGlobalHookOption::FireOnNewGlobalHook,
+                               &CompartmentOptions::default())
+        );
+
+        rooted!(in(cx) let mut rval = UndefinedValue());
+        assert!(rt.evaluate_script(global.handle(), "({ 'a': 7 })",
+                                   "test", 1, rval.handle_mut()).is_ok());
+        assert!(rval.is_object());
+
+        rooted!(in(cx) let object = rval.to_object());
+        let ids = IdVector::new(cx);
+        assert!(GetPropertyKeys(cx, object.handle(), JSITER_OWNONLY, ids.get()));
+
+        assert_eq!(ids.len(), 1);
+        rooted!(in(cx) let id = ids[0]);
+
+        assert!(RUST_JSID_IS_STRING(id.handle()));
+        rooted!(in(cx) let id = RUST_JSID_TO_STRING(id.handle()));
+
+        let mut matches = false;
+        assert!(JS_StringEqualsAscii(cx,
+                                     id.get(),
+                                     b"a\0" as *const _ as *const _,
+                                     &mut matches));
+        assert!(matches);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/evaluate.rs
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_use]
+extern crate js;
+
+use js::jsapi::root::JS::CompartmentOptions;
+use js::jsapi::root::JS_NewGlobalObject;
+use js::jsapi::root::JS::OnNewGlobalHookOption;
+use js::jsval::UndefinedValue;
+use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
+
+use std::ptr;
+
+#[test]
+fn evaluate() {
+    let rt = Runtime::new().unwrap();
+    let cx = rt.cx();
+
+    unsafe {
+
+        rooted!(in(cx) let global =
+            JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
+                               OnNewGlobalHookOption::FireOnNewGlobalHook,
+                               &CompartmentOptions::default())
+        );
+        rooted!(in(cx) let mut rval = UndefinedValue());
+        assert!(rt.evaluate_script(global.handle(), "1 + 1",
+                                   "test", 1, rval.handle_mut()).is_ok());
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/panic.rs
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_use]
+extern crate js;
+
+use js::jsapi::*;
+use js::jsval::UndefinedValue;
+use js::panic::wrap_panic;
+use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
+use std::ptr;
+use std::str;
+
+#[test]
+#[should_panic]
+fn panic() {
+    let runtime = Runtime::new().unwrap();
+    let context = runtime.cx();
+    let h_option = JS::OnNewGlobalHookOption::FireOnNewGlobalHook;
+    let c_option = JS::CompartmentOptions::default();
+
+    unsafe {
+        let global = JS_NewGlobalObject(context, &SIMPLE_GLOBAL_CLASS,
+                                        ptr::null_mut(), h_option, &c_option);
+        rooted!(in(context) let global_root = global);
+        let global = global_root.handle();
+        let _ac = js::ac::AutoCompartment::with_obj(context, global.get());
+        let function = JS_DefineFunction(context, global,
+                                         b"test\0".as_ptr() as *const _,
+                                         Some(test), 0, 0);
+        assert!(!function.is_null());
+        rooted!(in(context) let mut rval = UndefinedValue());
+        let _ = runtime.evaluate_script(global, "test();", "test.js", 0,
+                                        rval.handle_mut());
+    }
+}
+
+unsafe extern "C" fn test(_cx: *mut JSContext, _argc: u32, _vp: *mut JS::Value) -> bool {
+    wrap_panic(|| {
+        panic!()
+    }, false)
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/rooting.rs
@@ -0,0 +1,83 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![cfg(feature = "debugmozjs")]
+
+#[macro_use]
+extern crate js;
+#[macro_use]
+extern crate lazy_static;
+extern crate libc;
+
+use js::jsapi::*;
+use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS, define_methods};
+use std::ptr;
+
+#[test]
+fn rooting() {
+    unsafe {
+        let runtime = Runtime::new().unwrap();
+        JS_SetGCZeal(runtime.cx(), 2, 1);
+
+        let cx = runtime.cx();
+        let h_option = JS::OnNewGlobalHookOption::FireOnNewGlobalHook;
+        let c_option = JS::CompartmentOptions::default();
+
+        rooted!(in(cx) let global = JS_NewGlobalObject(cx,
+                                                       &SIMPLE_GLOBAL_CLASS,
+                                                       ptr::null_mut(),
+                                                       h_option,
+                                                       &c_option));
+        let _ac = js::ac::AutoCompartment::with_obj(cx, global.get());
+        rooted!(in(cx) let prototype_proto = JS_GetObjectPrototype(cx, global.handle()));
+        rooted!(in(cx) let proto = JS_NewObjectWithUniqueType(cx,
+                                                              &CLASS as *const _,
+                                                              prototype_proto.handle()));
+        define_methods(cx, proto.handle(), &METHODS[..]).unwrap();
+    }
+}
+
+unsafe extern "C" fn generic_method(_: *mut JSContext, _: u32, _: *mut JS::Value) -> bool {
+    true
+}
+
+lazy_static! {
+    static ref METHODS: [JSFunctionSpec; 4] = [
+        JSFunctionSpec {
+            name: b"addEventListener\0" as *const u8 as *const libc::c_char,
+            call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() },
+            nargs: 2,
+            flags: JSPROP_ENUMERATE as u16,
+            selfHostedName: 0 as *const libc::c_char
+        },
+        JSFunctionSpec {
+            name: b"removeEventListener\0" as *const u8 as *const libc::c_char,
+            call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() },
+            nargs: 2,
+            flags: JSPROP_ENUMERATE as u16,
+            selfHostedName: 0 as *const libc::c_char
+        },
+        JSFunctionSpec {
+            name: b"dispatchEvent\0" as *const u8 as *const libc::c_char,
+            call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() },
+            nargs: 1,
+            flags: JSPROP_ENUMERATE as u16,
+            selfHostedName: 0 as *const libc::c_char
+        },
+        JSFunctionSpec {
+            name: ptr::null(),
+            call: JSNativeWrapper { op: None, info: ptr::null() },
+            nargs: 0,
+            flags: 0,
+            selfHostedName: ptr::null()
+        }
+    ];
+}
+
+static CLASS: JSClass = JSClass {
+    name: b"EventTargetPrototype\0" as *const u8 as *const libc::c_char,
+    flags: 0,
+    cOps: 0 as *const _,
+    reserved: [0 as *mut _; 3]
+};
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/runtime.rs
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_use]
+extern crate js;
+extern crate libc;
+
+use js::jsapi::*;
+use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
+use std::ptr;
+
+#[test]
+fn runtime() {
+    unsafe {
+        let runtime = Runtime::new().unwrap();
+
+        let cx = runtime.cx();
+        let h_option = JS::OnNewGlobalHookOption::FireOnNewGlobalHook;
+        let c_option = JS::CompartmentOptions::default();
+
+        rooted!(in(cx) let global = JS_NewGlobalObject(cx,
+                                                       &SIMPLE_GLOBAL_CLASS,
+                                                       ptr::null_mut(),
+                                                       h_option,
+                                                       &c_option));
+        let _ac = js::ac::AutoCompartment::with_obj(cx, global.get());
+        rooted!(in(cx) let _object = JS_NewObject(cx, &CLASS as *const _));
+    }
+
+    assert!(Runtime::new().is_err());
+}
+
+unsafe extern fn finalize(_fop: *mut JSFreeOp, _object: *mut JSObject) {
+    assert!(!Runtime::get().is_null());
+}
+
+static CLASS_OPS: JSClassOps = JSClassOps {
+    addProperty: None,
+    delProperty: None,
+    enumerate: None,
+    newEnumerate: None,
+    resolve: None,
+    mayResolve: None,
+    finalize: Some(finalize),
+    call: None,
+    hasInstance: None,
+    construct: None,
+    trace: None,
+};
+
+static CLASS: JSClass = JSClass {
+    name: b"EventTargetPrototype\0" as *const u8 as *const libc::c_char,
+    flags: 0 | JSCLASS_FOREGROUND_FINALIZE,
+    cOps: &CLASS_OPS as *const JSClassOps,
+    reserved: [0 as *mut _; 3]
+};
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/stack_limit.rs
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_use]
+extern crate js;
+
+use js::jsapi::root::JS::CompartmentOptions;
+use js::jsapi::root::JS_NewGlobalObject;
+use js::jsapi::root::JS::OnNewGlobalHookOption;
+use js::jsval::UndefinedValue;
+use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
+
+use std::ptr;
+
+#[test]
+fn stack_limit() {
+    let rt = Runtime::new().unwrap();
+    let cx = rt.cx();
+
+    unsafe {
+        let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
+        let c_option = CompartmentOptions::default();
+        let global = JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS,
+                                        ptr::null_mut(), h_option, &c_option);
+        rooted!(in(cx) let global_root = global);
+        let global = global_root.handle();
+        rooted!(in(cx) let mut rval = UndefinedValue());
+        assert!(rt.evaluate_script(global, "function f() { f.apply() } f()",
+                                   "test", 1, rval.handle_mut()).is_err());
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/typedarray.rs
@@ -0,0 +1,91 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_use]
+extern crate js;
+
+use js::jsapi::*;
+use js::jsval::UndefinedValue;
+use js::rust::Runtime as Runtime_;
+use js::rust::SIMPLE_GLOBAL_CLASS;
+use js::typedarray::{CreateWith, Uint32Array};
+use std::ptr;
+
+#[test]
+fn typedarray() {
+    let rt = Runtime_::new().unwrap();
+    let cx = rt.cx();
+
+    unsafe {
+        rooted!(in(cx) let global =
+            JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
+                               JS::OnNewGlobalHookOption::FireOnNewGlobalHook,
+                               &JS::CompartmentOptions::default())
+        );
+
+        let _ac = js::ac::AutoCompartment::with_obj(cx, global.get());
+
+        rooted!(in(cx) let mut rval = UndefinedValue());
+        assert!(rt.evaluate_script(global.handle(), "new Uint8Array([0, 2, 4])",
+                                   "test", 1, rval.handle_mut()).is_ok());
+        assert!(rval.is_object());
+
+        typedarray!(in(cx) let array: Uint8Array = rval.to_object());
+        assert_eq!(array.unwrap().as_slice(), &[0, 2, 4][..]);
+
+        typedarray!(in(cx) let array: Uint16Array = rval.to_object());
+        assert!(array.is_err());
+
+        typedarray!(in(cx) let view: ArrayBufferView = rval.to_object());
+        assert_eq!(view.unwrap().get_array_type(), js::jsapi::js::Scalar::Type::Uint8);
+
+        rooted!(in(cx) let mut rval = ptr::null_mut());
+        assert!(Uint32Array::create(cx, CreateWith::Slice(&[1, 3, 5]), rval.handle_mut()).is_ok());
+
+        typedarray!(in(cx) let array: Uint32Array = rval.get());
+        assert_eq!(array.unwrap().as_slice(), &[1, 3, 5][..]);
+
+        typedarray!(in(cx) let mut array: Uint32Array = rval.get());
+        array.as_mut().unwrap().update(&[2, 4, 6]);
+        assert_eq!(array.unwrap().as_slice(), &[2, 4, 6][..]);
+
+        rooted!(in(cx) let rval = ptr::null_mut());
+        typedarray!(in(cx) let array: Uint8Array = rval.get());
+        assert!(array.is_err());
+
+        rooted!(in(cx) let mut rval = ptr::null_mut());
+        assert!(Uint32Array::create(cx, CreateWith::Length(5), rval.handle_mut()).is_ok());
+
+        typedarray!(in(cx) let array: Uint32Array = rval.get());
+        assert_eq!(array.unwrap().as_slice(), &[0, 0, 0, 0, 0]);
+
+        typedarray!(in(cx) let mut array: Uint32Array = rval.get());
+        array.as_mut().unwrap().update(&[0, 1, 2, 3]);
+        assert_eq!(array.unwrap().as_slice(), &[0, 1, 2, 3, 0]);
+
+        typedarray!(in(cx) let view: ArrayBufferView = rval.get());
+        assert_eq!(view.unwrap().get_array_type(), js::jsapi::js::Scalar::Type::Uint32);
+    }
+}
+
+#[test]
+#[should_panic]
+fn typedarray_update_panic() {
+    let rt = Runtime_::new().unwrap();
+    let cx = rt.cx();
+
+    unsafe {
+        rooted!(in(cx) let global =
+            JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(),
+                               JS::OnNewGlobalHookOption::FireOnNewGlobalHook,
+                               &JS::CompartmentOptions::default())
+        );
+
+        let _ac = js::ac::AutoCompartment::with_obj(cx, global.get());
+        rooted!(in(cx) let mut rval = ptr::null_mut());
+        let _ = Uint32Array::create(cx, CreateWith::Slice(&[1, 2, 3, 4, 5]), rval.handle_mut());
+        typedarray!(in(cx) let mut array: Uint32Array = rval.get());
+        array.as_mut().unwrap().update(&[0, 2, 4, 6, 8, 10]);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/rust/tests/vec_conversion.rs
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[macro_use]
+extern crate js;
+
+use js::ac::AutoCompartment;
+use js::conversions::ConversionBehavior;
+use js::conversions::ConversionResult;
+use js::conversions::FromJSValConvertible;
+use js::conversions::ToJSValConvertible;
+use js::jsapi::root::JS::CompartmentOptions;
+use js::jsapi::root::JS_InitStandardClasses;
+use js::jsapi::root::JS_NewGlobalObject;
+use js::jsapi::root::JS::OnNewGlobalHookOption;
+use js::jsval::UndefinedValue;
+use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
+
+use std::ptr;
+
+fn assert_is_array(cx: *mut js::jsapi::root::JSContext,
+                   val: js::jsapi::root::JS::HandleValue) {
+    let mut is_array = false;
+    assert!(unsafe {
+        js::jsapi::root::JS_IsArrayObject(cx, val, &mut is_array as *mut _)
+    });
+    assert!(is_array);
+}
+
+#[test]
+fn vec_conversion() {
+    let rt = Runtime::new().unwrap();
+    let cx = rt.cx();
+
+    let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
+    let c_option = CompartmentOptions::default();
+
+    unsafe {
+        let global = JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS,
+                                        ptr::null_mut(), h_option, &c_option);
+        rooted!(in(cx) let global_root = global);
+        let global = global_root.handle();
+
+        let _ac = AutoCompartment::with_obj(cx, global.get());
+        assert!(JS_InitStandardClasses(cx, global));
+
+        rooted!(in(cx) let mut rval = UndefinedValue());
+
+        let orig_vec: Vec<f32> = vec![1.0, 2.9, 3.0];
+        orig_vec.to_jsval(cx, rval.handle_mut());
+        assert_is_array(cx, rval.handle());
+        let converted = Vec::<f32>::from_jsval(cx, rval.handle(), ()).unwrap();
+        assert_eq!(&orig_vec, converted.get_success_value().unwrap());
+
+        let orig_vec: Vec<i32> = vec![1, 2, 3];
+        orig_vec.to_jsval(cx, rval.handle_mut());
+        assert_is_array(cx, rval.handle());
+        let converted = Vec::<i32>::from_jsval(cx, rval.handle(),
+                                               ConversionBehavior::Default).unwrap();
+
+        assert_eq!(&orig_vec, converted.get_success_value().unwrap());
+
+        rt.evaluate_script(global, "new Set([1, 2, 3])",
+                           "test", 1, rval.handle_mut()).unwrap();
+        let converted =
+          Vec::<i32>::from_jsval(cx, rval.handle(),
+                                 ConversionBehavior::Default).unwrap();
+
+        assert_eq!(&orig_vec, converted.get_success_value().unwrap());
+
+        rt.evaluate_script(global, "({})", "test", 1, rval.handle_mut()).unwrap();
+        let converted = Vec::<i32>::from_jsval(cx, rval.handle(),
+                                               ConversionBehavior::Default);
+        assert!(match converted {
+            Ok(ConversionResult::Failure(_)) => true,
+            _ => false,
+        });
+    }
+}
--- a/js/src/Cargo.lock
+++ b/js/src/Cargo.lock
@@ -1,48 +1,55 @@
 [root]
 name = "mozjs_sys"
 version = "0.0.0"
 dependencies = [
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
- "libz-sys 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "gcc"
 version = "0.3.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "libc"
 version = "0.2.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "libz-sys"
-version = "1.0.12"
+version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "num_cpus"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "pkg-config"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "vcpkg"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [metadata]
 "checksum gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "291055c78f59ca3d84c99026c9501c469413d386bb46be1e1cf1d285cd1db3b0"
 "checksum libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)" = "38f5c2b18a287cf78b4097db62e20f43cace381dc76ae5c0a3073067f78b7ddc"
-"checksum libz-sys 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "7616099a575493da60cddc1174b686fcfb00ece89dc6f61f31ff47c35f07bbe8"
+"checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
 "checksum num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a225d1e2717567599c24f88e49f00856c6e825a12125181ee42c4257e3688d39"
 "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
+"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b"
--- a/js/src/Cargo.toml
+++ b/js/src/Cargo.toml
@@ -13,9 +13,9 @@ promises = []
 name = "mozjs_sys"
 path = "lib.rs"
 
 [build-dependencies]
 num_cpus = "1.1.0"
 
 [dependencies]
 libc = "0.2"
-libz-sys = "1.0"
+libz-sys = "1.0.16"