Bug 642381, part 3: Hoist Maybe into mfbt and eliminate Gecko's use of jstl. r=jorendorff,luke
authorChris Jones <jones.chris.g@gmail.com>
Thu, 28 Apr 2011 17:48:52 -0500
changeset 69130 3dd6ec45084c7a79fbbae8aee42769a91dcf00ee
parent 69129 c8dcc08a4a8a02f006d2a8aa7f3e0ea9fc764fd1
child 69131 61bbaedfc2a3934a7fa821012756f6a3ee63046b
push id76
push userbzbarsky@mozilla.com
push dateTue, 05 Jul 2011 17:00:57 +0000
treeherdermozilla-beta@d3a2732c35f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff, luke
bugs642381
milestone6.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
Bug 642381, part 3: Hoist Maybe into mfbt and eliminate Gecko's use of jstl. r=jorendorff,luke
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
js/jetpack/JetpackActorCommon.cpp
js/src/Makefile.in
js/src/jstl.h
layout/generic/nsLineLayout.cpp
mfbt/Types.h
mfbt/Util.h
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -111,16 +111,18 @@
 // Force PR_LOGGING so we can get JS strict warnings even in release builds
 #define FORCE_PR_LOG 1
 #endif
 #include "prlog.h"
 #include "prthread.h"
 
 #include "mozilla/FunctionTimer.h"
 
+using namespace mozilla;
+
 const size_t gStackSize = 8192;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gJSDiagnostics;
 #endif
 
 // Thank you Microsoft!
 #ifndef WINCE
