merge mozilla-nbound to mozilla-central
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Sat, 07 Dec 2013 12:43:55 +0100
changeset 175028 add90fdc2a74ea3647344573a8f8762f0aed69c5
parent 175005 b789f8ff7590b1642009384d0d7702919afa8bcb (current diff)
parent 175027 33e4d9281b5dd50c23e1fd849427058923dcf876 (diff)
child 175029 f9bb0f904d4eb072278bd49acb70c861a60e84db
child 175043 393f409b98ecb0b45f17c0c8cd224aa70184d2c6
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-nbound to mozilla-central
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -1248,21 +1248,17 @@ nsAccessibilityService::CreateAccessible
 
   } else if (role.EqualsLiteral("xul:listitem")) {
     accessible = new XULListitemAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:menubar")) {
     accessible = new XULMenubarAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:menulist")) {
-      if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::droppable,
-                                nsGkAtoms::_false, eCaseMatters))
-      accessible = new XULTextFieldAccessible(aContent, aDoc);
-      else
-      accessible = new XULComboboxAccessible(aContent, aDoc);
+    accessible = new XULComboboxAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:menuitem")) {
     accessible = new XULMenuitemAccessibleWrap(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:menupopup")) {
 #ifdef MOZ_ACCESSIBILITY_ATK
     // ATK considers this node to be redundant when within menubars, and it makes menu
     // navigation with assistive technologies more difficult
--- a/accessible/src/xul/XULFormControlAccessible.cpp
+++ b/accessible/src/xul/XULFormControlAccessible.cpp
@@ -688,31 +688,18 @@ XULTextFieldAccessible::NativeState()
 
   nsCOMPtr<nsIContent> inputField(GetInputField());
   NS_ENSURE_TRUE(inputField, state);
 
   // Create a temporary accessible from the HTML text field to get
   // the accessible state from. Doesn't add to cache into document cache.
   nsRefPtr<HTMLTextFieldAccessible> tempAccessible =
     new HTMLTextFieldAccessible(inputField, mDoc);
-  if (!tempAccessible)
-    return state;
-
-  state |= tempAccessible->NativeState();
-
-  nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
-  if (menuList) {
-    // <xul:menulist droppable="false">
-    if (!mContent->AttrValueIs(kNameSpaceID_None,
-                               nsGkAtoms::editable,
-                               nsGkAtoms::_true, eIgnoreCase)) {
-      state |= states::READONLY;
-    }
-  }
-
+  if (tempAccessible)
+    return state | tempAccessible->NativeState();
   return state;
 }
 
 role
 XULTextFieldAccessible::NativeRole()
 {
   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                             nsGkAtoms::password, eIgnoreCase))
@@ -825,23 +812,16 @@ XULTextFieldAccessible::FrameSelection()
 ////////////////////////////////////////////////////////////////////////////////
 // XULTextFieldAccessible protected
 
 already_AddRefed<nsIContent>
 XULTextFieldAccessible::GetInputField() const
 {
   nsCOMPtr<nsIDOMNode> inputFieldDOMNode;
   nsCOMPtr<nsIDOMXULTextBoxElement> textBox = do_QueryInterface(mContent);
-  if (textBox) {
+  if (textBox)
     textBox->GetInputField(getter_AddRefs(inputFieldDOMNode));
 
-  } else {
-    // <xul:menulist droppable="false">
-    nsCOMPtr<nsIDOMXULMenuListElement> menuList = do_QueryInterface(mContent);
-    if (menuList)
-      menuList->GetInputField(getter_AddRefs(inputFieldDOMNode));
-  }
-
   NS_ASSERTION(inputFieldDOMNode, "No input field for XULTextFieldAccessible");
 
   nsCOMPtr<nsIContent> inputField = do_QueryInterface(inputFieldDOMNode);
   return inputField.forget();
 }
--- a/browser/components/tabview/test/browser.ini
+++ b/browser/components/tabview/test/browser.ini
@@ -36,16 +36,17 @@ support-files =
 [browser_tabview_bug595965.js]
 [browser_tabview_bug596781.js]
 [browser_tabview_bug597360.js]
 [browser_tabview_bug597399.js]
 [browser_tabview_bug598375.js]
 [browser_tabview_bug598600.js]
 [browser_tabview_bug599048.js]
 [browser_tabview_bug599626.js]
+skip-if = os == 'linux' # Bug 939620, much fail, so amaze
 [browser_tabview_bug600645.js]
 [browser_tabview_bug600812.js]
 [browser_tabview_bug604098.js]
 [browser_tabview_bug606657.js]
 [browser_tabview_bug606905.js]
 [browser_tabview_bug607108.js]
 [browser_tabview_bug608037.js]
 [browser_tabview_bug608153.js]
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -80,18 +80,16 @@ support-files =
 [browser_dbg_breakpoints-contextmenu.js]
 [browser_dbg_breakpoints-disabled-reload.js]
 [browser_dbg_breakpoints-editor.js]
 [browser_dbg_breakpoints-highlight.js]
 [browser_dbg_breakpoints-new-script.js]
 [browser_dbg_breakpoints-pane.js]
 [browser_dbg_chrome-debugging.js]
 [browser_dbg_clean-exit-window.js]
-# Disabled because of leaks - See Bug 933950.
-skip-if = true
 [browser_dbg_clean-exit.js]
 [browser_dbg_closure-inspection.js]
 [browser_dbg_cmd-blackbox.js]
 [browser_dbg_cmd-break.js]
 [browser_dbg_cmd-dbg.js]
 [browser_dbg_conditional-breakpoints-01.js]
 [browser_dbg_conditional-breakpoints-02.js]
 [browser_dbg_controller-evaluate-01.js]
--- a/build/clang-plugin/Makefile.in
+++ b/build/clang-plugin/Makefile.in
@@ -10,16 +10,17 @@ VPATH		 := @srcdir@
 # Helper for end
 NULL :=
 
 CPPSRCS := \
 	clang-plugin.cpp \
 	$(NULL)
 
 TESTSRCS := \
+	TestCustomHeap.cpp \
 	TestMustOverride.cpp \
 	TestNonHeapClass.cpp \
 	TestStackClass.cpp \
 	$(NULL)
 
 OBJS := $(patsubst %.cpp,%.o,$(CPPSRCS))
 TESTS := $(patsubst %.cpp,test-%,$(TESTSRCS))
 
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -219,35 +219,60 @@ AST_MATCHER(QualType, stackClassAggregat
   return getClassAttrs(Node) == StackClass;
 }
 
 /// This matcher will match any class with the stack class assertion or an
 /// array of such classes.
 AST_MATCHER(QualType, nonheapClassAggregate) {
   return getClassAttrs(Node) == NonHeapClass;
 }
+
+/// This matcher will match any function declaration that is declared as a heap
+/// allocator.
+AST_MATCHER(FunctionDecl, heapAllocator) {
+  return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator");
+}
 }
 }
 
 namespace {
 
+bool isPlacementNew(const CXXNewExpr *expr) {
+  // Regular new expressions aren't placement new
+  if (expr->getNumPlacementArgs() == 0)
+    return false;
+  if (MozChecker::hasCustomAnnotation(expr->getOperatorNew(),
+      "moz_heap_allocator"))
+    return false;
+  return true;
+}
+
 DiagnosticsMatcher::DiagnosticsMatcher() {
   // Stack class assertion: non-local variables of a stack class are forbidden
   // (non-localness checked in the callback)
   astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"),
     &stackClassChecker);
   // Stack class assertion: new stack class is forbidden (unless placement new)
   astMatcher.addMatcher(newExpr(hasType(pointerType(
       pointee(stackClassAggregate())
     ))).bind("node"), &stackClassChecker);
   // Non-heap class assertion: new non-heap class is forbidden (unless placement
   // new)
   astMatcher.addMatcher(newExpr(hasType(pointerType(
       pointee(nonheapClassAggregate())
     ))).bind("node"), &nonheapClassChecker);
+
+  // Any heap allocation function that returns a non-heap or a stack class is
+  // definitely doing something wrong
+  astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
+      returns(pointerType(pointee(nonheapClassAggregate()))))))).bind("node"),
+    &nonheapClassChecker);
+  astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
+      returns(pointerType(pointee(stackClassAggregate()))))))).bind("node"),
+    &stackClassChecker);
 }
 
 void DiagnosticsMatcher::StackClassChecker::run(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
     DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
   if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
@@ -255,20 +280,25 @@ void DiagnosticsMatcher::StackClassCheck
     if (d->hasLocalStorage())
       return;
 
     Diag.Report(d->getLocation(), stackID) << d->getType();
     noteInferred(d->getType(), Diag);
   } else if (const CXXNewExpr *expr =
       Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
     // If it's placement new, then this match doesn't count.
-    if (expr->getNumPlacementArgs() > 0)
+    if (isPlacementNew(expr))
       return;
     Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
     noteInferred(expr->getAllocatedType(), Diag);
+  } else if (const CallExpr *expr =
+      Result.Nodes.getNodeAs<CallExpr>("node")) {
+    QualType badType = expr->getCallReturnType()->getPointeeType();
+    Diag.Report(expr->getLocStart(), stackID) << badType;
+    noteInferred(badType, Diag);
   }
 }
 
 void DiagnosticsMatcher::StackClassChecker::noteInferred(QualType T,
     DiagnosticsEngine &Diag) {
   unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
     DiagnosticIDs::Note,
     "%0 is a stack class because it inherits from a stack class %1");
@@ -296,22 +326,27 @@ void DiagnosticsMatcher::StackClassCheck
   noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
 }
 
 void DiagnosticsMatcher::NonHeapClassChecker::run(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
     DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
-  const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node");
-  // If it's placement new, then this match doesn't count.
-  if (expr->getNumPlacementArgs() > 0)
-    return;
-  Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
-  noteInferred(expr->getAllocatedType(), Diag);
+  if (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
+    // If it's placement new, then this match doesn't count.
+    if (isPlacementNew(expr))
+      return;
+    Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
+    noteInferred(expr->getAllocatedType(), Diag);
+  } else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) {
+    QualType badType = expr->getCallReturnType()->getPointeeType();
+    Diag.Report(expr->getLocStart(), stackID) << badType;
+    noteInferred(badType, Diag);
+  }
 }
 
 void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T,
     DiagnosticsEngine &Diag) {
   unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
     DiagnosticIDs::Note,
     "%0 is a non-heap class because it inherits from a non-heap class %1");
   unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestCustomHeap.cpp
@@ -0,0 +1,28 @@
+#define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
+#define MOZ_HEAP_ALLOCATOR \
+  _Pragma("GCC diagnostic push") \
+  _Pragma("GCC diagnostic ignored \"-Wgcc-compat\"") \
+  __attribute__((annotate("moz_heap_allocator"))) \
+  _Pragma("GCC diagnostic pop")
+
+#include <stdlib.h>
+#include <memory>
+
+struct MOZ_NONHEAP_CLASS X {
+};
+
+void *operator new(size_t x, int qual) MOZ_HEAP_ALLOCATOR {
+  return ::operator new(x);
+}
+
+template <typename T>
+T *customAlloc() MOZ_HEAP_ALLOCATOR {
+  T *arg =  static_cast<T*>(malloc(sizeof(T)));
+  return new (arg) T();
+}
+
+template <typename T>
+void misuseX(T q) {
+  X *foo = customAlloc<X>(); // expected-error {{variable of type 'X' is not valid on the heap}}
+  X *foo2 = new (100) X(); // expected-error {{variable of type 'X' is not valid on the heap}}
+}
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -2160,17 +2160,16 @@ GK_ATOM(columnheader, "columnheader")
 GK_ATOM(containerAtomic, "container-atomic")
 GK_ATOM(containerBusy, "container-busy")
 GK_ATOM(containerLive, "container-live")
 GK_ATOM(containerLiveRole, "container-live-role")
 GK_ATOM(containerRelevant, "container-relevant")
 GK_ATOM(cycles, "cycles")
 GK_ATOM(datatable, "datatable")
 GK_ATOM(directory, "directory")
-GK_ATOM(droppable, "droppable")
 GK_ATOM(eventFromInput, "event-from-input")
 GK_ATOM(grammar, "grammar")
 GK_ATOM(gridcell, "gridcell")
 GK_ATOM(heading, "heading")
 GK_ATOM(InlineBlockFrame, "InlineBlockFrame")
 GK_ATOM(inlinevalue, "inline")
 GK_ATOM(invalid, "invalid")
 GK_ATOM(item, "item")
--- a/content/base/test/test_bug631615.html
+++ b/content/base/test/test_bug631615.html
@@ -27,12 +27,15 @@ function doTest() {
   } catch (e) {
     results = "throws: " + e + "\n";
   }
 
   monitor.appendChild(document.createTextNode(results));
   is(results.slice(0, 6), "throws", "looking for an exception");
 }
 
-SimpleTest.runTestExpectingConsoleMessages(doTest, []);
+SimpleTest.runTestExpectingConsoleMessages(doTest, [{
+  forbid: true,
+  message: /An invalid or illegal string was specified/
+}]);
 </script>
 </body>
 </html>
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1278,17 +1278,17 @@ nsGlobalWindow::~nsGlobalWindow()
     // If our outer window's inner window is this window, null out the
     // outer window's reference to this window that's being deleted.
     nsGlobalWindow *outer = GetOuterWindowInternal();
     if (outer) {
       outer->MaybeClearInnerWindow(this);
     }
   }
 
-  mDoc = nullptr;
+  ClearDelayedEventsAndDropDocument();
 
   // Outer windows are always supposed to call CleanUp before letting themselves
   // be destroyed. And while CleanUp generally seems to be intended to clean up
   // outers, we've historically called it for both. Changing this would probably
   // involve auditing all of the references that inners and outers can have, and
   // separating the handling into CleanUp() and FreeInnerObjects.
   if (IsInnerWindow()) {
     CleanUp();
@@ -1350,16 +1350,25 @@ nsGlobalWindow::MaybeForgiveSpamCount()
     SetPopupSpamWindow(false);
     --gOpenPopupSpamCount;
     NS_ASSERTION(gOpenPopupSpamCount >= 0,
                  "Unbalanced decrement of gOpenPopupSpamCount");
   }
 }
 
 void
+nsGlobalWindow::ClearDelayedEventsAndDropDocument()
+{
+  if (mDoc && mDoc->EventHandlingSuppressed()) {
+    mDoc->UnsuppressEventHandlingAndFireEvents(false);
+  }
+  mDoc = nullptr;
+}
+
+void
 nsGlobalWindow::CleanUp()
 {
   // Guarantee idempotence.
   if (mCleanedUp)
     return;
   mCleanedUp = true;
 
   mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
@@ -2768,17 +2777,17 @@ nsGlobalWindow::DetachFromDocShell()
     NS_ASSERTION(mDoc, "Must have doc!");
 
     // Remember the document's principal and URI.
     mDocumentPrincipal = mDoc->NodePrincipal();
     mDocumentURI = mDoc->GetDocumentURI();
     mDocBaseURI = mDoc->GetDocBaseURI();
 
     // Release our document reference
-    mDoc = nullptr;
+    ClearDelayedEventsAndDropDocument();
     mFocusedNode = nullptr;
   }
 
   ClearControllers();
 
   mChromeEventHandler = nullptr; // force release now
 
   if (mContext) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -973,16 +973,17 @@ protected:
 
   static bool sIdleObserversAPIFuzzTimeDisabled;
 
   friend class HashchangeCallback;
   friend class mozilla::dom::BarProp;
 
   // Object Management
   virtual ~nsGlobalWindow();
+  void ClearDelayedEventsAndDropDocument();
   void CleanUp();
   void ClearControllers();
   nsresult FinalClose();
 
   inline void MaybeClearInnerWindow(nsGlobalWindow* aExpectedInner)
   {
     if(mInnerWindow == aExpectedInner) {
       mInnerWindow = nullptr;
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1310,16 +1310,19 @@ MediaManager::GetUserMedia(JSContext* aC
 
   // No need for locking because we always do this in the main thread.
   listeners->AppendElement(listener);
 
   // Developer preference for turning off permission check.
   if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
     aPrivileged = true;
   }
+  if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
+    c.mVideo = false;
+  }
 
   /**
    * Pass runnables along to GetUserMediaRunnable so it can add the
    * MediaStreamListener to the runnable list.
    */
   if (c.mFake) {
     // Fake stream from default backend.
     gUMRunnable = new GetUserMediaRunnable(c, onSuccess.forget(),
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -562,17 +562,17 @@ struct JSClass {
 // member initial value.  The "original ... value" verbiage is there because
 // in ECMA-262, global properties naming class objects are read/write and
 // deleteable, for the most part.
 //
 // Implementing this efficiently requires that global objects have classes
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
-#define JSCLASS_GLOBAL_SLOT_COUNT      (3 + JSProto_LIMIT * 3 + 28)
+#define JSCLASS_GLOBAL_SLOT_COUNT      (3 + JSProto_LIMIT * 3 + 27)
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                                    \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
 
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -242,143 +242,143 @@ static inline void js_free(void* p)
  * adds news with up to 12 parameters. Add more versions of new below if
  * you need more than 12 parameters.
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS)\
     template <class T>\
-    QUALIFIERS T *NEWNAME() {\
+    QUALIFIERS T *NEWNAME() MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T, ())\
     }\
 \
     template <class T, class P1>\
-    QUALIFIERS T *NEWNAME(P1 &&p1) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1)))\
     }\
 \
     template <class T, class P1, class P2>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2)))\
     }\
 \
     template <class T, class P1, class P2, class P3>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4, class P5>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4),\
                      mozilla::Forward<P5>(p5)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4, class P5, class P6>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4),\
                      mozilla::Forward<P5>(p5),\
                      mozilla::Forward<P6>(p6)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4),\
                      mozilla::Forward<P5>(p5),\
                      mozilla::Forward<P6>(p6),\
                      mozilla::Forward<P7>(p7)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4),\
                      mozilla::Forward<P5>(p5),\
                      mozilla::Forward<P6>(p6),\
                      mozilla::Forward<P7>(p7),\
                      mozilla::Forward<P8>(p8)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4),\
                      mozilla::Forward<P5>(p5),\
                      mozilla::Forward<P6>(p6),\
                      mozilla::Forward<P7>(p7),\
                      mozilla::Forward<P8>(p8),\
                      mozilla::Forward<P9>(p9)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4),\
                      mozilla::Forward<P5>(p5),\
                      mozilla::Forward<P6>(p6),\
                      mozilla::Forward<P7>(p7),\
                      mozilla::Forward<P8>(p8),\
                      mozilla::Forward<P9>(p9),\
                      mozilla::Forward<P10>(p10)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4),\
                      mozilla::Forward<P5>(p5),\
                      mozilla::Forward<P6>(p6),\
                      mozilla::Forward<P7>(p7),\
                      mozilla::Forward<P8>(p8),\
                      mozilla::Forward<P9>(p9),\
                      mozilla::Forward<P10>(p10),\
                      mozilla::Forward<P11>(p11)))\
     }\
 \
     template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11, class P12>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11, P12 &&p12) {\
+    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11, P12 &&p12) MOZ_HEAP_ALLOCATOR {\
         JS_NEW_BODY(ALLOCATOR, T,\
                     (mozilla::Forward<P1>(p1),\
                      mozilla::Forward<P2>(p2),\
                      mozilla::Forward<P3>(p3),\
                      mozilla::Forward<P4>(p4),\
                      mozilla::Forward<P5>(p5),\
                      mozilla::Forward<P6>(p6),\
                      mozilla::Forward<P7>(p7),\
--- a/js/src/builtin/TypeRepresentation.cpp
+++ b/js/src/builtin/TypeRepresentation.cpp
@@ -7,16 +7,17 @@
 #include "builtin/TypeRepresentation.h"
 
 #include "mozilla/HashFunctions.h"
 
 #include "jscntxt.h"
 #include "jsnum.h"
 #include "jsutil.h"
 
+#include "builtin/TypedObject.h"
 #include "js/HashTable.h"
 #include "vm/Runtime.h"
 #include "vm/StringBuffer.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
@@ -63,18 +64,23 @@ TypeRepresentationHasher::match(TypeRepr
         return matchReferences(key1->asReference(), key2->asReference());
 
       case TypeRepresentation::X4:
         return matchX4s(key1->asX4(), key2->asX4());
 
       case TypeRepresentation::Struct:
         return matchStructs(key1->asStruct(), key2->asStruct());
 
-      case TypeRepresentation::Array:
-        return matchArrays(key1->asArray(), key2->asArray());
+      case TypeRepresentation::SizedArray:
+        return matchSizedArrays(key1->asSizedArray(),
+                                key2->asSizedArray());
+
+      case TypeRepresentation::UnsizedArray:
+        return matchUnsizedArrays(key1->asUnsizedArray(),
+                                  key2->asUnsizedArray());
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
 bool
 TypeRepresentationHasher::matchScalars(ScalarTypeRepresentation *key1,
                                        ScalarTypeRepresentation *key2)
@@ -99,52 +105,63 @@ TypeRepresentationHasher::matchX4s(X4Typ
 bool
 TypeRepresentationHasher::matchStructs(StructTypeRepresentation *key1,
                                        StructTypeRepresentation *key2)
 {
     if (key1->fieldCount() != key2->fieldCount())
         return false;
 
     for (size_t i = 0; i < key1->fieldCount(); i++) {
-        if (key1->field(i).id != key2->field(i).id)
+        if (key1->field(i).propertyName != key2->field(i).propertyName)
             return false;
 
         if (key1->field(i).typeRepr != key2->field(i).typeRepr)
             return false;
     }
 
     return true;
 }
 
 bool
-TypeRepresentationHasher::matchArrays(ArrayTypeRepresentation *key1,
-                                      ArrayTypeRepresentation *key2)
+TypeRepresentationHasher::matchSizedArrays(SizedArrayTypeRepresentation *key1,
+                                           SizedArrayTypeRepresentation *key2)
 {
     // We assume that these pointers have been canonicalized:
     return key1->element() == key2->element() &&
            key1->length() == key2->length();
 }
 
+bool
+TypeRepresentationHasher::matchUnsizedArrays(UnsizedArrayTypeRepresentation *key1,
+                                             UnsizedArrayTypeRepresentation *key2)
+{
+    // We assume that these pointers have been canonicalized:
+    return key1->element() == key2->element();
+}
+
 HashNumber
 TypeRepresentationHasher::hash(TypeRepresentation *key) {
     switch (key->kind()) {
       case TypeRepresentation::Scalar:
         return hashScalar(key->asScalar());
 
       case TypeRepresentation::Reference:
         return hashReference(key->asReference());
 
       case TypeRepresentation::X4:
         return hashX4(key->asX4());
 
       case TypeRepresentation::Struct:
         return hashStruct(key->asStruct());
 
-      case TypeRepresentation::Array:
-        return hashArray(key->asArray());
+      case TypeRepresentation::UnsizedArray:
+        return hashUnsizedArray(key->asUnsizedArray());
+
+      case TypeRepresentation::SizedArray:
+        return hashSizedArray(key->asSizedArray());
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
 HashNumber
 TypeRepresentationHasher::hashScalar(ScalarTypeRepresentation *key)
 {
@@ -163,146 +180,164 @@ TypeRepresentationHasher::hashX4(X4TypeR
     return HashGeneric(key->kind(), key->type());
 }
 
 HashNumber
 TypeRepresentationHasher::hashStruct(StructTypeRepresentation *key)
 {
     HashNumber hash = HashGeneric(key->kind());
     for (HashNumber i = 0; i < key->fieldCount(); i++) {
-        hash = AddToHash(hash, JSID_BITS(key->field(i).id.get()));
+        hash = AddToHash(hash, key->field(i).propertyName.get());
         hash = AddToHash(hash, key->field(i).typeRepr);
     }
     return hash;
 }
 
 HashNumber
-TypeRepresentationHasher::hashArray(ArrayTypeRepresentation *key)
+TypeRepresentationHasher::hashSizedArray(SizedArrayTypeRepresentation *key)
 {
     return HashGeneric(key->kind(), key->element(), key->length());
 }
 
+HashNumber
+TypeRepresentationHasher::hashUnsizedArray(UnsizedArrayTypeRepresentation *key)
+{
+    return HashGeneric(key->kind(), key->element());
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Constructors
 
-TypeRepresentation::TypeRepresentation(Kind kind, size_t size,
-                                       size_t align, bool opaque)
-  : size_(size),
-    alignment_(align),
-    kind_(kind),
+TypeRepresentation::TypeRepresentation(Kind kind, bool opaque)
+  : kind_(kind),
     opaque_(opaque)
 {}
 
+SizedTypeRepresentation::SizedTypeRepresentation(Kind kind, bool opaque,
+                                                 size_t size, size_t align)
+  : TypeRepresentation(kind, opaque),
+    size_(size),
+    alignment_(align)
+{}
+
 static size_t ScalarSizes[] = {
 #define SCALAR_SIZE(_kind, _type, _name)                        \
     sizeof(_type),
     JS_FOR_EACH_SCALAR_TYPE_REPR(SCALAR_SIZE) 0
 #undef SCALAR_SIZE
 };
 
 ScalarTypeRepresentation::ScalarTypeRepresentation(Type type)
-  : TypeRepresentation(Scalar, ScalarSizes[type], ScalarSizes[type], false),
+  : SizedTypeRepresentation(Scalar, false, ScalarSizes[type], ScalarSizes[type]),
     type_(type)
 {
 }
 
 static size_t X4Sizes[] = {
 #define X4_SIZE(_kind, _type, _name)                        \
     sizeof(_type) * 4,
     JS_FOR_EACH_X4_TYPE_REPR(X4_SIZE) 0
 #undef X4_SIZE
 };
 
 X4TypeRepresentation::X4TypeRepresentation(Type type)
-  : TypeRepresentation(X4, X4Sizes[type], X4Sizes[type], false),
+  : SizedTypeRepresentation(X4, false, X4Sizes[type], X4Sizes[type]),
     type_(type)
 {
 }
 
 ReferenceTypeRepresentation::ReferenceTypeRepresentation(Type type)
-  : TypeRepresentation(Reference, 0, 1, true),
+  : SizedTypeRepresentation(Reference, true, 0, 1),
     type_(type)
 {
     switch (type) {
       case TYPE_ANY:
         size_ = sizeof(js::HeapValue);
         alignment_ = MOZ_ALIGNOF(js::HeapValue);
         break;
 
       case TYPE_OBJECT:
       case TYPE_STRING:
         size_ = sizeof(js::HeapPtrObject);
         alignment_ = MOZ_ALIGNOF(js::HeapPtrObject);
         break;
     }
 }
 
-ArrayTypeRepresentation::ArrayTypeRepresentation(TypeRepresentation *element,
-                                                 size_t length)
-  : TypeRepresentation(Array, element->size() * length,
-                       element->alignment(), element->opaque()),
+SizedArrayTypeRepresentation::SizedArrayTypeRepresentation(SizedTypeRepresentation *element,
+                                                           size_t length)
+  : SizedTypeRepresentation(SizedArray, element->opaque(),
+                            element->size() * length, element->alignment()),
     element_(element),
     length_(length)
 {
 }
 
+UnsizedArrayTypeRepresentation::UnsizedArrayTypeRepresentation(SizedTypeRepresentation *element)
+  : TypeRepresentation(UnsizedArray, element->opaque()),
+    element_(element)
+{
+}
+
 static inline size_t alignTo(size_t address, size_t align) {
     JS_ASSERT(IsPowerOfTwo(align));
     return (address + align - 1) & -align;
 }
 
 StructField::StructField(size_t index,
-                         jsid &id,
-                         TypeRepresentation *typeRepr,
+                         PropertyName *propertyName,
+                         SizedTypeRepresentation *typeRepr,
                          size_t offset)
   : index(index),
-    id(id),
+    propertyName(propertyName),
     typeRepr(typeRepr),
     offset(offset)
 {}
 
 StructTypeRepresentation::StructTypeRepresentation()
-  : TypeRepresentation(Struct, 0, 1, false),
+  : SizedTypeRepresentation(Struct, false, 0, 1),
     fieldCount_(0) // see ::init() below!
 {
     // note: size_, alignment_, and opaque_ are computed in ::init() below
 }
 
 bool
 StructTypeRepresentation::init(JSContext *cx,
-                               AutoIdVector &ids,
+                               AutoPropertyNameVector &names,
                                AutoObjectVector &typeReprOwners)
 {
-    JS_ASSERT(ids.length() == typeReprOwners.length());
-    fieldCount_ = ids.length();
+    JS_ASSERT(names.length() == typeReprOwners.length());
+    fieldCount_ = names.length();
 
     // We compute alignment into the field `align_` directly in the
     // loop below, but not `size_` because we have to very careful
     // about overflow. For now, we always use a uint32_t for
     // consistency across build environments.
     uint32_t totalSize = 0;
 
     // These will be adjusted in the loop below:
     alignment_ = 1;
     opaque_ = false;
 
-    for (size_t i = 0; i < ids.length(); i++) {
-        TypeRepresentation *fieldTypeRepr = fromOwnerObject(*typeReprOwners[i]);
+    for (size_t i = 0; i < names.length(); i++) {
+        SizedTypeRepresentation *fieldTypeRepr =
+            fromOwnerObject(*typeReprOwners[i])->asSized();
 
         if (fieldTypeRepr->opaque())
             opaque_ = true;
 
         uint32_t alignedSize = alignTo(totalSize, fieldTypeRepr->alignment());
         if (alignedSize < totalSize) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                  JSMSG_TYPEDOBJECT_TOO_BIG);
             return false;
         }
 
-        new(fields() + i) StructField(i, ids[i], fieldTypeRepr, alignedSize);
+        new(fields() + i) StructField(i, names[i],
+                                      fieldTypeRepr, alignedSize);
         alignment_ = js::Max(alignment_, fieldTypeRepr->alignment());
 
         uint32_t incrementedSize = alignedSize + fieldTypeRepr->size();
         if (incrementedSize < alignedSize) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                  JSMSG_TYPEDOBJECT_TOO_BIG);
             return false;
         }
@@ -324,19 +359,35 @@ StructTypeRepresentation::init(JSContext
 ///////////////////////////////////////////////////////////////////////////
 // Interning
 
 JSObject *
 TypeRepresentation::addToTableOrFree(JSContext *cx,
                                      TypeRepresentationHash::AddPtr &p)
 {
     JS_ASSERT(!ownerObject_);
-
+    Rooted<GlobalObject*> global(cx, cx->global());
     JSCompartment *comp = cx->compartment();
 
+    // First, try to create the typed object to associate with this
+    // type representation. Since nothing is in the table yet, if this
+    // fails we can just return and pretend this whole endeavor was
+    // just a bad dream.
+    RootedObject proto(cx);
+    const Class *clasp;
+    if (!global->getTypedObjectModule().getSuitableClaspAndProto(cx, kind(),
+                                                                 &clasp, &proto))
+    {
+        return nullptr;
+    }
+    RootedTypeObject typeObject(cx, comp->types.newTypeObject(cx, clasp, proto));
+    if (!typeObject)
+        return nullptr;
+
+    // Next, attempt to add the type representation to the table.
     if (!comp->typeReprs.add(p, this)) {
         js_ReportOutOfMemory(cx);
         js_free(this); // do not finalize, not present in the table
         return nullptr;
     }
 
     // Now that the object is in the table, try to make the owner
     // object. If this succeeds, then the owner will remove from the
@@ -351,23 +402,31 @@ TypeRepresentation::addToTableOrFree(JSC
         js_free(this);
         return nullptr;
     }
 
     ownerObject->setPrivate(this);
 
     // Assign the various reserved slots:
     ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_KIND, Int32Value(kind()));
-    ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_SIZE, Int32Value(size()));
-    ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_ALIGNMENT, Int32Value(alignment()));
+
+    if (isSized()) {
+        ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_SIZE,
+                                      Int32Value(asSized()->size()));
+        ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_ALIGNMENT,
+                                      Int32Value(asSized()->alignment()));
+    }
 
     switch (kind()) {
-      case Array:
+      case UnsizedArray:
+        break;
+
+      case SizedArray:
         ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_LENGTH,
-                                      Int32Value(asArray()->length()));
+                                      Int32Value(asSizedArray()->length()));
         break;
 
       case Scalar:
         ownerObject->initReservedSlot(JS_TYPEREPR_SLOT_TYPE,
                                       Int32Value(asScalar()->type()));
         break;
 
       case Reference:
@@ -380,16 +439,17 @@ TypeRepresentation::addToTableOrFree(JSC
                                       Int32Value(asX4()->type()));
         break;
 
       case Struct:
         break;
     }
 
     ownerObject_.init(ownerObject);
+    typeObject_.init(typeObject);
     return &*ownerObject;
 }
 
 namespace js {
 class TypeRepresentationHelper {
   public:
     template<typename T>
     static JSObject *CreateSimple(JSContext *cx, typename T::Type type) {
@@ -447,63 +507,87 @@ ReferenceTypeRepresentation::Create(JSCo
         return nullptr;
     new(ptr) ReferenceTypeRepresentation(type);
 
     return ptr->addToTableOrFree(cx, p);
 }
 
 /*static*/
 JSObject *
-ArrayTypeRepresentation::Create(JSContext *cx,
-                                TypeRepresentation *element,
-                                size_t length)
+SizedArrayTypeRepresentation::Create(JSContext *cx,
+                                     SizedTypeRepresentation *element,
+                                     size_t length)
 {
     JSCompartment *comp = cx->compartment();
 
     // Overly conservative, since we are using `size_t` to represent
     // size, but `SafeMul` operators on `int32_t` types. Still, it
     // should be good enough for now.
     int32_t temp;
     if (!SafeMul(element->size(), length, &temp)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_TYPEDOBJECT_TOO_BIG);
         return nullptr;
     }
 
-    ArrayTypeRepresentation sample(element, length);
+    SizedArrayTypeRepresentation sample(element, length);
     TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
     if (p)
         return (*p)->ownerObject();
 
     // Note: cannot use cx->new_ because constructor is private.
-    ArrayTypeRepresentation *ptr =
-        (ArrayTypeRepresentation *) cx->malloc_(
-            sizeof(ArrayTypeRepresentation));
+    SizedArrayTypeRepresentation *ptr =
+        (SizedArrayTypeRepresentation *) cx->malloc_(
+            sizeof(SizedArrayTypeRepresentation));
     if (!ptr)
         return nullptr;
-    new(ptr) ArrayTypeRepresentation(element, length);
+    new(ptr) SizedArrayTypeRepresentation(element, length);
+
+    return ptr->addToTableOrFree(cx, p);
+}
+
+
+/*static*/
+JSObject *
+UnsizedArrayTypeRepresentation::Create(JSContext *cx,
+                                       SizedTypeRepresentation *element)
+{
+    JSCompartment *comp = cx->compartment();
+
+    UnsizedArrayTypeRepresentation sample(element);
+    TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(&sample);
+    if (p)
+        return (*p)->ownerObject();
+
+    // Note: cannot use cx->new_ because constructor is private.
+    UnsizedArrayTypeRepresentation *ptr =
+        (UnsizedArrayTypeRepresentation *) cx->malloc_(
+            sizeof(UnsizedArrayTypeRepresentation));
+    if (!ptr)
+        return nullptr;
+    new(ptr) UnsizedArrayTypeRepresentation(element);
 
     return ptr->addToTableOrFree(cx, p);
 }
 
 /*static*/
 JSObject *
 StructTypeRepresentation::Create(JSContext *cx,
-                                 AutoIdVector &ids,
+                                 AutoPropertyNameVector &names,
                                  AutoObjectVector &typeReprOwners)
 {
-    size_t count = ids.length();
+    size_t count = names.length();
     JSCompartment *comp = cx->compartment();
 
     // Note: cannot use cx->new_ because constructor is private.
     size_t size = sizeof(StructTypeRepresentation) + count * sizeof(StructField);
     StructTypeRepresentation *ptr =
         (StructTypeRepresentation *) cx->malloc_(size);
     new(ptr) StructTypeRepresentation();
-    if (!ptr->init(cx, ids, typeReprOwners))
+    if (!ptr->init(cx, names, typeReprOwners))
         return nullptr;
 
     TypeRepresentationHash::AddPtr p = comp->typeReprs.lookupForAdd(ptr);
     if (p) {
         js_free(ptr); // do not finalize, not present in the table
         return (*p)->ownerObject();
     }
 
@@ -516,16 +600,17 @@ StructTypeRepresentation::Create(JSConte
 void
 TypeRepresentation::mark(JSTracer *trace)
 {
     // Push our owner object onto the mark stack. When our owner
     // object's trace callback is called, we will trace its
     // contents. This is the typical scheme for marking objects.  See
     // gc/Marking.cpp for more details.
     gc::MarkObject(trace, &ownerObject_, "typeRepresentation_ownerObject");
+    gc::MarkTypeObject(trace, &typeObject_, "typeRepresentation_typeObject");
 }
 
 /*static*/ void
 TypeRepresentation::obj_trace(JSTracer *trace, JSObject *object)
 {
     fromOwnerObject(*object)->traceFields(trace);
 }
 
@@ -539,33 +624,44 @@ TypeRepresentation::traceFields(JSTracer
       case Reference:
       case X4:
         break;
 
       case Struct:
         asStruct()->traceStructFields(trace);
         break;
 
-      case Array:
-        asArray()->traceArrayFields(trace);
+      case SizedArray:
+        asSizedArray()->traceSizedArrayFields(trace);
+        break;
+
+      case UnsizedArray:
+        asUnsizedArray()->traceUnsizedArrayFields(trace);
         break;
     }
 }
 
 void
 StructTypeRepresentation::traceStructFields(JSTracer *trace)
 {
     for (size_t i = 0; i < fieldCount(); i++) {
-        gc::MarkId(trace, &fields()[i].id, "typerepr_field_id");
+        gc::MarkString(trace, &fields()[i].propertyName, "typerepr_field_propertyName");
         fields()[i].typeRepr->mark(trace);
     }
 }
 
 void
-ArrayTypeRepresentation::traceArrayFields(JSTracer *trace)
+SizedArrayTypeRepresentation::traceSizedArrayFields(JSTracer *trace)
+{
+    this->mark(trace);
+    element_->mark(trace);
+}
+
+void
+UnsizedArrayTypeRepresentation::traceUnsizedArrayFields(JSTracer *trace)
 {
     this->mark(trace);
     element_->mark(trace);
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Finalization
 
@@ -589,18 +685,21 @@ TypeRepresentation::appendString(JSConte
         return asScalar()->appendStringScalar(cx, contents);
 
       case Reference:
         return asReference()->appendStringReference(cx, contents);
 
       case X4:
         return asX4()->appendStringX4(cx, contents);
 
-      case Array:
-        return asArray()->appendStringArray(cx, contents);
+      case SizedArray:
+        return asSizedArray()->appendStringSizedArray(cx, contents);
+
+      case UnsizedArray:
+        return asUnsizedArray()->appendStringUnsizedArray(cx, contents);
 
       case Struct:
         return asStruct()->appendStringStruct(cx, contents);
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
     return false;
 }
@@ -657,31 +756,52 @@ X4TypeRepresentation::appendStringX4(JSC
         return contents.append("float32x4");
       case TYPE_INT32:
         return contents.append("int32x4");
     }
     MOZ_ASSUME_UNREACHABLE("Invalid type");
 }
 
 bool
-ArrayTypeRepresentation::appendStringArray(JSContext *cx, StringBuffer &contents)
+SizedArrayTypeRepresentation::appendStringSizedArray(JSContext *cx, StringBuffer &contents)
 {
-    if (!contents.append("ArrayType("))
+    SizedTypeRepresentation *elementType = element();
+    while (elementType->isSizedArray())
+        elementType = elementType->asSizedArray()->element();
+    if (!elementType->appendString(cx, contents))
         return false;
 
+    contents.append(".array(");
+    SizedArrayTypeRepresentation *arrayType = this;
+    while (arrayType != NULL) {
+        if (!NumberValueToStringBuffer(cx, NumberValue(length()), contents))
+            return false;
+
+        if (arrayType->element()->isSizedArray()) {
+            if (!contents.append(","))
+                return false;
+            arrayType = arrayType->element()->asSizedArray();
+        } else {
+            break;
+        }
+    }
+
+    if (!contents.append(")"))
+        return false;
+
+    return true;
+}
+
+bool
+UnsizedArrayTypeRepresentation::appendStringUnsizedArray(JSContext *cx, StringBuffer &contents)
+{
     if (!element()->appendString(cx, contents))
         return false;
 
-    if (!contents.append(", "))
-        return false;
-
-    if (!NumberValueToStringBuffer(cx, NumberValue(length()), contents))
-        return false;
-
-    if (!contents.append(")"))
+    if (!contents.append(".array()"))
         return false;
 
     return true;
 }
 
 bool
 StructTypeRepresentation::appendStringStruct(JSContext *cx, StringBuffer &contents)
 {
@@ -689,17 +809,17 @@ StructTypeRepresentation::appendStringSt
         return false;
 
     for (size_t i = 0; i < fieldCount(); i++) {
         const StructField &fld = field(i);
 
         if (i > 0)
             contents.append(", ");
 
-        RootedString idString(cx, IdToString(cx, fld.id));
+        RootedString idString(cx, fld.propertyName);
         if (!idString)
             return false;
 
         if (!contents.append(idString))
             return false;
 
         if (!contents.append(": "))
             return false;
@@ -714,41 +834,48 @@ StructTypeRepresentation::appendStringSt
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Walking memory
 
 template<typename V>
 static void
-visitReferences(TypeRepresentation *repr, uint8_t *mem, V& visitor)
+visitReferences(SizedTypeRepresentation *repr,
+                uint8_t *mem,
+                V& visitor)
 {
     if (repr->transparent())
         return;
 
     switch (repr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::X4:
         return;
 
       case TypeRepresentation::Reference:
         visitor.visitReference(repr->asReference(), mem);
         return;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
       {
-        ArrayTypeRepresentation *arrayRepr = repr->asArray();
-        TypeRepresentation *elementRepr = arrayRepr->element();
+        SizedArrayTypeRepresentation *arrayRepr = repr->asSizedArray();
+        SizedTypeRepresentation *elementRepr = arrayRepr->element();
         for (size_t i = 0; i < arrayRepr->length(); i++) {
             visitReferences(elementRepr, mem, visitor);
             mem += elementRepr->size();
         }
         return;
       }
 
+      case TypeRepresentation::UnsizedArray:
+      {
+        MOZ_ASSUME_UNREACHABLE("Only Sized Type representations");
+      }
+
       case TypeRepresentation::Struct:
       {
         StructTypeRepresentation *structRepr = repr->asStruct();
         for (size_t i = 0; i < structRepr->fieldCount(); i++) {
             const StructField &f = structRepr->field(i);
             visitReferences(f.typeRepr, mem + f.offset, visitor);
         }
         return;
@@ -801,22 +928,33 @@ js::MemoryInitVisitor::visitReference(Re
         return;
       }
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
 void
-TypeRepresentation::initInstance(const JSRuntime *rt, uint8_t *mem)
+SizedTypeRepresentation::initInstance(const JSRuntime *rt,
+                                      uint8_t *mem,
+                                      size_t length)
 {
     MemoryInitVisitor visitor(rt);
+
+    // Initialize the 0th instance
     memset(mem, 0, size());
     if (opaque())
         visitReferences(this, mem, visitor);
+
+    // Stamp out N copies of later instances
+    uint8_t *target = mem;
+    for (size_t i = 1; i < length; i++) {
+        target += size();
+        memcpy(target, mem, size());
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Tracing instances
 
 namespace js {
 class MemoryTracingVisitor {
     JSTracer *trace_;
@@ -860,30 +998,46 @@ js::MemoryTracingVisitor::visitReference
         return;
       }
     }
 
     MOZ_ASSUME_UNREACHABLE("Invalid kind");
 }
 
 void
-TypeRepresentation::traceInstance(JSTracer *trace, uint8_t *mem)
+SizedTypeRepresentation::traceInstance(JSTracer *trace,
+                                       uint8_t *mem,
+                                       size_t length)
 {
     MemoryTracingVisitor visitor(trace);
-    visitReferences(this, mem, visitor);
+
+    for (size_t i = 0; i < length; i++) {
+        visitReferences(this, mem, visitor);
+        mem += size();
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Misc
 
 const StructField *
 StructTypeRepresentation::fieldNamed(jsid id) const
 {
+    if (!JSID_IS_ATOM(id))
+        return nullptr;
+
+    uint32_t unused;
+    JSAtom *atom = JSID_TO_ATOM(id);
+    if (atom->isIndex(&unused))
+        return nullptr;
+
+    PropertyName *name = atom->asPropertyName();
+
     for (size_t i = 0; i < fieldCount(); i++) {
-        if (field(i).id.get() == id)
+        if (field(i).propertyName.get() == name)
             return &field(i);
     }
     return nullptr;
 }
 
 /*static*/ bool
 TypeRepresentation::isOwnerObject(JSObject &obj)
 {
--- a/js/src/builtin/TypeRepresentation.h
+++ b/js/src/builtin/TypeRepresentation.h
@@ -3,216 +3,247 @@
  * 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/. */
 
 #ifndef builtin_TypeRepresentation_h
 #define builtin_TypeRepresentation_h
 
 /*
-
-  Type Representations are the way that the compiler stores
-  information about binary data types that the user defines.  They are
-  always interned into a hashset in the compartment (typeReprs),
-  meaning that you can compare two type representations for equality
-  just using `==`.
-
-  # Creation and canonicalization:
-
-  Each kind of `TypeRepresentation` object includes a `New` method
-  that will create a canonical instance of it. So, for example, you
-  can do `ScalarTypeRepresentation::Create(Uint8)` to get the
-  canonical representation of uint8. The object that is returned is
-  designed to be immutable, and the API permits only read access.
-
-  # Memory management:
-
-  Each TypeRepresentations has a representative JSObject, called its
-  owner object. When this object is finalized, the TypeRepresentation*
-  will be freed (and removed from the interning table, see
-  below). Therefore, if you reference a TypeRepresentation*, you must
-  ensure this owner object is traced. In type objects, this is done by
-  invoking TypeRepresentation::mark(); in binary data type descriptors,
-  the owner object for the type is stored in `SLOT_TYPE_REPR`.
-
-  The canonicalization table maintains *weak references* to the
-  TypeRepresentation* pointers. That is, the table is not traced.
-  Instead, whenever an object is created, it is paired with its owner
-  object, and the finalizer of the owner object removes the pointer
-  from the table and then frees the pointer.
-
-  # Opacity
-
-  A type representation is considered "opaque" if it contains
-  references (strings, objects, any). In those cases we have to be
-  more limited with respect to aliasing etc to preserve portability
-  across engines (for example, we don't want to expose sizeof(Any))
-  and memory safety.
-
-  # Future plans:
-
-  The owner object will eventually be exposed to self-hosted script
-  and will offer methods for querying and manipulating the binary data
-  it describes.
-
+ *  Type Representations are canonical versions of user-defined type
+ *  descriptors. Using the typed objects API, users are free to define
+ *  new type descriptors representing C-like type definitions. Each such
+ *  type descriptor has a distinct prototype and identity, even if it
+ *  describes the same logical type as another type
+ *  descriptor. Generally speaking, though, the only thing the compiler
+ *  cares about is the binary data layout implied by a type descriptor.
+ *
+ *  Therefore, we link each such type descriptor to one canonical *type
+ *  representation*. The type representation is itself an object, but
+ *  this object is never exposed to user code (it is used by self-hosted
+ *  code). Type representations are interned into a hashset in the
+ *  compartment (typeReprs), meaning that you can compare two type
+ *  representations for equality just using `==`. If they are equal,
+ *  it means that they represent the same binary layout.
+ *
+ *  # Creation and canonicalization:
+ *
+ *  Each kind of `TypeRepresentation` object includes a `New` method
+ *  that will create a canonical instance of it. So, for example, you
+ *  can do `ScalarTypeRepresentation::Create(Uint8)` to get the
+ *  canonical representation of uint8. The object that is returned is
+ *  designed to be immutable, and the API permits only read access.
+ *
+ *  # Integration with TI:
+ *
+ *  Each TypeRepresentation has an associated Type Object. This Type
+ *  Object is used as the type object for all type descriptors with this
+ *  representation. The type object has an associated addendum linking
+ *  to the type representation, thus allowing the jit to deduce
+ *  information about type descriptors that appear in expressions.
+ *
+ *  # Memory management:
+ *
+ *  Each TypeRepresentations has an associated JSObject, called its
+ *  owned object. When this object is finalized, the TypeRepresentation*
+ *  will be freed (and removed from the interning table, see
+ *  below). Therefore, if you reference a TypeRepresentation*, you must
+ *  ensure this owner object is traced. In type objects, this is done by
+ *  invoking TypeRepresentation::mark(); in binary data type
+ *  descriptors, the owner object for the type is stored in
+ *  `SLOT_TYPE_REPR`.
+ *
+ *  The canonicalization table maintains *weak references* to the
+ *  TypeRepresentation* pointers. That is, the table is not traced.
+ *  Instead, whenever an object is created, it is paired with its owner
+ *  object, and the finalizer of the owner object removes the pointer
+ *  from the table and then frees the pointer.
+ *
+ *  # Opacity
+ *
+ *  A type representation is considered "opaque" if it contains
+ *  references (strings, objects, any). In those cases we have to be
+ *  more limited with respect to aliasing etc to preserve portability
+ *  across engines (for example, we don't want to expose sizeof(Any))
+ *  and memory safety.
  */
 
 #include "jsalloc.h"
 #include "jscntxt.h"
 #include "jspubtd.h"
 
 #include "builtin/TypedObjectConstants.h"
 #include "gc/Barrier.h"
 #include "js/HashTable.h"
 
 namespace js {
 
 class TypeRepresentation;
+class SizedTypeRepresentation;
 class ScalarTypeRepresentation;
 class ReferenceTypeRepresentation;
 class X4TypeRepresentation;
-class ArrayTypeRepresentation;
+class SizedArrayTypeRepresentation;
+class UnsizedArrayTypeRepresentation;
 class StructTypeRepresentation;
 
 struct Class;
 class StringBuffer;
 
 struct TypeRepresentationHasher
 {
     typedef TypeRepresentation *Lookup;
     static HashNumber hash(TypeRepresentation *key);
     static bool match(TypeRepresentation *key1, TypeRepresentation *key2);
 
   private:
     static HashNumber hashScalar(ScalarTypeRepresentation *key);
     static HashNumber hashReference(ReferenceTypeRepresentation *key);
     static HashNumber hashX4(X4TypeRepresentation *key);
     static HashNumber hashStruct(StructTypeRepresentation *key);
-    static HashNumber hashArray(ArrayTypeRepresentation *key);
+    static HashNumber hashUnsizedArray(UnsizedArrayTypeRepresentation *key);
+    static HashNumber hashSizedArray(SizedArrayTypeRepresentation *key);
 
     static bool matchScalars(ScalarTypeRepresentation *key1,
                              ScalarTypeRepresentation *key2);
     static bool matchReferences(ReferenceTypeRepresentation *key1,
                                 ReferenceTypeRepresentation *key2);
     static bool matchX4s(X4TypeRepresentation *key1,
                          X4TypeRepresentation *key2);
     static bool matchStructs(StructTypeRepresentation *key1,
                              StructTypeRepresentation *key2);
-    static bool matchArrays(ArrayTypeRepresentation *key1,
-                            ArrayTypeRepresentation *key2);
+    static bool matchSizedArrays(SizedArrayTypeRepresentation *key1,
+                                 SizedArrayTypeRepresentation *key2);
+    static bool matchUnsizedArrays(UnsizedArrayTypeRepresentation *key1,
+                                   UnsizedArrayTypeRepresentation *key2);
 };
 
 typedef js::HashSet<TypeRepresentation *,
                     TypeRepresentationHasher,
                     RuntimeAllocPolicy> TypeRepresentationHash;
 
 class TypeRepresentationHelper;
 
 class TypeRepresentation {
   public:
     enum Kind {
         Scalar = JS_TYPEREPR_SCALAR_KIND,
         Reference = JS_TYPEREPR_REFERENCE_KIND,
         X4 = JS_TYPEREPR_X4_KIND,
         Struct = JS_TYPEREPR_STRUCT_KIND,
-        Array = JS_TYPEREPR_ARRAY_KIND
+        SizedArray = JS_TYPEREPR_SIZED_ARRAY_KIND,
+        UnsizedArray = JS_TYPEREPR_UNSIZED_ARRAY_KIND,
     };
 
   protected:
-    TypeRepresentation(Kind kind, size_t size, size_t align, bool opaque);
+    TypeRepresentation(Kind kind, bool opaque);
 
     // in order to call addToTableOrFree()
     friend class TypeRepresentationHelper;
 
-    size_t size_;
-    size_t alignment_;
     Kind kind_;
     bool opaque_;
 
     JSObject *addToTableOrFree(JSContext *cx, TypeRepresentationHash::AddPtr &p);
 
   private:
     static const Class class_;
     static void obj_trace(JSTracer *trace, JSObject *object);
     static void obj_finalize(js::FreeOp *fop, JSObject *object);
 
     HeapPtrObject ownerObject_;
+    HeapPtrTypeObject typeObject_;
     void traceFields(JSTracer *tracer);
 
   public:
-    size_t size() const { return size_; }
-    size_t alignment() const { return alignment_; }
     Kind kind() const { return kind_; }
     bool opaque() const { return opaque_; }
     bool transparent() const { return !opaque_; }
     JSObject *ownerObject() const { return ownerObject_.get(); }
+    types::TypeObject *typeObject() const { return typeObject_.get(); }
 
     // Appends a stringified form of this type representation onto
     // buffer, for use in error messages and the like.
     bool appendString(JSContext *cx, StringBuffer &buffer);
 
-    // Initializes memory that contains an instance of this type
-    // with appropriate default values (typically 0).
-    void initInstance(const JSRuntime *rt, uint8_t *mem);
-
-    // Traces memory that contains an instance of this type.
-    void traceInstance(JSTracer *trace, uint8_t *mem);
-
     static bool isOwnerObject(JSObject &obj);
     static TypeRepresentation *fromOwnerObject(JSObject &obj);
 
+    static bool isSized(Kind kind) {
+        return kind > JS_TYPEREPR_MAX_UNSIZED_KIND;
+    }
+
+    bool isSized() const {
+        return isSized(kind());
+    }
+
+    inline SizedTypeRepresentation *asSized();
+
     bool isScalar() const {
         return kind() == Scalar;
     }
 
-    ScalarTypeRepresentation *asScalar() {
-        JS_ASSERT(isScalar());
-        return (ScalarTypeRepresentation*) this;
-    }
+    inline ScalarTypeRepresentation *asScalar();
 
     bool isReference() const {
         return kind() == Reference;
     }
 
-    ReferenceTypeRepresentation *asReference() {
-        JS_ASSERT(isReference());
-        return (ReferenceTypeRepresentation*) this;
-    }
+    inline ReferenceTypeRepresentation *asReference();
 
     bool isX4() const {
         return kind() == X4;
     }
 
-    X4TypeRepresentation *asX4() {
-        JS_ASSERT(isX4());
-        return (X4TypeRepresentation*) this;
+    inline X4TypeRepresentation *asX4();
+
+    bool isSizedArray() const {
+        return kind() == SizedArray;
     }
 
-    bool isArray() const {
-        return kind() == Array;
+    inline SizedArrayTypeRepresentation *asSizedArray();
+
+    bool isUnsizedArray() const {
+        return kind() == UnsizedArray;
     }
 
-    ArrayTypeRepresentation *asArray() {
-        JS_ASSERT(isArray());
-        return (ArrayTypeRepresentation*) this;
+    inline UnsizedArrayTypeRepresentation *asUnsizedArray();
+
+    bool isAnyArray() const {
+        return isSizedArray() || isUnsizedArray();
     }
 
     bool isStruct() const {
         return kind() == Struct;
     }
 
-    StructTypeRepresentation *asStruct() {
-        JS_ASSERT(isStruct());
-        return (StructTypeRepresentation*) this;
-    }
+    inline StructTypeRepresentation *asStruct();
 
     void mark(JSTracer *tracer);
 };
 
-class ScalarTypeRepresentation : public TypeRepresentation {
+class SizedTypeRepresentation : public TypeRepresentation {
+  protected:
+    SizedTypeRepresentation(Kind kind, bool opaque, size_t size, size_t align);
+
+    size_t size_;
+    size_t alignment_;
+
+  public:
+    size_t size() const { return size_; }
+    size_t alignment() const { return alignment_; }
+
+    // Initializes memory that contains `count` instances of this type
+    void initInstance(const JSRuntime *rt, uint8_t *mem, size_t count);
+
+    // Traces memory that contains `count` instances of this type.
+    void traceInstance(JSTracer *trace, uint8_t *mem, size_t count);
+};
+
+class ScalarTypeRepresentation : public SizedTypeRepresentation {
   public:
     // Must match order of JS_FOR_EACH_SCALAR_TYPE_REPR below
     enum Type {
         TYPE_INT8 = JS_SCALARTYPEREPR_INT8,
         TYPE_UINT8 = JS_SCALARTYPEREPR_UINT8,
         TYPE_INT16 = JS_SCALARTYPEREPR_INT16,
         TYPE_UINT16 = JS_SCALARTYPEREPR_UINT16,
         TYPE_INT32 = JS_SCALARTYPEREPR_INT32,
@@ -268,17 +299,17 @@ class ScalarTypeRepresentation : public 
     macro_(ScalarTypeRepresentation::TYPE_FLOAT32, float,    float32)         \
     macro_(ScalarTypeRepresentation::TYPE_FLOAT64, double,   float64)
 
 // Must be in same order as the enum ScalarTypeRepresentation::Type:
 #define JS_FOR_EACH_SCALAR_TYPE_REPR(macro_)                                    \
     JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_)                           \
     macro_(ScalarTypeRepresentation::TYPE_UINT8_CLAMPED, uint8_t, uint8Clamped)
 
-class ReferenceTypeRepresentation : public TypeRepresentation {
+class ReferenceTypeRepresentation : public SizedTypeRepresentation {
   public:
     // Must match order of JS_FOR_EACH_REFERENCE_TYPE_REPR below
     enum Type {
         TYPE_ANY = JS_REFERENCETYPEREPR_ANY,
         TYPE_OBJECT = JS_REFERENCETYPEREPR_OBJECT,
         TYPE_STRING = JS_REFERENCETYPEREPR_STRING,
     };
     static const int32_t TYPE_MAX = TYPE_STRING + 1;
@@ -307,17 +338,17 @@ class ReferenceTypeRepresentation : publ
     static JSObject *Create(JSContext *cx, Type type);
 };
 
 #define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_)                             \
     macro_(ReferenceTypeRepresentation::TYPE_ANY,    HeapValue, Any)        \
     macro_(ReferenceTypeRepresentation::TYPE_OBJECT, HeapPtrObject, Object) \
     macro_(ReferenceTypeRepresentation::TYPE_STRING, HeapPtrString, string)
 
-class X4TypeRepresentation : public TypeRepresentation {
+class X4TypeRepresentation : public SizedTypeRepresentation {
   public:
     enum Type {
         TYPE_INT32 = JS_X4TYPEREPR_INT32,
         TYPE_FLOAT32 = JS_X4TYPEREPR_FLOAT32,
     };
 
   private:
     // so TypeRepresentation can call appendStringScalar() etc
@@ -341,60 +372,84 @@ class X4TypeRepresentation : public Type
     static JSObject *Create(JSContext *cx, Type type);
 };
 
 // Must be in same order as the enum ScalarTypeRepresentation::Type:
 #define JS_FOR_EACH_X4_TYPE_REPR(macro_)                                      \
     macro_(X4TypeRepresentation::TYPE_INT32, int32_t, int32)                  \
     macro_(X4TypeRepresentation::TYPE_FLOAT32, float, float32)
 
-class ArrayTypeRepresentation : public TypeRepresentation {
+class UnsizedArrayTypeRepresentation : public TypeRepresentation {
   private:
     // so TypeRepresentation can call appendStringArray() etc
     friend class TypeRepresentation;
 
-    TypeRepresentation *element_;
-    size_t length_;
+    SizedTypeRepresentation *element_;
 
-    ArrayTypeRepresentation(TypeRepresentation *element,
-                            size_t length);
+    UnsizedArrayTypeRepresentation(SizedTypeRepresentation *element);
 
     // See TypeRepresentation::traceFields()
-    void traceArrayFields(JSTracer *trace);
+    void traceUnsizedArrayFields(JSTracer *trace);
 
     // See TypeRepresentation::appendString()
-    bool appendStringArray(JSContext *cx, StringBuffer &buffer);
+    bool appendStringUnsizedArray(JSContext *cx, StringBuffer &buffer);
 
   public:
-    TypeRepresentation *element() {
+    SizedTypeRepresentation *element() {
+        return element_;
+    }
+
+    static JSObject *Create(JSContext *cx,
+                            SizedTypeRepresentation *elementTypeRepr);
+};
+
+class SizedArrayTypeRepresentation : public SizedTypeRepresentation {
+  private:
+    // so TypeRepresentation can call appendStringSizedArray() etc
+    friend class TypeRepresentation;
+
+    SizedTypeRepresentation *element_;
+    size_t length_;
+
+    SizedArrayTypeRepresentation(SizedTypeRepresentation *element,
+                                 size_t length);
+
+    // See TypeRepresentation::traceFields()
+    void traceSizedArrayFields(JSTracer *trace);
+
+    // See TypeRepresentation::appendString()
+    bool appendStringSizedArray(JSContext *cx, StringBuffer &buffer);
+
+  public:
+    SizedTypeRepresentation *element() {
         return element_;
     }
 
     size_t length() {
         return length_;
     }
 
     static JSObject *Create(JSContext *cx,
-                            TypeRepresentation *elementTypeRepr,
+                            SizedTypeRepresentation *elementTypeRepr,
                             size_t length);
 };
 
 struct StructField {
     size_t index;
-    HeapId id;
-    TypeRepresentation *typeRepr;
+    HeapPtrPropertyName propertyName;
+    SizedTypeRepresentation *typeRepr;
     size_t offset;
 
     explicit StructField(size_t index,
-                         jsid &id,
-                         TypeRepresentation *typeRepr,
+                         PropertyName *propertyName,
+                         SizedTypeRepresentation *typeRepr,
                          size_t offset);
 };
 
-class StructTypeRepresentation : public TypeRepresentation {
+class StructTypeRepresentation : public SizedTypeRepresentation {
   private:
     // so TypeRepresentation can call appendStringStruct() etc
     friend class TypeRepresentation;
 
     size_t fieldCount_;
 
     // StructTypeRepresentations are allocated with extra space to
     // store the contents of the fields array.
@@ -402,17 +457,17 @@ class StructTypeRepresentation : public 
         return (StructField*) (this+1);
     }
     const StructField* fields() const {
         return (StructField*) (this+1);
     }
 
     StructTypeRepresentation();
     bool init(JSContext *cx,
-              AutoIdVector &ids,
+              AutoPropertyNameVector &names,
               AutoObjectVector &typeReprOwners);
 
     // See TypeRepresentation::traceFields()
     void traceStructFields(JSTracer *trace);
 
     // See TypeRepresentation::appendString()
     bool appendStringStruct(JSContext *cx, StringBuffer &buffer);
 
@@ -423,16 +478,66 @@ class StructTypeRepresentation : public 
 
     const StructField &field(size_t i) const {
         JS_ASSERT(i < fieldCount());
         return fields()[i];
     }
 
     const StructField *fieldNamed(jsid id) const;
 
+    // Creates a struct type from two parallel arrays:
+    // - `names`: the names of the struct type fields.
+    // - `typeReprOwners`: the types of each field, which are assumed
+    //   to be the owner objects for sized type representations.
     static JSObject *Create(JSContext *cx,
-                            AutoIdVector &ids,
+                            AutoPropertyNameVector &names,
                             AutoObjectVector &typeReprOwners);
 };
 
+// Definitions of the casting methods. These are pulled out of the
+// main class definition because both the super- and subtypes must be
+// defined for C++ to permit the static_cast.
+
+SizedTypeRepresentation *
+TypeRepresentation::asSized() {
+    JS_ASSERT(isSized());
+    return static_cast<SizedTypeRepresentation*>(this);
+}
+
+ScalarTypeRepresentation *
+TypeRepresentation::asScalar() {
+    JS_ASSERT(isScalar());
+    return static_cast<ScalarTypeRepresentation*>(this);
+}
+
+ReferenceTypeRepresentation *
+TypeRepresentation::asReference() {
+    JS_ASSERT(isReference());
+    return static_cast<ReferenceTypeRepresentation*>(this);
+}
+
+X4TypeRepresentation *
+TypeRepresentation::asX4() {
+    JS_ASSERT(isX4());
+    return static_cast<X4TypeRepresentation*>(this);
+}
+
+SizedArrayTypeRepresentation *
+TypeRepresentation::asSizedArray() {
+    JS_ASSERT(isSizedArray());
+    return static_cast<SizedArrayTypeRepresentation*>(this);
+}
+
+UnsizedArrayTypeRepresentation *
+TypeRepresentation::asUnsizedArray() {
+    JS_ASSERT(isUnsizedArray());
+    return static_cast<UnsizedArrayTypeRepresentation*>(this);
+}
+
+StructTypeRepresentation *
+TypeRepresentation::asStruct() {
+    JS_ASSERT(isStruct());
+    return static_cast<StructTypeRepresentation*>(this);
+}
+
 } // namespace js
 
 #endif
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -24,18 +24,19 @@
 #include "jsobjinlines.h"
 
 #include "vm/Shape-inl.h"
 
 using mozilla::DebugOnly;
 
 using namespace js;
 
-const Class js::TypedObjectClass = {
+const Class js::TypedObjectModuleObject::class_ = {
     "TypedObject",
+    JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
@@ -48,68 +49,64 @@ static const JSFunctionSpec TypedObjectM
 
 static void
 ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, const char *toType)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
                          InformalValueTypeName(fromValue), toType);
 }
 
-static void
-ReportCannotConvertTo(JSContext *cx, HandleObject fromObject, const char *toType)
-{
-    RootedValue fromValue(cx, ObjectValue(*fromObject));
-    ReportCannotConvertTo(cx, fromValue, toType);
-}
-
-static void
-ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, HandleString toType)
-{
-    const char *fnName = JS_EncodeString(cx, toType);
-    if (!fnName)
-        return;
-    ReportCannotConvertTo(cx, fromValue, fnName);
-    JS_free(cx, (void *) fnName);
-}
-
-static void
-ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, TypeRepresentation *toType)
-{
-    StringBuffer contents(cx);
-    if (!toType->appendString(cx, contents))
-        return;
-
-    RootedString result(cx, contents.finishString());
-    if (!result)
-        return;
-
-    ReportCannotConvertTo(cx, fromValue, result);
-}
-
-static int32_t
-Clamp(int32_t value, int32_t min, int32_t max)
-{
-    JS_ASSERT(min < max);
-    if (value < min)
-        return min;
-    if (value > max)
-        return max;
-    return value;
-}
-
 static inline JSObject *
 ToObjectIfObject(HandleValue value)
 {
     if (!value.isObject())
         return nullptr;
 
     return &value.toObject();
 }
 
 static inline bool
+IsTypedDatum(JSObject &obj)
+{
+    return obj.is<TypedObject>() || obj.is<TypedHandle>();
+}
+
+static inline uint8_t *
+TypedMem(JSObject &val)
+{
+    JS_ASSERT(IsTypedDatum(val));
+    return (uint8_t*) val.getPrivate();
+}
+
+static inline bool IsTypeObject(JSObject &type);
+
+/*
+ * Given a user-visible type descriptor object, returns the
+ * owner object for the TypeRepresentation* that we use internally.
+ */
+static JSObject *
+typeRepresentationOwnerObj(JSObject &typeObj)
+{
+    JS_ASSERT(IsTypeObject(typeObj));
+    return &typeObj.getReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR).toObject();
+}
+
+/*
+ * Given a user-visible type descriptor object, returns the
+ * TypeRepresentation* that we use internally.
+ *
+ * Note: this pointer is valid only so long as `typeObj` remains rooted.
+ */
+static TypeRepresentation *
+typeRepresentation(JSObject &typeObj)
+{
+    return TypeRepresentation::fromOwnerObject(*typeRepresentationOwnerObj(typeObj));
+}
+
+static inline bool
 IsScalarTypeObject(JSObject &type)
 {
     return type.hasClass(&ScalarType::class_);
 }
 
 static inline bool
 IsReferenceTypeObject(JSObject &type)
 {
@@ -151,49 +148,31 @@ IsTypeObject(JSObject &type)
 {
     return IsScalarTypeObject(type) ||
            IsReferenceTypeObject(type) ||
            IsX4TypeObject(type) ||
            IsComplexTypeObject(type);
 }
 
 static inline bool
-IsTypedDatum(JSObject &obj)
+IsTypeObjectOfKind(JSObject &type, TypeRepresentation::Kind kind)
 {
-    return obj.is<TypedObject>() || obj.is<TypedHandle>();
-}
-
-static inline uint8_t *
-TypedMem(JSObject &val)
-{
-    JS_ASSERT(IsTypedDatum(val));
-    return (uint8_t*) val.getPrivate();
+    if (!IsTypeObject(type))
+        return false;
+
+    return typeRepresentation(type)->kind() == kind;
 }
 
-/*
- * Given a user-visible type descriptor object, returns the
- * owner object for the TypeRepresentation* that we use internally.
- */
-static JSObject *
-typeRepresentationOwnerObj(JSObject &typeObj)
+static inline bool
+IsSizedTypeObject(JSObject &type)
 {
-    JS_ASSERT(IsTypeObject(typeObj));
-    return &typeObj.getReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR).toObject();
-}
-
-/*
- * Given a user-visible type descriptor object, returns the
- * TypeRepresentation* that we use internally.
- *
- * Note: this pointer is valid only so long as `typeObj` remains rooted.
- */
-static TypeRepresentation *
-typeRepresentation(JSObject &typeObj)
-{
-    return TypeRepresentation::fromOwnerObject(*typeRepresentationOwnerObj(typeObj));
+    if (!IsTypeObject(type))
+        return false;
+
+    return typeRepresentation(type)->isSized();
 }
 
 static inline JSObject *
 GetType(JSObject &datum)
 {
     JS_ASSERT(IsTypedDatum(datum));
     return &datum.getReservedSlot(JS_DATUM_SLOT_TYPE_OBJ).toObject();
 }
@@ -318,34 +297,83 @@ Reify(JSContext *cx,
 
     to.set(args.rval());
     return true;
 }
 
 static inline size_t
 TypeSize(JSObject &type)
 {
-    return typeRepresentation(type)->size();
+    return typeRepresentation(type)->asSized()->size();
+}
+
+static inline size_t
+DatumLength(JSObject &val)
+{
+    JS_ASSERT(IsTypedDatum(val));
+    JS_ASSERT(typeRepresentation(*GetType(val))->isAnyArray());
+    return val.getReservedSlot(JS_DATUM_SLOT_LENGTH).toInt32();
 }
 
 static inline size_t
 DatumSize(JSObject &val)
 {
-    return TypeSize(*GetType(val));
+    JSObject *typeObj = GetType(val);
+    TypeRepresentation *typeRepr = typeRepresentation(*typeObj);
+    switch (typeRepr->kind()) {
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::X4:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::SizedArray:
+        return typeRepr->asSized()->size();
+
+      case TypeRepresentation::UnsizedArray:
+        return typeRepr->asUnsizedArray()->element()->size() * DatumLength(val);
+    }
+    MOZ_ASSUME_UNREACHABLE("unhandled typerepresentation kind");
 }
 
 static inline bool
 IsTypedDatumOfKind(JSObject &obj, TypeRepresentation::Kind kind)
 {
     if (!IsTypedDatum(obj))
         return false;
     TypeRepresentation *repr = typeRepresentation(*GetType(obj));
     return repr->kind() == kind;
 }
 
+static inline bool
+IsArrayTypedDatum(JSObject &obj)
+{
+    if (!IsTypedDatum(obj))
+        return false;
+    TypeRepresentation *repr = typeRepresentation(*GetType(obj));
+    return repr->isAnyArray();
+}
+
+// Extracts the `prototype` property from `obj`, throwing if it is
+// missing or not an object.
+static JSObject *
+GetPrototype(JSContext *cx, HandleObject obj)
+{
+    RootedValue prototypeVal(cx);
+    if (!JSObject::getProperty(cx, obj, obj, cx->names().prototype,
+                               &prototypeVal))
+    {
+        return nullptr;
+    }
+    if (!prototypeVal.isObject()) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                             JSMSG_INVALID_PROTOTYPE);
+        return nullptr;
+    }
+    return &prototypeVal.toObject();
+}
+
 /***************************************************************************
  * Scalar type objects
  *
  * Scalar type objects like `uint8`, `uint16`, are all instances of
  * the ScalarType class. Like all type objects, they have a reserved
  * slot pointing to a TypeRepresentation object, which is used to
  * distinguish which scalar type object this actually is.
  */
@@ -366,16 +394,17 @@ const Class js::ScalarType::class_ = {
     nullptr,
     nullptr,
     nullptr
 };
 
 const JSFunctionSpec js::ScalarType::typeObjectMethods[] = {
     JS_FN("toSource", TypeObjectToSource, 0, 0),
     {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
     JS_FS_END
 };
 
 template <typename T>
 static T ConvertScalar(double d)
 {
     if (TypeIsFloatingPoint<T>()) {
@@ -450,16 +479,17 @@ const Class js::ReferenceType::class_ = 
     nullptr,
     nullptr,
     nullptr
 };
 
 const JSFunctionSpec js::ReferenceType::typeObjectMethods[] = {
     JS_FN("toSource", TypeObjectToSource, 0, 0),
     {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
     JS_FS_END
 };
 
 bool
 js::ReferenceType::call(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -537,41 +567,25 @@ js::ReferenceType::call(JSContext *cx, u
  * (And, again, same with respect to s and S.)
  *
  * This function creates the A.prototype/S.prototype object.  It takes
  * as an argument either the TypedObject.ArrayType or the
  * TypedObject.StructType constructor function, then returns an empty
  * object with the .prototype.prototype object as its [[Prototype]].
  */
 static JSObject *
-CreateComplexTypeInstancePrototype(JSContext *cx,
-                                   HandleObject typeObjectCtor)
+CreatePrototypeObjectForComplexTypeInstance(JSContext *cx,
+                                            HandleObject ctorPrototype)
 {
-    RootedValue ctorPrototypeVal(cx);
-    if (!JSObject::getProperty(cx, typeObjectCtor, typeObjectCtor,
-                               cx->names().prototype,
-                               &ctorPrototypeVal))
-    {
+    RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
+    if (!ctorPrototypePrototype)
         return nullptr;
-    }
-    JS_ASSERT(ctorPrototypeVal.isObject()); // immutable binding
-    RootedObject ctorPrototypeObj(cx, &ctorPrototypeVal.toObject());
-
-    RootedValue ctorPrototypePrototypeVal(cx);
-    if (!JSObject::getProperty(cx, ctorPrototypeObj, ctorPrototypeObj,
-                               cx->names().prototype,
-                               &ctorPrototypePrototypeVal))
-    {
-        return nullptr;
-    }
-
-    JS_ASSERT(ctorPrototypePrototypeVal.isObject()); // immutable binding
-    RootedObject proto(cx, &ctorPrototypePrototypeVal.toObject());
-    return NewObjectWithGivenProto(cx, &JSObject::class_, proto,
-                                   &typeObjectCtor->global());
+
+    return NewObjectWithGivenProto(cx, &JSObject::class_,
+                                   &*ctorPrototypePrototype, nullptr);
 }
 
 const Class ArrayType::class_ = {
     "ArrayType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEOBJ_ARRAY_SLOTS),
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
@@ -588,384 +602,254 @@ const Class ArrayType::class_ = {
 };
 
 const JSPropertySpec ArrayType::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec ArrayType::typeObjectMethods[] = {
     {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
-    JS_FN("repeat", ArrayType::repeat, 1, 0),
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
+    JS_FN("dimension", ArrayType::dimension, 1, 0),
     JS_FN("toSource", TypeObjectToSource, 0, 0),
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
     JS_FS_END
 };
 
 const JSPropertySpec ArrayType::typedObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec ArrayType::typedObjectMethods[] = {
-    JS_FN("subarray", ArrayType::subarray, 2, 0),
     {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"},
     {"redimension", {nullptr, nullptr}, 1, 0, "TypedArrayRedimension"},
     JS_FS_END
 };
 
 static JSObject *
-ArrayElementType(HandleObject array)
-{
-    JS_ASSERT(IsArrayTypeObject(*array));
-    return &array->getReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE).toObject();
-}
-
-static bool
-FillTypedArrayWithValue(JSContext *cx, HandleObject array, HandleValue val)
-{
-    JS_ASSERT(IsTypedDatumOfKind(*array, TypeRepresentation::Array));
-
-    RootedFunction func(
-        cx, SelfHostedFunction(cx, cx->names().FillTypedArrayWithValue));
-    if (!func)
-        return false;
-
-    InvokeArgs args(cx);
-    if (!args.init(2))
-        return false;
-
-    args.setCallee(ObjectValue(*func));
-    args[0].setObject(*array);
-    args[1].set(val);
-    return Invoke(cx, args);
-}
-
-bool
-ArrayType::repeat(JSContext *cx, unsigned int argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage,
-                             nullptr, JSMSG_MORE_ARGS_NEEDED,
-                             "repeat()", "0", "s");
-        return false;
-    }
-
-    RootedObject thisObj(cx, ToObjectIfObject(args.thisv()));
-    if (!thisObj || !IsArrayTypeObject(*thisObj)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_INCOMPATIBLE_PROTO,
-                             "ArrayType", "repeat",
-                             InformalValueTypeName(args.thisv()));
-        return false;
-    }
-
-    RootedObject binaryArray(cx, TypedObject::createZeroed(cx, thisObj));
-    if (!binaryArray)
-        return false;
-
-    RootedValue val(cx, args[0]);
-    if (!FillTypedArrayWithValue(cx, binaryArray, val))
-        return false;
-
-    args.rval().setObject(*binaryArray);
-    return true;
-}
-
-/**
- * The subarray function first creates an ArrayType instance
- * which will act as the elementType for the subarray.
- *
- * var MA = new ArrayType(elementType, 10);
- * var mb = MA.repeat(val);
- *
- * mb.subarray(begin, end=mb.length) => (Only for +ve)
- *     var internalSA = new ArrayType(elementType, end-begin);
- *     var ret = new internalSA()
- *     for (var i = begin; i < end; i++)
- *         ret[i-begin] = ret[i]
- *     return ret
- *
- * The range specified by the begin and end values is clamped to the valid
- * index range for the current array. If the computed length of the new
- * TypedArray would be negative, it is clamped to zero.
- * see: http://www.khronos.org/registry/typedarray/specs/latest/#7
- *
- */
-/* static */ bool
-ArrayType::subarray(JSContext *cx, unsigned int argc, Value *vp)
+ArrayElementType(JSObject &array)
 {
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage,
-                             nullptr, JSMSG_MORE_ARGS_NEEDED,
-                             "subarray()", "0", "s");
-        return false;
-    }
-
-    if (!args[0].isInt32()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_TYPEDOBJECT_SUBARRAY_INTEGER_ARG, "1");
-        return false;
-    }
-
-    RootedObject thisObj(cx, &args.thisv().toObject());
-    if (!IsTypedDatumOfKind(*thisObj, TypeRepresentation::Array)) {
-        ReportCannotConvertTo(cx, thisObj, "binary array");
-        return false;
-    }
-
-    RootedObject type(cx, GetType(*thisObj));
-    ArrayTypeRepresentation *typeRepr = typeRepresentation(*type)->asArray();
-    size_t length = typeRepr->length();
-
-    int32_t begin = args[0].toInt32();
-    int32_t end = length;
-
-    if (args.length() >= 2) {
-        if (!args[1].isInt32()) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                    JSMSG_TYPEDOBJECT_SUBARRAY_INTEGER_ARG, "2");
-            return false;
-        }
-
-        end = args[1].toInt32();
-    }
-
-    if (begin < 0)
-        begin = length + begin;
-    if (end < 0)
-        end = length + end;
-
-    begin = Clamp(begin, 0, length);
-    end = Clamp(end, 0, length);
-
-    int32_t sublength = end - begin; // end exclusive
-    sublength = Clamp(sublength, 0, length);
-
-    RootedObject globalObj(cx, cx->compartment()->maybeGlobal());
-    JS_ASSERT(globalObj);
-    Rooted<GlobalObject*> global(cx, &globalObj->as<GlobalObject>());
-    RootedObject arrayTypeGlobal(cx, global->getArrayType(cx));
-
-    RootedObject elementType(cx, ArrayElementType(type));
-    RootedObject subArrayType(cx, ArrayType::create(cx, arrayTypeGlobal,
-                                                    elementType, sublength));
-    if (!subArrayType)
-        return false;
-
-    int32_t elementSize = typeRepr->element()->size();
-    size_t offset = elementSize * begin;
-
-    RootedObject subarray(
-        cx, TypedDatum::createDerived(cx, subArrayType, thisObj, offset));
-    if (!subarray)
-        return false;
-
-    args.rval().setObject(*subarray);
-    return true;
-}
-
-static bool
-ArrayFillSubarray(JSContext *cx, unsigned int argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage,
-                             nullptr, JSMSG_MORE_ARGS_NEEDED,
-                             "fill()", "0", "s");
-        return false;
-    }
-
-    RootedObject thisObj(cx, ToObjectIfObject(args.thisv()));
-    if (!thisObj || !IsTypedDatumOfKind(*thisObj, TypeRepresentation::Array)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage,
-                             nullptr, JSMSG_INCOMPATIBLE_PROTO,
-                             "ArrayType", "fill",
-                             InformalValueTypeName(args.thisv()));
-        return false;
-    }
-
-    Value funArrayTypeVal = GetFunctionNativeReserved(&args.callee(), 0);
-    JS_ASSERT(funArrayTypeVal.isObject());
-
-    RootedObject type(cx, GetType(*thisObj));
-    TypeRepresentation *typeRepr = typeRepresentation(*type);
-    RootedObject funArrayType(cx, &funArrayTypeVal.toObject());
-    TypeRepresentation *funArrayTypeRepr = typeRepresentation(*funArrayType);
-    if (typeRepr != funArrayTypeRepr) {
-        RootedValue thisObjValue(cx, ObjectValue(*thisObj));
-        ReportCannotConvertTo(cx, thisObjValue, funArrayTypeRepr);
-        return false;
-    }
-
-    args.rval().setUndefined();
-    RootedValue val(cx, args[0]);
-    return FillTypedArrayWithValue(cx, thisObj, val);
+    JS_ASSERT(IsArrayTypeObject(array));
+    return &array.getReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE).toObject();
 }
 
 static bool
 InitializeCommonTypeDescriptorProperties(JSContext *cx,
                                          HandleObject obj,
                                          HandleObject typeReprOwnerObj)
 {
     TypeRepresentation *typeRepr =
         TypeRepresentation::fromOwnerObject(*typeReprOwnerObj);
 
-    if (typeRepr->transparent()) {
+    if (typeRepr->transparent() && typeRepr->isSized()) {
+        SizedTypeRepresentation *sizedTypeRepr = typeRepr->asSized();
+
         // byteLength
-        RootedValue typeByteLength(cx, NumberValue(typeRepr->size()));
+        RootedValue typeByteLength(cx, NumberValue(sizedTypeRepr->size()));
         if (!JSObject::defineProperty(cx, obj, cx->names().byteLength,
                                       typeByteLength,
                                       nullptr, nullptr,
                                       JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
 
         // byteAlignment
-        RootedValue typeByteAlignment(cx, NumberValue(typeRepr->alignment()));
+        RootedValue typeByteAlignment(
+            cx, NumberValue(sizedTypeRepr->alignment()));
         if (!JSObject::defineProperty(cx, obj, cx->names().byteAlignment,
                                       typeByteAlignment,
                                       nullptr, nullptr,
                                       JSPROP_READONLY | JSPROP_PERMANENT))
         {
             return false;
         }
+    } else {
+        RootedValue undef(cx, UndefinedValue());
+
+        // byteLength
+        if (!JSObject::defineProperty(cx, obj, cx->names().byteLength,
+                                      undef,
+                                      nullptr, nullptr,
+                                      JSPROP_READONLY | JSPROP_PERMANENT))
+        {
+            return false;
+        }
+
+        // byteAlignment
+        if (!JSObject::defineProperty(cx, obj, cx->names().byteAlignment,
+                                      undef,
+                                      nullptr, nullptr,
+                                      JSPROP_READONLY | JSPROP_PERMANENT))
+        {
+            return false;
+        }
     }
 
-    // variable -- always false since we do not yet support variable-size types
-    RootedValue variable(cx, JSVAL_FALSE);
+    // variable -- true for unsized arrays
+    RootedValue variable(cx, BooleanValue(!typeRepr->isSized()));
     if (!JSObject::defineProperty(cx, obj, cx->names().variable,
                                   variable,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return false;
     }
 
     return true;
 }
 
 JSObject *
 ArrayType::create(JSContext *cx,
-                  HandleObject metaTypeObject,
-                  HandleObject elementType,
-                  size_t length)
+                  HandleObject arrayTypePrototype,
+                  HandleObject arrayTypeReprObj,
+                  HandleObject elementType)
 {
-    JS_ASSERT(elementType);
+    JS_ASSERT(TypeRepresentation::isOwnerObject(*arrayTypeReprObj));
     JS_ASSERT(IsTypeObject(*elementType));
 
-    TypeRepresentation *elementTypeRepr = typeRepresentation(*elementType);
-    RootedObject typeReprObj(
-        cx, ArrayTypeRepresentation::Create(cx, elementTypeRepr, length));
-    if (!typeReprObj)
-        return nullptr;
-
-    RootedValue prototypeVal(cx);
-    if (!JSObject::getProperty(cx, metaTypeObject, metaTypeObject,
-                               cx->names().prototype,
-                               &prototypeVal))
-    {
-        return nullptr;
-    }
-    JS_ASSERT(prototypeVal.isObject()); // immutable binding
-
     RootedObject obj(
-        cx, NewObjectWithGivenProto(cx, &ArrayType::class_,
-                                    &prototypeVal.toObject(), cx->global()));
+        cx, NewObjectWithClassProto(cx, &ArrayType::class_,
+                                    arrayTypePrototype, nullptr));
     if (!obj)
         return nullptr;
-    obj->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
+    obj->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR,
+                          ObjectValue(*arrayTypeReprObj));
 
     RootedValue elementTypeVal(cx, ObjectValue(*elementType));
     if (!JSObject::defineProperty(cx, obj, cx->names().elementType,
                                   elementTypeVal, nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return nullptr;
 
     obj->initReservedSlot(JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE, elementTypeVal);
 
-    RootedValue lengthVal(cx, Int32Value(length));
-    if (!JSObject::defineProperty(cx, obj, cx->names().length,
-                                  lengthVal, nullptr, nullptr,
-                                  JSPROP_READONLY | JSPROP_PERMANENT))
-        return nullptr;
-
-    if (!InitializeCommonTypeDescriptorProperties(cx, obj, typeReprObj))
+    if (!InitializeCommonTypeDescriptorProperties(cx, obj, arrayTypeReprObj))
         return nullptr;
 
     RootedObject prototypeObj(
-        cx, CreateComplexTypeInstancePrototype(cx, metaTypeObject));
+        cx, CreatePrototypeObjectForComplexTypeInstance(cx, arrayTypePrototype));
     if (!prototypeObj)
         return nullptr;
 
     if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
         return nullptr;
 
-    JSFunction *fillFun = DefineFunctionWithReserved(cx, prototypeObj, "fill", ArrayFillSubarray, 1, 0);
-    if (!fillFun)
-        return nullptr;
-
-    // This is important
-    // so that A.prototype.fill.call(b, val)
-    // where b.type != A raises an error
-    SetFunctionNativeReserved(fillFun, 0, ObjectValue(*obj));
-
     return obj;
 }
 
 bool
 ArrayType::construct(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!args.isConstructing()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_NOT_FUNCTION, "ArrayType");
         return false;
     }
 
-    if (args.length() != 2 ||
-        !args[0].isObject() ||
-        !args[1].isNumber() ||
-        args[1].toNumber() < 0)
+    RootedObject arrayTypeGlobal(cx, &args.callee());
+
+    // Expect one argument which is a sized type object
+    if (args.length() < 1) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
+                             "ArrayType", "0", "");
+        return false;
+    }
+
+    if (!args[0].isObject() || !IsSizedTypeObject(args[0].toObject())) {
+        ReportCannotConvertTo(cx, args[0], "ArrayType element specifier");
+        return false;
+    }
+
+    RootedObject elementType(cx, &args[0].toObject());
+    SizedTypeRepresentation *elementTypeRepr =
+        typeRepresentation(*elementType)->asSized();
+
+    // construct the type repr
+    RootedObject arrayTypeReprObj(
+        cx, UnsizedArrayTypeRepresentation::Create(cx, elementTypeRepr));
+    if (!arrayTypeReprObj)
+        return false;
+
+    // Extract ArrayType.prototype
+    RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal));
+    if (!arrayTypePrototype)
+        return nullptr;
+
+    // Create the instance of ArrayType
+    RootedObject obj(
+        cx, create(cx, arrayTypePrototype, arrayTypeReprObj, elementType));
+    if (!obj)
+        return false;
+
+    // Add `length` property, which is undefined for an unsized array.
+    RootedValue lengthVal(cx, UndefinedValue());
+    if (!JSObject::defineProperty(cx, obj, cx->names().length,
+                                  lengthVal, nullptr, nullptr,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+        return nullptr;
+
+    args.rval().setObject(*obj);
+    return true;
+}
+
+/*static*/ bool
+ArrayType::dimension(JSContext *cx, unsigned int argc, jsval *vp)
+{
+    // Expect that the `this` pointer is an unsized array type
+    // and the first argument is an integer size.
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() != 1 ||
+        !args.thisv().isObject() ||
+        !IsTypeObjectOfKind(args.thisv().toObject(), TypeRepresentation::UnsizedArray) ||
+        !args[0].isInt32() ||
+        args[0].toInt32() < 0)
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                              JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
         return false;
     }
 
-    RootedObject arrayTypeGlobal(cx, &args.callee());
-    RootedObject elementType(cx, &args[0].toObject());
-
-    if (!IsTypeObject(*elementType)) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
+    // Extract arguments.
+    RootedObject unsizedTypeObj(cx, &args.thisv().toObject());
+    UnsizedArrayTypeRepresentation *unsizedTypeRepr =
+        typeRepresentation(*unsizedTypeObj)->asUnsizedArray();
+    int32_t length = args[0].toInt32();
+
+    // Create sized type representation.
+    RootedObject sizedTypeReprObj(
+        cx, SizedArrayTypeRepresentation::Create(
+            cx, unsizedTypeRepr->element(), length));
+    if (!sizedTypeReprObj)
         return false;
-    }
-
-    if (!args[1].isInt32()) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
-        return false;
-    }
-
-    int32_t length = args[1].toInt32();
-    if (length < 0) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
-                             JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
-        return false;
-    }
-
-    RootedObject obj(cx, create(cx, arrayTypeGlobal, elementType, length));
+
+    // Create the sized type object.
+    RootedObject elementType(cx, ArrayElementType(*unsizedTypeObj));
+    RootedObject obj(
+        cx, create(cx, unsizedTypeObj, sizedTypeReprObj, elementType));
     if (!obj)
         return false;
+
+    // Add `length` property.
+    RootedValue lengthVal(cx, Int32Value(length));
+    if (!JSObject::defineProperty(cx, obj, cx->names().length,
+                                  lengthVal, nullptr, nullptr,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+        return nullptr;
+
+    // Add `unsized` property, which is a link from the sized
+    // array to the unsized array.
+    RootedValue unsizedTypeObjValue(cx, ObjectValue(*unsizedTypeObj));
+    if (!JSObject::defineProperty(cx, obj, cx->names().unsized,
+                                  unsizedTypeObjValue, nullptr, nullptr,
+                                  JSPROP_READONLY | JSPROP_PERMANENT))
+        return nullptr;
+
     args.rval().setObject(*obj);
     return true;
 }
 
 /*********************************
  * StructType class
  */
 
@@ -989,16 +873,17 @@ const Class StructType::class_ = {
 };
 
 const JSPropertySpec StructType::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec StructType::typeObjectMethods[] = {
     {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
     JS_FN("toSource", TypeObjectToSource, 0, 0),
     {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
     JS_FS_END
 };
 
 const JSPropertySpec StructType::typedObjectProperties[] = {
     JS_PS_END
 };
@@ -1014,54 +899,61 @@ const JSFunctionSpec StructType::typedOb
  */
 bool
 StructType::layout(JSContext *cx, HandleObject structType, HandleObject fields)
 {
     AutoIdVector ids(cx);
     if (!GetPropertyNames(cx, fields, JSITER_OWNONLY, &ids))
         return false;
 
+    AutoPropertyNameVector fieldNames(cx);
     AutoValueVector fieldTypeObjs(cx);
     AutoObjectVector fieldTypeReprObjs(cx);
 
     RootedValue fieldTypeVal(cx);
     RootedId id(cx);
     for (unsigned int i = 0; i < ids.length(); i++) {
         id = ids[i];
 
-        if (!JSObject::getGeneric(cx, fields, fields, id, &fieldTypeVal))
-            return false;
-
-        uint32_t index;
-        if (js_IdIsIndex(id, &index)) {
+        // Check that all the property names are non-numeric strings.
+        uint32_t unused;
+        if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&unused)) {
             RootedValue idValue(cx, IdToJsval(id));
             ReportCannotConvertTo(cx, idValue, "StructType field name");
             return false;
         }
 
+        if (!fieldNames.append(JSID_TO_ATOM(id)->asPropertyName())) {
+            js_ReportOutOfMemory(cx);
+            return false;
+        }
+
+        if (!JSObject::getGeneric(cx, fields, fields, id, &fieldTypeVal))
+            return false;
+
         RootedObject fieldType(cx, ToObjectIfObject(fieldTypeVal));
-        if (!fieldType || !IsTypeObject(*fieldType)) {
+        if (!fieldType || !IsSizedTypeObject(*fieldType)) {
             ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
             return false;
         }
 
         if (!fieldTypeObjs.append(fieldTypeVal)) {
             js_ReportOutOfMemory(cx);
             return false;
         }
 
         if (!fieldTypeReprObjs.append(typeRepresentationOwnerObj(*fieldType))) {
             js_ReportOutOfMemory(cx);
             return false;
         }
     }
 
     // Construct the `TypeRepresentation*`.
-    RootedObject typeReprObj(
-        cx, StructTypeRepresentation::Create(cx, ids, fieldTypeReprObjs));
+    RootedObject typeReprObj(cx);
+    typeReprObj = StructTypeRepresentation::Create(cx, fieldNames, fieldTypeReprObjs);
     if (!typeReprObj)
         return false;
     StructTypeRepresentation *typeRepr =
         TypeRepresentation::fromOwnerObject(*typeReprObj)->asStruct();
     structType->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR,
                                  ObjectValue(*typeReprObj));
 
     // Construct for internal use an array with the type object for each field.
@@ -1097,17 +989,17 @@ StructType::layout(JSContext *cx, Handle
     // fieldOffsets : { string: integer, ... }
     // fieldTypes : { string: Type, ... }
     RootedObject fieldOffsets(
         cx, NewObjectWithClassProto(cx, &JSObject::class_, nullptr, nullptr));
     RootedObject fieldTypes(
         cx, NewObjectWithClassProto(cx, &JSObject::class_, nullptr, nullptr));
     for (size_t i = 0; i < typeRepr->fieldCount(); i++) {
         const StructField &field = typeRepr->field(i);
-        RootedId fieldId(cx, field.id);
+        RootedId fieldId(cx, NameToId(field.propertyName));
 
         // fieldOffsets[id] = offset
         RootedValue offset(cx, NumberValue(field.offset));
         if (!JSObject::defineGeneric(cx, fieldOffsets, fieldId,
                                      offset, nullptr, nullptr,
                                      JSPROP_READONLY | JSPROP_PERMANENT))
             return false;
 
@@ -1129,45 +1021,43 @@ StructType::layout(JSContext *cx, Handle
                                   fieldTypesValue, nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return false;
 
     return true;
 }
 
 JSObject *
-StructType::create(JSContext *cx, HandleObject metaTypeObject,
+StructType::create(JSContext *cx,
+                   HandleObject metaTypeObject,
                    HandleObject fields)
 {
-    RootedValue prototypeVal(cx);
-    if (!JSObject::getProperty(cx, metaTypeObject, metaTypeObject,
-                               cx->names().prototype,
-                               &prototypeVal))
+    RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeObject));
+    if (!structTypePrototype)
         return nullptr;
-    JS_ASSERT(prototypeVal.isObject()); // immutable binding
 
     RootedObject obj(
-        cx, NewObjectWithGivenProto(cx, &StructType::class_,
-                                    &prototypeVal.toObject(), cx->global()));
+        cx, NewObjectWithClassProto(cx, &StructType::class_,
+                                    structTypePrototype, nullptr));
     if (!obj)
         return nullptr;
 
     if (!StructType::layout(cx, obj, fields))
         return nullptr;
 
     RootedObject fieldsProto(cx);
     if (!JSObject::getProto(cx, fields, &fieldsProto))
         return nullptr;
 
     RootedObject typeReprObj(cx, typeRepresentationOwnerObj(*obj));
     if (!InitializeCommonTypeDescriptorProperties(cx, obj, typeReprObj))
         return nullptr;
 
     RootedObject prototypeObj(
-        cx, CreateComplexTypeInstancePrototype(cx, metaTypeObject));
+        cx, CreatePrototypeObjectForComplexTypeInstance(cx, structTypePrototype));
     if (!prototypeObj)
         return nullptr;
 
     if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
         return nullptr;
 
     return obj;
 }
@@ -1305,40 +1195,58 @@ const Class X4Type::class_ = {
     nullptr
 };
 
 // These classes just exist to group together various properties and so on.
 namespace js {
 class Int32x4Defn {
   public:
     static const X4TypeRepresentation::Type type = X4TypeRepresentation::TYPE_INT32;
+    static const JSFunctionSpec TypeDescriptorMethods[];
     static const JSPropertySpec TypedObjectProperties[];
     static const JSFunctionSpec TypedObjectMethods[];
 };
 class Float32x4Defn {
   public:
     static const X4TypeRepresentation::Type type = X4TypeRepresentation::TYPE_FLOAT32;
+    static const JSFunctionSpec TypeDescriptorMethods[];
     static const JSPropertySpec TypedObjectProperties[];
     static const JSFunctionSpec TypedObjectMethods[];
 };
 } // namespace js
 
+const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = {
+    JS_FN("toSource", TypeObjectToSource, 0, 0),
+    {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
+    {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
+    JS_FS_END
+};
+
 const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = {
     JS_SELF_HOSTED_GET("x", "Int32x4Lane0", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("y", "Int32x4Lane1", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("z", "Int32x4Lane2", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("w", "Int32x4Lane3", JSPROP_PERMANENT),
     JS_PS_END
 };
 
 const JSFunctionSpec js::Int32x4Defn::TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0),
     JS_FS_END
 };
 
+const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = {
+    JS_FN("toSource", TypeObjectToSource, 0, 0),
+    {"handle", {nullptr, nullptr}, 2, 0, "HandleCreate"},
+    {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
+    {"equivalent", {nullptr, nullptr}, 1, 0, "TypeObjectEquivalent"},
+    JS_FS_END
+};
+
 const JSPropertySpec js::Float32x4Defn::TypedObjectProperties[] = {
     JS_SELF_HOSTED_GET("x", "Float32x4Lane0", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("y", "Float32x4Lane1", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("z", "Float32x4Lane2", JSPROP_PERMANENT),
     JS_SELF_HOSTED_GET("w", "Float32x4Lane3", JSPROP_PERMANENT),
     JS_PS_END
 };
 
@@ -1384,16 +1292,19 @@ CreateX4Class(JSContext *cx, Handle<Glob
     }
 
     // Link type constructor to the type representation
 
     x4->initReservedSlot(JS_TYPEOBJ_SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
 
     // Link constructor to prototype and install properties
 
+    if (!JS_DefineFunctions(cx, x4, T::TypeDescriptorMethods))
+        return nullptr;
+
     if (!LinkConstructorAndPrototype(cx, x4, proto) ||
         !DefinePropertiesAndBrand(cx, proto, T::TypedObjectProperties,
                                   T::TypedObjectMethods))
     {
         return nullptr;
     }
 
     return x4;
@@ -1413,17 +1324,17 @@ X4Type::call(JSContext *cx, unsigned arg
 
     double values[LANES];
     for (uint32_t i = 0; i < LANES; i++) {
         if (!ToNumber(cx, args[i], &values[i]))
             return false;
     }
 
     RootedObject typeObj(cx, &args.callee());
-    RootedObject result(cx, TypedObject::createZeroed(cx, typeObj));
+    RootedObject result(cx, TypedObject::createZeroed(cx, typeObj, 0));
     if (!result)
         return false;
 
     X4TypeRepresentation *typeRepr = typeRepresentation(*typeObj)->asX4();
     switch (typeRepr->type()) {
 #define STORE_LANES(_constant, _type, _name)                                  \
       case _constant:                                                         \
       {                                                                       \
@@ -1440,17 +1351,19 @@ X4Type::call(JSContext *cx, unsigned arg
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 
 template<typename T>
 static JSObject *
 DefineMetaTypeObject(JSContext *cx,
-                     Handle<GlobalObject*> global)
+                     Handle<GlobalObject*> global,
+                     HandleObject module,
+                     TypedObjectModuleObject::Slot protoSlot)
 {
     RootedAtom className(cx, Atomize(cx, T::class_.name,
                                      strlen(T::class_.name)));
     if (!className)
         return nullptr;
 
     RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
     if (!funcProto)
@@ -1480,60 +1393,74 @@ DefineMetaTypeObject(JSContext *cx,
                                   protoProtoValue,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return nullptr;
 
     // Create ctor itself
 
     const int constructorLength = 2;
-    RootedFunction ctor(cx,
-                        global->createConstructor(cx, T::construct,
-                                                  className, constructorLength));
+    RootedFunction ctor(cx);
+    ctor = global->createConstructor(cx, T::construct, className, constructorLength);
     if (!ctor ||
         !LinkConstructorAndPrototype(cx, ctor, proto) ||
         !DefinePropertiesAndBrand(cx, proto,
                                   T::typeObjectProperties,
                                   T::typeObjectMethods) ||
         !DefinePropertiesAndBrand(cx, protoProto,
                                   T::typedObjectProperties,
                                   T::typedObjectMethods))
     {
         return nullptr;
     }
 
+    module->initReservedSlot(protoSlot, ObjectValue(*proto));
+
     return ctor;
 }
 
+bool
+GlobalObject::initTypedObjectModule(JSContext *cx, Handle<GlobalObject*> global)
+{
+    RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+    if (!objProto)
+        return false;
+
+    RootedObject module(cx, NewObjectWithClassProto(cx, &TypedObjectModuleObject::class_,
+                                                    objProto, global));
+    if (!module)
+        return false;
+
+    if (!JS_DefineFunctions(cx, module, TypedObjectMethods))
+        return false;
+
+    RootedValue moduleValue(cx, ObjectValue(*module));
+
+    global->setConstructor(JSProto_TypedObject, moduleValue);
+    return true;
+}
+
 JSObject *
-js_InitTypedObjectClass(JSContext *cx, HandleObject obj)
+js_InitTypedObjectModuleObject(JSContext *cx, HandleObject obj)
 {
     /*
      * The initialization strategy for TypedObjects is mildly unusual
      * compared to other classes. Because all of the types are members
      * of a single global, `TypedObject`, we basically make the
      * initialized for the `TypedObject` class populate the
      * `TypedObject` global (which is referred to as "module" herein).
      */
 
     JS_ASSERT(obj->is<GlobalObject>());
     Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
 
-    RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
-    if (!objProto)
-        return nullptr;
-
-    RootedObject module(cx, NewObjectWithGivenProto(cx, &JSObject::class_,
-                                                    objProto, global));
+    RootedObject module(cx, global->getOrCreateTypedObjectModule(cx));
     if (!module)
         return nullptr;
 
-    if (!JS_DefineFunctions(cx, module, TypedObjectMethods))
-        return nullptr;
-
     // Define TypedObject global.
 
     RootedValue moduleValue(cx, ObjectValue(*module));
 
     // uint8, uint16, any, etc
 
 #define BINARYDATA_SCALAR_DEFINE(constant_, type_, name_)                       \
     if (!DefineSimpleTypeObject<ScalarType>(cx, global, module, constant_,      \
@@ -1578,49 +1505,44 @@ js_InitTypedObjectClass(JSContext *cx, H
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
     // ArrayType.
 
-    RootedObject arrayType(cx, DefineMetaTypeObject<ArrayType>(cx, global));
+    RootedObject arrayType(cx);
+    arrayType = DefineMetaTypeObject<ArrayType>(
+        cx, global, module, TypedObjectModuleObject::ArrayTypePrototype);
     if (!arrayType)
         return nullptr;
 
     RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
     if (!JSObject::defineProperty(cx, module, cx->names().ArrayType,
                                   arrayTypeValue,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return nullptr;
 
     // StructType.
 
-    RootedObject structType(cx, DefineMetaTypeObject<StructType>(cx, global));
+    RootedObject structType(cx);
+    structType = DefineMetaTypeObject<StructType>(
+        cx, global, module, TypedObjectModuleObject::StructTypePrototype);
     if (!structType)
         return nullptr;
 
     RootedValue structTypeValue(cx, ObjectValue(*structType));
     if (!JSObject::defineProperty(cx, module, cx->names().StructType,
                                   structTypeValue,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
         return nullptr;
 
-    // Everything is setup, install module on the global object:
-    if (!JSObject::defineProperty(cx, global, cx->names().TypedObject,
-                                  moduleValue,
-                                  nullptr, nullptr,
-                                  0))
-        return nullptr;
-    global->setConstructor(JSProto_TypedObject, moduleValue);
-    global->setArrayType(arrayType);
-
     //  Handle
 
     RootedObject handle(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
     if (!module)
         return nullptr;
 
     if (!JS_DefineFunctions(cx, handle, TypedHandle::handleStaticMethods))
         return nullptr;
@@ -1629,57 +1551,108 @@ js_InitTypedObjectClass(JSContext *cx, H
     if (!JSObject::defineProperty(cx, module, cx->names().Handle,
                                   handleValue,
                                   nullptr, nullptr,
                                   JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
+    // Everything is setup, install module on the global object:
+    if (!JSObject::defineProperty(cx, global, cx->names().TypedObject,
+                                  moduleValue,
+                                  nullptr, nullptr,
+                                  0))
+        return nullptr;
+
     return module;
 }
 
 JSObject *
 js_InitTypedObjectDummy(JSContext *cx, HandleObject obj)
 {
     /*
      * This function is entered into the jsprototypes.h table
      * as the initializer for `TypedObject`. It should not
      * be executed via the `standard_class_atoms` mechanism.
      */
 
     MOZ_ASSUME_UNREACHABLE("shouldn't be initializing TypedObject via the JSProtoKey initializer mechanism");
 }
 
+bool
+TypedObjectModuleObject::getSuitableClaspAndProto(JSContext *cx,
+                                                  TypeRepresentation::Kind kind,
+                                                  const Class **clasp,
+                                                  MutableHandleObject proto)
+{
+    switch (kind) {
+      case TypeRepresentation::Scalar:
+        *clasp = &ScalarType::class_;
+        proto.set(global().getOrCreateFunctionPrototype(cx));
+        break;
+
+      case TypeRepresentation::Reference:
+        *clasp = &ReferenceType::class_;
+        proto.set(global().getOrCreateFunctionPrototype(cx));
+        break;
+
+      case TypeRepresentation::X4:
+        *clasp = &X4Type::class_;
+        proto.set(global().getOrCreateFunctionPrototype(cx));
+        break;
+
+      case TypeRepresentation::Struct:
+        *clasp = &StructType::class_;
+        proto.set(&getSlot(StructTypePrototype).toObject());
+        break;
+
+      case TypeRepresentation::SizedArray:
+        *clasp = &ArrayType::class_;
+        proto.set(&getSlot(ArrayTypePrototype).toObject());
+        break;
+
+      case TypeRepresentation::UnsizedArray:
+        *clasp = &ArrayType::class_;
+        proto.set(&getSlot(ArrayTypePrototype).toObject());
+        break;
+    }
+
+    return !!proto;
+}
+
 /******************************************************************************
  * Typed datums
  *
  * Datums represent either typed objects or handles. See comment in
  * TypedObject.h.
  */
 
 template<class T>
 /*static*/ T *
 TypedDatum::createUnattached(JSContext *cx,
-                             HandleObject type)
+                             HandleObject type,
+                             int32_t length)
 {
     JS_STATIC_ASSERT(T::IsTypedDatumClass);
     JS_ASSERT(IsTypeObject(*type));
 
-    RootedObject obj(cx, createUnattachedWithClass(cx, &T::class_, type));
+    RootedObject obj(
+        cx, createUnattachedWithClass(cx, &T::class_, type, length));
     if (!obj)
         return nullptr;
 
     return &obj->as<T>();
 }
 
 /*static*/ TypedDatum *
 TypedDatum::createUnattachedWithClass(JSContext *cx,
                                       const Class *clasp,
-                                      HandleObject type)
+                                      HandleObject type,
+                                      int32_t length)
 {
     JS_ASSERT(IsTypeObject(*type));
     JS_ASSERT(clasp == &TypedObject::class_ || clasp == &TypedHandle::class_);
     JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_DATUM_SLOTS);
     JS_ASSERT(clasp->hasPrivate());
 
     RootedObject proto(cx);
     if (IsSimpleTypeObject(*type)) {
@@ -1698,26 +1671,31 @@ TypedDatum::createUnattachedWithClass(JS
     RootedObject obj(
         cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr));
     if (!obj)
         return nullptr;
 
     obj->setPrivate(nullptr);
     obj->initReservedSlot(JS_DATUM_SLOT_TYPE_OBJ, ObjectValue(*type));
     obj->initReservedSlot(JS_DATUM_SLOT_OWNER, NullValue());
+    obj->initReservedSlot(JS_DATUM_SLOT_LENGTH, Int32Value(length));
 
     // Tag the type object for this instance with the type
     // representation, if that has not been done already.
     if (cx->typeInferenceEnabled() && !IsSimpleTypeObject(*type)) {
         // FIXME Bug 929651           ^~~~~~~~~~~~~~~~~~~~~~~~~~~
         RootedTypeObject typeObj(cx, obj->getType(cx));
         if (typeObj) {
             TypeRepresentation *typeRepr = typeRepresentation(*type);
-            if (!typeObj->addTypedObjectAddendum(cx, typeRepr))
+            if (!typeObj->addTypedObjectAddendum(cx,
+                                                 types::TypeTypedObject::Datum,
+                                                 typeRepr))
+            {
                 return nullptr;
+            }
         }
     }
 
     return static_cast<TypedDatum*>(&*obj);
 }
 
 /*static*/ void
 TypedDatum::attach(uint8_t *memory)
@@ -1737,26 +1715,52 @@ TypedDatum::attach(JSObject &datum, uint
 
     // find the owner, which is often but not always `datum`
     JSObject &owner = datum.getReservedSlot(JS_DATUM_SLOT_OWNER).toObject();
 
     setPrivate(mem);
     setReservedSlot(JS_DATUM_SLOT_OWNER, ObjectValue(owner));
 }
 
+// Returns a suitable JS_DATUM_SLOT_LENGTH value for an instance of
+// the type `type`. `type` must not be an unsized array.
+static int32_t
+DatumLengthFromType(JSObject &type)
+{
+    TypeRepresentation *typeRepr = typeRepresentation(type);
+    switch (typeRepr->kind()) {
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::X4:
+        return 0;
+
+      case TypeRepresentation::SizedArray:
+        return typeRepr->asSizedArray()->length();
+
+      case TypeRepresentation::UnsizedArray:
+        MOZ_ASSUME_UNREACHABLE("DatumLengthFromType() invoked on unsized type");
+    }
+    MOZ_ASSUME_UNREACHABLE("Invalid kind");
+}
+
 /*static*/ TypedDatum *
 TypedDatum::createDerived(JSContext *cx, HandleObject type,
                           HandleObject datum, size_t offset)
 {
+    JS_ASSERT(IsTypeObject(*type));
     JS_ASSERT(IsTypedDatum(*datum));
     JS_ASSERT(offset <= DatumSize(*datum));
     JS_ASSERT(offset + TypeSize(*type) <= DatumSize(*datum));
 
+    int32_t length = DatumLengthFromType(*type);
+
     const js::Class *clasp = datum->getClass();
-    Rooted<TypedDatum*> obj(cx, createUnattachedWithClass(cx, clasp, type));
+    Rooted<TypedDatum*> obj(
+        cx, createUnattachedWithClass(cx, clasp, type, length));
     if (!obj)
         return nullptr;
 
     obj->attach(*datum, offset);
     return obj;
 }
 
 static bool
@@ -1791,20 +1795,34 @@ ReportDatumTypeError(JSContext *cx,
 /*static*/ void
 TypedDatum::obj_trace(JSTracer *trace, JSObject *object)
 {
     JS_ASSERT(IsTypedDatum(*object));
 
     for (size_t i = 0; i < JS_DATUM_SLOTS; i++)
         gc::MarkSlot(trace, &object->getReservedSlotRef(i), "TypedObjectSlot");
 
-    uint8_t *mem = TypedMem(*object);
     TypeRepresentation *repr = typeRepresentation(*GetType(*object));
-    if (repr->opaque())
-        repr->traceInstance(trace, mem);
+    if (repr->opaque()) {
+        uint8_t *mem = TypedMem(*object);
+        switch (repr->kind()) {
+          case TypeRepresentation::Scalar:
+          case TypeRepresentation::Reference:
+          case TypeRepresentation::Struct:
+          case TypeRepresentation::SizedArray:
+          case TypeRepresentation::X4:
+            repr->asSized()->traceInstance(trace, mem, 1);
+            break;
+
+          case TypeRepresentation::UnsizedArray:
+            repr->asUnsizedArray()->element()->traceInstance(
+                trace, mem, DatumLength(*object));
+            break;
+        }
+    }
 }
 
 /*static*/ void
 TypedDatum::obj_finalize(js::FreeOp *op, JSObject *obj)
 {
     // if the obj owns the memory, indicating by the owner slot being
     // set to itself, then we must free it when finalized.
 
@@ -1829,17 +1847,18 @@ TypedDatum::obj_lookupGeneric(JSContext 
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
       case TypeRepresentation::X4:
         break;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
       {
         uint32_t index;
         if (js_IdIsIndex(id, &index))
             return obj_lookupElement(cx, obj, index, objp, propp);
 
         if (JSID_IS_ATOM(id, cx->names().length)) {
             MarkNonNativePropertyFound(propp);
             objp.set(obj);
@@ -2008,19 +2027,27 @@ TypedDatum::obj_getGeneric(JSContext *cx
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
         break;
 
       case TypeRepresentation::X4:
         break;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         if (JSID_IS_ATOM(id, cx->names().length)) {
-            vp.setInt32(typeRepr->asArray()->length());
+            if (!TypedMem(*obj)) { // unattached
+                JS_ReportErrorNumber(
+                    cx, js_GetErrorMessage,
+                    nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
+                return false;
+            }
+
+            vp.setInt32(DatumLength(*obj));
             return true;
         }
         break;
 
       case TypeRepresentation::Struct: {
         StructTypeRepresentation *structTypeRepr = typeRepr->asStruct();
         const StructField *field = structTypeRepr->fieldNamed(id);
         if (!field)
@@ -2069,28 +2096,33 @@ TypedDatum::obj_getElementIfPresent(JSCo
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
       case TypeRepresentation::X4:
       case TypeRepresentation::Struct:
         break;
 
-      case TypeRepresentation::Array: {
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
+      {
+        JS_ASSERT(IsArrayTypedDatum(*obj));
+
         *present = true;
-        ArrayTypeRepresentation *arrayTypeRepr = typeRepr->asArray();
-
-        if (index >= arrayTypeRepr->length()) {
+
+        if (index >= DatumLength(*obj)) {
             vp.setUndefined();
             return true;
         }
 
-        RootedObject elementType(cx, ArrayElementType(type));
-        size_t offset = arrayTypeRepr->element()->size() * index;
-        return Reify(cx, arrayTypeRepr->element(), elementType, obj, offset, vp);
+        RootedObject elementType(cx, ArrayElementType(*GetType(*obj)));
+        SizedTypeRepresentation *elementTypeRepr =
+            typeRepresentation(*elementType)->asSized();
+        size_t offset = elementTypeRepr->size() * index;
+        return Reify(cx, elementTypeRepr, elementType, obj, offset, vp);
       }
     }
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         *present = false;
         vp.setUndefined();
         return true;
@@ -2122,17 +2154,18 @@ TypedDatum::obj_setGeneric(JSContext *cx
     switch (typeRepr->kind()) {
       case ScalarTypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
         break;
 
       case ScalarTypeRepresentation::X4:
         break;
 
-      case ScalarTypeRepresentation::Array:
+      case ScalarTypeRepresentation::SizedArray:
+      case ScalarTypeRepresentation::UnsizedArray:
         if (JSID_IS_ATOM(id, cx->names().length)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
                                  nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
             return false;
         }
         break;
 
       case ScalarTypeRepresentation::Struct: {
@@ -2157,39 +2190,41 @@ TypedDatum::obj_setProperty(JSContext *c
                              bool strict)
 {
     RootedId id(cx, NameToId(name));
     return obj_setGeneric(cx, obj, id, vp, strict);
 }
 
 bool
 TypedDatum::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
-                            MutableHandleValue vp, bool strict)
+                           MutableHandleValue vp, bool strict)
 {
     RootedObject type(cx, GetType(*obj));
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
       case TypeRepresentation::X4:
       case TypeRepresentation::Struct:
         break;
 
-      case TypeRepresentation::Array: {
-        ArrayTypeRepresentation *arrayTypeRepr = typeRepr->asArray();
-
-        if (index >= arrayTypeRepr->length()) {
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
+      {
+        if (index >= DatumLength(*obj)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
                                  nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
             return false;
         }
 
-        RootedObject elementType(cx, ArrayElementType(type));
-        size_t offset = arrayTypeRepr->element()->size() * index;
+        RootedObject elementType(cx, ArrayElementType(*GetType(*obj)));
+        SizedTypeRepresentation *elementTypeRepr =
+            typeRepresentation(*elementType)->asSized();
+        size_t offset = elementTypeRepr->size() * index;
         return ConvertAndCopyTo(cx, elementType, obj, offset, vp);
       }
     }
 
     return ReportDatumTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, obj);
 }
 
 bool
@@ -2212,17 +2247,18 @@ TypedDatum::obj_getGenericAttributes(JSC
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
         break;
 
       case TypeRepresentation::X4:
         break;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         if (js_IdIsIndex(id, &index)) {
             *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
             return true;
         }
         if (JSID_IS_ATOM(id, cx->names().length)) {
             *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
             return true;
         }
@@ -2253,17 +2289,18 @@ IsOwnId(JSContext *cx, HandleObject obj,
     TypeRepresentation *typeRepr = typeRepresentation(*type);
 
     switch (typeRepr->kind()) {
       case TypeRepresentation::Scalar:
       case TypeRepresentation::Reference:
       case TypeRepresentation::X4:
         return false;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         return js_IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length);
 
       case TypeRepresentation::Struct:
         return typeRepr->asStruct()->fieldNamed(id) != nullptr;
     }
 
     return false;
 }
@@ -2357,32 +2394,33 @@ TypedDatum::obj_enumerate(JSContext *cx,
 
           case JSENUMERATE_NEXT:
           case JSENUMERATE_DESTROY:
             statep.setNull();
             break;
         }
         break;
 
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         switch (enum_op) {
           case JSENUMERATE_INIT_ALL:
           case JSENUMERATE_INIT:
             statep.setInt32(0);
-            idp.set(INT_TO_JSID(typeRepr->asArray()->length()));
+            idp.set(INT_TO_JSID(DatumLength(*obj)));
             break;
 
           case JSENUMERATE_NEXT:
             index = static_cast<int32_t>(statep.toInt32());
 
-            if (index < typeRepr->asArray()->length()) {
+            if (index < DatumLength(*obj)) {
                 idp.set(INT_TO_JSID(index));
                 statep.setInt32(index + 1);
             } else {
-                JS_ASSERT(index == typeRepr->asArray()->length());
+                JS_ASSERT(index == DatumLength(*obj));
                 statep.setNull();
             }
 
             break;
 
           case JSENUMERATE_DESTROY:
             statep.setNull();
             break;
@@ -2396,17 +2434,17 @@ TypedDatum::obj_enumerate(JSContext *cx,
             statep.setInt32(0);
             idp.set(INT_TO_JSID(typeRepr->asStruct()->fieldCount()));
             break;
 
           case JSENUMERATE_NEXT:
             index = static_cast<uint32_t>(statep.toInt32());
 
             if (index < typeRepr->asStruct()->fieldCount()) {
-                idp.set(typeRepr->asStruct()->field(index).id);
+                idp.set(NameToId(typeRepr->asStruct()->field(index).propertyName));
                 statep.setInt32(index + 1);
             } else {
                 statep.setNull();
             }
 
             break;
 
           case JSENUMERATE_DESTROY:
@@ -2417,17 +2455,17 @@ TypedDatum::obj_enumerate(JSContext *cx,
     }
 
     return true;
 }
 
 /* static */ size_t
 TypedDatum::dataOffset()
 {
-    return JSObject::getPrivateDataOffset(JS_DATUM_SLOTS + 1);
+    return JSObject::getPrivateDataOffset(JS_DATUM_SLOTS);
 }
 
 /******************************************************************************
  * Typed Objects
  */
 
 const Class TypedObject::class_ = {
     "TypedObject",
@@ -2473,47 +2511,122 @@ const Class TypedObject::class_ = {
         TypedDatum::obj_deleteElement,
         TypedDatum::obj_deleteSpecial,
         nullptr, nullptr, // watch/unwatch
         TypedDatum::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
-/*static*/ JSObject *
-TypedObject::createZeroed(JSContext *cx, HandleObject type)
+/*static*/ TypedObject *
+TypedObject::createZeroed(JSContext *cx,
+                          HandleObject typeObj,
+                          int32_t length)
 {
-    Rooted<TypedObject*> obj(cx, createUnattached<TypedObject>(cx, type));
+    // Create unattached wrapper object.
+    Rooted<TypedObject*> obj(cx);
+    obj = createUnattached<TypedObject>(cx, typeObj, length);
     if (!obj)
         return nullptr;
 
-    TypeRepresentation *typeRepr = typeRepresentation(*type);
-    size_t memsize = typeRepr->size();
-    uint8_t *memory = (uint8_t*) cx->malloc_(memsize);
-    if (!memory)
-        return nullptr;
-    typeRepr->initInstance(cx->runtime(), memory);
-    obj->attach(memory);
-    return obj;
+    // Allocate and initialize the memory for this instance.
+    // Also initialize the JS_DATUM_SLOT_LENGTH slot.
+    TypeRepresentation *typeRepr = typeRepresentation(*typeObj);
+    switch (typeRepr->kind()) {
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::X4:
+      {
+        uint8_t *memory = (uint8_t*) cx->malloc_(typeRepr->asSized()->size());
+        if (!memory)
+            return nullptr;
+        typeRepr->asSized()->initInstance(cx->runtime(), memory, 1);
+        obj->attach(memory);
+        return obj;
+      }
+
+      case TypeRepresentation::SizedArray:
+      {
+        uint8_t *memory = (uint8_t*) cx->malloc_(typeRepr->asSizedArray()->size());
+        if (!memory)
+            return nullptr;
+        typeRepr->asSizedArray()->initInstance(cx->runtime(), memory, 1);
+        obj->attach(memory);
+        return obj;
+      }
+
+      case TypeRepresentation::UnsizedArray:
+      {
+        SizedTypeRepresentation *elementTypeRepr =
+            typeRepr->asUnsizedArray()->element();
+
+        int32_t totalSize;
+        if (!SafeMul(elementTypeRepr->size(), length, &totalSize)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
+                                 JSMSG_TYPEDOBJECT_TOO_BIG);
+            return nullptr;
+        }
+
+        uint8_t *memory = (uint8_t*) JS_malloc(cx, totalSize);
+        if (!memory)
+            return nullptr;
+
+        elementTypeRepr->initInstance(cx->runtime(), memory, length);
+        obj->attach(memory);
+        return obj;
+      }
+    }
+
+    MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind");
 }
 
 /*static*/ bool
 TypedObject::construct(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject callee(cx, &args.callee());
     JS_ASSERT(IsTypeObject(*callee));
-
-    RootedObject obj(cx, createZeroed(cx, callee));
+    TypeRepresentation *typeRepr = typeRepresentation(*callee);
+
+    // Determine the length based on the target type.
+    uint32_t nextArg = 0;
+    int32_t length = 0;
+    switch (typeRepr->kind()) {
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Struct:
+      case TypeRepresentation::X4:
+        length = 0;
+        break;
+
+      case TypeRepresentation::SizedArray:
+        length = typeRepr->asSizedArray()->length();
+        break;
+
+      case TypeRepresentation::UnsizedArray:
+        // First argument is a length.
+        if (nextArg >= argc || !args[nextArg].isInt32()) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                 nullptr, JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS,
+                                 "1", "array length");
+            return false;
+        }
+        length = args[nextArg++].toInt32();
+        break;
+    }
+
+    // Create zeroed wrapper object.
+    Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
     if (!obj)
-        return false;
-
-    if (argc == 1) {
-        RootedValue initial(cx, args[0]);
+        return nullptr;
+
+    if (nextArg < argc) {
+        RootedValue initial(cx, args[nextArg++]);
         if (!ConvertAndCopyTo(cx, obj, initial))
             return false;
     }
 
     args.rval().setObject(*obj);
     return true;
 }
 
@@ -2582,21 +2695,22 @@ const JSFunctionSpec TypedHandle::handle
  * Intrinsics
  */
 
 bool
 js::NewTypedHandle(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(argc == 1);
-    JS_ASSERT(args[0].isObject() && IsTypeObject(args[0].toObject()));
+    JS_ASSERT(args[0].isObject() && IsSizedTypeObject(args[0].toObject()));
 
     RootedObject typeObj(cx, &args[0].toObject());
-    Rooted<TypedHandle*> obj(
-        cx, TypedDatum::createUnattached<TypedHandle>(cx, typeObj));
+    int32_t length = DatumLengthFromType(*typeObj);
+    Rooted<TypedHandle*> obj(cx);
+    obj = TypedDatum::createUnattached<TypedHandle>(cx, typeObj, length);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 js::NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp)
@@ -2740,21 +2854,21 @@ js::Memcpy(ThreadSafeContext *, unsigned
     return true;
 }
 
 const JSJitInfo js::MemcpyJitInfo =
     JS_JITINFO_NATIVE_PARALLEL(
         JSParallelNativeThreadSafeWrapper<js::Memcpy>);
 
 bool
-js::StandardTypeObjectDescriptors(JSContext *cx, unsigned argc, Value *vp)
+js::GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     Rooted<GlobalObject*> global(cx, cx->global());
-    args.rval().setObject(global->getTypedObject());
+    args.rval().setObject(global->getTypedObjectModule());
     return true;
 }
 
 #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name)                       \
 bool                                                                          \
 js::StoreScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp)       \
 {                                                                             \
     CallArgs args = CallArgsFromVp(argc, vp);                                 \
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -32,17 +32,17 @@
  * http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects or the
  * ES6 spec (not finalized at the time of this writing).
  *
  * - Initialization:
  *
  * Currently, all "globals" related to typed objects are packaged
  * within a single "module" object `TypedObject`. This module has its
  * own js::Class and when that class is initialized, we also create
- * and define all other values (in `js_InitTypedObjectClass()`).
+ * and define all other values (in `js_InitTypedObjectModuleClass()`).
  *
  * - Type objects, meta type objects, and type representations:
  *
  * There are a number of pre-defined type objects, one for each
  * scalar type (`uint8` etc). Each of these has its own class_,
  * defined in `DefineNumericClass()`.
  *
  * There are also meta type objects (`ArrayType`, `StructType`).
@@ -96,17 +96,31 @@
 
 namespace js {
 
 /*
  * This object exists in order to encapsulate the typed object types
  * somewhat, rather than sticking them all into the global object.
  * Eventually it will go away and become a module.
  */
-extern const Class TypedObjectClass;
+class TypedObjectModuleObject : public JSObject {
+  public:
+    enum Slot {
+        ArrayTypePrototype,
+        StructTypePrototype,
+        SlotCount
+    };
+
+    static const Class class_;
+
+    bool getSuitableClaspAndProto(JSContext *cx,
+                                  TypeRepresentation::Kind kind,
+                                  const Class **clasp,
+                                  MutableHandleObject proto);
+};
 
 // Type for scalar type constructors like `uint8`. All such type
 // constructors share a common js::Class and JSFunctionSpec. Scalar
 // types are non-opaque (their storage is visible unless combined with
 // an opaque reference type.)
 class ScalarType
 {
   public:
@@ -143,16 +157,28 @@ class X4Type : public JSObject
 };
 
 /*
  * Type descriptor created by `new ArrayType(...)`
  */
 class ArrayType : public JSObject
 {
   private:
+    // Helper for creating a new ArrayType object, either sized or unsized.
+    // - `arrayTypePrototype` - prototype for the new object to be created,
+    //                          either ArrayType.prototype or
+    //                          unsizedArrayType.__proto__ depending on
+    //                          whether this is a sized or unsized array
+    // - `arrayTypeReprObj` - a type representation object for the array
+    // - `elementType` - type object for the elements in the array
+    static JSObject *create(JSContext *cx,
+                            HandleObject arrayTypePrototype,
+                            HandleObject arrayTypeReprObj,
+                            HandleObject elementType);
+
   public:
     static const Class class_;
 
     // Properties and methods to be installed on ArrayType.prototype,
     // and hence inherited by all array type objects:
     static const JSPropertySpec typeObjectProperties[];
     static const JSFunctionSpec typeObjectMethods[];
 
@@ -160,20 +186,19 @@ class ArrayType : public JSObject
     // and hence inherited by all array *typed* objects:
     static const JSPropertySpec typedObjectProperties[];
     static const JSFunctionSpec typedObjectMethods[];
 
     // This is the function that gets called when the user
     // does `new ArrayType(elem)`. It produces an array type object.
     static bool construct(JSContext *cx, unsigned argc, Value *vp);
 
-    static JSObject *create(JSContext *cx, HandleObject arrayTypeGlobal,
-                            HandleObject elementType, size_t length);
-    static bool repeat(JSContext *cx, unsigned argc, Value *vp);
-    static bool subarray(JSContext *cx, unsigned argc, Value *vp);
+    // This is the sized method on unsized array type objects.  It
+    // produces a sized variant.
+    static bool dimension(JSContext *cx, unsigned int argc, jsval *vp);
 
     static JSObject *elementType(JSContext *cx, HandleObject obj);
 };
 
 /*
  * Type descriptor created by `new StructType(...)`
  */
 class StructType : public JSObject
@@ -301,50 +326,57 @@ class TypedDatum : public JSObject
     // object or this object may alias data owned by someone else.)
     // This function returns the offset in bytes within the object
     // where the `void*` pointer can be found. It is intended for use
     // by the JIT.
     static size_t dataOffset();
 
     static TypedDatum *createUnattachedWithClass(JSContext *cx,
                                                  const Class *clasp,
-                                                 HandleObject type);
+                                                 HandleObject type,
+                                                 int32_t length);
 
     // Creates an unattached typed object or handle (depending on the
     // type parameter T). Note that it is only legal for unattached
     // handles to escape to the end user; for non-handles, the caller
     // should always invoke one of the `attach()` methods below.
     //
     // Arguments:
     // - type: type object for resulting object
+    // - length: 0 unless this is an array, otherwise the length
     template<class T>
-    static T *createUnattached(JSContext *cx, HandleObject type);
+    static T *createUnattached(JSContext *cx, HandleObject type, int32_t length);
 
     // Creates a datum that aliases the memory pointed at by `owner`
     // at the given offset. The datum will be a handle iff type is a
     // handle and a typed object otherwise.
     static TypedDatum *createDerived(JSContext *cx,
                                      HandleObject type,
                                      HandleObject typedContents,
                                      size_t offset);
 
+
     // If `this` is the owner of the memory, use this.
     void attach(uint8_t *mem);
 
     // Otherwise, use this to attach to memory referenced by another datum.
     void attach(JSObject &datum, uint32_t offset);
 };
 
 class TypedObject : public TypedDatum
 {
   public:
     static const Class class_;
 
-    // creates zeroed memory of size of type
-    static JSObject *createZeroed(JSContext *cx, HandleObject type);
+    // Creates a new typed object whose memory is freshly allocated
+    // and initialized with zeroes (or, in the case of references, an
+    // appropriate default value).
+    static TypedObject *createZeroed(JSContext *cx,
+                                     HandleObject typeObj,
+                                     int32_t length);
 
     // user-accessible constructor (`new TypeDescriptor(...)`)
     static bool construct(JSContext *cx, unsigned argc, Value *vp);
 };
 
 class TypedHandle : public TypedDatum
 {
   public:
@@ -355,16 +387,23 @@ class TypedHandle : public TypedDatum
 /*
  * Usage: NewTypedHandle(typeObj)
  *
  * Constructs a new, unattached instance of `Handle`.
  */
 bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp);
 
 /*
+ * Usage: NewTypedHandle(typeObj)
+ *
+ * Constructs a new, unattached instance of `Handle`.
+ */
+bool NewTypedHandle(JSContext *cx, unsigned argc, Value *vp);
+
+/*
  * Usage: NewDerivedTypedDatum(typeObj, owner, offset)
  *
  * Constructs a new, unattached instance of `Handle`.
  */
 bool NewDerivedTypedDatum(JSContext *cx, unsigned argc, Value *vp);
 
 /*
  * Usage: AttachHandle(handle, newOwner, newOffset)
@@ -434,25 +473,25 @@ extern const JSJitInfo ClampToUint8JitIn
  * `targetDatum` at `targetOffset`.
  *
  * Both `sourceDatum` and `targetDatum` must be attached.
  */
 bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp);
 extern const JSJitInfo MemcpyJitInfo;
 
 /*
- * Usage: StandardTypeObjectDescriptors()
+ * Usage: GetTypedObjectModule()
  *
- * Returns the global "typed object" object, which provides access
+ * Returns the global "typed object" module, which provides access
  * to the various builtin type descriptors. These are currently
  * exported as immutable properties so it is safe for self-hosted code
  * to access them; eventually this should be linked into the module
  * system.
  */
-bool StandardTypeObjectDescriptors(JSContext *cx, unsigned argc, Value *vp);
+bool GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp);
 
 /*
  * Usage: Store_int8(targetDatum, targetOffset, value)
  *        ...
  *        Store_uint8(targetDatum, targetOffset, value)
  *        ...
  *        Store_float32(targetDatum, targetOffset, value)
  *        Store_float64(targetDatum, targetOffset, value)
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -9,16 +9,18 @@
     UnsafeGetReservedSlot(obj, JS_TYPEOBJ_SLOT_TYPE_REPR)
 
 // Typed object slots
 
 #define DATUM_TYPE_OBJ(obj) \
     UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_TYPE_OBJ)
 #define DATUM_OWNER(obj) \
     UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_OWNER)
+#define DATUM_LENGTH(obj) \
+    TO_INT32(UnsafeGetReservedSlot(obj, JS_DATUM_SLOT_LENGTH))
 
 // Type repr slots
 
 #define REPR_KIND(obj)   \
     TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_KIND))
 #define REPR_SIZE(obj)   \
     TO_INT32(UnsafeGetReservedSlot(obj, JS_TYPEREPR_SLOT_SIZE))
 #define REPR_ALIGNMENT(obj) \
@@ -90,38 +92,51 @@ TypedObjectPointer.prototype.reset = fun
   this.offset = inPtr.offset;
   return this;
 };
 
 TypedObjectPointer.prototype.kind = function() {
   return REPR_KIND(this.typeRepr);
 }
 
+TypedObjectPointer.prototype.length = function() {
+  switch (this.kind()) {
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+    return REPR_LENGTH(this.typeRepr);
+
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
+    return DATUM_LENGTH(this.datum);
+  }
+  assert(false, "length() invoked on non-array-type");
+  return 0;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Moving the pointer
 //
 // The methods in this section adjust `this` in place to point at
 // subelements or subproperties.
 
 // Adjusts `this` in place so that it points at the property
 // `propName`.  Throws if there is no such property. Returns `this`.
 TypedObjectPointer.prototype.moveTo = function(propName) {
   switch (this.kind()) {
   case JS_TYPEREPR_SCALAR_KIND:
   case JS_TYPEREPR_REFERENCE_KIND:
   case JS_TYPEREPR_X4_KIND:
     break;
 
-  case JS_TYPEREPR_ARRAY_KIND:
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
     // For an array, property must be an element. Note that we use the
     // length as loaded from the type *representation* as opposed to
     // the type *object*; this is because some type objects represent
     // unsized arrays and hence do not have a length.
     var index = TO_INT32(propName);
-    if (index === propName && index >= 0 && index < REPR_LENGTH(this.typeRepr))
+    if (index === propName && index >= 0 && index < this.length())
       return this.moveToElem(index);
     break;
 
   case JS_TYPEREPR_STRUCT_KIND:
     if (HAS_PROPERTY(this.typeObj.fieldTypes, propName))
       return this.moveToField(propName);
     break;
   }
@@ -129,21 +144,22 @@ TypedObjectPointer.prototype.moveTo = fu
   ThrowError(JSMSG_TYPEDOBJECT_NO_SUCH_PROP, propName);
   return undefined;
 };
 
 // Adjust `this` in place to point at the element `index`.  `this`
 // must be a array type and `index` must be within bounds. Returns
 // `this`.
 TypedObjectPointer.prototype.moveToElem = function(index) {
-  assert(this.kind() == JS_TYPEREPR_ARRAY_KIND,
+  assert(this.kind() == JS_TYPEREPR_SIZED_ARRAY_KIND ||
+         this.kind() == JS_TYPEREPR_UNSIZED_ARRAY_KIND,
          "moveToElem invoked on non-array");
   assert(TO_INT32(index) === index,
          "moveToElem invoked with non-integer index");
-  assert(index >= 0 && index < REPR_LENGTH(this.typeRepr),
+  assert(index >= 0 && index < this.length(),
          "moveToElem invoked with out-of-bounds index: " + index);
 
   var elementTypeObj = this.typeObj.elementType;
   var elementTypeRepr = TYPE_TYPE_REPR(elementTypeObj);
   this.typeRepr = elementTypeRepr;
   this.typeObj = elementTypeObj;
   var elementSize = REPR_SIZE(elementTypeRepr);
 
@@ -195,19 +211,24 @@ TypedObjectPointer.prototype.get = funct
     return this.getScalar();
 
   case JS_TYPEREPR_REFERENCE_KIND:
     return this.getReference();
 
   case JS_TYPEREPR_X4_KIND:
     return this.getX4();
 
-  case JS_TYPEREPR_ARRAY_KIND:
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+    return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset);
+
   case JS_TYPEREPR_STRUCT_KIND:
     return NewDerivedTypedDatum(this.typeObj, this.datum, this.offset);
+
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
+    assert(false, "Unhandled repr kind: " + REPR_KIND(this.typeRepr));
   }
 
   assert(false, "Unhandled kind: " + REPR_KIND(this.typeRepr));
   return undefined;
 }
 
 TypedObjectPointer.prototype.getScalar = function() {
   var type = REPR_TYPE(this.typeRepr);
@@ -256,17 +277,17 @@ TypedObjectPointer.prototype.getReferenc
   }
 
   assert(false, "Unhandled scalar type: " + type);
   return undefined;
 }
 
 TypedObjectPointer.prototype.getX4 = function() {
   var type = REPR_TYPE(this.typeRepr);
-  var T = StandardTypeObjectDescriptors();
+  var T = GetTypedObjectModule();
   switch (type) {
   case JS_X4TYPEREPR_FLOAT32:
     var x = Load_float32(this.datum, this.offset + 0);
     var y = Load_float32(this.datum, this.offset + 4);
     var z = Load_float32(this.datum, this.offset + 8);
     var w = Load_float32(this.datum, this.offset + 12);
     return T.float32x4(x, y, z, w);
 
@@ -293,18 +314,18 @@ TypedObjectPointer.prototype.getX4 = fun
 TypedObjectPointer.prototype.set = function(fromValue) {
   assert(ObjectIsAttached(this.datum), "set() called with unattached datum");
 
   var typeRepr = this.typeRepr;
 
   // Fast path: `fromValue` is a typed object with same type
   // representation as the destination. In that case, we can just do a
   // memcpy.
-  if (IsObject(fromValue) && HaveSameClass(fromValue, this.datum)) {
-    if (DATUM_TYPE_REPR(fromValue) === typeRepr) {
+  if (IsObject(fromValue) && ObjectIsTypedDatum(fromValue)) {
+    if (!typeRepr.variable && DATUM_TYPE_REPR(fromValue) === typeRepr) {
       if (!ObjectIsAttached(fromValue))
         ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
 
       var size = REPR_SIZE(typeRepr);
       Memcpy(this.datum, this.offset, fromValue, 0, size);
       return;
     }
   }
@@ -317,22 +338,23 @@ TypedObjectPointer.prototype.set = funct
   case JS_TYPEREPR_REFERENCE_KIND:
     this.setReference(fromValue);
     return;
 
   case JS_TYPEREPR_X4_KIND:
     this.setX4(fromValue);
     return;
 
-  case JS_TYPEREPR_ARRAY_KIND:
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
     if (!IsObject(fromValue))
       break;
 
     // Check that "array-like" fromValue has an appropriate length.
-    var length = REPR_LENGTH(typeRepr);
+    var length = this.length();
     if (fromValue.length !== length)
       break;
 
     // Adapt each element.
     if (length > 0) {
       var tempPtr = this.copy().moveToElem(0);
       var size = REPR_SIZE(tempPtr.typeRepr);
       for (var i = 0; i < length; i++) {
@@ -536,28 +558,41 @@ function TypedArrayRedimension(newArrayT
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
 
   if (!IsObject(newArrayType) || !ObjectIsTypeObject(newArrayType))
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1, "type object");
 
   // Peel away the outermost array layers from the type of `this` to find
   // the core element type. In the process, count the number of elements.
   var oldArrayType = DATUM_TYPE_OBJ(this);
+  var oldArrayReprKind = REPR_KIND(TYPE_TYPE_REPR(oldArrayType));
   var oldElementType = oldArrayType;
   var oldElementCount = 1;
-  while (REPR_KIND(TYPE_TYPE_REPR(oldElementType)) == JS_TYPEREPR_ARRAY_KIND) {
+  switch (oldArrayReprKind) {
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
+    oldElementCount *= this.length;
+    oldElementType = oldElementType.elementType;
+    break;
+
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+    break;
+
+  default:
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, "this", "typed array");
+  }
+  while (REPR_KIND(TYPE_TYPE_REPR(oldElementType)) === JS_TYPEREPR_SIZED_ARRAY_KIND) {
     oldElementCount *= oldElementType.length;
     oldElementType = oldElementType.elementType;
   }
 
   // Peel away the outermost array layers from `newArrayType`. In the
   // process, count the number of elements.
   var newElementType = newArrayType;
   var newElementCount = 1;
-  while (REPR_KIND(TYPE_TYPE_REPR(newElementType)) == JS_TYPEREPR_ARRAY_KIND) {
+  while (REPR_KIND(TYPE_TYPE_REPR(newElementType)) == JS_TYPEREPR_SIZED_ARRAY_KIND) {
     newElementCount *= newElementType.length;
     newElementType = newElementType.elementType;
   }
 
   // Check that the total number of elements does not change.
   if (oldElementCount !== newElementCount) {
     ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS, 1,
                "New number of elements does not match old number of elements");
@@ -587,16 +622,28 @@ function TypedArrayRedimension(newArrayT
 // This is the `handle([obj, [...path]])` method on type objects.
 // User exposed!
 //
 // FIXME bug 929656 -- label algorithms with steps from the spec
 function HandleCreate(obj, ...path) {
   if (!IsObject(this) || !ObjectIsTypeObject(this))
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "Type", "handle", "value");
 
+  switch (REPR_KIND(TYPE_TYPE_REPR(this))) {
+  case JS_TYPEREPR_SCALAR_KIND:
+  case JS_TYPEREPR_REFERENCE_KIND:
+  case JS_TYPEREPR_X4_KIND:
+  case JS_TYPEREPR_SIZED_ARRAY_KIND:
+  case JS_TYPEREPR_STRUCT_KIND:
+    break;
+
+  case JS_TYPEREPR_UNSIZED_ARRAY_KIND:
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED);
+  }
+
   var handle = NewTypedHandle(this);
 
   if (obj !== undefined)
     HandleMoveInternal(handle, obj, path)
 
   return handle;
 }
 
@@ -724,27 +771,44 @@ function X4ToSource() {
 
 ///////////////////////////////////////////////////////////////////////////
 // Miscellaneous
 
 // This is the `objectType()` function defined in the spec.
 // It returns the type of its argument.
 //
 // Warning: user exposed!
+function ArrayShorthand(...dims) {
+  if (!IsObject(this) || !ObjectIsTypeObject(this))
+    ThrowError(JSMSG_TYPEDOBJECT_HANDLE_BAD_ARGS,
+               "this", "typed object");
+
+  var T = GetTypedObjectModule();
+
+  if (dims.length == 0)
+    return new T.ArrayType(this);
+
+  var accum = this;
+  for (var i = dims.length - 1; i >= 0; i--)
+    accum = new T.ArrayType(accum).dimension(dims[i]);
+  return accum;
+}
+
+// Warning: user exposed!
 function TypeOfTypedDatum(obj) {
   if (IsObject(obj) && ObjectIsTypedDatum(obj))
     return DATUM_TYPE_OBJ(obj);
 
   // Note: Do not create bindings for `Any`, `String`, etc in
   // Utilities.js, but rather access them through
-  // `StandardTypeObjectDescriptors()`. The reason is that bindings
+  // `GetTypedObjectModule()`. The reason is that bindings
   // you create in Utilities.js are part of the self-hosted global,
   // vs the user-accessible global, and hence should not escape to
   // user script.
-  var T = StandardTypeObjectDescriptors();
+  var T = GetTypedObjectModule();
   switch (typeof obj) {
     case "object": return T.Object;
     case "function": return T.Object;
     case "string": return T.String;
     case "number": return T.float64;
     case "undefined": return T.Any;
     default: return T.Any;
   }
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -24,55 +24,57 @@
 #define JS_TYPEOBJ_SCALAR_SLOTS            1  // Maximum number
 
 // Slots on references
 #define JS_TYPEOBJ_REFERENCE_SLOTS         1  // Maximum number
 
 // Slots on x4s
 #define JS_TYPEOBJ_X4_SLOTS                1  // Maximum number
 
-// Slots on arrays
+// Slots on array type objects
 #define JS_TYPEOBJ_SLOT_ARRAY_ELEM_TYPE    1
-#define JS_TYPEOBJ_ARRAY_SLOTS             2  // Maximum number
+#define JS_TYPEOBJ_ARRAY_SLOTS             3  // Maximum number
 
 // Slots on structs
 #define JS_TYPEOBJ_SLOT_STRUCT_FIELD_TYPES 1
 #define JS_TYPEOBJ_STRUCT_SLOTS            2  // Maximum number
 
 ///////////////////////////////////////////////////////////////////////////
 // Slots for type representation objects
 //
 // Some slots apply to all type representations and some are specific
 // to particular kinds of type representations. Because all type
 // representations share the same class, however, they always have the
 // same number of slots, though not all of them will be initialized or
 // used in the same way.
 
-// Slots on *all* type objects:
+// Slots on *all* type representations:
 #define JS_TYPEREPR_SLOT_KIND      0 // One of the `kind` constants below
 #define JS_TYPEREPR_SLOT_SIZE      1 // Size in bytes.
 #define JS_TYPEREPR_SLOT_ALIGNMENT 2 // Alignment in bytes.
 
-// Slots on arrays:
+// Slots on sized arrays:
 #define JS_TYPEREPR_SLOT_LENGTH    3 // Length of the array
 
 // Slots on scalars, references, and X4s:
 #define JS_TYPEREPR_SLOT_TYPE      3 // One of the constants below
 
 // Maximum number of slots for any type representation
 #define JS_TYPEREPR_SLOTS          4
 
 // These constants are for use exclusively in JS code. In C++ code,
 // prefer TypeRepresentation::Scalar etc, which allows you to
 // write a switch which will receive a warning if you omit a case.
-#define JS_TYPEREPR_SCALAR_KIND    0
-#define JS_TYPEREPR_REFERENCE_KIND 1
-#define JS_TYPEREPR_X4_KIND        2
-#define JS_TYPEREPR_STRUCT_KIND    3
-#define JS_TYPEREPR_ARRAY_KIND     4
+#define JS_TYPEREPR_UNSIZED_ARRAY_KIND  0
+#define JS_TYPEREPR_MAX_UNSIZED_KIND    0    // Unsized kinds go above here
+#define JS_TYPEREPR_SCALAR_KIND         1
+#define JS_TYPEREPR_REFERENCE_KIND      2
+#define JS_TYPEREPR_STRUCT_KIND         3
+#define JS_TYPEREPR_SIZED_ARRAY_KIND    4
+#define JS_TYPEREPR_X4_KIND             5
 
 // These constants are for use exclusively in JS code. In C++ code,
 // prefer ScalarTypeRepresentation::TYPE_INT8 etc, which allows
 // you to write a switch which will receive a warning if you omit a
 // case.
 #define JS_SCALARTYPEREPR_INT8          0
 #define JS_SCALARTYPEREPR_UINT8         1
 #define JS_SCALARTYPEREPR_INT16         2
@@ -98,11 +100,17 @@
 #define JS_X4TYPEREPR_INT32         0
 #define JS_X4TYPEREPR_FLOAT32       1
 
 ///////////////////////////////////////////////////////////////////////////
 // Slots for typed objects (actually, any TypedContents objects)
 
 #define JS_DATUM_SLOT_TYPE_OBJ 0  // Type object for a given typed object
 #define JS_DATUM_SLOT_OWNER    1  // Owner of data (if null, this is owner)
-#define JS_DATUM_SLOTS         2  // Number of slots for typed objs
+#define JS_DATUM_SLOT_LENGTH   2  // Length of array (see (*) below)
+#define JS_DATUM_SLOTS         3  // Number of slots for typed objs
+
+// (*) The JS_DATUM_SLOT_LENGTH slot stores the length for datums of
+// sized and unsized array type. The slot contains 0 for non-arrays.
+// The slot also contains 0 for *unattached* datums, no matter what
+// type they have.
 
 #endif
--- a/js/src/devtools/rootAnalysis/computeCallgraph.js
+++ b/js/src/devtools/rootAnalysis/computeCallgraph.js
@@ -5,16 +5,18 @@
 loadRelativeToScript('utility.js');
 loadRelativeToScript('annotations.js');
 loadRelativeToScript('CFG.js');
 
 var subclasses = {};
 var superclasses = {};
 var classFunctions = {};
 
+var fieldCallSeen = {};
+
 function addClassEntry(index, name, other)
 {
     if (!(name in index)) {
         index[name] = [other];
         return;
     }
 
     for (var entry of index[name]) {
@@ -140,19 +142,28 @@ function getCallees(edge)
                 if (suppressed[0]) {
                     // Field call known to not GC; mark it as suppressed so
                     // direct invocations will be ignored
                     callees.push({'kind': "field", 'csu': csuName, 'field': fieldName,
                                   'suppressed': true});
                 }
             }
             if (functions) {
-                // Known set of virtual call targets.
-                for (var name of functions)
+                // Known set of virtual call targets. Treat them as direct
+                // calls to all possible resolved types, but also record edges
+                // from this field call to each final callee. When the analysis
+                // is checking whether an edge can GC and it sees an unrooted
+                // pointer held live across this field call, it will know
+                // whether any of the direct callees can GC or not.
+                var targets = [];
+                for (var name of functions) {
                     callees.push({'kind': "direct", 'name': name});
+                    targets.push({'kind': "direct", 'name': name});
+                }
+                callees.push({'kind': "resolved-field", 'csu': csuName, 'field': fieldName, 'callees': targets});
             } else {
                 // Unknown set of call targets. Non-virtual field call,
                 // or virtual call on an nsISupports object.
                 callees.push({'kind': "field", 'csu': csuName, 'field': fieldName});
             }
         } else if (callee.Exp[0].Kind == "Var") {
             // indirect call through a variable.
             callees.push({'kind': "indirect", 'variable': callee.Exp[0].Variable.Name[0]});
@@ -195,16 +206,31 @@ function processBody(caller, body)
             if (callee.kind == 'direct') {
                 if (!(callee.name in seen)) {
                     seen[name] = true;
                     printOnce("D " + prologue + memo(callee.name));
                 }
             } else if (callee.kind == 'field') {
                 var { csu, field } = callee;
                 printOnce("F " + prologue + "CLASS " + csu + " FIELD " + field);
+            } else if (callee.kind == 'resolved-field') {
+                // Fully-resolved field call (usually a virtual method). Record
+                // the callgraph edges. Do not consider suppression, since it
+                // is local to this callsite and we are writing out a global
+                // record here.
+                //
+                // Any field call that does *not* have an R entry must be
+                // assumed to call anything.
+                var { csu, field, callees } = callee;
+                var fullFieldName = csu + "." + field;
+                if (!(fullFieldName in fieldCallSeen)) {
+                    fieldCallSeen[fullFieldName] = true;
+                    for (var target of callees)
+                        printOnce("R " + memo(fullFieldName) + " " + memo(target.name));
+                }
             } else if (callee.kind == 'indirect') {
                 printOnce("I " + prologue + "VARIABLE " + callee.variable);
             } else if (callee.kind == 'unknown') {
                 printOnce("I " + prologue + "VARIABLE UNKNOWN");
             } else {
                 printErr("invalid " + callee.kind + " callee");
                 debugger;
             }
--- a/js/src/devtools/rootAnalysis/computeGCFunctions.js
+++ b/js/src/devtools/rootAnalysis/computeGCFunctions.js
@@ -34,20 +34,20 @@ printErr("Writing " + gcFunctionsList_fi
 redirect(gcFunctionsList_filename);
 for (var name in gcFunctions) {
     print(name);
 }
 
 // gcEdges is a list of edges that can GC for more specific reasons than just
 // calling a function that is in gcFunctions.txt.
 //
-// Right now, it is unused. It was mean for ~AutoCompartment when it might wrap
-// an exception, but anything held live across ~AC will have to be held live
-// across the corresponding constructor (and hence the whole scope of the AC),
-// and in that case it'll be held live across whatever could create an
+// Right now, it is unused. It was meant for ~AutoCompartment when it might
+// wrap an exception, but anything held live across ~AC will have to be held
+// live across the corresponding constructor (and hence the whole scope of the
+// AC), and in that case it'll be held live across whatever could create an
 // exception within the AC scope. So ~AC edges are redundant. I will leave the
 // stub machinery here for now.
 printErr("Writing " + gcEdges_filename);
 redirect(gcEdges_filename);
 for (var block in gcEdges) {
   for (var edge in gcEdges[block]) {
       var func = gcEdges[block][edge];
     print([ block, edge, func ].join(" || "));
--- a/js/src/devtools/rootAnalysis/loadCallgraph.js
+++ b/js/src/devtools/rootAnalysis/loadCallgraph.js
@@ -35,16 +35,17 @@ function addCallEdge(caller, callee, sup
     callerGraph[callee].push({caller:caller, suppressed:suppressed});
 }
 
 var functionNames = [""];
 
 function loadCallgraph(file)
 {
     var suppressedFieldCalls = {};
+    var resolvedFunctions = {};
 
     var textLines = snarf(file).split('\n');
     for (var line of textLines) {
         var match;
         if (match = /^\#(\d+) (.*)/.exec(line)) {
             assert(functionNames.length == match[1]);
             functionNames.push(match[2]);
             continue;
@@ -67,66 +68,89 @@ function loadCallgraph(file)
             if (suppressed)
                 suppressedFieldCalls[fullfield] = true;
             else if (!fieldCallCannotGC(csu, fullfield))
                 addGCFunction(caller, "FieldCall: " + fullfield);
         } else if (match = /^D (\d+) (\d+)/.exec(line)) {
             var caller = functionNames[match[1]];
             var callee = functionNames[match[2]];
             addCallEdge(caller, callee, suppressed);
+        } else if (match = /^R (\d+) (\d+)/.exec(line)) {
+            var callerField = functionNames[match[1]];
+            var callee = functionNames[match[2]];
+            addCallEdge(callerField, callee, false);
+            resolvedFunctions[callerField] = true;
         }
     }
 
+    // Initialize suppressedFunctions to the set of all functions, and the
+    // worklist to all toplevel callers.
     var worklist = [];
-    for (var name in callerGraph)
-        suppressedFunctions[name] = true;
-    for (var name in calleeGraph) {
-        if (!(name in callerGraph)) {
-            suppressedFunctions[name] = true;
-            worklist.push(name);
+    for (var callee in callerGraph)
+        suppressedFunctions[callee] = true;
+    for (var caller in calleeGraph) {
+        if (!(caller in callerGraph)) {
+            suppressedFunctions[caller] = true;
+            worklist.push(caller);
         }
     }
+
+    // Find all functions reachable via an unsuppressed call chain, and remove
+    // them from the suppressedFunctions set. Everything remaining is only
+    // reachable when GC is suppressed.
     while (worklist.length) {
         name = worklist.pop();
         if (shouldSuppressGC(name))
             continue;
         if (!(name in suppressedFunctions))
             continue;
         delete suppressedFunctions[name];
         if (!(name in calleeGraph))
             continue;
         for (var entry of calleeGraph[name]) {
             if (!entry.suppressed)
                 worklist.push(entry.callee);
         }
     }
 
+    // Such functions are known to not GC.
     for (var name in gcFunctions) {
         if (name in suppressedFunctions)
             delete gcFunctions[name];
     }
 
     for (var name in suppressedFieldCalls) {
         suppressedFunctions[name] = true;
     }
 
     for (var gcName of [ 'jsgc.cpp:void Collect(JSRuntime*, uint8, int64, uint32, uint32)',
                          'void js::MinorGC(JSRuntime*, uint32)' ])
     {
         assert(gcName in callerGraph);
         addGCFunction(gcName, "GC");
     }
 
+    // Initialize the worklist to all known gcFunctions.
     var worklist = [];
     for (var name in gcFunctions)
         worklist.push(name);
 
+    // Recursively find all callers and add them to the set of gcFunctions.
     while (worklist.length) {
         name = worklist.pop();
         assert(name in gcFunctions);
         if (!(name in callerGraph))
             continue;
         for (var entry of callerGraph[name]) {
             if (!entry.suppressed && addGCFunction(entry.caller, name))
                 worklist.push(entry.caller);
         }
     }
+
+    // Any field call that has been resolved to all possible callees can be
+    // trusted to not GC if all of those callees are known to not GC.
+    for (var name in resolvedFunctions) {
+        if (!(name in gcFunctions)) {
+            suppressedFunctions[name] = true;
+            printErr("Adding " + name);
+        }
+    }
 }
--- a/js/src/jit-test/tests/TypedObject/fuzz10.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz10.js
@@ -1,8 +1,8 @@
 // |jit-test| error:Error
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new Error("type too large");
 
-var AA = new TypedObject.ArrayType(new ArrayType(TypedObject.uint8, 2147483647), 5);
+var AA = TypedObject.uint8.array(2147483647).array(5);
 var aa = new AA();
 var aa0 = aa[0];
--- a/js/src/jit-test/tests/TypedObject/fuzz11.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz11.js
@@ -1,13 +1,13 @@
 // |jit-test| error:Error
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new Error("type too large");
 
-var A = new TypedObject.ArrayType(TypedObject.uint8, 2147483647);
+var A = TypedObject.uint8.array(2147483647);
 var S = new TypedObject.StructType({a: A,
                                     b: A,
                                     c: A,
                                     d: A,
                                     e: A});
 var aa = new S();
 var aa0 = aa.a;
--- a/js/src/jit-test/tests/TypedObject/fuzz4.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz4.js
@@ -1,10 +1,10 @@
 // |jit-test| error:Error;
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new Error();
 
-var A = new TypedObject.ArrayType(TypedObject.uint8, 10);
+var A = TypedObject.uint8.array(10);
 var a = new A();
 a.forEach(function(val, i) {
   assertEq(arguments[5], a);
 });
--- a/js/src/jit-test/tests/TypedObject/fuzz8.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz8.js
@@ -1,6 +1,6 @@
 // |jit-test| error:Error
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new Error();
 
-new TypedObject.ArrayType(TypedObject.uint8, .0000000009);
+TypedObject.uint8.array(.0000000009);
--- a/js/src/jit-test/tests/TypedObject/fuzz9.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz9.js
@@ -1,10 +1,10 @@
 // |jit-test| error: TypeError
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new TypeError();
 
-var Vec3 = new TypedObject.ArrayType(TypedObject.float32, 3);
-var Sprite = new TypedObject.ArrayType(Vec3, 3);
+var Vec3 = TypedObject.float32.array(3);
+var Sprite = Vec3.array(3);
 var mario = new Sprite();
 mario[/\u00ee[]/] = new Vec3([1, 0, 0]);
 
--- a/js/src/jit-test/tests/TypedObject/jit-complex.js
+++ b/js/src/jit-test/tests/TypedObject/jit-complex.js
@@ -1,30 +1,33 @@
 // Test that we can optimize stuff like line.target.x without
 // creating an intermediate object.
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
+setJitCompilerOption("ion.usecount.trigger", 30);
+
 var PointType = new TypedObject.StructType({x: TypedObject.float64,
                                             y: TypedObject.float64});
 var LineType = new TypedObject.StructType({source: PointType,
                                            target: PointType});
 
-function manhattenDistance(line) {
+function manhattanDistance(line) {
   return (Math.abs(line.target.x - line.source.x) +
           Math.abs(line.target.y - line.source.y));
 }
 
 function foo() {
-  var N = 30000;
+  var N = 100;
   var points = [];
   var obj;
   var s;
 
   var fromAToB = new LineType({source: {x: 22, y: 44},
                                target: {x: 66, y: 88}});
 
-  for (var i = 0; i < N; i++)
-    assertEq(manhattenDistance(fromAToB), 88);
+  for (var i = 0; i < N; i++) {
+    assertEq(manhattanDistance(fromAToB), 88);
+  }
 }
 
 foo();
--- a/js/src/jit-test/tests/TypedObject/jit-prefix.js
+++ b/js/src/jit-test/tests/TypedObject/jit-prefix.js
@@ -1,11 +1,13 @@
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
+setJitCompilerOption("ion.usecount.trigger", 30);
+
 var PointType2 =
   new TypedObject.StructType({
     x: TypedObject.float64,
     y: TypedObject.float64});
 
 var PointType3 =
   new TypedObject.StructType({
     x: TypedObject.float64,
@@ -17,17 +19,17 @@ function xPlusY(p) {
 }
 
 function xPlusYTweak(p) {
   p.x = 22;
   return xPlusY(p);
 }
 
 function foo() {
-  var N = 30000;
+  var N = 100;
   var points = [];
   var obj;
   var s;
 
   for (var i = 0; i < N; i++) {
     if ((i % 2) == 0 || true)
       obj = new PointType2({x: i, y: i+1});
     else
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/jit-read-many.js
@@ -0,0 +1,62 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+if (!this.hasOwnProperty("TypedObject"))
+  quit();
+
+// Test a single function (`bar`) being used with arrays that are all
+// of known length, but not the same length.
+
+setJitCompilerOption("ion.usecount.trigger", 30);
+
+var N0 = 50;
+var N1 = 100;
+var N2 = 150;
+var T = TypedObject;
+var Array0 = T.uint32.array(N0);
+var Array1 = T.uint32.array(N1);
+var Array2 = T.uint32.array(N2);
+
+function bar(array, i, v) {
+  assertEq(array[i], v);
+}
+
+function foo() {
+  var array0 = new Array0();
+  var array1 = new Array1();
+  var array2 = new Array2();
+
+  for (var i = 0; i < N0; i++)
+    array0[i] = i + 0;
+
+  for (var i = 0; i < N1; i++)
+    array1[i] = i + 1;
+
+  for (var i = 0; i < N2; i++)
+    array2[i] = i + 2;
+
+  // get it primed up..
+  for (var i = 0; i < N0; i++) {
+    bar(array0, i, i);
+    bar(array1, i, i + 1);
+    bar(array2, i, i + 2);
+  }
+
+  // ...do some OOB accesses...
+  for (var i = N0; i < N1; i++) {
+    bar(array0, i, undefined);
+    bar(array1, i, i + 1);
+    bar(array2, i, i + 2);
+  }
+
+  // ...and some more.
+  for (var i = N1; i < N2; i++) {
+    bar(array0, i, undefined);
+    bar(array1, i, undefined);
+    bar(array2, i, i + 2);
+  }
+}
+
+foo();
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-mdim-array.js
@@ -1,18 +1,19 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var PointType = new TypedObject.ArrayType(TypedObject.uint16, 3);
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var T = TypedObject;
+var PointType = T.uint16.array(3);
+var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       [i,   i+1, i+2],
       [i+3, i+4, i+5],
       [i+6, i+7, i+8]]);
     var sum = vec[0][0] + vec[0][1] + vec[0][2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array-in-struct.js
@@ -5,17 +5,17 @@
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var PointType = new TypedObject.StructType({x: TypedObject.uint16,
                                             y: TypedObject.uint16,
                                             z: TypedObject.uint16});
 
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var VecPointType = PointType.array(3);
 
 var PairVecType = new TypedObject.StructType({fst: VecPointType,
                                               snd: VecPointType});
 
 function foo() {
   for (var i = 0; i < 5000; i += 9) {
     var p = new PairVecType({fst: [{x:    i, y: i+1, z:i+2},
                                    {x:  i+3, y: i+4, z:i+5},
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-struct-array.js
@@ -5,17 +5,17 @@
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var PointType = new TypedObject.StructType({x: TypedObject.uint16,
                                             y: TypedObject.uint16,
                                             z: TypedObject.uint16});
 
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       {x: i,   y:i+1, z:i+2},
       {x: i+3, y:i+4, z:i+5},
       {x: i+6, y:i+7, z:i+8}]);
     var sum = vec[0].x + vec[0].y + vec[0].z;
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array-in-struct.js
@@ -1,17 +1,17 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var Vec3u16Type = new TypedObject.ArrayType(TypedObject.uint16, 3);
+var Vec3u16Type = TypedObject.uint16.array(3);
 var PairVec3u16Type = new TypedObject.StructType({fst: Vec3u16Type,
                                                   snd: Vec3u16Type});
 
 function foo_u16() {
   for (var i = 0; i < 15000; i += 6) {
     var p = new PairVec3u16Type({fst: [i, i+1, i+2],
                                  snd: [i+3,i+4,i+5]});
     var sum = p.fst[0] + p.fst[1] + p.fst[2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u16-from-u16-array.js
@@ -1,17 +1,17 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var Vec3u16Type = new TypedObject.ArrayType(TypedObject.uint16, 3);
+var Vec3u16Type = TypedObject.uint16.array(3);
 
 function foo_u16() {
   for (var i = 0; i < 30000; i += 3) {
     var vec = new Vec3u16Type([i, i+1, i+2]);
     var sum = vec[0] + vec[1] + vec[2];
     assertEq(sum, 3*i + 3);
   }
 }
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-mdim-array.js
@@ -1,18 +1,18 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var PointType = new TypedObject.ArrayType(TypedObject.uint32, 3);
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var PointType = TypedObject.uint32.array(3);
+var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       [i,   i+1, i+2],
       [i+3, i+4, i+5],
       [i+6, i+7, i+8]]);
     var sum = vec[0][0] + vec[0][1] + vec[0][2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array-in-struct.js
@@ -5,17 +5,17 @@
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var PointType = new TypedObject.StructType({x: TypedObject.uint32,
                                             y: TypedObject.uint32,
                                             z: TypedObject.uint32});
 
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var VecPointType = PointType.array(3);
 
 var PairVecType = new TypedObject.StructType({fst: VecPointType,
                                               snd: VecPointType});
 
 function foo() {
   for (var i = 0; i < 5000; i += 9) {
     var p = new PairVecType({fst: [{x:    i, y: i+1, z:i+2},
                                    {x:  i+3, y: i+4, z:i+5},
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-struct-array.js
@@ -5,17 +5,17 @@
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var PointType = new TypedObject.StructType({x: TypedObject.uint32,
                                             y: TypedObject.uint32,
                                             z: TypedObject.uint32});
 
-var VecPointType = new TypedObject.ArrayType(PointType, 3);
+var VecPointType = PointType.array(3);
 
 function foo() {
   for (var i = 0; i < 10000; i += 9) {
     var vec = new VecPointType([
       {x: i,   y:i+1, z:i+2},
       {x: i+3, y:i+4, z:i+5},
       {x: i+6, y:i+7, z:i+8}]);
     var sum = vec[0].x + vec[0].y + vec[0].z;
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array-in-struct.js
@@ -1,17 +1,17 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var Vec3u32Type = new TypedObject.ArrayType(TypedObject.uint32, 3);
+var Vec3u32Type = TypedObject.uint32.array(3);
 var PairVec3u32Type = new TypedObject.StructType({fst: Vec3u32Type,
                                                   snd: Vec3u32Type});
 
 function foo_u32() {
   for (var i = 0; i < 15000; i += 6) {
     var p = new PairVec3u32Type({fst: [i, i+1, i+2],
                                  snd: [i+3,i+4,i+5]});
     var sum = p.fst[0] + p.fst[1] + p.fst[2];
--- a/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js
+++ b/js/src/jit-test/tests/TypedObject/jit-read-u32-from-u32-array.js
@@ -1,17 +1,17 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
-var Vec3u32Type = new TypedObject.ArrayType(TypedObject.uint32, 3);
+var Vec3u32Type = TypedObject.uint32.array(3);
 
 function foo_u32() {
   for (var i = 0; i < 30000; i += 3) {
     var vec = new Vec3u32Type([i, i+1, i+2]);
     var sum = vec[0] + vec[1] + vec[2];
     assertEq(sum, 3*i + 3);
   }
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/TypedObject/jit-read-unsized.js
@@ -0,0 +1,47 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+if (!this.hasOwnProperty("TypedObject"))
+  quit();
+
+setJitCompilerOption("ion.usecount.trigger", 30);
+
+var N = 100;
+var T = TypedObject;
+var Point = new T.StructType({x: T.uint32, y: T.uint32, z: T.uint32});
+var PointArray = Point.array();
+
+function bar(array, i, x, y, z) {
+  assertEq(array[i].x, x);
+  assertEq(array[i].y, y);
+  assertEq(array[i].z, z);
+}
+
+function foo() {
+  var array = new PointArray(N);
+  for (var i = 0; i < N; i++) {
+    array[i].x = i + 0;
+    array[i].y = i + 1;
+    array[i].z = i + 2;
+  }
+
+  // get it primed up..
+  for (var i = 0; i < N; i++)
+    bar(array, i, i, i + 1, i + 2);
+
+  // ...do some OOB accesses...
+  for (var i = 0; i < N; i++) {
+    try {
+      bar(array, N, undefined, undefined, undefined);
+      assertEq(false, true);
+    } catch(e) { }
+  }
+
+  // ...test again.
+  for (var i = 0; i < N; i++)
+    bar(array, i, i, i + 1, i + 2);
+}
+
+foo();
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6590,95 +6590,124 @@ IonBuilder::getElemTryTypedObject(bool *
         return false;
 
     if (!objTypeReprs.allOfArrayKind())
         return true;
 
     TypeRepresentationSet elemTypeReprs;
     if (!objTypeReprs.arrayElementType(*this, &elemTypeReprs))
         return false;
+    if (elemTypeReprs.empty())
+        return true;
+
+    JS_ASSERT(TypeRepresentation::isSized(elemTypeReprs.kind()));
 
     size_t elemSize;
     if (!elemTypeReprs.allHaveSameSize(&elemSize))
         return true;
 
     switch (elemTypeReprs.kind()) {
       case TypeRepresentation::X4:
         // FIXME (bug 894104): load into a MIRType_float32x4 etc
         return true;
 
       case TypeRepresentation::Struct:
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
         return getElemTryComplexElemOfTypedObject(emitted,
                                                   obj,
                                                   index,
                                                   objTypeReprs,
                                                   elemTypeReprs,
                                                   elemSize);
       case TypeRepresentation::Scalar:
         return getElemTryScalarElemOfTypedObject(emitted,
                                                  obj,
                                                  index,
                                                  objTypeReprs,
                                                  elemTypeReprs,
                                                  elemSize);
 
       case TypeRepresentation::Reference:
         return true;
+
+      case TypeRepresentation::UnsizedArray:
+        MOZ_ASSUME_UNREACHABLE("Unsized arrays cannot be element types");
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad kind");
 }
 
 static MIRType
 MIRTypeForTypedArrayRead(ScalarTypeRepresentation::Type arrayType,
                          bool observedDouble);
 
 bool
+IonBuilder::checkTypedObjectIndexInBounds(size_t elemSize,
+                                          MDefinition *obj,
+                                          MDefinition *index,
+                                          MDefinition **indexAsByteOffset,
+                                          TypeRepresentationSet objTypeReprs)
+{
+    // Ensure index is an integer.
+    MInstruction *idInt32 = MToInt32::New(alloc(), index);
+    current->add(idInt32);
+
+    // If we know the length statically from the type, just embed it.
+    // Otherwise, load it from the appropriate reserved slot on the
+    // typed object.  We know it's an int32, so we can convert from
+    // Value to int32 using truncation.
+    size_t lenOfAll;
+    MDefinition *length;
+    if (objTypeReprs.hasKnownArrayLength(&lenOfAll)) {
+        length = constantInt(lenOfAll);
+    } else {
+        MInstruction *lengthValue = MLoadFixedSlot::New(alloc(), obj, JS_DATUM_SLOT_LENGTH);
+        current->add(lengthValue);
+
+        MInstruction *length32 = MTruncateToInt32::New(alloc(), lengthValue);
+        current->add(length32);
+
+        length = length32;
+    }
+
+    index = addBoundsCheck(idInt32, length);
+
+    // Since we passed the bounds check, it is impossible for the
+    // result of multiplication to overflow; so enable imul path.
+    MMul *mul = MMul::New(alloc(), index, constantInt(elemSize),
+                          MIRType_Int32, MMul::Integer);
+    current->add(mul);
+
+    *indexAsByteOffset = mul;
+    return true;
+}
+
+bool
 IonBuilder::getElemTryScalarElemOfTypedObject(bool *emitted,
                                               MDefinition *obj,
                                               MDefinition *index,
                                               TypeRepresentationSet objTypeReprs,
                                               TypeRepresentationSet elemTypeReprs,
                                               size_t elemSize)
 {
-    JS_ASSERT(objTypeReprs.allOfArrayKind());
+    JS_ASSERT(objTypeReprs.kind() == TypeRepresentation::SizedArray);
 
     // Must always be loading the same scalar type
     if (!elemTypeReprs.singleton())
         return true;
     ScalarTypeRepresentation *elemTypeRepr = elemTypeReprs.getTypeRepresentation()->asScalar();
-
-    // Get the length.
-    size_t lenOfAll = objTypeReprs.arrayLength();
-    if (lenOfAll >= size_t(INT_MAX)) // int32 max is bound
-        return true;
-    MInstruction *length = MConstant::New(alloc(), Int32Value(int32_t(lenOfAll)));
-
-    *emitted = true;
-    current->add(length);
-
-    // Ensure index is an integer.
-    MInstruction *idInt32 = MToInt32::New(alloc(), index);
-    current->add(idInt32);
-    index = idInt32;
-
-    // Typed-object accesses usually in bounds (bail out otherwise).
-    index = addBoundsCheck(index, length);
-
-    // Since we passed the bounds check, it is impossible for the
-    // result of multiplication to overflow; so enable imul path.
-    const int32_t alignment = elemTypeRepr->alignment();
-    MMul *indexAsByteOffset = MMul::New(alloc(), index, constantInt(alignment),
-                                        MIRType_Int32, MMul::Integer);
-    current->add(indexAsByteOffset);
+    JS_ASSERT(elemSize == elemTypeRepr->alignment());
+
+    MDefinition *indexAsByteOffset;
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objTypeReprs))
+        return false;
 
     // Find location within the owner object.
     MDefinition *elements, *scaledOffset;
-    loadTypedObjectElements(obj, indexAsByteOffset, alignment, &elements, &scaledOffset);
+    loadTypedObjectElements(obj, indexAsByteOffset, elemSize, &elements, &scaledOffset);
 
     // Load the element.
     MLoadTypedArrayElement *load = MLoadTypedArrayElement::New(alloc(), elements, scaledOffset, elemTypeRepr->type());
     current->add(load);
     current->push(load);
 
     // If we are reading in-bounds elements, we can use knowledge about
     // the array type to determine the result type, even if the opcode has
@@ -6687,75 +6716,53 @@ IonBuilder::getElemTryScalarElemOfTypedO
     types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc);
     bool allowDouble = resultTypes->hasType(types::Type::DoubleType());
     MIRType knownType = MIRTypeForTypedArrayRead(elemTypeRepr->type(), allowDouble);
     // Note: we can ignore the type barrier here, we know the type must
     // be valid and unbarriered.
     load->setResultType(knownType);
     load->setResultTypeSet(resultTypes);
 
+    *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::getElemTryComplexElemOfTypedObject(bool *emitted,
                                                MDefinition *obj,
                                                MDefinition *index,
                                                TypeRepresentationSet objTypeReprs,
                                                TypeRepresentationSet elemTypeReprs,
                                                size_t elemSize)
 {
     JS_ASSERT(objTypeReprs.allOfArrayKind());
 
     MDefinition *type = loadTypedObjectType(obj);
     MDefinition *elemType = typeObjectForElementFromArrayStructType(type);
 
-    // Get the length.
-    size_t lenOfAll = objTypeReprs.arrayLength();
-    if (lenOfAll >= size_t(INT_MAX)) // int32 max is bound
-        return true;
-    MInstruction *length = MConstant::New(alloc(), Int32Value(int32_t(lenOfAll)));
-
-    *emitted = true;
-    current->add(length);
-
-    // Ensure index is an integer.
-    MInstruction *idInt32 = MToInt32::New(alloc(), index);
-    current->add(idInt32);
-    index = idInt32;
-
-    // Typed-object accesses usually in bounds (bail out otherwise).
-    index = addBoundsCheck(index, length);
-
-    // Convert array index to element data offset.
-    MConstant *alignment = MConstant::New(alloc(), Int32Value(elemSize));
-    current->add(alignment);
-
-    // Since we passed the bounds check, it is impossible for the
-    // result of multiplication to overflow; so enable imul path.
-    MMul *indexAsByteOffset = MMul::New(alloc(), index, alignment, MIRType_Int32,
-                                        MMul::Integer);
-    current->add(indexAsByteOffset);
+    MDefinition *indexAsByteOffset;
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, &indexAsByteOffset, objTypeReprs))
+        return false;
 
     // Find location within the owner object.
     MDefinition *owner, *ownerOffset;
     loadTypedObjectData(obj, indexAsByteOffset, &owner, &ownerOffset);
 
     // Create the derived type object.
     MInstruction *derived = MNewDerivedTypedObject::New(alloc(),
                                                         elemTypeReprs,
                                                         elemType,
                                                         owner,
                                                         ownerOffset);
 
     types::TemporaryTypeSet *resultTypes = bytecodeTypes(pc);
     derived->setResultTypeSet(resultTypes);
     current->add(derived);
     current->push(derived);
-
+    *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::getElemTryDense(bool *emitted, MDefinition *obj, MDefinition *index)
 {
     JS_ASSERT(*emitted == false);
 
@@ -8291,28 +8298,31 @@ IonBuilder::getPropTryTypedObject(bool *
       case TypeRepresentation::Reference:
         return true;
 
       case TypeRepresentation::X4:
         // FIXME (bug 894104): load into a MIRType_float32x4 etc
         return true;
 
       case TypeRepresentation::Struct:
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
         return getPropTryComplexPropOfTypedObject(emitted,
                                                   fieldOffset,
                                                   fieldTypeReprs,
                                                   fieldIndex,
                                                   resultTypes);
 
       case TypeRepresentation::Scalar:
         return getPropTryScalarPropOfTypedObject(emitted,
                                                  fieldOffset,
                                                  fieldTypeReprs,
                                                  resultTypes);
+
+      case TypeRepresentation::UnsizedArray:
+        MOZ_ASSUME_UNREACHABLE("Field of unsized array type");
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad kind");
 }
 
 bool
 IonBuilder::getPropTryScalarPropOfTypedObject(bool *emitted,
                                               int32_t fieldOffset,
@@ -8858,17 +8868,18 @@ IonBuilder::setPropTryTypedObject(bool *
 
     switch (fieldTypeReprs.kind()) {
       case TypeRepresentation::X4:
         // FIXME (bug 894104): store into a MIRType_float32x4 etc
         return true;
 
       case TypeRepresentation::Reference:
       case TypeRepresentation::Struct:
-      case TypeRepresentation::Array:
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
         // For now, only optimize storing scalars.
         return true;
 
       case TypeRepresentation::Scalar:
         return setPropTryScalarPropOfTypedObject(emitted, obj, fieldOffset,
                                                  value, fieldTypeReprs);
     }
 
@@ -9672,34 +9683,46 @@ IonBuilder::lookupTypeRepresentationSet(
     *out = TypeRepresentationSet(); // default to unknown
 
     // Extract TypeRepresentationSet directly if we can
     if (typedObj->isNewDerivedTypedObject()) {
         *out = typedObj->toNewDerivedTypedObject()->set();
         return true;
     }
 
+    types::TemporaryTypeSet *types = typedObj->resultTypeSet();
+    return typeSetToTypeRepresentationSet(types, out,
+                                          types::TypeTypedObject::Datum);
+}
+
+bool
+IonBuilder::typeSetToTypeRepresentationSet(types::TemporaryTypeSet *types,
+                                           TypeRepresentationSet *out,
+                                           types::TypeTypedObject::Kind kind)
+{
     // Extract TypeRepresentationSet directly if we can
-    types::TemporaryTypeSet *types = typedObj->resultTypeSet();
     if (!types || types->getKnownTypeTag() != JSVAL_TYPE_OBJECT)
         return true;
 
     // And only known objects.
     if (types->unknownObject())
         return true;
 
     TypeRepresentationSetBuilder set;
     for (uint32_t i = 0; i < types->getObjectCount(); i++) {
         types::TypeObject *type = types->getTypeObject(i);
         if (!type || type->unknownProperties())
             return true;
 
         if (!type->hasTypedObject())
             return true;
 
+        if (type->typedObject()->kind != kind)
+            return true;
+
         TypeRepresentation *typeRepr = type->typedObject()->typeRepr;
         if (!set.insert(typeRepr))
             return false;
     }
 
     return set.build(*this, out);
 }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -426,16 +426,19 @@ class IonBuilder : public MIRGenerator
                                            TypeRepresentationSet fieldTypeReprs);
     bool setPropTryCache(bool *emitted, MDefinition *obj,
                          PropertyName *name, MDefinition *value,
                          bool barrier, types::TemporaryTypeSet *objTypes);
 
     // binary data lookup helpers.
     bool lookupTypeRepresentationSet(MDefinition *typedObj,
                                      TypeRepresentationSet *out);
+    bool typeSetToTypeRepresentationSet(types::TemporaryTypeSet *types,
+                                        TypeRepresentationSet *out,
+                                        types::TypeTypedObject::Kind kind);
     bool lookupTypedObjectField(MDefinition *typedObj,
                                 PropertyName *name,
                                 int32_t *fieldOffset,
                                 TypeRepresentationSet *fieldTypeReprs,
                                 size_t *fieldIndex);
     MDefinition *loadTypedObjectType(MDefinition *value);
     void loadTypedObjectData(MDefinition *typedObj,
                              MDefinition *offset,
@@ -448,16 +451,21 @@ class IonBuilder : public MIRGenerator
                                  MDefinition **ownerScaledOffset);
     MDefinition *typeObjectForElementFromArrayStructType(MDefinition *typedObj);
     MDefinition *typeObjectForFieldFromStructType(MDefinition *type,
                                                   size_t fieldIndex);
     bool storeScalarTypedObjectValue(MDefinition *typedObj,
                                      MDefinition *offset,
                                      ScalarTypeRepresentation *typeRepr,
                                      MDefinition *value);
+    bool checkTypedObjectIndexInBounds(size_t elemSize,
+                                       MDefinition *obj,
+                                       MDefinition *index,
+                                       MDefinition **indexAsByteOffset,
+                                       TypeRepresentationSet objTypeReprs);
 
     // jsop_setelem() helpers.
     bool setElemTryTyped(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
     bool setElemTryTypedStatic(bool *emitted, MDefinition *object,
                                MDefinition *index, MDefinition *value);
     bool setElemTryDense(bool *emitted, MDefinition *object,
                          MDefinition *index, MDefinition *value);
--- a/js/src/jit/TypeRepresentationSet.cpp
+++ b/js/src/jit/TypeRepresentationSet.cpp
@@ -179,17 +179,29 @@ TypeRepresentationSet::getTypeRepresenta
 }
 
 bool
 TypeRepresentationSet::allOfArrayKind()
 {
     if (empty())
         return false;
 
-    return kind() == TypeRepresentation::Array;
+    switch (kind()) {
+      case TypeRepresentation::SizedArray:
+      case TypeRepresentation::UnsizedArray:
+        return true;
+
+      case TypeRepresentation::X4:
+      case TypeRepresentation::Reference:
+      case TypeRepresentation::Scalar:
+      case TypeRepresentation::Struct:
+        return false;
+    }
+
+    MOZ_ASSUME_UNREACHABLE("Invalid kind() in TypeRepresentationSet");
 }
 
 bool
 TypeRepresentationSet::allOfKind(TypeRepresentation::Kind aKind)
 {
     if (empty())
         return false;
 
@@ -197,55 +209,78 @@ TypeRepresentationSet::allOfKind(TypeRep
 }
 
 bool
 TypeRepresentationSet::allHaveSameSize(size_t *out)
 {
     if (empty())
         return false;
 
-    size_t size = get(0)->size();
+    JS_ASSERT(TypeRepresentation::isSized(kind()));
+
+    size_t size = get(0)->asSized()->size();
     for (size_t i = 1; i < length(); i++) {
-        if (get(i)->size() != size)
+        if (get(i)->asSized()->size() != size)
             return false;
     }
 
     *out = size;
     return true;
 }
 
 TypeRepresentation::Kind
 TypeRepresentationSet::kind()
 {
     JS_ASSERT(!empty());
     return get(0)->kind();
 }
 
-size_t
-TypeRepresentationSet::arrayLength()
+bool
+TypeRepresentationSet::hasKnownArrayLength(size_t *l)
 {
-    JS_ASSERT(kind() == TypeRepresentation::Array);
-    const size_t result = get(0)->asArray()->length();
-    for (size_t i = 1; i < length(); i++) {
-        if (get(i)->asArray()->length() != result)
-            return SIZE_MAX;
+    switch (kind()) {
+      case TypeRepresentation::UnsizedArray:
+        return false;
+
+      case TypeRepresentation::SizedArray:
+      {
+        const size_t result = get(0)->asSizedArray()->length();
+        for (size_t i = 1; i < length(); i++) {
+            if (get(i)->asSizedArray()->length() != result)
+                return false;
+        }
+        *l = result;
+        return true;
+      }
+
+      default:
+        MOZ_ASSUME_UNREACHABLE("Invalid array size for call to arrayLength()");
     }
-    return result;
 }
 
 bool
 TypeRepresentationSet::arrayElementType(IonBuilder &builder,
                                         TypeRepresentationSet *out)
 {
-    JS_ASSERT(kind() == TypeRepresentation::Array);
-
     TypeRepresentationSetBuilder elementTypes;
     for (size_t i = 0; i < length(); i++) {
-        if (!elementTypes.insert(get(i)->asArray()->element()))
-            return false;
+        switch (kind()) {
+          case TypeRepresentation::UnsizedArray:
+            if (!elementTypes.insert(get(i)->asUnsizedArray()->element()))
+                return false;
+            break;
+
+          case TypeRepresentation::SizedArray:
+            if (!elementTypes.insert(get(i)->asSizedArray()->element()))
+                return false;
+            break;
+
+          default:
+            MOZ_ASSUME_UNREACHABLE("Invalid kind for arrayElementType()");
+        }
     }
     return elementTypes.build(builder, out);
 }
 
 bool
 TypeRepresentationSet::fieldNamed(IonBuilder &builder,
                                   jsid id,
                                   size_t *offset,
--- a/js/src/jit/TypeRepresentationSet.h
+++ b/js/src/jit/TypeRepresentationSet.h
@@ -108,23 +108,24 @@ class TypeRepresentationSet {
     TypeRepresentation::Kind kind();
 
     //////////////////////////////////////////////////////////////////////
     // The following operations are only valid on a singleton set:
 
     TypeRepresentation *getTypeRepresentation();
 
     //////////////////////////////////////////////////////////////////////
-    // Array operations
+    // SizedArray operations
     //
-    // Only valid when `kind() == TypeRepresentation::Array`
+    // Only valid when `kind() == TypeRepresentation::SizedArray`
 
-    // Returns the length of the arrays in this set, or SIZE_MAX
-    // if they are not all the same.
-    size_t arrayLength();
+    // Determines whether all arrays in this set have the same,
+    // statically known, array length and return that length
+    // (via `*length`) if so. Otherwise returns false.
+    bool hasKnownArrayLength(size_t *length);
 
     // Returns a `TypeRepresentationSet` representing the element
     // types of the various array types in this set. The returned set
     // may be the empty set.
     bool arrayElementType(IonBuilder &builder, TypeRepresentationSet *out);
 
     //////////////////////////////////////////////////////////////////////
     // Struct operations
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -426,8 +426,10 @@ MSG_DEF(JSMSG_RC_AFTER_IMPORT_SPEC_LIST,
 MSG_DEF(JSMSG_FROM_AFTER_IMPORT_SPEC_SET, 372, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import specifier set")
 MSG_DEF(JSMSG_DECLARATION_AFTER_IMPORT, 373, 0, JSEXN_SYNTAXERR, "missing declaration after 'import' keyword")
 MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM,   374, 0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword")
 MSG_DEF(JSMSG_MODULES_NOT_IMPLEMENTED,  375, 0, JSEXN_SYNTAXERR, "modules are not implemented yet")
 MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL, 376, 0, JSEXN_SYNTAXERR, "export declarations may only appear at top level")
 MSG_DEF(JSMSG_RC_AFTER_EXPORT_SPEC_LIST, 377, 0, JSEXN_SYNTAXERR, "missing '}' after export specifier list")
 MSG_DEF(JSMSG_NO_EXPORT_NAME,           378, 0, JSEXN_SYNTAXERR, "missing export name")
 MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT, 379, 0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword")
+MSG_DEF(JSMSG_INVALID_PROTOTYPE,        380, 0, JSEXN_TYPEERR, "prototype field is not an object")
+MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_TO_UNSIZED, 381, 0, JSEXN_TYPEERR, "cannot create a handle to an unsized type")
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -875,16 +875,29 @@ class AutoStringVector : public AutoVect
         : AutoVectorRooter<JSString *>(cx, STRINGVECTOR)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+class AutoPropertyNameVector : public AutoVectorRooter<PropertyName *>
+{
+  public:
+    explicit AutoPropertyNameVector(JSContext *cx
+                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+        : AutoVectorRooter<PropertyName *>(cx, STRINGVECTOR)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 class AutoShapeVector : public AutoVectorRooter<Shape *>
 {
   public:
     explicit AutoShapeVector(JSContext *cx
                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : AutoVectorRooter<Shape *>(cx, SHAPEVECTOR)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -4409,17 +4409,19 @@ TypeScript::printTypes(JSContext *cx, Ha
 }
 #endif /* DEBUG */
 
 /////////////////////////////////////////////////////////////////////
 // Binary data
 /////////////////////////////////////////////////////////////////////
 
 bool
-TypeObject::addTypedObjectAddendum(JSContext *cx, TypeRepresentation *repr)
+TypeObject::addTypedObjectAddendum(JSContext *cx,
+                                   TypeTypedObject::Kind kind,
+                                   TypeRepresentation *repr)
 {
     if (!cx->typeInferenceEnabled())
         return true;
 
     JS_ASSERT(repr);
 
     if (flags & OBJECT_FLAG_ADDENDUM_CLEARED)
         return true;
@@ -4427,17 +4429,17 @@ TypeObject::addTypedObjectAddendum(JSCon
     JS_ASSERT(!unknownProperties());
 
     if (addendum) {
         JS_ASSERT(hasTypedObject());
         JS_ASSERT(typedObject()->typeRepr == repr);
         return true;
     }
 
-    TypeTypedObject *typedObject = js_new<TypeTypedObject>(repr);
+    TypeTypedObject *typedObject = js_new<TypeTypedObject>(kind, repr);
     if (!typedObject)
         return false;
     addendum = typedObject;
     return true;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Type object addenda constructor
@@ -4446,13 +4448,15 @@ TypeObject::addTypedObjectAddendum(JSCon
 TypeObjectAddendum::TypeObjectAddendum(Kind kind)
   : kind(kind)
 {}
 
 TypeNewScript::TypeNewScript()
   : TypeObjectAddendum(NewScript)
 {}
 
-TypeTypedObject::TypeTypedObject(TypeRepresentation *repr)
+TypeTypedObject::TypeTypedObject(Kind kind,
+                                 TypeRepresentation *repr)
   : TypeObjectAddendum(TypedObject),
+    kind(kind),
     typeRepr(repr)
 {
 }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -827,19 +827,33 @@ struct TypeNewScript : public TypeObject
     };
     Initializer *initializerList;
 
     static inline void writeBarrierPre(TypeNewScript *newScript);
 };
 
 struct TypeTypedObject : public TypeObjectAddendum
 {
-    TypeTypedObject(TypeRepresentation *repr);
+    enum Kind {
+        TypeDescriptor,
+        Datum,
+    };
+
+    TypeTypedObject(Kind kind, TypeRepresentation *repr);
 
+    const Kind kind;
     TypeRepresentation *const typeRepr;
+
+    bool isTypeDescriptor() const {
+        return kind == TypeDescriptor;
+    }
+
+    bool isDatum() const {
+        return kind == Datum;
+    }
 };
 
 /*
  * Lazy type objects overview.
  *
  * Type objects which represent at most one JS object are constructed lazily.
  * These include types for native functions, standard classes, scripted
  * functions defined at the top level of global/eval scripts, and in some
@@ -919,17 +933,19 @@ struct TypeObject : gc::BarrieredCell<Ty
 
     /*
      * Tag the type object for a binary data type descriptor, instance,
      * or handle with the type representation of the data it points at.
      * If this type object is already tagged with a binary data addendum,
      * this addendum must already be associated with the same TypeRepresentation,
      * and the method has no effect.
      */
-    bool addTypedObjectAddendum(JSContext *cx, TypeRepresentation *repr);
+    bool addTypedObjectAddendum(JSContext *cx,
+                                TypeTypedObject::Kind kind ,
+                                TypeRepresentation *repr);
 
     /*
      * Properties of this object. This may contain JSID_VOID, representing the
      * types of all integer indexes of the object, and/or JSID_EMPTY, holding
      * constraints listening to changes to the object's state.
      *
      * The type sets in the properties of a type object describe the possible
      * values that can be read out of that property in actual JS objects.
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -88,14 +88,14 @@
     real(Uint8ClampedArray,     30,     js_InitTypedArrayClasses,  TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)) \
     real(Proxy,                 31,     js_InitProxyClass,         &ProxyObject::uncallableClass_) \
     real(WeakMap,               32,     js_InitWeakMapClass,       OCLASP(WeakMap)) \
     real(Map,                   33,     js_InitMapClass,           OCLASP(Map)) \
     real(Set,                   34,     js_InitSetClass,           OCLASP(Set)) \
     real(DataView,              35,     js_InitTypedArrayClasses,  OCLASP(DataView)) \
 IF_PJS(real,imaginary)  (ParallelArray,         36,     js_InitParallelArrayClass, OCLASP(ParallelArray)) \
 IF_INTL(real,imaginary) (Intl,                  37,     js_InitIntlClass,          CLASP(Intl)) \
-IF_BDATA(real,imaginary)(TypedObject,           38,     js_InitTypedObjectClass,   CLASP(TypedObject)) \
+IF_BDATA(real,imaginary)(TypedObject,           38,     js_InitTypedObjectModuleObject,   OCLASP(TypedObjectModule)) \
     imaginary(GeneratorFunction,     39,     js_InitIteratorClasses, dummy) \
 
 #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
 
 #endif /* jsprototypes_h */
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -353,17 +353,25 @@ js::WaitForOffThreadParsingToFinish(JSRu
                 parseInProgress |= !!state.threads[i].parseTask;
             if (!parseInProgress)
                 break;
         }
         state.wait(WorkerThreadState::CONSUMER);
     }
 }
 
+#ifdef XP_WIN
+// The default stack size for new threads on Windows is 1MB, but specifying a
+// smaller explicit size to NSPR on thread creation causes our visible memory
+// usage to increase. Just use the default stack size on Windows.
+static const uint32_t WORKER_STACK_SIZE = 0;
+#else
 static const uint32_t WORKER_STACK_SIZE = 512 * 1024;
+#endif
+
 static const uint32_t WORKER_STACK_QUOTA = 450 * 1024;
 
 bool
 WorkerThreadState::init()
 {
     JS_ASSERT(numThreads == 0);
 
     if (!runtime->useHelperThreads())
--- a/js/src/tests/ecma_6/TypedObject/arrayequiv.js
+++ b/js/src/tests/ecma_6/TypedObject/arrayequiv.js
@@ -24,36 +24,36 @@ function assertNotEquivalent(t1, t2) {
   assertEq(false, t2.equivalent(t1));
 }
 
 function runTests() {
   print(BUGNUMBER + ": " + summary);
 
   // Create a line:
   var PixelType1 = new StructType({x: uint8, y: uint8});
-  var PixelsType1 = new ArrayType(PixelType1, 22);
+  var PixelsType1 = PixelType1.array(22);
 
   // Sanity checks about type equivalence:
   assertEquivalent(PixelType1, PixelType1);
   assertEquivalent(PixelsType1, PixelsType1);
   assertNotEquivalent(PixelType1, PixelsType1);
 
   // Define the same two types again. Equivalent.
   var PixelType2 = new StructType({x: uint8, y: uint8});
-  var PixelsType2 = new ArrayType(PixelType2, 22);
+  var PixelsType2 = PixelType2.array(22);
   assertEquivalent(PixelType1, PixelType2);
   assertEquivalent(PixelsType1, PixelsType2);
 
   // Define the pixel type with field order reversed. Not equivalent.
   var PixelType3 = new StructType({y: uint8, x: uint8});
-  var PixelsType3 = new ArrayType(PixelType3, 22);
+  var PixelsType3 = PixelType3.array(22);
   assertNotEquivalent(PixelType1, PixelType3);
   assertNotEquivalent(PixelsType1, PixelsType3);
 
   // Define the pixels type with different number of elements. Not equivalent.
-  var PixelsType3 = new ArrayType(PixelType1, 23);
+  var PixelsType3 = PixelType1.array(23);
   assertNotEquivalent(PixelsType1, PixelsType3);
 
   reportCompare(true, true);
   print("Tests complete");
 }
 
 runTests();
--- a/js/src/tests/ecma_6/TypedObject/arrayofstructs.js
+++ b/js/src/tests/ecma_6/TypedObject/arrayofstructs.js
@@ -2,19 +2,19 @@
 var BUGNUMBER = 578700;
 var summary = 'TypedObjects StructType prototype chains';
 
 var ArrayType = TypedObject.ArrayType;
 var StructType = TypedObject.StructType;
 var float32 = TypedObject.float32;
 
 function runTests() {
-  var Point = new ArrayType(float32, 3);
+  var Point = new ArrayType(float32).dimension(3);
   var Line = new StructType({from: Point, to: Point});
-  var Lines = new ArrayType(Line, 3);
+  var Lines = new ArrayType(Line).dimension(3);
 
   var lines = new Lines([
     {from: [1, 2, 3], to: [4, 5, 6]},
     {from: [7, 8, 9], to: [10, 11, 12]},
     {from: [13, 14, 15], to: [16, 17, 18]}
   ]);
 
   assertEq(lines[1].to[1], 11);
--- a/js/src/tests/ecma_6/TypedObject/arraytype.js
+++ b/js/src/tests/ecma_6/TypedObject/arraytype.js
@@ -17,35 +17,30 @@ var ArrayType = TypedObject.ArrayType;
 var uint8 = TypedObject.uint8;
 var float32 = TypedObject.float32;
 var uint32 = TypedObject.uint32;
 
 function runTests() {
     print(BUGNUMBER + ": " + summary);
 
     assertEq(typeof ArrayType.prototype.prototype.forEach == "function", true);
-    assertEq(typeof ArrayType.prototype.prototype.subarray == "function", true);
 
     assertThrows(function() ArrayType(uint8, 10));
     assertThrows(function() new ArrayType());
     assertThrows(function() new ArrayType(""));
     assertThrows(function() new ArrayType(5));
-    assertThrows(function() new ArrayType(uint8, -1));
-    var A = new ArrayType(uint8, 10);
-    assertEq(A.__proto__, ArrayType.prototype);
+    assertThrows(function() new ArrayType(uint8).dimension(-1));
+    var A = new ArrayType(uint8).dimension(10);
+    assertEq(A.__proto__.__proto__, ArrayType.prototype);
     assertEq(A.length, 10);
     assertEq(A.elementType, uint8);
     assertEq(A.byteLength, 10);
-    assertEq(A.toSource(), "ArrayType(uint8, 10)");
+    assertEq(A.toSource(), "uint8.array(10)");
 
-    assertEq(A.prototype.__proto__, ArrayType.prototype.prototype);
-    assertEq(typeof A.prototype.fill, "function");
-
-    var X = { __proto__: A };
-    assertThrows(function() X.repeat(42));
+    assertEq(A.prototype.__proto__.__proto__, ArrayType.prototype.prototype);
 
     var a = new A();
     assertEq(a.__proto__, A.prototype);
     assertEq(a.length, 10);
 
     assertThrows(function() a.length = 2);
 
     for (var i = 0; i < a.length; i++)
@@ -74,18 +69,18 @@ function runTests() {
     for (var i = 0; i < b.length; i++)
         assertEq(b[i], i%2);
 
     assertThrows(function() new A(5));
     assertThrows(function() new A(/fail/));
     // Length different
     assertThrows(function() new A([0, 1, 0, 1, 0, 1, 0, 1, 0]));
 
-    var Vec3 = new ArrayType(float32, 3);
-    var Sprite = new ArrayType(Vec3, 3); // say for position, velocity, and direction
+    var Vec3 = new ArrayType(float32).dimension(3);
+    var Sprite = new ArrayType(Vec3).dimension(3); // say for position, velocity, and direction
     assertEq(Sprite.elementType, Vec3);
     assertEq(Sprite.elementType.elementType, float32);
 
 
     var mario = new Sprite();
     // setting using binary data
     mario[0] = new Vec3([1, 0, 0]);
     // setting using JS array conversion
@@ -96,77 +91,20 @@ function runTests() {
     assertEq(mario[0][1], 0);
     assertEq(mario[0][2], 0);
 
     assertThrows(function() mario[1] = 5);
     mario[1][1] = {};
     assertEq(Number.isNaN(mario[1][1]), true);
 
     // ok this is just for kicks
-    var AllSprites = new ArrayType(Sprite, 65536);
+    var AllSprites = new ArrayType(Sprite).dimension(65536);
     var as = new AllSprites();
     assertEq(as.length, 65536);
 
-    // test methods
-    var c = new A();
-    c.fill(3);
-    for (var i = 0; i < c.length; i++)
-        assertEq(c[i], 3);
-
-    assertThrows(function() Vec3.prototype.fill.call(c, 2));
-
-    var d = A.repeat(10);
-    for (var i = 0; i < d.length; i++)
-        assertEq(d[i], 10);
-
-    assertThrows(function() ArrayType.prototype.repeat.call(d, 2));
-
-    var MA = new ArrayType(uint32, 5);
-    var ma = new MA([1, 2, 3, 4, 5]);
-
-    var mb = ma.subarray(2);
-    assertEq(mb.length, 3);
-    assertEq(mb[0], 3);
-    assertEq(mb[1], 4);
-    assertEq(mb[2], 5);
-
-    assertThrows(function() ma.subarray());
-    assertThrows(function() ma.subarray(2.14));
-    assertThrows(function() ma.subarray({}));
-    assertThrows(function() ma.subarray(2, []));
-
-    var range = ma.subarray(0, 3);
-    assertEq(range.length, 3);
-    assertEq(range[0], 1);
-    assertEq(range[1], 2);
-    assertEq(range[2], 3);
-
-    assertEq(ma.subarray(ma.length).length, 0);
-    assertEq(ma.subarray(ma.length, ma.length-1).length, 0);
-
-    var rangeNeg = ma.subarray(-2);
-    assertEq(rangeNeg.length, 2);
-    assertEq(rangeNeg[0], 4);
-    assertEq(rangeNeg[1], 5);
-
-    var rangeNeg = ma.subarray(-5, -3);
-    assertEq(rangeNeg.length, 2);
-    assertEq(rangeNeg[0], 1);
-    assertEq(rangeNeg[1], 2);
-
-    assertEq(ma.subarray(-2, -3).length, 0);
-    assertEq(ma.subarray(-6).length, ma.length);
-
-    var modifyOriginal = ma.subarray(2);
-    modifyOriginal[0] = 42;
-    assertEq(ma[2], 42);
-
-    ma[4] = 97;
-    assertEq(modifyOriginal[2], 97);
-
     var indexPropDesc = Object.getOwnPropertyDescriptor(as, '0');
     assertEq(typeof indexPropDesc == "undefined", false);
     assertEq(indexPropDesc.configurable, false);
     assertEq(indexPropDesc.enumerable, true);
     assertEq(indexPropDesc.writable, true);
 
     var lengthPropDesc = Object.getOwnPropertyDescriptor(as, 'length');
     assertEq(typeof lengthPropDesc == "undefined", false);
@@ -178,17 +116,17 @@ function runTests() {
     for (var nm in as) {
       assertEq(+nm, counter++);
     }
     assertEq(counter, as.length);
 
     assertThrows(function() Object.defineProperty(o, "foo", { value: "bar" }));
 
     // check if a reference acts the way it should
-    var AA = new ArrayType(new ArrayType(uint8, 5), 5);
+    var AA = uint8.array(5, 5);
     var aa = new AA();
     var aa0 = aa[0];
     aa[0] = [0,1,2,3,4];
     for (var i = 0; i < aa0.length; i++)
         assertEq(aa0[i], i);
 
     if (typeof reportCompare === "function")
         reportCompare(true, true);
--- a/js/src/tests/ecma_6/TypedObject/arrayzerolen.js
+++ b/js/src/tests/ecma_6/TypedObject/arrayzerolen.js
@@ -2,16 +2,16 @@
 var BUGNUMBER = 926401;
 var summary = 'TypedObjects ArrayType implementation';
 
 // Test creation of zero-length array
 
 function runTest() {
   var T = TypedObject;
   var Color = new T.StructType({'r': T.uint8, 'g': T.uint8, 'b': T.uint8});
-  var Rainbow = new T.ArrayType(Color, 0);
+  var Rainbow = Color.array(0);
   var theOneISawWasJustBlack = new Rainbow([]);
   if (typeof reportCompare === "function")
     reportCompare(true, true);
   print("Tests complete");
 }
 
 runTest();
--- a/js/src/tests/ecma_6/TypedObject/handle.js
+++ b/js/src/tests/ecma_6/TypedObject/handle.js
@@ -6,19 +6,19 @@
  */
 
 var BUGNUMBER = 898342;
 var summary = 'Handles';
 
 var T = TypedObject;
 
 function runTests() {
-  var Point = new T.ArrayType(T.float32, 3);
+  var Point = T.float32.array(3);
   var Line = new T.StructType({from: Point, to: Point});
-  var Lines = new T.ArrayType(Line, 3);
+  var Lines = Line.array(3);
 
   var lines = new Lines([
     {from: [1, 2, 3], to: [4, 5, 6]},
     {from: [7, 8, 9], to: [10, 11, 12]},
     {from: [13, 14, 15], to: [16, 17, 18]}
   ]);
 
   var handle = Lines.handle(lines);
--- a/js/src/tests/ecma_6/TypedObject/handle_get_set.js
+++ b/js/src/tests/ecma_6/TypedObject/handle_get_set.js
@@ -5,21 +5,21 @@
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 var BUGNUMBER = 898342;
 var summary = 'Handle Move';
 
 var T = TypedObject;
 
-var Point = new T.ArrayType(T.float32, 3);
+var Point = T.float32.array(3);
 var Line = new T.StructType({from: Point, to: Point});
-var Lines = new T.ArrayType(Line, 3);
+var Lines = Line.array(3);
 
-var Objects = new T.ArrayType(T.Object, 3);
+var Objects = T.Object.array(3);
 
 function runTests() {
   function testHandleGetSetWithScalarType() {
     var lines = new Lines([
       {from: [1, 2, 3], to: [4, 5, 6]},
       {from: [7, 8, 9], to: [10, 11, 12]},
       {from: [13, 14, 15], to: [16, 17, 18]}
     ]);
--- a/js/src/tests/ecma_6/TypedObject/handle_move.js
+++ b/js/src/tests/ecma_6/TypedObject/handle_move.js
@@ -5,19 +5,19 @@
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 var BUGNUMBER = 898342;
 var summary = 'Handle Move';
 
 var T = TypedObject;
 
-var Point = new T.ArrayType(T.float32, 3);
+var Point = T.float32.array(3);
 var Line = new T.StructType({from: Point, to: Point});
-var Lines = new T.ArrayType(Line, 3);
+var Lines = Line.array(3);
 
 function runTests() {
   function testHandleToPoint() {
     var lines = new Lines([
       {from: [1, 2, 3], to: [4, 5, 6]},
       {from: [7, 8, 9], to: [10, 11, 12]},
       {from: [13, 14, 15], to: [16, 17, 18]}
     ]);
@@ -85,17 +85,17 @@ function runTests() {
     // Spot check the results
     assertEq(lines[0].from[0], 101);
     assertEq(lines[1].to[1], 111);
     assertEq(lines[2].to[2], 118);
   }
   testHandleToFloat();
 
   function testHandleToEquivalentType() {
-    var Point2 = new T.ArrayType(T.float32, 3);
+    var Point2 = T.float32.array(3);
 
     assertEq(Point.equivalent(Point2), true);
 
     var lines = new Lines([
       {from: [1, 2, 3], to: [4, 5, 6]},
       {from: [7, 8, 9], to: [10, 11, 12]},
       {from: [13, 14, 15], to: [16, 17, 18]}
     ]);
--- a/js/src/tests/ecma_6/TypedObject/handle_unattached.js
+++ b/js/src/tests/ecma_6/TypedObject/handle_unattached.js
@@ -7,22 +7,26 @@
 
 var BUGNUMBER = 898342;
 var summary = 'Unattached handles';
 
 var T = TypedObject;
 
 function runTests() {
   var Line = new T.StructType({from: T.uint8, to: T.uint8});
-  var Lines = new T.ArrayType(Line, 3);
+  var Lines = Line.array(3);
 
   // Create unattached handle to array, struct:
   var handle = Lines.handle();
   var handle0 = Line.handle();
 
+  // Accessing length throws:
+  assertThrowsInstanceOf(function() handle.length, TypeError,
+                         "handle.length did not yield error");
+
   // Accessing properties throws:
   assertThrowsInstanceOf(function() handle[0], TypeError,
                          "Unattached handle did not yield error");
   assertThrowsInstanceOf(function() handle0.from, TypeError,
                          "Unattached handle did not yield error");
 
   // Handle.get() throws:
   assertThrowsInstanceOf(function() T.Handle.get(handle), TypeError,
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/handle_unsized.js
@@ -0,0 +1,25 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 922115;
+var summary = 'check we cannot create handles to unsized arrays';
+
+var T = TypedObject;
+
+function runTests() {
+  var Line = new T.StructType({from: T.uint8, to: T.uint8});
+  var Lines = Line.array();
+  assertThrowsInstanceOf(function() Lines.handle(), TypeError,
+                         "was able to create handle to unsized array");
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
+
+
--- a/js/src/tests/ecma_6/TypedObject/memory.js
+++ b/js/src/tests/ecma_6/TypedObject/memory.js
@@ -18,49 +18,56 @@ var int16 = TypedObject.int16;
 var int32 = TypedObject.int32;
 var float32 = TypedObject.float32;
 var float64 = TypedObject.float64;
 
 
 function runTests() {
     print(BUGNUMBER + ": " + summary);
 
-    var AA = new ArrayType(new ArrayType(uint8, 5), 5);
+    var AA = uint8.array(5, 5);
     var aa = new AA();
     var aa0 = aa[0];
     aa[0] = [0,1,2,3,4];
 
     aa = null;
 
     gc();
     spin();
 
     for (var i = 0; i < aa0.length; i++)
         assertEq(aa0[i], i);
 
-    var AAA = new ArrayType(AA, 5);
+    var AAA = AA.array(5);
     var aaa = new AAA();
     var a0 = aaa[0][0];
 
     for (var i = 0; i < a0.length; i++)
         assertEq(a0[i], 0);
 
     aaa[0] = [[0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4], [0,1,2,3,4]];
 
     aaa = null;
 
     gc();
     spin();
     for (var i = 0; i < a0.length; i++)
         assertEq(a0[i], i);
 
     var Color = new StructType({'r': uint8, 'g': uint8, 'b': uint8});
-    var Rainbow = new ArrayType(Color, 7);
+    var Rainbow = Color.array(7);
 
-    var theOneISawWasJustBlack = Rainbow.repeat({'r': 0, 'g': 0, 'b': 0});
+    var theOneISawWasJustBlack = new Rainbow([
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0},
+      {'r': 0, 'g': 0, 'b': 0}]);
 
     var middleBand = theOneISawWasJustBlack[3];
     theOneISawWasJustBlack = null;
     gc();
     spin();
     assertEq(middleBand['r'] == 0 && middleBand['g'] == 0 && middleBand['b'] == 0, true);
     middleBand.r = 255;
     middleBand.g = 207;
--- a/js/src/tests/ecma_6/TypedObject/objecttype.js
+++ b/js/src/tests/ecma_6/TypedObject/objecttype.js
@@ -5,19 +5,19 @@ var summary = 'objecttype';
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 var T = TypedObject;
 
 function runTests() {
-  var Point = new T.ArrayType(T.float32, 3);
+  var Point = T.float32.array(3);
   var Line = new T.StructType({from: Point, to: Point});
-  var Lines = new T.ArrayType(Line, 3);
+  var Lines = Line.array(3);
 
   var lines = new Lines([
     {from: [1, 2, 3], to: [4, 5, 6]},
     {from: [7, 8, 9], to: [10, 11, 12]},
     {from: [13, 14, 15], to: [16, 17, 18]}
   ]);
 
   assertEq(T.objectType(lines), Lines);
--- a/js/src/tests/ecma_6/TypedObject/redimension.js
+++ b/js/src/tests/ecma_6/TypedObject/redimension.js
@@ -8,54 +8,50 @@ var summary = 'redimension method';
  */
 
 var T = TypedObject;
 
 function runTests() {
   var counter;
 
   // create an array of 40 bytes with some initial data
-  var Bytes40 = new T.ArrayType(T.uint8, 40);
+  var Bytes40 = T.uint8.array(40);
   var bytes40 = new Bytes40();
   for (var i = 0, counter = 0; i < 40; i++)
     bytes40[i] = counter++;
 
   // redimension to an array of 10x4 bytes, check data is unchanged
-  var Bytes10times4 = new T.ArrayType(new T.ArrayType(T.uint8, 4), 10);
+  var Bytes10times4 = T.uint8.array(10, 4);
   var bytes10times4 = bytes40.redimension(Bytes10times4);
   counter = 0;
   for (var i = 0; i < 10; i++)
     for (var j = 0; j < 4; j++)
       assertEq(counter++, bytes10times4[i][j]);
 
   // redimension to an array of 2x5x2x2, check data is unchanged
-  var Bytes2times5times2times2 =
-    new T.ArrayType(
-      new T.ArrayType(
-        new T.ArrayType(
-          new T.ArrayType(T.uint8, 2), 2), 5), 2);
+  var Bytes2times5times2times2 = T.uint8.array(2, 5, 2, 2);
   var bytes2times5times2times2 = bytes10times4.redimension(Bytes2times5times2times2);
   counter = 0;
   for (var i = 0; i < 2; i++)
     for (var j = 0; j < 5; j++)
       for (var k = 0; k < 2; k++)
         for (var l = 0; l < 2; l++)
           assertEq(counter++, bytes2times5times2times2[i][j][k][l]);
 
   // test what happens if number of elements does not match
   assertThrowsInstanceOf(() => {
-    var Bytes10times5 = new T.ArrayType(new T.ArrayType(T.uint8, 5), 10);
+    var Bytes10times5 = T.uint8.array(10, 5);
     bytes40.redimension(Bytes10times5);
-  }, TypeError, "New number of elements does not match old number of elements");
+  }, TypeError);
 
   // test what happens if inner type does not match, even if size is the same
   assertThrowsInstanceOf(() => {
-    var Words40 = new T.ArrayType(T.uint8Clamped, 40);
+    var Words40 = T.uint8Clamped.array(40);
     bytes40.redimension(Words40);
-  }, TypeError, "New element type is not equivalent to old element type");
+  }, TypeError);
 
   reportCompare(true, true);
   print("Tests complete");
 }
 
 runTests();
 
 
--- a/js/src/tests/ecma_6/TypedObject/referencetypecoercions.js
+++ b/js/src/tests/ecma_6/TypedObject/referencetypecoercions.js
@@ -22,17 +22,17 @@ function TestValues(type, values) {
   }
 
   for (var i = 0; i < values.length; i++) {
     var struct = new Struct();
     struct.f = values[i].input;
     compare(struct.f, values[i]);
   }
 
-  var Array = new ArrayType(type, 1);
+  var Array = new ArrayType(type).dimension(1);
   for (var i = 0; i < values.length; i++) {
     var array = new Array();
     array[0] = values[i].input;
     compare(array[0], values[i]);
   }
 
   function compare(v, spec) {
     if (spec.source)
--- a/js/src/tests/ecma_6/TypedObject/referencetypetrace.js
+++ b/js/src/tests/ecma_6/TypedObject/referencetypetrace.js
@@ -34,27 +34,36 @@ function TestStructFields(RefType) {
   var s1 = new S1({f: rabbit});
   assertCanReach(s1, rabbit);
   s1.f = null;
   assertCannotReach(s1, rabbit);
 }
 
 function TestArrayElements(RefType) {
   var rabbit = {};
-  var S1 = new ArrayType(RefType, 1);
+  var S1 = new ArrayType(RefType).dimension(1);
   var s1 = new S1([rabbit]);
   assertCanReach(s1, rabbit);
   s1[0] = null;
   assertCannotReach(s1, rabbit);
 }
 
+function TestUnsizedArrayElements(RefType) {
+  var rabbit = {};
+  var S1 = new ArrayType(RefType);
+  var s1 = new S1(1, [rabbit]);
+  assertCanReach(s1, rabbit);
+  s1[0] = null;
+  assertCannotReach(s1, rabbit);
+}
+
 function TestStructInArray(RefType) {
   var rabbit = {};
   var S2 = new StructType({f: RefType, g: RefType});
-  var S1 = new ArrayType(S2, 1);
+  var S1 = new ArrayType(S2).dimension(1);
   var s1 = new S1([{f: rabbit, g: {}}]);
   assertCanReach(s1, rabbit);
   s1[0].f = null;
   assertCannotReach(s1, rabbit);
 }
 
 function TestStringInStruct() {
   // Rather subtle hair-pullingly maddening testing phenomena: If you
@@ -77,16 +86,19 @@ function runTests()
   printStatus(summary);
 
   TestStructFields(Object);
   TestStructFields(Any);
 
   TestArrayElements(Object);
   TestArrayElements(Any);
 
+  TestUnsizedArrayElements(Object);
+  TestUnsizedArrayElements(Any);
+
   TestStructInArray(Object);
   TestStructInArray(Any);
 
   TestStringInStruct();
 
   reportCompare(true, true, "TypedObjects trace tests");
 }
 
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4handle.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4handle.js
@@ -10,17 +10,17 @@ var summary = 'float32x4 handles';
 var ArrayType = TypedObject.ArrayType;
 var float32x4 = TypedObject.float32x4;
 var float32 = TypedObject.float32;
 var Handle = TypedObject.Handle;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(float32x4, 3);
+  var Array = float32x4.array(3);
   var array = new Array([float32x4(1, 2, 3, 4),
                          float32x4(5, 6, 7, 8),
                          float32x4(9, 10, 11, 12)]);
 
   // Test that trying to create handle into the interior of a
   // float32x4 fails.
 
   assertThrowsInstanceOf(function() {
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4reify.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4reify.js
@@ -8,17 +8,17 @@ var summary = 'float32x4 reify';
  */
 
 var ArrayType = TypedObject.ArrayType;
 var float32x4 = TypedObject.float32x4;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(float32x4, 3);
+  var Array = float32x4.array(3);
   var array = new Array([float32x4(1, 2, 3, 4),
                          float32x4(5, 6, 7, 8),
                          float32x4(9, 10, 11, 12)]);
 
   // Test that reading array[1] produces a *copy* of float32x4, not an
   // alias into the array.
 
   var f = array[1];
--- a/js/src/tests/ecma_6/TypedObject/simd/float32x4setter.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/float32x4setter.js
@@ -8,17 +8,17 @@ var summary = 'float32x4 setting';
  */
 
 var ArrayType = TypedObject.ArrayType;
 var float32x4 = TypedObject.float32x4;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(float32x4, 3);
+  var Array = float32x4.array(3);
   var array = new Array([float32x4(1, 2, 3, 4),
                          float32x4(5, 6, 7, 8),
                          float32x4(9, 10, 11, 12)]);
   assertEq(array[1].w, 8);
 
   // Test that we are allowed to write float32x4 values into array,
   // but not other things.
 
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4handle.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4handle.js
@@ -10,17 +10,17 @@ var summary = 'int32x4 handles';
 var ArrayType = TypedObject.ArrayType;
 var int32x4 = TypedObject.int32x4;
 var int32 = TypedObject.int32;
 var Handle = TypedObject.Handle;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(int32x4, 3);
+  var Array = int32x4.array(3);
   var array = new Array([int32x4(1, 2, 3, 4),
                          int32x4(5, 6, 7, 8),
                          int32x4(9, 10, 11, 12)]);
 
   // Test that trying to create handle into the interior of a
   // int32x4 fails.
 
   assertThrowsInstanceOf(function() {
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4reify.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4reify.js
@@ -8,17 +8,17 @@ var summary = 'int32x4 reify';
  */
 
 var ArrayType = TypedObject.ArrayType;
 var int32x4 = TypedObject.int32x4;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(int32x4, 3);
+  var Array = int32x4.array(3);
   var array = new Array([int32x4(1, 2, 3, 4),
                          int32x4(5, 6, 7, 8),
                          int32x4(9, 10, 11, 12)]);
 
   // Test that reading array[1] produces a *copy* of int32x4, not an
   // alias into the array.
 
   var f = array[1];
--- a/js/src/tests/ecma_6/TypedObject/simd/int32x4setter.js
+++ b/js/src/tests/ecma_6/TypedObject/simd/int32x4setter.js
@@ -8,17 +8,17 @@ var summary = 'int32x4 setting';
  */
 
 var ArrayType = TypedObject.ArrayType;
 var int32x4 = TypedObject.int32x4;
 
 function test() {
   print(BUGNUMBER + ": " + summary);
 
-  var Array = new ArrayType(int32x4, 3);
+  var Array = int32x4.array(3);
   var array = new Array([int32x4(1, 2, 3, 4),
                          int32x4(5, 6, 7, 8),
                          int32x4(9, 10, 11, 12)]);
   assertEq(array[1].w, 8);
 
   // Test that we are allowed to write int32x4 values into array,
   // but not other things.
 
--- a/js/src/tests/ecma_6/TypedObject/size_and_alignment.js
+++ b/js/src/tests/ecma_6/TypedObject/size_and_alignment.js
@@ -34,19 +34,19 @@ function runTests() {
 
     {type: float32, size: 4, alignment: 4},
     {type: float64, size: 8, alignment: 8},
 
     {type: new StructType({a: uint8, b: uint16, c: uint8}), size: 6, alignment: 2},
 
     {type: new StructType({a: uint8, b: uint8, c: uint16}), size: 4, alignment: 2},
 
-    {type: new ArrayType(uint8, 32), size: 32, alignment: 1},
-    {type: new ArrayType(uint16, 16), size: 32, alignment: 2},
-    {type: new ArrayType(uint32, 8), size: 32, alignment: 4},
+    {type: new ArrayType(uint8).dimension(32), size: 32, alignment: 1},
+    {type: new ArrayType(uint16).dimension(16), size: 32, alignment: 2},
+    {type: new ArrayType(uint32).dimension(8), size: 32, alignment: 4},
   ];
 
   for (var i = 0; i < typesAndAlignments.length; i++) {
     var test = typesAndAlignments[i];
     print("Type:", test.type.toSource(),
           "Size:", test.type.byteLength,
           "Alignment:", test.type.byteAlignment);
     assertEq(test.type.byteLength, test.size);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/unsizedarrays.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 922115;
+var summary = 'TypedObjects ArrayType implementation';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var { ArrayType, StructType, uint8, float32, uint32 } = TypedObject;
+var ObjectType = TypedObject.Object;
+
+function runTests() {
+  print(BUGNUMBER + ": " + summary);
+
+  (function SimpleArrayOfTwoObjects() {
+    var Objects = new ArrayType(ObjectType);
+    var objects2 = new Objects(2, [{f: "Hello"},
+                                   {f: "World"}]);
+    assertEq(objects2[0].f, "Hello");
+    assertEq(objects2[1].f, "World");
+    assertEq(objects2.length, 2);
+  })();
+
+  (function EmbedUnsizedArraysBad() {
+    var Objects = new ArrayType(ObjectType);
+    assertThrows(() => new ArrayType(Objects));
+    assertThrows(() => new StructType({f: Objects}));
+  })();
+
+  (function MultipleSizes() {
+    var Uints = new ArrayType(uint32);
+    var Point = new StructType({values: new ArrayType(uint32).dimension(3)});
+
+    var uints = new Uints(3, [0, 1, 2]);
+    var point = new Point({values: uints});
+
+    assertEq(uints.length, point.values.length);
+    for (var i = 0; i < uints.length; i++) {
+      assertEq(uints[i], i);
+      assertEq(uints[i], point.values[i]);
+    }
+  })();
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/unsizedarraysanddim.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 922115;
+var summary = 'TypedObjects ArrayType implementation';
+
+var ArrayType = TypedObject.ArrayType;
+var StructType = TypedObject.StructType;
+var uint8 = TypedObject.uint8;
+var float32 = TypedObject.float32;
+var uint32 = TypedObject.uint32;
+var ObjectType = TypedObject.Object;
+
+function runTests() {
+  (function DimensionLinkedToUndimension() {
+    var UintsA = uint32.array();
+    var FiveUintsA = UintsA.dimension(5);
+    var FiveUintsB = uint32.array(5);
+
+    assertEq(true, FiveUintsA.equivalent(FiveUintsB));
+    assertEq(true, FiveUintsA.unsized === UintsA);
+    assertEq(true, FiveUintsB.unsized !== UintsA);
+  })();
+
+  (function PrototypeHierarchy() {
+    var Uint8s = uint8.array();
+    assertEq(Uint8s.prototype.__proto__, ArrayType.prototype.prototype);
+    Uint8s.prototype.sum = function() {
+      var r = 0;
+      for (var i = 0; i < this.length; i++)
+        r = uint8(r + this[i]);
+      return r;
+    };
+
+    var FiveUint8s = Uint8s.dimension(5);
+    assertEq(FiveUint8s.__proto__, Uint8s);
+
+    var fiveUint8s = new FiveUint8s([128, 128, 128, 128, 128]);
+    assertEq(128, fiveUint8s.sum());
+  })();
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+
+  print("Tests complete");
+}
+
+runTests();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedObject/unsizedarraysembedded.js
@@ -0,0 +1,23 @@
+// |reftest| skip-if(!this.hasOwnProperty("TypedObject"))
+var BUGNUMBER = 922115;
+var summary = 'cannot embed unsized array in a struct';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var T = TypedObject;
+
+function runTests() {
+  print(BUGNUMBER + ": " + summary);
+
+  var Uints = T.uint32.array();
+  assertThrowsInstanceOf(() => { new T.StructType({f: Uints}) }, TypeError);
+  assertThrowsInstanceOf(() => { Uints.array() }, TypeError);
+
+  reportCompare(true, true);
+  print("Tests complete");
+}
+
+runTests();
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -162,16 +162,17 @@
     macro(toUTCString, toUTCString, "toUTCString") \
     macro(true, true_, "true") \
     macro(unescape, unescape, "unescape") \
     macro(uneval, uneval, "uneval") \
     macro(uint8, uint8, "uint8") \
     macro(uint8Clamped, uint8Clamped, "uint8Clamped") \
     macro(uint16, uint16, "uint16") \
     macro(uint32, uint32, "uint32") \
+    macro(unsized, unsized, "unsized") \
     macro(unwatch, unwatch, "unwatch") \
     macro(url, url, "url") \
     macro(usage, usage, "usage") \
     macro(useGrouping, useGrouping, "useGrouping") \
     macro(useAsm, useAsm, "use asm") \
     macro(useStrict, useStrict, "use strict") \
     macro(value, value, "value") \
     macro(valueOf, valueOf, "valueOf") \
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -16,26 +16,38 @@
 
 #include "builtin/Eval.h"
 #if EXPOSE_INTL_API
 # include "builtin/Intl.h"
 #endif
 #include "builtin/MapObject.h"
 #include "builtin/Object.h"
 #include "builtin/RegExp.h"
+#include "builtin/TypedObject.h"
 #include "vm/RegExpStatics.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 
+// This method is not in the header file to avoid having to include
+// TypedObject.h from GlobalObject.h. It is not generally perf
+// sensitive.
+TypedObjectModuleObject&
+js::GlobalObject::getTypedObjectModule() const {
+    Value v = getConstructor(JSProto_TypedObject);
+    // only gets called from contexts where TypedObject must be initialized
+    JS_ASSERT(v.isObject());
+    return v.toObject().as<TypedObjectModuleObject>();
+}
+
 JSObject *
 js_InitObjectClass(JSContext *cx, HandleObject obj)
 {
     JS_ASSERT(obj->isNative());
 
     return obj->as<GlobalObject>().getOrCreateObjectPrototype(cx);
 }
 
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -22,21 +22,22 @@ js_InitObjectClass(JSContext *cx, js::Ha
 
 extern JSObject *
 js_InitFunctionClass(JSContext *cx, js::HandleObject obj);
 
 extern JSObject *
 js_InitTypedArrayClasses(JSContext *cx, js::HandleObject obj);
 
 extern JSObject *
-js_InitTypedObjectClass(JSContext *cx, js::HandleObject obj);
+js_InitTypedObjectModuleObject(JSContext *cx, js::HandleObject obj);
 
 namespace js {
 
 class Debugger;
+class TypedObjectModuleObject;
 
 /*
  * Global object slots are reserved as follows:
  *
  * [0, APPLICATION_SLOTS)
  *   Pre-reserved slots in all global objects set aside for the embedding's
  *   use. As with all reserved slots these start out as UndefinedValue() and
  *   are traced for GC purposes. Apart from that the engine never touches
@@ -105,20 +106,19 @@ class GlobalObject : public JSObject
     static const unsigned COLLATOR_PROTO          = SET_ITERATOR_PROTO + 1;
     static const unsigned NUMBER_FORMAT_PROTO     = COLLATOR_PROTO + 1;
     static const unsigned DATE_TIME_FORMAT_PROTO  = NUMBER_FORMAT_PROTO + 1;
     static const unsigned REGEXP_STATICS          = DATE_TIME_FORMAT_PROTO + 1;
     static const unsigned WARNED_WATCH_DEPRECATED = REGEXP_STATICS + 1;
     static const unsigned RUNTIME_CODEGEN_ENABLED = WARNED_WATCH_DEPRECATED + 1;
     static const unsigned DEBUGGERS               = RUNTIME_CODEGEN_ENABLED + 1;
     static const unsigned INTRINSICS              = DEBUGGERS + 1;
-    static const unsigned ARRAY_TYPE              = INTRINSICS + 1;
 
     /* Total reserved-slot count for global objects. */
-    static const unsigned RESERVED_SLOTS = ARRAY_TYPE + 1;
+    static const unsigned RESERVED_SLOTS = INTRINSICS + 1;
 
     /*
      * The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and
      * we won't expose GlobalObject, so just assert that the two values are
      * synchronized.
      */
     static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
                   "global object slot counts are inconsistent");
@@ -407,54 +407,38 @@ class GlobalObject : public JSObject
             return nullptr;
         return &self->getPrototype(key).toObject();
     }
 
     JSObject *getOrCreateIntlObject(JSContext *cx) {
         return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_Intl, initIntlObject);
     }
 
-    JSObject &getTypedObject() {
-        Value v = getConstructor(JSProto_TypedObject);
-        // only gets called from contexts where TypedObject must be initialized
-        JS_ASSERT(v.isObject());
-        return v.toObject();
+    JSObject *getOrCreateTypedObjectModule(JSContext *cx) {
+        return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_TypedObject, initTypedObjectModule);
     }
 
+    TypedObjectModuleObject &getTypedObjectModule() const;
+
     JSObject *getIteratorPrototype() {
         return &getPrototype(JSProto_Iterator).toObject();
     }
 
     JSObject *getOrCreateCollatorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, COLLATOR_PROTO, initCollatorProto);
     }
 
     JSObject *getOrCreateNumberFormatPrototype(JSContext *cx) {
         return getOrCreateObject(cx, NUMBER_FORMAT_PROTO, initNumberFormatProto);
     }
 
     JSObject *getOrCreateDateTimeFormatPrototype(JSContext *cx) {
         return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initDateTimeFormatProto);
     }
 
-    JSObject *getArrayType(JSContext *cx) {
-        const Value &v = getReservedSlot(ARRAY_TYPE);
-
-        MOZ_ASSERT(v.isObject(),
-                   "GlobalObject::arrayType must only be called from "
-                   "TypedObject code that can assume TypedObject has "
-                   "been initialized");
-
-        return &v.toObject();
-    }
-
-    void setArrayType(JSObject *obj) {
-        initReservedSlot(ARRAY_TYPE, ObjectValue(*obj));
-    }
-
   private:
     typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
 
     JSObject *getOrCreateObject(JSContext *cx, unsigned slot, ObjectInitOp init) {
         Value v = getSlotRef(slot);
         if (v.isObject())
             return &v.toObject();
         Rooted<GlobalObject*> self(cx, this);
@@ -605,16 +589,19 @@ class GlobalObject : public JSObject
     static bool initSetIteratorProto(JSContext *cx, Handle<GlobalObject*> global);
 
     // Implemented in Intl.cpp.
     static bool initIntlObject(JSContext *cx, Handle<GlobalObject*> global);
     static bool initCollatorProto(JSContext *cx, Handle<GlobalObject*> global);
     static bool initNumberFormatProto(JSContext *cx, Handle<GlobalObject*> global);
     static bool initDateTimeFormatProto(JSContext *cx, Handle<GlobalObject*> global);
 
+    // Implemented in builtin/TypedObject.cpp
+    static bool initTypedObjectModule(JSContext *cx, Handle<GlobalObject*> global);
+
     static bool initStandardClasses(JSContext *cx, Handle<GlobalObject*> global);
 
     typedef js::Vector<js::Debugger *, 0, js::SystemAllocPolicy> DebuggerVector;
 
     /*
      * The collection of Debugger objects debugging this global. If this global
      * is not a debuggee, this returns either nullptr or an empty vector.
      */
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -671,19 +671,17 @@ static const JSFunctionSpec intrinsic_fu
           js::NewTypedHandle,
           1, 0),
     JS_FNINFO("ClampToUint8",
               JSNativeThreadSafeWrapper<js::ClampToUint8>,
               &js::ClampToUint8JitInfo, 1, 0),
     JS_FNINFO("Memcpy",
               JSNativeThreadSafeWrapper<js::Memcpy>,
               &js::MemcpyJitInfo, 5, 0),
-    JS_FN("StandardTypeObjectDescriptors",
-          js::StandardTypeObjectDescriptors,
-          0, 0),
+    JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0),
 
 #define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name)               \
     JS_FNINFO("Store_" #_name,                                                \
               JSNativeThreadSafeWrapper<js::StoreScalar##_type::Func>,        \
               &js::StoreScalar##_type::JitInfo, 3, 0),                        \
     JS_FNINFO("Load_" #_name,                                                 \
               JSNativeThreadSafeWrapper<js::LoadScalar##_type::Func>,         \
               &js::LoadScalar##_type::JitInfo, 3, 0),
--- a/layout/style/nsCSSScanner.cpp
+++ b/layout/style/nsCSSScanner.cpp
@@ -345,16 +345,17 @@ nsCSSScanner::nsCSSScanner(const nsAStri
   , mOffset(0)
   , mCount(aBuffer.Length())
   , mLineNumber(aLineNumber)
   , mLineOffset(0)
   , mTokenLineNumber(aLineNumber)
   , mTokenLineOffset(0)
   , mTokenOffset(0)
   , mRecordStartOffset(0)
+  , mEOFCharacters(eEOFCharacters_None)
   , mReporter(nullptr)
   , mSVGMode(false)
   , mRecording(false)
   , mSeenBadToken(false)
 {
   MOZ_COUNT_CTOR(nsCSSScanner);
 }
 
@@ -507,23 +508,32 @@ void
 nsCSSScanner::SkipComment()
 {
   MOZ_ASSERT(Peek() == '/' && Peek(1) == '*', "should not have been called");
   Advance(2);
   for (;;) {
     int32_t ch = Peek();
     if (ch < 0) {
       mReporter->ReportUnexpectedEOF("PECommentEOF");
+      SetEOFCharacters(eEOFCharacters_Asterisk | eEOFCharacters_Slash);
       return;
     }
-    if (ch == '*' && Peek(1) == '/') {
-      Advance(2);
-      return;
-    }
-    if (IsVertSpace(ch)) {
+    if (ch == '*') {
+      Advance();
+      ch = Peek();
+      if (ch < 0) {
+        mReporter->ReportUnexpectedEOF("PECommentEOF");
+        SetEOFCharacters(eEOFCharacters_Slash);
+        return;
+      }
+      if (ch == '/') {
+        Advance();
+        return;
+      }
+    } else if (IsVertSpace(ch)) {
       AdvanceLine();
     } else {
       Advance();
     }
   }
 }
 
 /**
@@ -538,18 +548,21 @@ nsCSSScanner::GatherEscape(nsString& aOu
 {
   MOZ_ASSERT(Peek() == '\\', "should not have been called");
   int32_t ch = Peek(1);
   if (ch < 0) {
     // If we are in a string (or a url() containing a string), we want to drop
     // the backslash on the floor.  Otherwise, we want to treat it as a U+FFFD
     // character.
     Advance();
-    if (!aInString) {
+    if (aInString) {
+      SetEOFCharacters(eEOFCharacters_DropBackslash);
+    } else {
       aOutput.Append(UCS2_REPLACEMENT_CHAR);
+      SetEOFCharacters(eEOFCharacters_ReplacementChar);
     }
     return true;
   }
   if (IsVertSpace(ch)) {
     if (aInString) {
       // In strings (and in url() containing a string), escaped
       // newlines are completely removed, to allow splitting over
       // multiple lines.
@@ -674,16 +687,18 @@ nsCSSScanner::GatherText(uint8_t aClass,
  * both of which begin indistinguishably from an identifier.  It can
  * produce a Symbol token when an apparent identifier actually led
  * into an invalid escape sequence.
  */
 bool
 nsCSSScanner::ScanIdent(nsCSSToken& aToken)
 {
   if (MOZ_UNLIKELY(!GatherText(IS_IDCHAR, aToken.mIdent))) {
+    MOZ_ASSERT(Peek() == '\\',
+               "unexpected IsIdentStart character that did not begin an ident");
     aToken.mSymbol = Peek();
     Advance();
     return true;
   }
 
   if (MOZ_LIKELY(Peek() != '(')) {
     aToken.mType = eCSSToken_Ident;
     return true;
@@ -906,16 +921,18 @@ nsCSSScanner::ScanString(nsCSSToken& aTo
   aToken.mSymbol = PRUnichar(aStop); // Remember how it's quoted.
   Advance();
 
   for (;;) {
     GatherText(IS_STRING, aToken.mIdent);
 
     int32_t ch = Peek();
     if (ch == -1) {
+      AddEOFCharacters(aStop == '"' ? eEOFCharacters_DoubleQuote :
+                                      eEOFCharacters_SingleQuote);
       break; // EOF ends a string token with no error.
     }
     if (ch == aStop) {
       Advance();
       break;
     }
     // Both " and ' are excluded from IS_STRING.
     if (ch == '"' || ch == '\'') {
@@ -1011,16 +1028,89 @@ nsCSSScanner::ScanURange(nsCSSToken& aRe
 
   aResult.mInteger = low;
   aResult.mInteger2 = high;
   aResult.mIntegerValid = valid;
   aResult.mType = eCSSToken_URange;
   return true;
 }
 
+#ifdef DEBUG
+/* static */ void
+nsCSSScanner::AssertEOFCharactersValid(uint32_t c)
+{
+  MOZ_ASSERT(c == eEOFCharacters_None ||
+             c == eEOFCharacters_ReplacementChar ||
+             c == eEOFCharacters_Slash ||
+             c == (eEOFCharacters_Asterisk |
+                   eEOFCharacters_Slash) ||
+             c == eEOFCharacters_DoubleQuote ||
+             c == eEOFCharacters_SingleQuote ||
+             c == (eEOFCharacters_DropBackslash |
+                   eEOFCharacters_DoubleQuote) ||
+             c == (eEOFCharacters_DropBackslash |
+                   eEOFCharacters_SingleQuote) ||
+             c == eEOFCharacters_CloseParen ||
+             c == (eEOFCharacters_ReplacementChar |
+                   eEOFCharacters_CloseParen) ||
+             c == (eEOFCharacters_DoubleQuote |
+                   eEOFCharacters_CloseParen) ||
+             c == (eEOFCharacters_SingleQuote |
+                   eEOFCharacters_CloseParen) ||
+             c == (eEOFCharacters_DropBackslash |
+                   eEOFCharacters_DoubleQuote |
+                   eEOFCharacters_CloseParen) ||
+             c == (eEOFCharacters_DropBackslash |
+                   eEOFCharacters_SingleQuote |
+                   eEOFCharacters_CloseParen),
+             "invalid EOFCharacters value");
+}
+#endif
+
+void
+nsCSSScanner::SetEOFCharacters(uint32_t aEOFCharacters)
+{
+  mEOFCharacters = EOFCharacters(aEOFCharacters);
+}
+
+void
+nsCSSScanner::AddEOFCharacters(uint32_t aEOFCharacters)
+{
+  mEOFCharacters = EOFCharacters(mEOFCharacters | aEOFCharacters);
+}
+
+static const PRUnichar kImpliedEOFCharacters[] = {
+  UCS2_REPLACEMENT_CHAR, '*', '/', '"', '\'', ')', 0
+};
+
+/* static */ void
+nsCSSScanner::AdjustTokenStreamForEOFCharacters(EOFCharacters aEOFCharacters,
+                                                nsAString& aResult)
+{
+  uint32_t c = aEOFCharacters;
+
+  // First, handle eEOFCharacters_DropBackslash.
+  if (c & eEOFCharacters_DropBackslash) {
+    MOZ_ASSERT(aResult[aResult.Length() - 1] == '\\');
+    aResult.SetLength(aResult.Length() - 1);
+  }
+
+  c >>= 1;
+
+  // All of the remaining EOFCharacters bits represent appended characters,
+  // and the bits are in the order that they need appending.
+  for (const PRUnichar* p = kImpliedEOFCharacters; *p && c; p++, c >>= 1) {
+    if (c & 1) {
+      aResult.Append(*p);
+    }
+  }
+
+  MOZ_ASSERT(c == 0, "too many bits in mEOFCharacters");
+}
+
 /**
  * Consume the part of an URL token after the initial 'url('.  Caller
  * is assumed to have consumed 'url(' already.  Will always produce
  * either an URL or a Bad_URL token.
  *
  * Exposed for use by nsCSSParser::ParseMozDocumentRule, which applies
  * the special lexical rules for URL tokens in a nonstandard context.
  */
@@ -1053,16 +1143,19 @@ nsCSSScanner::NextURL(nsCSSToken& aToken
   }
 
   // Consume trailing whitespace and then look for a close parenthesis.
   SkipWhitespace();
   ch = Peek();
   if (MOZ_LIKELY(ch < 0 || ch == ')')) {
     Advance();
     aToken.mType = eCSSToken_URL;
+    if (ch < 0) {
+      AddEOFCharacters(eEOFCharacters_CloseParen);
+    }
   } else {
     mSeenBadToken = true;
     aToken.mType = eCSSToken_Bad_URL;
   }
   return true;
 }
 
 /**
--- a/layout/style/nsCSSScanner.h
+++ b/layout/style/nsCSSScanner.h
@@ -189,16 +189,56 @@ class nsCSSScanner {
 
   // Abandons recording of the input stream.
   void StopRecording();
 
   // Stops recording of the input stream and appends the recorded
   // input to aBuffer.
   void StopRecording(nsString& aBuffer);
 
+  enum EOFCharacters {
+    eEOFCharacters_None =                    0x0000,
+
+    // to handle \<EOF> inside strings
+    eEOFCharacters_DropBackslash =           0x0001,
+
+    // to handle \<EOF> outside strings
+    eEOFCharacters_ReplacementChar =         0x0002,
+
+    // to close comments
+    eEOFCharacters_Asterisk =                0x0004,
+    eEOFCharacters_Slash =                   0x0008,
+
+    // to close double-quoted strings
+    eEOFCharacters_DoubleQuote =             0x0010,
+
+    // to close single-quoted strings
+    eEOFCharacters_SingleQuote =             0x0020,
+
+    // to close URLs
+    eEOFCharacters_CloseParen =              0x0040,
+  };
+
+  // Appends or drops any characters to/from the specified string
+  // the input stream to make the last token not rely on special EOF handling
+  // behavior.
+  static void AdjustTokenStreamForEOFCharacters(EOFCharacters aEOFCharacters,
+                                                nsAString& aString);
+
+  EOFCharacters GetEOFCharacters() const {
+#ifdef DEBUG
+    AssertEOFCharactersValid(mEOFCharacters);
+#endif
+    return mEOFCharacters;
+  }
+
+#ifdef DEBUG
+  static void AssertEOFCharactersValid(uint32_t c);
+#endif
+
 protected:
   int32_t Peek(uint32_t n = 0);
   void Advance(uint32_t n = 1);
   void AdvanceLine();
 
   void SkipWhitespace();
   void SkipComment();
 
@@ -207,28 +247,32 @@ protected:
 
   bool ScanIdent(nsCSSToken& aResult);
   bool ScanAtKeyword(nsCSSToken& aResult);
   bool ScanHash(nsCSSToken& aResult);
   bool ScanNumber(nsCSSToken& aResult);
   bool ScanString(nsCSSToken& aResult);
   bool ScanURange(nsCSSToken& aResult);
 
+  void SetEOFCharacters(uint32_t aEOFCharacters);
+  void AddEOFCharacters(uint32_t aEOFCharacters);
+
   const PRUnichar *mBuffer;
   uint32_t mOffset;
   uint32_t mCount;
 
   uint32_t mLineNumber;
   uint32_t mLineOffset;
 
   uint32_t mTokenLineNumber;
   uint32_t mTokenLineOffset;
   uint32_t mTokenOffset;
 
   uint32_t mRecordStartOffset;
+  EOFCharacters mEOFCharacters;
 
   mozilla::css::ErrorReporter *mReporter;
 
   // True if we are in SVG mode; false in "normal" CSS
   bool mSVGMode;
   bool mRecording;
   bool mSeenBadToken;
 };
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -21,16 +21,17 @@
 #include "cpr_socket.h"
 #include "debug-psipcc-types.h"
 #include "prcvar.h"
 
 #include "mozilla/Telemetry.h"
 
 #ifdef MOZILLA_INTERNAL_API
 #include "mozilla/dom/RTCPeerConnectionBinding.h"
+#include "mozilla/Preferences.h"
 #endif
 
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "mozilla/Services.h"
 #include "StaticPtr.h"
 extern "C" {
 #include "../sipcc/core/common/thread_monitor.h"
@@ -68,16 +69,20 @@ MediaConstraintsExternal::MediaConstrain
 
 MediaConstraintsExternal::MediaConstraintsExternal(
     const MediaConstraintsInternal &aSrc) {
   cc_media_constraints_t* c = &mConstraints;
   memset(c, 0, sizeof(*c));
 #ifdef MOZILLA_INTERNAL_API
   Apply(aSrc.mMandatory.mOfferToReceiveAudio, &c->offer_to_receive_audio, true);
   Apply(aSrc.mMandatory.mOfferToReceiveVideo, &c->offer_to_receive_video, true);
+  if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) {
+    c->offer_to_receive_video.was_passed = true;
+    c->offer_to_receive_video.value = false;
+  }
   Apply(aSrc.mMandatory.mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel,
         true);
   if (aSrc.mOptional.WasPassed()) {
     const Sequence<MediaConstraintSet> &array = aSrc.mOptional.Value();
     for (uint32_t i = 0; i < array.Length(); i++) {
       Apply(array[i].mOfferToReceiveAudio, &c->offer_to_receive_audio);
       Apply(array[i].mOfferToReceiveVideo, &c->offer_to_receive_video);
       Apply(array[i].mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -240,16 +240,21 @@ PeerConnectionMedia::AddStream(nsIDOMMed
 
   DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream);
 
   CSFLogDebug(logTag, "%s: MediaStream: %p",
     __FUNCTION__, aMediaStream);
 
   // Adding tracks here based on nsDOMMediaStream expectation settings
   uint32_t hints = stream->GetHintContents();
+#ifdef MOZILLA_INTERNAL_API
+  if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) {
+    hints &= ~(DOMMediaStream::HINT_CONTENTS_VIDEO);
+  }
+#endif
 
   if (!(hints & (DOMMediaStream::HINT_CONTENTS_AUDIO |
         DOMMediaStream::HINT_CONTENTS_VIDEO))) {
     CSFLogDebug(logTag, "Empty Stream !!");
     return NS_OK;
   }
 
   // Now see if we already have a stream of this type, since we only
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -404,25 +404,39 @@
  *   attribute need not be provided in such cases.
  * MOZ_NONHEAP_CLASS: Applies to all classes. Any class with this annotation is
  *   expected to live on the stack or in static storage, so it is a compile-time
  *   error to use it, or an array of such objects, as the type of a new
  *   expression (unless placement new is being used). If a member of another
  *   class uses this class, or if another class inherits from this class, then
  *   it is considered to be a non-heap class as well, although this attribute
  *   need not be provided in such cases.
+ * MOZ_HEAP_ALLOCATOR: Applies to any function. This indicates that the return
+ *   value is allocated on the heap, and will as a result check such allocations
+ *   during MOZ_STACK_CLASS and MOZ_NONHEAP_CLASS annotation checking.
  */
 #ifdef MOZ_CLANG_PLUGIN
-# define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
-# define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
-# define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
+#  define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
+#  define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
+#  define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
+/*
+ * It turns out that clang doesn't like void func() __attribute__ {} without a
+ * warning, so use pragmas to disable the warning. This code won't work on GCC
+ * anyways, so the warning is safe to ignore.
+ */
+#  define MOZ_HEAP_ALLOCATOR \
+    _Pragma("clang diagnostic push") \
+    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+    __attribute__((annotate("moz_heap_allocator"))) \
+    _Pragma("clang diagnostic pop")
 #else
-# define MOZ_MUST_OVERRIDE /* nothing */
-# define MOZ_STACK_CLASS /* nothing */
-# define MOZ_NONHEAP_CLASS /* nothing */
+#  define MOZ_MUST_OVERRIDE /* nothing */
+#  define MOZ_STACK_CLASS /* nothing */
+#  define MOZ_NONHEAP_CLASS /* nothing */
+#  define MOZ_HEAP_ALLOCATOR /* nothing */
 #endif /* MOZ_CLANG_PLUGIN */
 
 /*
  * MOZ_THIS_IN_INITIALIZER_LIST is used to avoid a warning when we know that
  * it's safe to use 'this' in an initializer list.
  */
 #ifdef _MSC_VER
 #  define MOZ_THIS_IN_INITIALIZER_LIST() \
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -32,16 +32,17 @@ skip-if = processor == "x86"
 skip-if = processor == "x86"
 [testInputUrlBar]
 [testJarReader]
 [testJNI]
 [testLinkContextMenu]
 [testLoad]
 [testMailToContextMenu]
 [testMasterPassword]
+# [testMozPay] # see bug 945675
 [testNewTab]
 [testOrderedBroadcast]
 [testOverscroll]
 [testPanCorrectness]
 # disabled on x86 only; bug 927476
 skip-if = processor == "x86"
 # [testPasswordEncrypt] # see bug 824067
 [testPasswordProvider]
@@ -52,19 +53,16 @@ skip-if = processor == "x86"
 [testPromptGridInput]
 # [testReaderMode] # see bug 913254
 # disabled on x86 only; bug 936224
 # skip-if = processor == "x86"
 [testSearchSuggestions]
 [testSessionOOMSave]
 [testSessionOOMRestore]
 [testSettingsMenuItems]
-[testMozPay]
-# disabled on tegras/Android 2.2; bug 945675
-skip-if = android_version == "8"
 [testSharedPreferences]
 # [testShareLink] # see bug 915897
 [testSystemPages]
 # disabled on x86 only; bug 907383
 skip-if = processor == "x86"
 # [testThumbnails] # see bug 813107
 # [testVkbOverlap] # see bug 907274
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -225,27 +225,30 @@ pref("media.webm.enabled", true);
 #ifdef MOZ_GSTREAMER
 pref("media.gstreamer.enabled", true);
 #endif
 #ifdef MOZ_APPLEMEDIA
 pref("media.apple.mp3.enabled", true);
 #endif
 #ifdef MOZ_WEBRTC
 pref("media.navigator.enabled", true);
+pref("media.navigator.video.enabled", true);
 pref("media.navigator.load_adapt", false);
 pref("media.navigator.video.default_width",640);
 pref("media.navigator.video.default_height",480);
 pref("media.navigator.video.default_fps",30);
 pref("media.navigator.video.default_minfps",10);
 #ifdef MOZ_WIDGET_GONK
 pref("media.peerconnection.enabled", false);
+pref("media.peerconnection.video.enabled", false);
 pref("media.navigator.video.max_fs", 1200); // 640x480 == 1200mb
 pref("media.navigator.video.max_fr", 30);
 #else
 pref("media.peerconnection.enabled", true);
+pref("media.peerconnection.video.enabled", true);
 pref("media.navigator.video.max_fs", 0); // unrestricted
 pref("media.navigator.video.max_fr", 0); // unrestricted
 #endif
 pref("media.navigator.permission.disabled", false);
 pref("media.peerconnection.default_iceservers", "[{\"url\": \"stun:stun.services.mozilla.com\"}]");
 pref("media.peerconnection.trickle_ice", true);
 pref("media.peerconnection.use_document_iceservers", true);
 // These values (aec, agc, and noice) are from media/webrtc/trunk/webrtc/common_types.h
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -791,62 +791,112 @@ SimpleTest.finish = function () {
           });
         });
     }
 };
 
 /**
  * Monitor console output from now until endMonitorConsole is called.
  *
- * Expect to receive as many console messages as there are elements of
- * |msgs|, an array; each element is an object which may have any
- * number of the following properties:
+ * Expect to receive all console messages described by the elements of
+ * |msgs|, an array, in the order listed in |msgs|; each element is an
+ * object which may have any number of the following properties:
  *   message, errorMessage, sourceName, sourceLine, category:
  *     string or regexp
  *   lineNumber, columnNumber: number
  *   isScriptError, isWarning, isException, isStrict: boolean
  * Strings, numbers, and booleans must compare equal to the named
  * property of the Nth console message.  Regexps must match.  Any
  * fields present in the message but not in the pattern object are ignored.
  *
+ * In addition to the above properties, elements in |msgs| may have a |forbid|
+ * boolean property.  When |forbid| is true, a failure is logged each time a
+ * matching message is received.
+ *
+ * If |forbidUnexpectedMsgs| is true, then the messages received in the console
+ * must exactly match the non-forbidden messages in |msgs|; for each received
+ * message not described by the next element in |msgs|, a failure is logged.  If
+ * false, then other non-forbidden messages are ignored, but all expected
+ * messages must still be received.
+ *
  * After endMonitorConsole is called, |continuation| will be called
  * asynchronously.  (Normally, you will want to pass |SimpleTest.finish| here.)
  *
  * It is incorrect to use this function in a test which has not called
  * SimpleTest.waitForExplicitFinish.
  */
-SimpleTest.monitorConsole = function (continuation, msgs) {
+SimpleTest.monitorConsole = function (continuation, msgs, forbidUnexpectedMsgs) {
   if (SimpleTest._stopOnLoad) {
     ok(false, "Console monitoring requires use of waitForExplicitFinish.");
   }
 
+  function msgMatches(msg, pat) {
+    for (k in pat) {
+      if (!(k in msg)) {
+        return false;
+      }
+      if (pat[k] instanceof RegExp && typeof(msg[k]) === 'string') {
+        if (!pat[k].test(msg[k])) {
+          return false;
+        }
+      } else if (msg[k] !== pat[k]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  var forbiddenMsgs = [];
+  var i = 0;
+  while (i < msgs.length) {
+    var pat = msgs[i];
+    if ("forbid" in pat) {
+      var forbid = pat.forbid;
+      delete pat.forbid;
+      if (forbid) {
+        forbiddenMsgs.push(pat);
+        msgs.splice(i, 1);
+        continue;
+      }
+    }
+    i++;
+  }
+
   var counter = 0;
   function listener(msg) {
     if (msg.message === "SENTINEL" && !msg.isScriptError) {
       is(counter, msgs.length, "monitorConsole | number of messages");
       SimpleTest.executeSoon(continuation);
-    } else if (counter >= msgs.length) {
-      ok(false, "monitorConsole | extra message | " + JSON.stringify(msg));
+      return;
+    }
+    for (var pat of forbiddenMsgs) {
+      if (msgMatches(msg, pat)) {
+        ok(false, "monitorConsole | observed forbidden message " +
+                  JSON.stringify(msg));
+        return;
+      }
+    }
+    if (counter >= msgs.length) {
+      var str = "monitorConsole | extra message | " + JSON.stringify(msg);
+      if (forbidUnexpectedMsgs) {
+        ok(false, str);
+      } else {
+        info(str);
+      }
+      return;
+    }
+    var matches = msgMatches(msg, msgs[counter]);
+    if (forbidUnexpectedMsgs) {
+      ok(matches, "monitorConsole | [" + counter + "] must match " +
+                  JSON.stringify(msg));
     } else {
-      var pat = msgs[counter];
-      for (k in pat) {
-        ok(k in msg, "monitorConsole | [" + counter + "]." + k + " present");
-        if (k in msg) {
-          if (pat[k] instanceof RegExp && typeof(msg[k]) === 'string') {
-            ok(pat[k].test(msg[k]),
-               "monitorConsole | [" + counter + "]." + k + " value - " +
-               msg[k].quote() + " contains /" + pat[k].source + "/");
-          } else {
-            ise(msg[k], pat[k],
-                "monitorConsole | [" + counter + "]." + k + " value");
-          }
-        }
-      }
-      counter++;
+      info("monitorConsole | [" + counter + "] " +
+           (matches ? "matched " : "did not match ") + JSON.stringify(msg));
     }
+    counter++;
   }
   SpecialPowers.registerConsoleListener(listener);
 };
 
 /**
  * Stop monitoring console output.
  */
 SimpleTest.endMonitorConsole = function () {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/satchel/AutoCompleteE10S.jsm
@@ -0,0 +1,157 @@
+/* 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 strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = [ "AutoCompleteE10S" ];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+// nsITreeView implementation that feeds the autocomplete popup
+// with the search data.
+let AutoCompleteE10SView = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView,
+                                         Ci.nsIAutoCompleteController]),
+  treeBox: null,
+  selection: null,
+  treeData: [],
+
+  get rowCount()                     { return this.treeData.length; },
+  setTree: function(treeBox)         { this.treeBox = treeBox; },
+  getCellText: function(idx, column) { return this.treeData[idx] },
+  isContainer: function(idx)         { return false; },
+  getCellValue: function(idx, column){ return false },
+  isContainerOpen: function(idx)     { return false; },
+  isContainerEmpty: function(idx)    { return false; },
+  isSeparator: function(idx)         { return false; },
+  isSorted: function()               { return false; },
+  isEditable: function(idx, column)  { return false; },
+  canDrop: function(idx, orientation, dt) { return false; },
+  getLevel: function(idx)            { return 0; },
+  getParentIndex: function(idx)      { return -1; },
+  hasNextSibling: function(idx, after) { return idx < this.treeData.length - 1 },
+  toggleOpenState: function(idx)     { },
+  getCellProperties: function(idx, column) { return ""; },
+  getRowProperties: function(idx)    { return ""; },
+  getImageSrc: function(idx, column) { return null; },
+  getProgressMode : function(idx, column) { },
+  cycleHeader: function(column) { },
+  cycleCell: function(idx, column) { },
+  selectionChanged: function() { },
+  performAction: function(action) { },
+  performActionOnCell: function(action, index, column) { },
+  getColumnProperties: function(column) { return ""; },
+
+  get matchCount() this.rowCount,
+
+
+  clearResults: function() {
+    this.treeData = [];
+  },
+
+  addResult: function(result) {
+    this.treeData.push(result);
+  },
+
+  handleEnter: function(aIsPopupSelection) {
+    AutoCompleteE10S.handleEnter(aIsPopupSelection);
+  },
+
+  stopSearch: function(){}
+};
+
+this.AutoCompleteE10S = {
+  init: function() {
+    let messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
+                         getService(Ci.nsIMessageListenerManager);
+    messageManager.addMessageListener("FormAutoComplete:SelectBy", this);
+    messageManager.addMessageListener("FormAutoComplete:GetSelectedIndex", this);
+    messageManager.addMessageListener("FormAutoComplete:ClosePopup", this);
+  },
+
+  search: function(message) {
+    let browserWindow = message.target.ownerDocument.defaultView;
+    this.browser = browserWindow.gBrowser.selectedBrowser;
+    this.popup = this.browser.autoCompletePopup;
+    this.popup.hidden = false;
+    this.popup.setAttribute("width", message.data.width);
+
+    this.x = message.data.left;
+    this.y = message.data.top + message.data.height;
+
+    let formAutoComplete = Cc["@mozilla.org/satchel/form-autocomplete;1"]
+                             .getService(Ci.nsIFormAutoComplete);
+
+    formAutoComplete.autoCompleteSearchAsync(message.data.inputName,
+                                             message.data.untrimmedSearchString,
+                                             null,
+                                             null,
+                                             this.onSearchComplete.bind(this));
+  },
+
+  onSearchComplete: function(results) {
+    AutoCompleteE10SView.clearResults();
+
+    let resultsArray = [];
+    let count = results.matchCount;
+    for (let i = 0; i < count; i++) {
+      let result = results.getValueAt(i);
+      resultsArray.push(result);
+      AutoCompleteE10SView.addResult(result);
+    }
+
+    this.popup.view = AutoCompleteE10SView;
+
+    this.browser.messageManager.sendAsyncMessage(
+      "FormAutoComplete:AutoCompleteSearchAsyncResult",
+      {results: resultsArray}
+    );
+
+    this.popup.selectedIndex = -1;
+    this.popup.invalidate();
+
+    if (count > 0) {
+      this.popup.openPopup(this.browser, "overlap", this.x, this.y, true, true);
+      // Bug 947503 - This openPopup call is not triggering the "popupshowing"
+      // event, which autocomplete.xml uses to track the openness of the popup
+      // by setting its mPopupOpen flag. This flag needs to be properly set
+      // for closePopup to work. For now, we set it manually.
+      this.popup.mPopupOpen = true;
+    } else {
+      this.popup.closePopup();
+    }
+  },
+
+  receiveMessage: function(message) {
+    switch (message.name) {
+      case "FormAutoComplete:SelectBy":
+        this.popup.selectBy(message.data.reverse, message.data.page);
+        break;
+
+      case "FormAutoComplete:GetSelectedIndex":
+        return this.popup.selectedIndex;
+
+      case "FormAutoComplete:ClosePopup":
+        this.popup.closePopup();
+        break;
+    }
+  },
+
+  handleEnter: function(aIsPopupSelection) {
+    this.browser.messageManager.sendAsyncMessage(
+      "FormAutoComplete:HandleEnter",
+      { selectedIndex: this.popup.selectedIndex,
+        IsPopupSelection: aIsPopupSelection }
+    );
+  },
+
+  stopSearch: function() {}
+}
+
+this.AutoCompleteE10S.init();
--- a/toolkit/components/satchel/FormHistoryStartup.js
+++ b/toolkit/components/satchel/FormHistoryStartup.js
@@ -6,16 +6,19 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
                                   "resource://gre/modules/FormHistory.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "AutoCompleteE10S",
+                                  "resource://gre/modules/AutoCompleteE10S.jsm");
+
 function FormHistoryStartup() { }
 
 FormHistoryStartup.prototype = {
   classID: Components.ID("{3A0012EB-007F-4BB8-AA81-A07385F77A25}"),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference,
@@ -54,25 +57,36 @@ FormHistoryStartup.prototype = {
     // triggers needed service cleanup and db shutdown
     Services.obs.addObserver(this, "profile-before-change", true);
     Services.obs.addObserver(this, "formhistory-expire-now", true);
 
     let messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
                          getService(Ci.nsIMessageListenerManager);
     messageManager.loadFrameScript("chrome://satchel/content/formSubmitListener.js", true);
     messageManager.addMessageListener("FormHistory:FormSubmitEntries", this);
+    messageManager.addMessageListener("FormHistory:AutoCompleteSearchAsync", this);
   },
 
   receiveMessage: function(message) {
-    let entries = message.json;
-    let changes = entries.map(function(entry) {
-      return {
-        op : "bump",
-        fieldname : entry.name,
-        value : entry.value,
+    switch (message.name) {
+      case "FormHistory:FormSubmitEntries": {
+        let entries = message.data;
+        let changes = entries.map(function(entry) {
+          return {
+            op : "bump",
+            fieldname : entry.name,
+            value : entry.value,
+          }
+        });
+
+        FormHistory.update(changes);
+        break;
       }
-    });
 
-    FormHistory.update(changes);
+      case "FormHistory:AutoCompleteSearchAsync": {
+        AutoCompleteE10S.search(message);
+        break;
+      }
+    }
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FormHistoryStartup]);
--- a/toolkit/components/satchel/moz.build
+++ b/toolkit/components/satchel/moz.build
@@ -30,16 +30,17 @@ EXTRA_COMPONENTS += [
     'satchel.manifest',
 ]
 
 EXTRA_PP_COMPONENTS += [
     'nsFormHistory.js',
 ]
 
 EXTRA_JS_MODULES += [
+    'AutoCompleteE10S.jsm',
     'nsFormAutoCompleteResult.jsm',
 ]
 
 EXTRA_PP_JS_MODULES += [
     'FormHistory.jsm',
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/toolkit/components/satchel/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/nsFormAutoComplete.js
@@ -14,16 +14,21 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/Deprecated.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
                                   "resource://gre/modules/FormHistory.jsm");
 
 function FormAutoComplete() {
     this.init();
 }
 
+/**
+ * FormAutoComplete
+ *
+ * Implements the nsIFormAutoComplete interface in the main process.
+ */
 FormAutoComplete.prototype = {
     classID          : Components.ID("{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}"),
     QueryInterface   : XPCOMUtils.generateQI([Ci.nsIFormAutoComplete, Ci.nsISupportsWeakReference]),
 
     _prefBranch         : null,
     _debug              : true, // mirrors browser.formfill.debug
     _enabled            : true, // mirrors browser.formfill.enable preference
     _agedWeight         : 2,
@@ -299,18 +304,110 @@ FormAutoComplete.prototype = {
         boundaryCalc += this._prefixWeight *
                         (entry.textLowerCase.
                          indexOf(aSearchString) == 0);
         entry.totalScore = Math.round(entry.frecency * Math.max(1, boundaryCalc));
     }
 
 }; // end of FormAutoComplete implementation
 
+/**
+ * FormAutoCompleteChild
+ *
+ * Implements the nsIFormAutoComplete interface in a child content process,
+ * and forwards the auto-complete requests to the parent process which
+ * also implements a nsIFormAutoComplete interface and has
+ * direct access to the FormHistory database.
+ */
+function FormAutoCompleteChild() {
+  this.init();
+}
 
+FormAutoCompleteChild.prototype = {
+    classID          : Components.ID("{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}"),
+    QueryInterface   : XPCOMUtils.generateQI([Ci.nsIFormAutoComplete, Ci.nsISupportsWeakReference]),
 
+    _debug: false,
+    _enabled: true,
+
+    /*
+     * init
+     *
+     * Initializes the content-process side of the FormAutoComplete component,
+     * and add a listener for the message that the parent process sends when
+     * a result is produced.
+     */
+    init: function() {
+      this._debug    = Services.prefs.getBoolPref("browser.formfill.debug");
+      this._enabled  = Services.prefs.getBoolPref("browser.formfill.enable");
+      this.log("init");
+    },
+
+    /*
+     * log
+     *
+     * Internal function for logging debug messages
+     */
+    log : function (message) {
+      if (!this._debug)
+        return;
+      dump("FormAutoCompleteChild: " + message + "\n");
+    },
+
+    autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult) {
+      // This function is deprecated
+    },
+
+    autoCompleteSearchAsync : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult, aListener) {
+      this.log("autoCompleteSearchAsync");
+
+      this._pendingListener = aListener;
+
+      let rect = aField.getBoundingClientRect();
+
+      let topLevelDocshell = aField.ownerDocument.defaultView
+                                   .QueryInterface(Ci.nsIInterfaceRequestor)
+                                   .getInterface(Ci.nsIDocShell)
+                                   .sameTypeRootTreeItem
+                                   .QueryInterface(Ci.nsIDocShell);
+
+      let mm = topLevelDocshell.QueryInterface(Ci.nsIInterfaceRequestor)
+                               .getInterface(Ci.nsIContentFrameMessageManager);
+
+      mm.sendAsyncMessage("FormHistory:AutoCompleteSearchAsync", {
+        inputName: aInputName,
+        untrimmedSearchString: aUntrimmedSearchString,
+        left: rect.left,
+        top: rect.top,
+        width: rect.width,
+        height: rect.height
+      });
+
+      mm.addMessageListener("FormAutoComplete:AutoCompleteSearchAsyncResult",
+        function searchFinished(message) {
+          mm.removeMessageListener("FormAutoComplete:AutoCompleteSearchAsyncResult", searchFinished);
+          let result = new FormAutoCompleteResult(
+            null,
+            [{text: res} for (res of message.data.results)],
+            null,
+            null
+          );
+          if (aListener) {
+            aListener.onSearchCompletion(result);
+          }
+        }
+      );
+
+      this.log("autoCompleteSearchAsync message was sent");
+    },
+
+    stopAutoCompleteSearch : function () {
+       this.log("stopAutoCompleteSearch");
+    },
+}; // end of FormAutoCompleteChild implementation
 
 // nsIAutoCompleteResult implementation
 function FormAutoCompleteResult (formHistory, entries, fieldName, searchString) {
     this.formHistory = formHistory;
     this.entries = entries;
     this.fieldName = fieldName;
     this.searchString = searchString;
 }
@@ -385,10 +482,20 @@ FormAutoCompleteResult.prototype = {
         if (removeFromDB) {
           this.formHistory.update({ op: "remove",
                                     fieldname: this.fieldName,
                                     value: removedEntry.text });
         }
     }
 };
 
-let component = [FormAutoComplete];
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
+
+if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT &&
+    Services.prefs.prefHasUserValue("browser.tabs.remote") &&
+    Services.prefs.getBoolPref("browser.tabs.remote")) {
+  // Register the stub FormAutoComplete module in the child which will
+  // forward messages to the parent through the process message manager.
+  let component = [FormAutoCompleteChild];
+  this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
+} else {
+  let component = [FormAutoComplete];
+  this.NSGetFactory = XPCOMUtils.generateNSGetFactory(component);
+}
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -4,16 +4,17 @@
 
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import("resource://gre/modules/RemoteAddonsChild.jsm");
+Cu.import("resource://gre/modules/Timer.jsm");
 
 let SyncHandler = {
   init: function() {
     sendAsyncMessage("SetSyncHandler", {}, {handler: this});
   },
 
   getFocusedElementAndWindow: function() {
     let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
@@ -242,8 +243,71 @@ addEventListener("ImageContentLoaded", f
   }
 }, false);
 
 RemoteAddonsChild.init(this);
 
 addMessageListener("History:UseGlobalHistory", function (aMessage) {
   docShell.useGlobalHistory = aMessage.data.enabled;
 });
+
+let AutoCompletePopup = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup]),
+
+  init: function() {
+    // Hook up the form fill autocomplete controller.
+    let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"]
+                       .getService(Ci.nsIFormFillController);
+
+    controller.attachToBrowser(docShell, this.QueryInterface(Ci.nsIAutoCompletePopup));
+
+    this._input = null;
+    this._popupOpen = false;
+
+    addMessageListener("FormAutoComplete:HandleEnter", message => {
+      this.selectedIndex = message.data.selectedIndex;
+
+      let controller = Components.classes["@mozilla.org/autocomplete/controller;1"].
+                  getService(Components.interfaces.nsIAutoCompleteController);
+      controller.handleEnter(message.data.isPopupSelection);
+    });
+  },
+
+  get input () { return this._input; },
+  get overrideValue () { return null; },
+  set selectedIndex (index) { },
+  get selectedIndex () {
+    // selectedIndex getter must be synchronous because we need the
+    // correct value when the controller is in controller::HandleEnter.
+    // We can't easily just let the parent inform us the new value every
+    // time it changes because not every action that can change the
+    // selectedIndex is trivial to catch (e.g. moving the mouse over the
+    // list).
+    return sendSyncMessage("FormAutoComplete:GetSelectedIndex", {});
+  },
+  get popupOpen () {
+    return this._popupOpen;
+  },
+
+  openAutocompletePopup: function (input, element) {
+    this._input = input;
+    this._popupOpen = true;
+  },
+
+  closePopup: function () {
+    this._popupOpen = false;
+    sendAsyncMessage("FormAutoComplete:ClosePopup", {});
+  },
+
+  invalidate: function () {
+  },
+
+  selectBy: function(reverse, page) {
+    this._index = sendSyncMessage("FormAutoComplete:SelectBy", {
+      reverse: reverse,
+      page: page
+    });
+  }
+}
+
+addMessageListener("FormAutoComplete:InitPopup", function (aMessage) {
+  setTimeout(function() AutoCompletePopup.init(), 0);
+});
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -114,16 +114,20 @@
                 readonly="true"/>
 
       <field name="_imageDocument">null</field>
 
       <property name="imageDocument"
                 onget="return this._imageDocument"
                 readonly="true"/>
 
+      <property name="autoCompletePopup"
+                onget="return document.getElementById(this.getAttribute('autocompletepopup'))"
+                readonly="true"/>
+
       <constructor>
         <![CDATA[
           let jsm = "resource://gre/modules/RemoteWebNavigation.jsm";
           let RemoteWebNavigation = Cu.import(jsm, {}).RemoteWebNavigation;
           this._remoteWebNavigation = new RemoteWebNavigation(this);
 
           this.messageManager.addMessageListener("DOMTitleChanged", this);
           this.messageManager.addMessageListener("ImageDocumentLoaded", this);
@@ -143,16 +147,20 @@
 
           jsm = "resource://gre/modules/RemoteAddonsParent.jsm";
           let RemoteAddonsParent = Components.utils.import(jsm, {}).RemoteAddonsParent;
           RemoteAddonsParent.init();
 
           if (!this.hasAttribute("disableglobalhistory")) {
             this.messageManager.sendAsyncMessage("History:UseGlobalHistory", {enabled:true});
           }
+
+          if (this.autoCompletePopup) {
+            this.messageManager.sendAsyncMessage("FormAutoComplete:InitPopup");
+          }
         ]]>
       </constructor>
 
       <method name="receiveMessage">
         <parameter name="aMessage"/>
         <body><![CDATA[
           let data = aMessage.data;
           switch (aMessage.name) {
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -476,19 +476,16 @@ function BrowserTabActor(aConnection, aB
   this.conn = aConnection;
   this._browser = aBrowser;
   this._tabbrowser = aTabBrowser;
   this._tabActorPool = null;
   // A map of actor names to actor instances provided by extensions.
   this._extraActors = {};
 
   this._onWindowCreated = this.onWindowCreated.bind(this);
-
-  // Number of event loops nested.
-  this._nestedEventLoopDepth = 0;
 }
 
 // XXX (bug 710213): BrowserTabActor attach/detach/exit/disconnect is a
 // *complete* mess, needs to be rethought asap.
 
 BrowserTabActor.prototype = {
   get browser() { return this._browser; },
 
@@ -610,19 +607,16 @@ BrowserTabActor.prototype = {
     }
 
     if (this.attached) {
       this._detach();
       this.conn.send({ from: this.actorID,
                        type: "tabDetached" });
     }
 
-    // Pop all nested event loops if we haven't already.
-    while (this._nestedEventLoopDepth > 0)
-      this.postNest();
     this._browser = null;
     this._tabbrowser = null;
   },
 
   /* Support for DebuggerServer.addTabActor. */
   _createExtraActors: CommonCreateExtraActors,
   _appendExtraActors: CommonAppendExtraActors,
 
@@ -761,43 +755,35 @@ BrowserTabActor.prototype = {
       // The tab is already closed.
       return;
     }
     let windowUtils = this.window
                           .QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
     windowUtils.suppressEventHandling(true);
     windowUtils.suspendTimeouts();
-    this._nestedEventLoopDepth++;
   },
 
   /**
    * Prepare to exit a nested event loop by enabling debuggee events.
    */
   postNest: function BTA_postNest(aNestData) {
     if (!this.window) {
-      // The tab is already closed, so there is no way to resume events and
-      // timeouts.
-      // TODO: this wouldn't be necessary if closing a browser window could be
-      // identified early enough while this.window is still available. This is
-      // still cauisng leaks that we need to fix (bug 933950).
-      this._nestedEventLoopDepth--;
-      this._pendingNavigation = null;
+      // The tab is already closed.
       return;
     }
     let windowUtils = this.window
                           .QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIDOMWindowUtils);
     windowUtils.resumeTimeouts();
     windowUtils.suppressEventHandling(false);
     if (this._pendingNavigation) {
       this._pendingNavigation.resume();
       this._pendingNavigation = null;
     }
-    this._nestedEventLoopDepth--;
   },
 
   /**
    * Handle location changes, by clearing the previous debuggees and enabling
    * debugging, which may have been disabled temporarily by the
    * DebuggerProgressListener.
    */
   onWindowCreated:
--- a/tools/mach_commands.py
+++ b/tools/mach_commands.py
@@ -285,19 +285,19 @@ class PastebinProvider(object):
         except urllib2.URLError:
             print('ERROR. Could not connect to pastebin.mozilla.org.')
             return 1
         return 0
 
 
 @CommandProvider
 class ReviewboardToolsProvider(MachCommandBase):
-    @Command('rbt', category='devenv',
+    @Command('rbt', category='devenv', allow_all_args=True,
         description='Run Reviewboard Tools')
-    @CommandArgument('args', nargs='*', help='Arguments to rbt tool')
+    @CommandArgument('args', nargs='...', help='Arguments to rbt tool')
     def rbt(self, args):
         if not args:
             args = ['help']
 
         self._activate_virtualenv()
         self.virtualenv_manager.install_pip_package('RBTools')
 
         from rbtools.commands.main import main
new file mode 100644
--- /dev/null
+++ b/webapprt/locales/en-US/webapprt/overrides/appstrings.properties
@@ -0,0 +1,18 @@
+# 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/.
+
+netReset=The connection to the server was reset while the application was loading.
+redirectLoop=A server is redirecting a request in a way that will never complete.
+confirmRepostPrompt=To continue, the application must send information that will repeat an action (such as a search or order confirmation) that was performed earlier.
+unknownSocketType=The application doesn't know how to communicate with the server.
+notCached=The application requested a document which is no longer available.
+netOffline=The application is currently in offline mode and can't access the network.
+deniedPortAccess=The application tried to access a network port that it should not have access to. The application has canceled the request for your protection.
+proxyResolveFailure=The application is configured to use a proxy server that can't be found.
+proxyConnectFailure=The application is configured to use a proxy server that is refusing connections.
+contentEncodingError=The application recieved a response from a server which used an invalid or unsupported form of compression.
+unsafeContentType=The application cannot continue because it accessed a file type that may not be safe to open. Please contact the application authors to inform them of this problem.
+cspFrameAncestorBlocked=This application tried to access a resource that has a content security policy that prevents it from being embedded in this way.
+corruptedContentError=The application cannot continue loading because an error in the data transmission was detected.
+remoteXUL=This application tried to use an unsupported technology that is no longer available.
new file mode 100644
--- /dev/null
+++ b/webapprt/locales/en-US/webapprt/overrides/dom.properties
@@ -0,0 +1,148 @@
+# 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/.
+
+KillScriptTitle=Warning: Unresponsive script
+KillScriptMessage=A script may be busy, or it may have stopped responding. You can stop the script now, or you can continue to see if the script will complete.
+KillScriptWithDebugMessage=A script may be busy, or it may have stopped responding. You can stop the script now, open the script in the debugger, or let the script continue.
+KillScriptLocation=Script: %S
+StopScriptButton=Stop script
+DebugScriptButton=Debug script
+WaitForScriptButton=Continue
+DontAskAgain=&Don't ask me again
+JSURLLoadBlockedWarning=Attempt to load a javascript: URL from one host\nin a window displaying content from another host\nwas blocked by the security manager.
+WindowCloseBlockedWarning=Scripts may not close windows that were not opened by script.
+OnBeforeUnloadTitle=Are you sure?
+OnBeforeUnloadMessage=This application is asking you to confirm that you want to leave - data you have entered may not be saved.
+OnBeforeUnloadStayButton=Stay
+OnBeforeUnloadLeaveButton=Leave
+UnexpectedCanvasVariantStyle=canvas: an attempt to set strokeStyle or fillStyle to a value that is neither a string, a CanvasGradient, or a CanvasPattern was ignored.
+EmptyGetElementByIdParam=Empty string passed to getElementById().
+LowMemoryTitle=Warning: Low memory
+LowMemoryMessage=An application script has been stopped due to a low memory condition.
+SpeculationFailed=An unbalanced tree was written using document.write() causing data from the network to be reparsed. For more information https://developer.mozilla.org/en/Optimizing_Your_Pages_for_Speculative_Parsing
+DocumentWriteIgnored=A call to document.write() from an asynchronously-loaded external script was ignored.
+FormValidationTextTooLong=Please shorten this text to %S characters or less (you are currently using %S characters).
+FormValidationValueMissing=Please fill out this field.
+FormValidationCheckboxMissing=Please check this box if you want to proceed.
+FormValidationRadioMissing=Please select one of these options.
+FormValidationFileMissing=Please select a file.
+FormValidationSelectMissing=Please select an item in the list.
+FormValidationInvalidEmail=Please enter an email address.
+FormValidationInvalidURL=Please enter a URL.
+FormValidationPatternMismatch=Please match the requested format.
+# LOCALIZATION NOTE (FormValidationPatternMismatchWithTitle): %S is the (possibly truncated) title attribute value.
+FormValidationPatternMismatchWithTitle=Please match the requested format: %S.
+# LOCALIZATION NOTE (FormValidationRangeOverflow): %S can be a number, a date or a time.
+FormValidationRangeOverflow=Please select a value that is lower than %S.
+# LOCALIZATION NOTE (FormValidationRangeUnderflow): %S can be a number, a date or a time.
+FormValidationRangeUnderflow=Please select a value that is higher than %S.
+# LOCALIZATION NOTE (FormValidationStepMismatch): both %S can be a number, a date or a time.
+FormValidationStepMismatch=Please select a valid value. The two nearest valid values are %S and %S.
+# LOCALIZATION NOTE (FormValidationStepMismatchOneValue): %S can be a number, a date or a time. This is called instead of FormValidationStepMismatch when the second value is the same as the first.
+FormValidationStepMismatchOneValue=Please select a valid value. The nearest valid value is %S.
+GetAttributeNodeWarning=Use of getAttributeNode() is deprecated. Use getAttribute() instead.
+SetAttributeNodeWarning=Use of setAttributeNode() is deprecated. Use setAttribute() instead.
+GetAttributeNodeNSWarning=Use of getAttributeNodeNS() is deprecated. Use getAttributeNS() instead.
+SetAttributeNodeNSWarning=Use of setAttributeNodeNS() is deprecated. Use setAttributeNS() instead.
+RemoveAttributeNodeWarning=Use of removeAttributeNode() is deprecated. Use removeAttribute() instead.
+CreateAttributeWarning=Use of document.createAttribute() is deprecated. Use element.setAttribute() instead.
+CreateAttributeNSWarning=Use of document.createAttributeNS() is deprecated. Use element.setAttributeNS() instead.
+SpecifiedWarning=Use of attributes' specified attribute is deprecated. It always returns true.
+OwnerElementWarning=Use of attributes' ownerElement attribute is deprecated.
+NodeValueWarning=Use of attributes' nodeValue attribute is deprecated. Use value instead.
+TextContentWarning=Use of attributes' textContent attribute is deprecated. Use value instead.
+EnablePrivilegeWarning=Use of enablePrivilege is deprecated.  Please use code that runs with the system principal (e.g. an extension) instead.
+nsIJSONDecodeDeprecatedWarning=nsIJSON.decode is deprecated.  Please use JSON.parse instead.
+nsIJSONEncodeDeprecatedWarning=nsIJSON.encode is deprecated.  Please use JSON.stringify instead.
+nsIDOMWindowInternalWarning=Use of nsIDOMWindowInternal is deprecated. Use nsIDOMWindow instead.
+InputEncodingWarning=Use of inputEncoding is deprecated.
+# LOCALIZATION NOTE: Do not translate "MozBeforePaint" and "mozRequestAnimationFrame"
+MozBeforePaintWarning=MozBeforePaint events are no longer supported.  mozRequestAnimationFrame must be passed a non-null callback argument.
+FullScreenDeniedBlocked=Request for full-screen was denied because this domain has been blocked from full-screen by user.
+FullScreenDeniedDisabled=Request for full-screen was denied because full-screen API is disabled by user preference.
+FullScreenDeniedFocusedPlugin=Request for full-screen was denied because a windowed plugin is focused.
+FullScreenDeniedHidden=Request for full-screen was denied because the document is no longer visible.
+FullScreenDeniedIframeNotAllowed=Request for full-screen was denied because at least one of the document's containing iframes does not have an "allowfullscreen" attribute.
+FullScreenDeniedNotInputDriven=Request for full-screen was denied because Element.mozRequestFullScreen() was not called from inside a short running user-generated event handler.
+FullScreenDeniedNotInDocument=Request for full-screen was denied because requesting element is no longer in its document.
+FullScreenDeniedMovedDocument=Request for full-screen was denied because requesting element has moved document.
+FullScreenDeniedLostWindow=Request for full-screen was denied because we no longer have a window.
+FullScreenDeniedSubDocFullScreen=Request for full-screen was denied because a subdocument of the document requesting full-screen is already full-screen.
+FullScreenDeniedNotDescendant=Request for full-screen was denied because requesting element is not a descendant of the current full-screen element.
+FullScreenDeniedNotFocusedTab=Request for full-screen was denied because requesting element is not in the currently focused tab.
+FullScreenDeniedContentOnly=Request for full-screen was denied because requesting element is in the chrome document and the fullscreen API is configured for content only.
+RemovedFullScreenElement=Exited full-screen because full-screen element was removed from document.
+FocusedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin was focused.
+HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode.
+InvalidRedirectChannelWarning=Unable to redirect to %S because the channel doesn't implement nsIWritablePropertyBag2.
+ResponseTypeSyncXHRWarning=Use of XMLHttpRequest's responseType attribute is no longer supported in the synchronous mode in window context.
+WithCredentialsSyncXHRWarning=Use of XMLHttpRequest's withCredentials attribute is no longer supported in the synchronous mode in window context.
+TimeoutSyncXHRWarning=Use of XMLHttpRequest's timeout attribute is not supported in the synchronous mode in window context.
+JSONCharsetWarning=An attempt was made to declare a non-UTF-8 encoding for JSON retrieved using XMLHttpRequest. Only UTF-8 is supported for decoding JSON.
+# LOCALIZATION NOTE: Do not translate AudioBufferSourceNode
+MediaBufferSourceNodeResampleOutOfMemory=Insufficient memory to resample the AudioBufferSourceNode for playback.
+# LOCALIZATION NOTE: Do not translate decodeAudioData.
+MediaDecodeAudioDataUnknownContentType=The buffer passed to decodeAudioData contains an unknown content type.
+# LOCALIZATION NOTE: Do not translate decodeAudioData.
+MediaDecodeAudioDataUnknownError=An unknown error occured while processing decodeAudioData.
+# LOCALIZATION NOTE: Do not translate decodeAudioData.
+MediaDecodeAudioDataInvalidContent=The buffer passed to decodeAudioData contains invalid content which cannot be decoded successfully.
+# LOCALIZATION NOTE: Do not translate decodeAudioData.
+MediaDecodeAudioDataNoAudio=The buffer passed to decodeAudioData does not contain any audio.
+MediaLoadExhaustedCandidates=All candidate resources failed to load. Media load paused.
+MediaLoadSourceMissingSrc=<source> element has no "src" attribute. Media resource load failed.
+# LOCALIZATION NOTE: %1$S is the Http error code the server returned (e.g. 404, 500, etc), %2$S is the URL of the media resource which failed to load.
+MediaLoadHttpError=HTTP load failed with status %1$S. Load of media resource %2$S failed.
+# LOCALIZATION NOTE: %S is the URL of the media resource which failed to load.
+MediaLoadInvalidURI=Invalid URI. Load of media resource %S failed.
+# LOCALIZATION NOTE: %1$S is the media resource's format/codec type (basically equivalent to the file type, e.g. MP4,AVI,WMV,MOV etc), %2$S is the URL of the media resource which failed to load.
+MediaLoadUnsupportedTypeAttribute=Specified "type" attribute of "%1$S" is not supported. Load of media resource %2$S failed.
+# LOCALIZATION NOTE: %1$S is the "media" attribute value of the <source> element. It is a media query. %2$S is the URL of the media resource which failed to load.
+MediaLoadSourceMediaNotMatched=Specified "media" attribute of "%1$S" does not match the environment. Load of media resource %2$S failed.
+# LOCALIZATION NOTE: %1$S is the MIME type HTTP header being sent by the web server, %2$S is the URL of the media resource which failed to load.
+MediaLoadUnsupportedMimeType=HTTP "Content-Type" of "%1$S" is not supported. Load of media resource %2$S failed.
+# LOCALIZATION NOTE: %S is the URL of the media resource which failed to load because of error in decoding.
+MediaLoadDecodeError=Media resource %S could not be decoded.
+# LOCALIZATION NOTE: Do not translate "DOMException", "code" and "name"
+DOMExceptionCodeWarning=Use of DOMException's code attribute is deprecated. Use name instead.
+# LOCALIZATION NOTE: Do not translate "__exposedProps__"
+NoExposedPropsWarning=Exposing chrome JS objects to content without __exposedProps__ is insecure and deprecated. See https://developer.mozilla.org/en/XPConnect_wrappers for more information.
+# LOCALIZATION NOTE: Do not translate "Mutation Event" and "MutationObserver"
+MutationEventWarning=Use of Mutation Events is deprecated. Use MutationObserver instead.
+# LOCALIZATION NOTE: Do not translate "Blob", "mozSlice", or "slice"
+MozSliceWarning=Use of mozSlice on the Blob object is deprecated.  Use slice instead.
+# LOCALIZATION NOTE: Do not translate "Components"
+ComponentsWarning=The Components object is deprecated. It will soon be removed.
+PluginHangUITitle=Warning: Unresponsive plugin
+PluginHangUIMessage=%S may be busy, or it may have stopped responding. You can stop the plugin now, or you can continue to see if the plugin will complete.
+PluginHangUIWaitButton=Continue
+PluginHangUIStopButton=Stop plugin
+# LOCALIZATION NOTE: Do not translate "mozHidden", "mozVisibilityState", "hidden", or "visibilityState"
+PrefixedVisibilityApiWarning='mozHidden' and 'mozVisibilityState' are deprecated.  Please use the unprefixed 'hidden' and 'visibilityState' instead.
+# LOCALIZATION NOTE: Do not translate "NodeIterator" or "detach()".
+NodeIteratorDetachWarning=Calling detach() on a NodeIterator no longer has an effect.
+# LOCALIZATION NOTE: Do not translate "Mozilla Audio Data API" and "Web Audio API".
+MozAudioDataWarning=The Mozilla Audio Data API is deprecated.  Please use the Web Audio API instead.
+# LOCALIZATION NOTE: Do not translate "LenientThis" and "this"
+LenientThisWarning=Ignoring get or set of property that has [LenientThis] because the "this" object is incorrect.
+# LOCALIZATION NOTE: Do not translate "nsIDOMWindowUtils", "getWindowWithOuterId", or "nsIWindowMediator"
+GetWindowWithOuterIdWarning=Use of nsIDOMWindowUtils.getOuterWindowWithId() is deprecated.  Instead, use the nsIWindowMediator method of the same name.
+# LOCALIZATION NOTE: Do not translate "getPreventDefault" or "defaultPrevented".
+GetPreventDefaultWarning=Use of getPreventDefault() is deprecated.  Use defaultPrevented instead.
+# LOCALIZATION NOTE: Do not translate "getUserData", "setUserData", "WeakMap", or "element.dataset".
+GetSetUserDataWarning=Use of getUserData() or setUserData() is deprecated.  Use WeakMap or element.dataset instead.
+# LOCALIZATION NOTE: Do not translate "mozGetAsFile" or "toBlob"
+MozGetAsFileWarning=The non-standard mozGetAsFile method is deprecated and will soon be removed.  Use the standard toBlob method instead.
+# LOCALIZATION NOTE: Do not translate "captureEvents()" or "addEventListener()"
+UseOfCaptureEventsWarning=Use of captureEvents() is deprecated. To upgrade your code, use the DOM 2 addEventListener() method. For more help http://developer.mozilla.org/en/docs/DOM:element.addEventListener
+# LOCALIZATION NOTE: Do not translate "releaseEvents()" or "removeEventListener()"
+UseOfReleaseEventsWarning=Use of releaseEvents() is deprecated. To upgrade your code, use the DOM 2 removeEventListener() method. For more help http://developer.mozilla.org/en/docs/DOM:element.removeEventListener
+# LOCALIZATION NOTE: Do not translate "document.load()" or "XMLHttpRequest"
+UseOfDOM3LoadMethodWarning=Use of document.load() is deprecated. To upgrade your code, use the DOM XMLHttpRequest object. For more help https://developer.mozilla.org/en/XMLHttpRequest
+# LOCALIZATION NOTE: Do not translate "window.showModalDialog()" or "window.open()" 
+ShowModalDialogWarning=Use of window.showModalDialog() is deprecated. Use window.open() instead. For more help https://developer.mozilla.org/en-US/docs/Web/API/Window.open
+# LOCALIZATION NOTE: Do not translate "cloneNode()"
+UnsafeCloneNodeWarning=The behavior of cloneNode() with no boolean argument is about to change from doing a deep clone to doing a shallow clone.  Make sure to pass an explicit boolean argument to keep your current behavior.
+# LOCALIZATION NOTE: Do not translate "importNode()"
+UnsafeImportNodeWarning=The behavior of importNode() with no boolean argument is about to change from doing a deep clone to doing a shallow clone.  Make sure to pass an explicit boolean argument to keep your current behavior.
--- a/webapprt/locales/jar.mn
+++ b/webapprt/locales/jar.mn
@@ -3,10 +3,15 @@
 # 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/.
 
 @AB_CD@.jar:
 % locale webapprt @AB_CD@ %locale/webapprt/
     locale/webapprt/webapp.dtd                     (%webapprt/webapp.dtd)
     locale/webapprt/webapp.properties              (%webapprt/webapp.properties)
     locale/webapprt/getUserMediaDialog.dtd         (%webapprt/getUserMediaDialog.dtd)
+    locale/webapprt/appstrings.properties         (%webapprt/overrides/appstrings.properties)
+    locale/webapprt/dom.properties         (%webapprt/overrides/dom.properties)
 
 % locale branding @AB_CD@ resource://webappbranding/
+
+% override chrome://global/locale/appstrings.properties  chrome://webapprt/locale/appstrings.properties
+% override chrome://global/locale/dom/dom.properties  chrome://webapprt/locale/dom.properties