@@ -1881,18 +1883,18 @@ nsJSContext::CallEventHandler(nsISupport
 
     jsval funval = OBJECT_TO_JSVAL(funobj);
     JSAutoEnterCompartment ac;
     if (!ac.enter(mContext, funobj) || !JS_WrapObject(mContext, &target)) {
       sSecurityManager->PopContextPrincipal(mContext);
       return NS_ERROR_FAILURE;
     }
 
-    js::Maybe<nsAutoPoolRelease> poolRelease;
-    js::Maybe<js::AutoArrayRooter> tvr;
+    Maybe<nsAutoPoolRelease> poolRelease;
+    Maybe<js::AutoArrayRooter> tvr;
 
     // Use |target| as the scope for wrapping the arguments, since aScope is
     // the safe scope in many cases, which isn't very useful.  Wrapping aTarget
     // was OK because those typically have PreCreate methods that give them the
     // right scope anyway, and we want to make sure that the arguments end up
     // in the same scope as aTarget.
     rv = ConvertSupportsTojsvals(aargv, target, &argc,
                                  &argv, poolRelease, tvr);
@@ -2370,18 +2372,18 @@ nsJSContext::InitializeExternalClasses()
 nsresult
 nsJSContext::SetProperty(void *aTarget, const char *aPropName, nsISupports *aArgs)
 {
   PRUint32  argc;
   jsval    *argv = nsnull;
 
   JSAutoRequest ar(mContext);
 
-  js::Maybe<nsAutoPoolRelease> poolRelease;
-  js::Maybe<js::AutoArrayRooter> tvr;
+  Maybe<nsAutoPoolRelease> poolRelease;
+  Maybe<js::AutoArrayRooter> tvr;
 
   nsresult rv;
   rv = ConvertSupportsTojsvals(aArgs, GetNativeGlobal(), &argc,
                                &argv, poolRelease, tvr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   jsval vargs;
 
@@ -2411,18 +2413,18 @@ nsJSContext::SetProperty(void *aTarget, 
   return rv;
 }
 
 nsresult
 nsJSContext::ConvertSupportsTojsvals(nsISupports *aArgs,
                                      void *aScope,
                                      PRUint32 *aArgc,
                                      jsval **aArgv,
-                                     js::Maybe<nsAutoPoolRelease> &aPoolRelease,
-                                     js::Maybe<js::AutoArrayRooter> &aRooter)
+                                     Maybe<nsAutoPoolRelease> &aPoolRelease,
+                                     Maybe<js::AutoArrayRooter> &aRooter)
 {
   nsresult rv = NS_OK;
 
   // If the array implements nsIJSArgArray, just grab the values directly.
   nsCOMPtr<nsIJSArgArray> fastArray = do_QueryInterface(aArgs);
   if (fastArray != nsnull)
     return fastArray->GetArgs(aArgc, reinterpret_cast<void **>(aArgv));
 
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -46,16 +46,18 @@
 #include "prtime.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsScriptNameSpaceManager.h"
 
 class nsIXPConnectJSObjectHolder;
 class nsAutoPoolRelease;
 namespace js {
 class AutoArrayRooter;
+}
+namespace mozilla {
 template <class> class Maybe;
 }
 
 class nsJSContext : public nsIScriptContext,
                     public nsIXPCScriptNotify
 {
 public:
   nsJSContext(JSRuntime *aRuntime);
@@ -200,18 +202,18 @@ public:
 protected:
   nsresult InitializeExternalClasses();
 
   // Helper to convert xpcom datatypes to jsvals.
   nsresult ConvertSupportsTojsvals(nsISupports *aArgs,
                                    void *aScope,
                                    PRUint32 *aArgc,
                                    jsval **aArgv,
-                                   js::Maybe<nsAutoPoolRelease> &aPoolRelease,
-                                   js::Maybe<js::AutoArrayRooter> &aRooter);
+                                   mozilla::Maybe<nsAutoPoolRelease> &aPoolRelease,
+                                   mozilla::Maybe<js::AutoArrayRooter> &aRooter);
 
   nsresult AddSupportsPrimitiveTojsvals(nsISupports *aArg, jsval *aArgv);
 
   // given an nsISupports object (presumably an event target or some other
   // DOM object), get (or create) the JSObject wrapping it.
   nsresult JSObjectFromInterface(nsISupports *aSup, void *aScript, 
                                  JSObject **aRet);
 
--- a/js/jetpack/JetpackActorCommon.cpp
+++ b/js/jetpack/JetpackActorCommon.cpp
@@ -35,36 +35,27 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "base/basictypes.h"
 #include "jscntxt.h"
 
 #include "jsapi.h"
-#include "jstl.h"
 #include "jshashtable.h"
 
 #include "mozilla/jetpack/JetpackActorCommon.h"
 #include "mozilla/jetpack/PJetpack.h"
 #include "mozilla/jetpack/PHandleParent.h"
 #include "mozilla/jetpack/PHandleChild.h"
 #include "mozilla/jetpack/Handle.h"
 
 #include "nsJSUtils.h"
 
-using mozilla::jetpack::JetpackActorCommon;
-using mozilla::jetpack::PHandleParent;
-using mozilla::jetpack::HandleParent;
-using mozilla::jetpack::PHandleChild;
-using mozilla::jetpack::HandleChild;
-using mozilla::jetpack::KeyValue;
-using mozilla::jetpack::PrimVariant;
-using mozilla::jetpack::CompVariant;
-using mozilla::jetpack::Variant;
+using namespace mozilla::jetpack;
 
 class JetpackActorCommon::OpaqueSeenType
 {
 public:
   typedef JSObject* KeyType;
   typedef size_t IdType;
   typedef js::HashMap<
     KeyType, IdType,
@@ -171,17 +162,17 @@ JetpackActorCommon::jsval_to_PrimVariant
 
 bool
 JetpackActorCommon::jsval_to_CompVariant(JSContext* cx, JSType type, jsval from,
                                          CompVariant* to, OpaqueSeenType* seen)
 {
   if (type != JSTYPE_OBJECT)
     return false;
 
-  js::Maybe<OpaqueSeenType> lost;
+  Maybe<OpaqueSeenType> lost;
   if (!seen) {
     lost.construct();
     seen = lost.addr();
     if (!seen->ok())
       return false;
   }
 
   OpaqueSeenType::KeyType obj = JSVAL_TO_OBJECT(from);
@@ -332,17 +323,17 @@ JetpackActorCommon::jsval_from_PrimVaria
 }
 
 bool
 JetpackActorCommon::jsval_from_CompVariant(JSContext* cx,
                                            const CompVariant& from,
                                            jsval* to,
                                            OpaqueSeenType* seen)
 {
-  js::Maybe<OpaqueSeenType> lost;
+  Maybe<OpaqueSeenType> lost;
   if (!seen) {
     lost.construct();
     seen = lost.addr();
     if (!seen->ok())
       return false;
   }
 
   JSObject* obj = NULL;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -287,16 +287,17 @@ EXPORTS_vm = \
 #
 VPATH		+= \
 		$(srcdir)/../../mfbt \
 		$(NULL)
 
 EXPORTS_NAMESPACES += mozilla
 
 EXPORTS_mozilla = \
+		Types.h		\
 		Util.h          \
 		$(NULL)
 
 ifdef ENABLE_TRACEJIT
 VPATH		+= \
 		$(srcdir)/tracejit \
 		$(srcdir)/nanojit \
 
@@ -723,16 +724,21 @@ distclean::
 	$(RM) $(DIST_GARBAGE)
 
 # our build system doesn't handle subdir srcs very gracefully today
 export::
 	$(MKDIR) -p nanojit
 
 DEFINES		+= -DEXPORT_JS_API
 
+# mfbt is always packed with us, so if we're building a shared object,
+# we need to declare "exported" mfbt symbols on its behalf when we use
+# its headers.
+DEFINES		+= -DIMPL_MFBT
+
 # Some platforms that have stdint.h include it in system headers.  So
 # to reliably get limit macros defined, we'd always have to define the
 # one below before including any header, but that's obscure and
 # fragile, so we do it here.
 DEFINES		+= -D__STDC_LIMIT_MACROS
 
 INCLUDES	+= -I$(srcdir)
 
--- a/js/src/jstl.h
+++ b/js/src/jstl.h
@@ -229,133 +229,16 @@ RoundUpPow2(size_t x)
  */
 template <class T>
 JS_ALWAYS_INLINE size_t
 PointerRangeSize(T *begin, T *end)
 {
     return (size_t(end) - size_t(begin)) / sizeof(T);
 }
 
-/*
- * This utility pales in comparison to Boost's aligned_storage. The utility
- * simply assumes that JSUint64 is enough alignment for anyone. This may need
- * to be extended one day...
- *
- * As an important side effect, pulling the storage into this template is
- * enough obfuscation to confuse gcc's strict-aliasing analysis into not giving
- * false negatives when we cast from the char buffer to whatever type we've
- * constructed using the bytes.
- */
-template <size_t nbytes>
-struct AlignedStorage
-{
-    union U {
-        char bytes[nbytes];
-        uint64 _;
-    } u;
-
-    const void *addr() const { return u.bytes; }
-    void *addr() { return u.bytes; }
-};
-
-template <class T>
-struct AlignedStorage2
-{
-    union U {
-        char bytes[sizeof(T)];
-        uint64 _;
-    } u;
-
-    const T *addr() const { return (const T *)u.bytes; }
-    T *addr() { return (T *)u.bytes; }
-};
-
-/*
- * Small utility for lazily constructing objects without using dynamic storage.
- * When a Maybe<T> is constructed, it is |empty()|, i.e., no value of T has
- * been constructed and no T destructor will be called when the Maybe<T> is
- * destroyed. Upon calling |construct|, a T object will be constructed with the
- * given arguments and that object will be destroyed when the owning Maybe<T>
- * is destroyed.
- *
- * N.B. GCC seems to miss some optimizations with Maybe and may generate extra
- * branches/loads/stores. Use with caution on hot paths.
- */
-template <class T>
-class Maybe
-{
-    AlignedStorage2<T> storage;
-    bool constructed;
-
-    T &asT() { return *storage.addr(); }
-
-    explicit Maybe(const Maybe &other);
-    const Maybe &operator=(const Maybe &other);
-
-  public:
-    Maybe() { constructed = false; }
-    ~Maybe() { if (constructed) asT().~T(); }
-
-    bool empty() const { return !constructed; }
-
-    void construct() {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T();
-        constructed = true;
-    }
-
-    template <class T1>
-    void construct(const T1 &t1) {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T(t1);
-        constructed = true;
-    }
-
-    template <class T1, class T2>
-    void construct(const T1 &t1, const T2 &t2) {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T(t1, t2);
-        constructed = true;
-    }
-
-    template <class T1, class T2, class T3>
-    void construct(const T1 &t1, const T2 &t2, const T3 &t3) {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T(t1, t2, t3);
-        constructed = true;
-    }
-
-    template <class T1, class T2, class T3, class T4>
-    void construct(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) {
-        JS_ASSERT(!constructed);
-        new(storage.addr()) T(t1, t2, t3, t4);
-        constructed = true;
-    }
-
-    T *addr() {
-        JS_ASSERT(constructed);
-        return &asT();
-    }
-
-    T &ref() {
-        JS_ASSERT(constructed);
-        return asT();
-    }
-
-    void destroy() {
-        ref().~T();
-        constructed = false;
-    }
-
-    void destroyIfConstructed() {
-        if (!empty())
-            destroy();
-    }
-};
-
 template <class T>
 class AlignedPtrAndFlag
 {
     uintptr_t bits;
 
   public:
     AlignedPtrAndFlag(T *t, bool flag) {
         JS_ASSERT((uintptr_t(t) & 1) == 0);
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -41,16 +41,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* state and methods used while laying out a single line of a block frame */
 
 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
 #include "plarena.h"
 
+#include "mozilla/Util.h"
 #include "nsCOMPtr.h"
 #include "nsLineLayout.h"
 #include "nsBlockFrame.h"
 #include "nsInlineFrame.h"
 #include "nsStyleConsts.h"
 #include "nsHTMLContainerFrame.h"
 #include "nsFloatManager.h"
 #include "nsStyleContext.h"
@@ -61,17 +62,16 @@
 #include "nsIDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsIContent.h"
 #include "nsTextFragment.h"
 #include "nsBidiUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsTextFrame.h"
 #include "nsCSSRendering.h"
-#include "jstl.h"
 
 #ifdef DEBUG
 #undef  NOISY_HORIZONTAL_ALIGN
 #undef  NOISY_VERTICAL_ALIGN
 #undef  REALLY_NOISY_VERTICAL_ALIGN
 #undef  NOISY_REFLOW
 #undef  REALLY_NOISY_REFLOW
 #undef  NOISY_PUSHING
@@ -79,16 +79,18 @@
 #undef  DEBUG_ADD_TEXT
 #undef  NOISY_MAX_ELEMENT_SIZE
 #undef  REALLY_NOISY_MAX_ELEMENT_SIZE
 #undef  NOISY_CAN_PLACE_FRAME
 #undef  NOISY_TRIM
 #undef  REALLY_NOISY_TRIM
 #endif
 
+using namespace mozilla;
+
 //----------------------------------------------------------------------
 
 #define FIX_BUG_50257
 
 nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
                            nsFloatManager* aFloatManager,
                            const nsHTMLReflowState* aOuterReflowState,
                            const nsLineList::iterator* aLine)
@@ -781,17 +783,17 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
   // reflects the space left on the line.
   NS_WARN_IF_FALSE(psd->mRightEdge != NS_UNCONSTRAINEDSIZE,
                    "have unconstrained width; this should only result from "
                    "very large sizes, not attempts at intrinsic width "
                    "calculation");
   nscoord availableSpaceOnLine = psd->mRightEdge - psd->mX;
 
   // Setup reflow state for reflowing the frame
-  js::Maybe<nsHTMLReflowState> reflowStateHolder;
+  Maybe<nsHTMLReflowState> reflowStateHolder;
   if (!isText) {
     reflowStateHolder.construct(mPresContext, *psd->mReflowState,
                                 aFrame, availSize);
     nsHTMLReflowState& reflowState = reflowStateHolder.ref();
     reflowState.mLineLayout = this;
     reflowState.mFlags.mIsTopOfPage = GetFlag(LL_ISTOPOFPAGE);
     if (reflowState.ComputedWidth() == NS_UNCONSTRAINEDSIZE)
       reflowState.availableWidth = availableSpaceOnLine;
new file mode 100644
--- /dev/null
+++ b/mfbt/Types.h
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99 ft=cpp:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyrigght (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * NB: This header must be both valid C and C++.  It must be
+ * include-able by code embedding SpiderMonkey *and* Gecko.
+ */
+
+#ifndef mozilla_Types_h_
+#define mozilla_Types_h_
+
+/* 
+ * mfbt is logically "lower level" than js/src, but needs basic
+ * definitions of numerical types and macros for compiler/linker
+ * directives.  js/src already goes through some pain to provide them
+ * on numerous platforms, so instead of moving all that goop here,
+ * this header makes use of the fact that for the foreseeable future
+ * mfbt code will be part and parcel with libmozjs, static or not.
+ *
+ * For now, the policy is to use jstypes definitions but add a layer
+ * of indirection on top of them in case a Great Refactoring ever
+ * happens.
+ */
+#include "jstypes.h"
+
+/*
+ * The numerical types provided by jstypes.h that are allowed within
+ * mfbt code are
+ *
+ *   stddef types:  size_t, ptrdiff_t, etc.
+ *   stdin [sic] types:  int8, uint32, etc.
+ *
+ * stdint types (int8_t etc.), are available for use here, but doing
+ * so would change SpiderMonkey's and Gecko's contracts with
+ * embedders: stdint types have not yet appeared in public APIs.
+ */
+
+#define MOZ_EXPORT_API(type_)  JS_EXPORT_API(type_)
+#define MOZ_IMPORT_API(type_)  JS_IMPORT_API(type_)
+
+/*
+ * mfbt definitions need to see export declarations when built, but
+ * other code needs to see import declarations when using mfbt.
+ */
+#if defined(IMPL_MFBT)
+# define MFBT_API(type_)       MOZ_EXPORT_API(type_)
+#else
+# define MFBT_API(type_)       MOZ_IMPORT_API(type_)
+#endif
+
+
+#define MOZ_BEGIN_EXTERN_C     JS_BEGIN_EXTERN_C
+#define MOZ_END_EXTERN_C       JS_END_EXTERN_C
+
+#endif  // mozilla_Types_h_
--- a/mfbt/Util.h
+++ b/mfbt/Util.h
@@ -35,16 +35,52 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_Util_h_
 #define mozilla_Util_h_
 
+#include "mozilla/Types.h"
+
+/*
+ * XXX: we're cheating here in order to avoid creating object files
+ * for mfbt /just/ to provide a function like FatalError() to be used
+ * by MOZ_ASSERT().  (It'll happen eventually, but for just ASSERT()
+ * it isn't worth the pain.)  JS_Assert(), although unfortunately
+ * named, is part of SpiderMonkey's stable, external API, so this
+ * isn't quite as bad as it seems.
+ *
+ * Once mfbt needs object files, this unholy union with JS_Assert()
+ * will be broken.
+ */
+MOZ_BEGIN_EXTERN_C
+
+extern MFBT_API(void)
+JS_Assert(const char *s, const char *file, JSIntn ln);
+
+MOZ_END_EXTERN_C
+
+/*
+ * MOZ_ASSERT() is a "strong" assertion of state, like libc's
+ * assert().  If a MOZ_ASSERT() fails in a debug build, the process in
+ * which it fails will stop running in a loud and dramatic way.
+ */
+#ifdef DEBUG
+
+# define MOZ_ASSERT(expr_)                                      \
+    ((expr_) ? (void)0 : JS_Assert(#expr_, __FILE__, __LINE__))
+
+#else
+
+# define MOZ_ASSERT(expr_) ((void)0)
+
+#endif  // DEBUG
+
 #ifdef __cplusplus
 
 namespace mozilla {
 
 /**
  * DebugOnly contains a value of type T, but only in debug builds.  In
  * release builds, it does not contain a value.  This helper is
  * intended to be used along with ASSERT()-style macros, allowing one
@@ -86,13 +122,131 @@ struct DebugOnly
     /*
      * DebugOnly must always have a destructor or else it will
      * generate "unused variable" warnings, exactly what it's intended
      * to avoid!
      */
     ~DebugOnly() {}
 };
 
+
+/*
+ * This utility pales in comparison to Boost's aligned_storage. The utility
+ * simply assumes that JSUint64 is enough alignment for anyone. This may need
+ * to be extended one day...
+ *
+ * As an important side effect, pulling the storage into this template is
+ * enough obfuscation to confuse gcc's strict-aliasing analysis into not giving
+ * false negatives when we cast from the char buffer to whatever type we've
+ * constructed using the bytes.
+ */
+template <size_t nbytes>
+struct AlignedStorage
+{
+    union U {
+        char bytes[nbytes];
+        uint64 _;
+    } u;
+
+    const void *addr() const { return u.bytes; }
+    void *addr() { return u.bytes; }
+};
+
+template <class T>
+struct AlignedStorage2
+{
+    union U {
+        char bytes[sizeof(T)];
+        uint64 _;
+    } u;
+
+    const T *addr() const { return (const T *)u.bytes; }
+    T *addr() { return (T *)u.bytes; }
+};
+
+/*
+ * Small utility for lazily constructing objects without using dynamic storage.
+ * When a Maybe<T> is constructed, it is |empty()|, i.e., no value of T has
+ * been constructed and no T destructor will be called when the Maybe<T> is
+ * destroyed. Upon calling |construct|, a T object will be constructed with the
+ * given arguments and that object will be destroyed when the owning Maybe<T>
+ * is destroyed.
+ *
+ * N.B. GCC seems to miss some optimizations with Maybe and may generate extra
+ * branches/loads/stores. Use with caution on hot paths.
+ */
+template <class T>
+class Maybe
+{
+    AlignedStorage2<T> storage;
+    bool constructed;
+
+    T &asT() { return *storage.addr(); }
+
+    explicit Maybe(const Maybe &other);
+    const Maybe &operator=(const Maybe &other);
+
+  public:
+    Maybe() { constructed = false; }
+    ~Maybe() { if (constructed) asT().~T(); }
+
+    bool empty() const { return !constructed; }
+
+    void construct() {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T();
+        constructed = true;
+    }
+
+    template <class T1>
+    void construct(const T1 &t1) {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T(t1);
+        constructed = true;
+    }
+
+    template <class T1, class T2>
+    void construct(const T1 &t1, const T2 &t2) {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T(t1, t2);
+        constructed = true;
+    }
+
+    template <class T1, class T2, class T3>
+    void construct(const T1 &t1, const T2 &t2, const T3 &t3) {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T(t1, t2, t3);
+        constructed = true;
+    }
+
+    template <class T1, class T2, class T3, class T4>
+    void construct(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) {
+        MOZ_ASSERT(!constructed);
+        new(storage.addr()) T(t1, t2, t3, t4);
+        constructed = true;
+    }
+
+    T *addr() {
+        MOZ_ASSERT(constructed);
+        return &asT();
+    }
+
+    T &ref() {
+        MOZ_ASSERT(constructed);
+        return asT();
+    }
+
+    void destroy() {
+        ref().~T();
+        constructed = false;
+    }
+
+    void destroyIfConstructed() {
+        if (!empty())
+            destroy();
+    }
+};
+
 } /* namespace mozilla */
 
 #endif /* __cplusplus */
 
 #endif  /* mozilla_Util_h_ */