Bug 1461821 - Convert the trailing array of BindingNames at the end of the various kinds of scope data into raw unsigned chars into which those BindingNames are placement-new'd, rather than memcpy-ing non-trivial classes around and failing to comply with the C++ object model. r=jandem
authorJeff Walden <jwalden@mit.edu>
Wed, 16 May 2018 10:34:55 -0700
changeset 418608 73c36389c5f7
parent 418607 ead72cce7f0e
child 418609 2ddbcf79bc48
push id103351
push userjwalden@mit.edu
push dateThu, 17 May 2018 07:17:38 +0000
treeherdermozilla-inbound@e016aa76775e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1461821
milestone62.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 1461821 - Convert the trailing array of BindingNames at the end of the various kinds of scope data into raw unsigned chars into which those BindingNames are placement-new'd, rather than memcpy-ing non-trivial classes around and failing to comply with the C++ object model. r=jandem
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/Parser.cpp
js/src/gc/Marking.cpp
js/src/vm/Scope.cpp
js/src/vm/Scope.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -332,17 +332,17 @@ class TryFinallyControl : public Bytecod
     bool emittingSubroutine() const {
         return emittingSubroutine_;
     }
 };
 
 static inline void
 MarkAllBindingsClosedOver(LexicalScope::Data& data)
 {
-    BindingName* names = data.names;
+    TrailingNamesArray& names = data.trailingNames;
     for (uint32_t i = 0; i < data.length; i++)
         names[i] = BindingName(names[i].name(), true);
 }
 
 // A scope that introduces bindings.
 class BytecodeEmitter::EmitterScope : public Nestable<BytecodeEmitter::EmitterScope>
 {
     // The cache of bound names that may be looked up in the
@@ -9255,17 +9255,18 @@ BytecodeEmitter::isRestParameter(ParseNo
 
     JSAtom* name = pn->name();
     Maybe<NameLocation> paramLoc = locationOfNameBoundInFunctionScope(name);
     if (paramLoc && lookupName(name) == *paramLoc) {
         FunctionScope::Data* bindings = funbox->functionScopeBindings();
         if (bindings->nonPositionalFormalStart > 0) {
             // |paramName| can be nullptr when the rest destructuring syntax is
             // used: `function f(...[]) {}`.
-            JSAtom* paramName = bindings->names[bindings->nonPositionalFormalStart - 1].name();
+            JSAtom* paramName =
+                bindings->trailingNames[bindings->nonPositionalFormalStart - 1].name();
             return paramName && name == paramName;
         }
     }
 
     return false;
 }
 
 bool
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -329,17 +329,17 @@ void
 LexicalScopeNode::dump(GenericPrinter& out, int indent)
 {
     const char* name = parseNodeNames[size_t(getKind())];
     out.printf("(%s [", name);
     int nameIndent = indent + strlen(name) + 3;
     if (!isEmptyScope()) {
         LexicalScope::Data* bindings = scopeBindings();
         for (uint32_t i = 0; i < bindings->length; i++) {
-            JSAtom* name = bindings->names[i].name();
+            JSAtom* name = bindings->trailingNames[i].name();
             JS::AutoCheckCannotGC nogc;
             if (name->hasLatin1Chars())
                 DumpName(out, name->latin1Chars(nogc), name->length());
             else
                 DumpName(out, name->twoByteChars(nogc), name->length());
             if (i < bindings->length - 1)
                 IndentNewLine(out, nameIndent);
         }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -18,16 +18,18 @@
  */
 
 #include "frontend/Parser.h"
 
 #include "mozilla/Range.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TypeTraits.h"
 
+#include <new>
+
 #include "jsapi.h"
 #include "jstypes.h"
 
 #include "builtin/ModuleObject.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/TokenStream.h"
@@ -1756,25 +1758,37 @@ Parser<FullParseHandler, CharT>::checkSt
     return true;
 }
 
 template <typename Scope>
 typename Scope::Data*
 NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings)
 {
     size_t allocSize = Scope::sizeOfData(numBindings);
-    typename Scope::Data* bindings = static_cast<typename Scope::Data*>(alloc.alloc(allocSize));
+    auto* bindings = static_cast<typename Scope::Data*>(alloc.alloc(allocSize));
     if (!bindings) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
     PodZero(bindings);
     return bindings;
 }
 
+/**
+ * Copy-construct |BindingName|s from |bindings| into |cursor|, then return
+ * the location one past the newly-constructed |BindingName|s.
+ */
+static MOZ_MUST_USE BindingName*
+FreshlyInitializeBindings(BindingName* cursor, const Vector<BindingName>& bindings)
+{
+    for (const BindingName& binding : bindings)
+        new (cursor++) BindingName(binding);
+    return cursor;
+}
+
 Maybe<GlobalScope::Data*>
 NewGlobalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
 {
 
     Vector<BindingName> funs(context);
     Vector<BindingName> vars(context);
     Vector<BindingName> lets(context);
     Vector<BindingName> consts(context);
@@ -1809,32 +1823,30 @@ NewGlobalScopeData(JSContext* context, P
     uint32_t numBindings = funs.length() + vars.length() + lets.length() + consts.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<GlobalScope>(context, alloc, numBindings);
         if (!bindings)
             return Nothing();
 
         // The ordering here is important. See comments in GlobalScope.
-        BindingName* start = bindings->names;
+        BindingName* start = bindings->trailingNames.start();
         BindingName* cursor = start;
 
-        PodCopy(cursor, funs.begin(), funs.length());
-        cursor += funs.length();
+        cursor = FreshlyInitializeBindings(cursor, funs);
 
         bindings->varStart = cursor - start;
-        PodCopy(cursor, vars.begin(), vars.length());
-        cursor += vars.length();
+        cursor = FreshlyInitializeBindings(cursor, vars);
 
         bindings->letStart = cursor - start;
-        PodCopy(cursor, lets.begin(), lets.length());
-        cursor += lets.length();
+        cursor = FreshlyInitializeBindings(cursor, lets);
 
         bindings->constStart = cursor - start;
-        PodCopy(cursor, consts.begin(), consts.length());
+        cursor = FreshlyInitializeBindings(cursor, consts);
+
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
 Maybe<GlobalScope::Data*>
 ParserBase::newGlobalScopeData(ParseContext::Scope& scope)
@@ -1881,32 +1893,30 @@ NewModuleScopeData(JSContext* context, P
     uint32_t numBindings = imports.length() + vars.length() + lets.length() + consts.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<ModuleScope>(context, alloc, numBindings);
         if (!bindings)
             return Nothing();
 
         // The ordering here is important. See comments in ModuleScope.
-        BindingName* start = bindings->names;
+        BindingName* start = bindings->trailingNames.start();
         BindingName* cursor = start;
 
-        PodCopy(cursor, imports.begin(), imports.length());
-        cursor += imports.length();
+        cursor = FreshlyInitializeBindings(cursor, imports);
 
         bindings->varStart = cursor - start;
-        PodCopy(cursor, vars.begin(), vars.length());
-        cursor += vars.length();
+        cursor = FreshlyInitializeBindings(cursor, vars);
 
         bindings->letStart = cursor - start;
-        PodCopy(cursor, lets.begin(), lets.length());
-        cursor += lets.length();
+        cursor = FreshlyInitializeBindings(cursor, lets);
 
         bindings->constStart = cursor - start;
-        PodCopy(cursor, consts.begin(), consts.length());
+        cursor = FreshlyInitializeBindings(cursor, consts);
+
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
 Maybe<ModuleScope::Data*>
 ParserBase::newModuleScopeData(ParseContext::Scope& scope)
@@ -1937,26 +1947,26 @@ NewEvalScopeData(JSContext* context, Par
     EvalScope::Data* bindings = nullptr;
     uint32_t numBindings = funs.length() + vars.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<EvalScope>(context, alloc, numBindings);
         if (!bindings)
             return Nothing();
 
-        BindingName* start = bindings->names;
+        BindingName* start = bindings->trailingNames.start();
         BindingName* cursor = start;
 
         // Keep track of what vars are functions. This is only used in BCE to omit
         // superfluous DEFVARs.
-        PodCopy(cursor, funs.begin(), funs.length());
-        cursor += funs.length();
+        cursor = FreshlyInitializeBindings(cursor, funs);
 
         bindings->varStart = cursor - start;
-        PodCopy(cursor, vars.begin(), vars.length());
+        cursor = FreshlyInitializeBindings(cursor, vars);
+
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
 Maybe<EvalScope::Data*>
 ParserBase::newEvalScopeData(ParseContext::Scope& scope)
@@ -2035,28 +2045,27 @@ NewFunctionScopeData(JSContext* context,
     uint32_t numBindings = positionalFormals.length() + formals.length() + vars.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<FunctionScope>(context, alloc, numBindings);
         if (!bindings)
             return Nothing();
 
         // The ordering here is important. See comments in FunctionScope.
-        BindingName* start = bindings->names;
+        BindingName* start = bindings->trailingNames.start();
         BindingName* cursor = start;
 
-        PodCopy(cursor, positionalFormals.begin(), positionalFormals.length());
-        cursor += positionalFormals.length();
+        cursor = FreshlyInitializeBindings(cursor, positionalFormals);
 
         bindings->nonPositionalFormalStart = cursor - start;
-        PodCopy(cursor, formals.begin(), formals.length());
-        cursor += formals.length();
+        cursor = FreshlyInitializeBindings(cursor, formals);
 
         bindings->varStart = cursor - start;
-        PodCopy(cursor, vars.begin(), vars.length());
+        cursor = FreshlyInitializeBindings(cursor, vars);
+
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
 Maybe<FunctionScope::Data*>
 ParserBase::newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs)
@@ -2083,20 +2092,21 @@ NewVarScopeData(JSContext* context, Pars
     uint32_t numBindings = vars.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<VarScope>(context, alloc, numBindings);
         if (!bindings)
             return Nothing();
 
         // The ordering here is important. See comments in FunctionScope.
-        BindingName* start = bindings->names;
+        BindingName* start = bindings->trailingNames.start();
         BindingName* cursor = start;
 
-        PodCopy(cursor, vars.begin(), vars.length());
+        cursor = FreshlyInitializeBindings(cursor, vars);
+
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
 Maybe<VarScope::Data*>
 ParserBase::newVarScopeData(ParseContext::Scope& scope)
@@ -2136,24 +2146,24 @@ NewLexicalScopeData(JSContext* context, 
     uint32_t numBindings = lets.length() + consts.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<LexicalScope>(context, alloc, numBindings);
         if (!bindings)
             return Nothing();
 
         // The ordering here is important. See comments in LexicalScope.
-        BindingName* cursor = bindings->names;
+        BindingName* cursor = bindings->trailingNames.start();
         BindingName* start = cursor;
 
-        PodCopy(cursor, lets.begin(), lets.length());
-        cursor += lets.length();
+        cursor = FreshlyInitializeBindings(cursor, lets);
 
         bindings->constStart = cursor - start;
-        PodCopy(cursor, consts.begin(), consts.length());
+        cursor = FreshlyInitializeBindings(cursor, consts);
+
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
 Maybe<LexicalScope::Data*>
 ParserBase::newLexicalScopeData(ParseContext::Scope& scope)
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1290,55 +1290,55 @@ BindingName::trace(JSTracer* trc)
 void
 BindingIter::trace(JSTracer* trc)
 {
     TraceNullableBindingNames(trc, names_, length_);
 }
 void
 LexicalScope::Data::trace(JSTracer* trc)
 {
-    TraceBindingNames(trc, names, length);
+    TraceBindingNames(trc, trailingNames.start(), length);
 }
 void
 FunctionScope::Data::trace(JSTracer* trc)
 {
     TraceNullableEdge(trc, &canonicalFunction, "scope canonical function");
-    TraceNullableBindingNames(trc, names, length);
+    TraceNullableBindingNames(trc, trailingNames.start(), length);
 }
 void
 VarScope::Data::trace(JSTracer* trc)
 {
-    TraceBindingNames(trc, names, length);
+    TraceBindingNames(trc, trailingNames.start(), length);
 }
 void
 GlobalScope::Data::trace(JSTracer* trc)
 {
-    TraceBindingNames(trc, names, length);
+    TraceBindingNames(trc, trailingNames.start(), length);
 }
 void
 EvalScope::Data::trace(JSTracer* trc)
 {
-    TraceBindingNames(trc, names, length);
+    TraceBindingNames(trc, trailingNames.start(), length);
 }
 void
 ModuleScope::Data::trace(JSTracer* trc)
 {
     TraceNullableEdge(trc, &module, "scope module");
-    TraceBindingNames(trc, names, length);
+    TraceBindingNames(trc, trailingNames.start(), length);
 }
 void
 WasmInstanceScope::Data::trace(JSTracer* trc)
 {
     TraceNullableEdge(trc, &instance, "wasm instance");
-    TraceBindingNames(trc, names, length);
+    TraceBindingNames(trc, trailingNames.start(), length);
 }
 void
 WasmFunctionScope::Data::trace(JSTracer* trc)
 {
-    TraceBindingNames(trc, names, length);
+    TraceBindingNames(trc, trailingNames.start(), length);
 }
 void
 Scope::traceChildren(JSTracer* trc)
 {
     TraceNullableEdge(trc, &enclosing_, "scope enclosing");
     TraceNullableEdge(trc, &environmentShape_, "scope env shape");
     switch (kind_) {
       case ScopeKind::Function:
@@ -1378,96 +1378,96 @@ Scope::traceChildren(JSTracer* trc)
 }
 inline void
 js::GCMarker::eagerlyMarkChildren(Scope* scope)
 {
     if (scope->enclosing_)
         traverseEdge(scope, static_cast<Scope*>(scope->enclosing_));
     if (scope->environmentShape_)
         traverseEdge(scope, static_cast<Shape*>(scope->environmentShape_));
-    BindingName* names = nullptr;
+    TrailingNamesArray* names = nullptr;
     uint32_t length = 0;
     switch (scope->kind_) {
       case ScopeKind::Function: {
         FunctionScope::Data* data = reinterpret_cast<FunctionScope::Data*>(scope->data_);
         traverseEdge(scope, static_cast<JSObject*>(data->canonicalFunction));
-        names = data->names;
+        names = &data->trailingNames;
         length = data->length;
         break;
       }
 
       case ScopeKind::FunctionBodyVar:
       case ScopeKind::ParameterExpressionVar: {
         VarScope::Data* data = reinterpret_cast<VarScope::Data*>(scope->data_);
-        names = data->names;
+        names = &data->trailingNames;
         length = data->length;
         break;
       }
 
       case ScopeKind::Lexical:
       case ScopeKind::SimpleCatch:
       case ScopeKind::Catch:
       case ScopeKind::NamedLambda:
       case ScopeKind::StrictNamedLambda: {
         LexicalScope::Data* data = reinterpret_cast<LexicalScope::Data*>(scope->data_);
-        names = data->names;
+        names = &data->trailingNames;
         length = data->length;
         break;
       }
 
       case ScopeKind::Global:
       case ScopeKind::NonSyntactic: {
         GlobalScope::Data* data = reinterpret_cast<GlobalScope::Data*>(scope->data_);
-        names = data->names;
+        names = &data->trailingNames;
         length = data->length;
         break;
       }
 
       case ScopeKind::Eval:
       case ScopeKind::StrictEval: {
         EvalScope::Data* data = reinterpret_cast<EvalScope::Data*>(scope->data_);
-        names = data->names;
+        names = &data->trailingNames;
         length = data->length;
         break;
       }
 
       case ScopeKind::Module: {
         ModuleScope::Data* data = reinterpret_cast<ModuleScope::Data*>(scope->data_);
         traverseEdge(scope, static_cast<JSObject*>(data->module));
-        names = data->names;
+        names = &data->trailingNames;
         length = data->length;
         break;
       }
 
       case ScopeKind::With:
         break;
 
       case ScopeKind::WasmInstance: {
         WasmInstanceScope::Data* data = reinterpret_cast<WasmInstanceScope::Data*>(scope->data_);
         traverseEdge(scope, static_cast<JSObject*>(data->instance));
-        names = data->names;
+        names = &data->trailingNames;
         length = data->length;
         break;
       }
 
       case ScopeKind::WasmFunction: {
         WasmFunctionScope::Data* data = reinterpret_cast<WasmFunctionScope::Data*>(scope->data_);
-        names = data->names;
+        names = &data->trailingNames;
         length = data->length;
         break;
       }
     }
     if (scope->kind_ == ScopeKind::Function) {
         for (uint32_t i = 0; i < length; i++) {
-            if (JSAtom* name = names[i].name())
+            if (JSAtom* name = names->operator[](i).name())
                 traverseEdge(scope, static_cast<JSString*>(name));
         }
     } else {
         for (uint32_t i = 0; i < length; i++)
-            traverseEdge(scope, static_cast<JSString*>(names[i].name()));
+            traverseEdge(scope, static_cast<JSString*>(names->operator[](i).name()));
     }
 }
 
 void
 js::ObjectGroup::traceChildren(JSTracer* trc)
 {
     AutoSweepObjectGroup sweep(this);
 
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -285,17 +285,17 @@ Scope::XDRSizedBindingNames(XDRState<mod
     auto dataGuard = mozilla::MakeScopeExit([&] () {
         if (mode == XDR_DECODE) {
             DeleteScopeData(data.get());
             data.set(nullptr);
         }
     });
 
     for (uint32_t i = 0; i < length; i++)
-        MOZ_TRY(XDRBindingName(xdr, &data->names[i]));
+        MOZ_TRY(XDRBindingName(xdr, &data->trailingNames[i]));
 
     dataGuard.release();
     return Ok();
 }
 
 /* static */ Scope*
 Scope::create(JSContext* cx, ScopeKind kind, HandleScope enclosing, HandleShape envShape)
 {
@@ -1248,28 +1248,29 @@ WasmInstanceScope::create(JSContext* cx,
         size_t globalsCount = instance->instance().metadata().globals.length();
         namesCount += globalsCount;
 
         Rooted<UniquePtr<Data>> data(cx, NewEmptyScopeData<WasmInstanceScope>(cx, namesCount));
         if (!data)
             return nullptr;
 
         size_t nameIndex = 0;
+        RootedAtom name(cx);
         if (instance->instance().memory()) {
-            RootedAtom name(cx, GenerateWasmName(cx, "memory", /* index = */ 0));
+            name = GenerateWasmName(cx, "memory", /* index = */ 0);
             if (!name)
                 return nullptr;
-            data->names[nameIndex] = BindingName(name, false);
+            new (&data->trailingNames[nameIndex]) BindingName(name, false);
             nameIndex++;
         }
         for (size_t i = 0; i < globalsCount; i++) {
-            RootedAtom name(cx, GenerateWasmName(cx, "global", i));
+            name = GenerateWasmName(cx, "global", i);
             if (!name)
                 return nullptr;
-            data->names[nameIndex] = BindingName(name, false);
+            new (&data->trailingNames[nameIndex]) BindingName(name, false);
             nameIndex++;
         }
         MOZ_ASSERT(nameIndex == namesCount);
 
         data->instance.init(instance);
         data->memoriesStart = 0;
         data->globalsStart = globalsStart;
         data->length = namesCount;
@@ -1316,21 +1317,22 @@ WasmFunctionScope::create(JSContext* cx,
     uint32_t namesCount = locals.length();
 
     Rooted<UniquePtr<Data>> data(cx, NewEmptyScopeData<WasmFunctionScope>(cx, namesCount));
     if (!data)
         return nullptr;
 
     data->funcIndex = funcIndex;
     data->length = namesCount;
+    RootedAtom name(cx);
     for (size_t i = 0; i < namesCount; i++) {
-        RootedAtom name(cx, GenerateWasmName(cx, "var", i));
+        name = GenerateWasmName(cx, "var", i);
         if (!name)
             return nullptr;
-        data->names[i] = BindingName(name, false);
+        new (&data->trailingNames[i]) BindingName(name, false);
     }
 
     Scope* scope = Scope::create(cx, ScopeKind::WasmFunction, enclosing, /* envShape = */ nullptr);
     if (!scope)
         return nullptr;
 
     wasmFunctionScope = &scope->as<WasmFunctionScope>();
     wasmFunctionScope->initData(Move(data.get()));
@@ -1415,29 +1417,29 @@ BindingIter::init(LexicalScope::Data& da
     // Named lambda scopes can only have environment slots. If the callee
     // isn't closed over, it is accessed via JSOP_CALLEE.
     if (flags & IsNamedLambda) {
         // Named lambda binding is weird. Normal BindingKind ordering rules
         // don't apply.
         init(0, 0, 0, 0, 0, 0,
              CanHaveEnvironmentSlots | flags,
              firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
-             data.names, data.length);
+             data.trailingNames.start(), data.length);
     } else {
         //            imports - [0, 0)
         // positional formals - [0, 0)
         //      other formals - [0, 0)
         //    top-level funcs - [0, 0)
         //               vars - [0, 0)
         //               lets - [0, data.constStart)
         //             consts - [data.constStart, data.length)
         init(0, 0, 0, 0, 0, data.constStart,
              CanHaveFrameSlots | CanHaveEnvironmentSlots | flags,
              firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
-             data.names, data.length);
+             data.trailingNames.start(), data.length);
     }
 }
 
 void
 BindingIter::init(FunctionScope::Data& data, uint8_t flags)
 {
     flags = CanHaveFrameSlots | CanHaveEnvironmentSlots | flags;
     if (!(flags & HasFormalParameterExprs))
@@ -1448,49 +1450,49 @@ BindingIter::init(FunctionScope::Data& d
     //      other formals - [data.nonPositionalParamStart, data.varStart)
     //    top-level funcs - [data.varStart, data.varStart)
     //               vars - [data.varStart, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
     init(0, data.nonPositionalFormalStart, data.varStart, data.varStart, data.length, data.length,
          flags,
          0, JSSLOT_FREE(&CallObject::class_),
-         data.names, data.length);
+         data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(VarScope::Data& data, uint32_t firstFrameSlot)
 {
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
     //    top-level funcs - [0, 0)
     //               vars - [0, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
     init(0, 0, 0, 0, data.length, data.length,
          CanHaveFrameSlots | CanHaveEnvironmentSlots,
          firstFrameSlot, JSSLOT_FREE(&VarEnvironmentObject::class_),
-         data.names, data.length);
+         data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(GlobalScope::Data& data)
 {
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
     //    top-level funcs - [0, data.varStart)
     //               vars - [data.varStart, data.letStart)
     //               lets - [data.letStart, data.constStart)
     //             consts - [data.constStart, data.length)
     init(0, 0, 0, data.varStart, data.letStart, data.constStart,
          CannotHaveSlots,
          UINT32_MAX, UINT32_MAX,
-         data.names, data.length);
+         data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(EvalScope::Data& data, bool strict)
 {
     uint32_t flags;
     uint32_t firstFrameSlot;
     uint32_t firstEnvironmentSlot;
@@ -1508,65 +1510,65 @@ BindingIter::init(EvalScope::Data& data,
     // positional formals - [0, 0)
     //      other formals - [0, 0)
     //    top-level funcs - [0, data.varStart)
     //               vars - [data.varStart, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
     init(0, 0, 0, data.varStart, data.length, data.length,
          flags, firstFrameSlot, firstEnvironmentSlot,
-         data.names, data.length);
+         data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(ModuleScope::Data& data)
 {
     //            imports - [0, data.varStart)
     // positional formals - [data.varStart, data.varStart)
     //      other formals - [data.varStart, data.varStart)
     //    top-level funcs - [data.varStart, data.varStart)
     //               vars - [data.varStart, data.letStart)
     //               lets - [data.letStart, data.constStart)
     //             consts - [data.constStart, data.length)
     init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart,
          CanHaveFrameSlots | CanHaveEnvironmentSlots,
          0, JSSLOT_FREE(&ModuleEnvironmentObject::class_),
-         data.names, data.length);
+         data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(WasmInstanceScope::Data& data)
 {
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
     //    top-level funcs - [0, 0)
     //               vars - [0, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
     init(0, 0, 0, 0, data.length, data.length,
          CanHaveFrameSlots | CanHaveEnvironmentSlots,
          UINT32_MAX, UINT32_MAX,
-         data.names, data.length);
+         data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(WasmFunctionScope::Data& data)
 {
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
     //    top-level funcs - [0, 0)
     //               vars - [0, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
     init(0, 0, 0, 0, data.length, data.length,
          CanHaveFrameSlots | CanHaveEnvironmentSlots,
          UINT32_MAX, UINT32_MAX,
-         data.names, data.length);
+         data.trailingNames.start(), data.length);
 }
 
 PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script)
   : BindingIter(script)
 {
     // Reinit with flags = 0, i.e., iterate over all positional parameters.
     if (script->bodyScope()->is<FunctionScope>())
         init(script->bodyScope()->as<FunctionScope>().data(), /* flags = */ 0);
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -124,16 +124,49 @@ class BindingName
 
     bool closedOver() const {
         return bits_ & ClosedOverFlag;
     }
 
     void trace(JSTracer* trc);
 };
 
+/**
+ * The various {Global,Module,...}Scope::Data classes consist of always-present
+ * bits, then a trailing array of BindingNames.  The various Data classes all
+ * end in a TrailingNamesArray that contains sized/aligned space for *one*
+ * BindingName.  Data instances that contain N BindingNames, are then allocated
+ * in sizeof(Data) + (space for (N - 1) BindingNames).  Because this class's
+ * |data_| field is properly sized/aligned, the N-BindingName array can start
+ * at |data_|.
+ *
+ * This is concededly a very low-level representation, but we want to only
+ * allocate once for data+bindings both, and this does so approximately as
+ * elegantly as C++ allows.
+ */
+class TrailingNamesArray
+{
+  private:
+    alignas(BindingName) unsigned char data_[sizeof(BindingName)];
+
+  private:
+    // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
+    // -Werror compile error) to reinterpret_cast<> |data_| to |T*|, even
+    // through |void*|.  Placing the latter cast in these separate functions
+    // breaks the chain such that affected GCC versions no longer warn/error.
+    void* ptr() {
+        return data_;
+    }
+
+  public:
+    BindingName* start() { return reinterpret_cast<BindingName*>(ptr()); }
+
+    BindingName& operator[](size_t i) { return start()[i]; }
+};
+
 class BindingLocation
 {
   public:
     enum class Kind {
         Global,
         Argument,
         Frame,
         Environment,
@@ -383,27 +416,27 @@ class LexicalScope : public Scope
         uint32_t length;
 
         // Frame slots [0, nextFrameSlot) are live when this is the innermost
         // scope.
         uint32_t nextFrameSlot;
 
         // The array of tagged JSAtom* names, allocated beyond the end of the
         // struct.
-        BindingName names[1];
+        TrailingNamesArray trailingNames;
 
         void trace(JSTracer* trc);
     };
 
     static size_t sizeOfData(uint32_t length) {
         return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
     }
 
     static void getDataNamesAndLength(Data* data, BindingName** names, uint32_t* length) {
-        *names = data->names;
+        *names = data->trailingNames.start();
         *length = data->length;
     }
 
     static LexicalScope* create(JSContext* cx, ScopeKind kind, Handle<Data*> data,
                                 uint32_t firstFrameSlot, HandleScope enclosing);
 
     template <XDRMode mode>
     static XDRResult XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
@@ -505,28 +538,28 @@ class FunctionScope : public Scope
         uint32_t length;
 
         // Frame slots [0, nextFrameSlot) are live when this is the innermost
         // scope.
         uint32_t nextFrameSlot;
 
         // The array of tagged JSAtom* names, allocated beyond the end of the
         // struct.
-        BindingName names[1];
+        TrailingNamesArray trailingNames;
 
         void trace(JSTracer* trc);
         Zone* zone() const;
     };
 
     static size_t sizeOfData(uint32_t length) {
         return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
     }
 
     static void getDataNamesAndLength(Data* data, BindingName** names, uint32_t* length) {
-        *names = data->names;
+        *names = data->trailingNames.start();
         *length = data->length;
     }
 
     static FunctionScope* create(JSContext* cx, Handle<Data*> data,
                                  bool hasParameterExprs, bool needsEnvironment,
                                  HandleFunction fun, HandleScope enclosing);
 
     static FunctionScope* clone(JSContext* cx, Handle<FunctionScope*> scope, HandleFunction fun,
@@ -607,27 +640,27 @@ class VarScope : public Scope
         uint32_t length;
 
         // Frame slots [firstFrameSlot(), nextFrameSlot) are live when this is
         // the innermost scope.
         uint32_t nextFrameSlot;
 
         // The array of tagged JSAtom* names, allocated beyond the end of the
         // struct.
-        BindingName names[1];
+        TrailingNamesArray trailingNames;
 
         void trace(JSTracer* trc);
     };
 
     static size_t sizeOfData(uint32_t length) {
         return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
     }
 
     static void getDataNamesAndLength(Data* data, BindingName** names, uint32_t* length) {
-        *names = data->names;
+        *names = data->trailingNames.start();
         *length = data->length;
     }
 
     static VarScope* create(JSContext* cx, ScopeKind kind, Handle<Data*> data,
                             uint32_t firstFrameSlot, bool needsEnvironment,
                             HandleScope enclosing);
 
     template <XDRMode mode>
@@ -701,27 +734,27 @@ class GlobalScope : public Scope
         //          consts - [constStart, length)
         uint32_t varStart;
         uint32_t letStart;
         uint32_t constStart;
         uint32_t length;
 
         // The array of tagged JSAtom* names, allocated beyond the end of the
         // struct.
-        BindingName names[1];
+        TrailingNamesArray trailingNames;
 
         void trace(JSTracer* trc);
     };
 
     static size_t sizeOfData(uint32_t length) {
         return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
     }
 
     static void getDataNamesAndLength(Data* data, BindingName** names, uint32_t* length) {
-        *names = data->names;
+        *names = data->trailingNames.start();
         *length = data->length;
     }
 
     static GlobalScope* create(JSContext* cx, ScopeKind kind, Handle<Data*> data);
 
     static GlobalScope* createEmpty(JSContext* cx, ScopeKind kind) {
         return create(cx, kind, nullptr);
     }
@@ -806,27 +839,27 @@ class EvalScope : public Scope
         uint32_t length;
 
         // Frame slots [0, nextFrameSlot) are live when this is the innermost
         // scope.
         uint32_t nextFrameSlot;
 
         // The array of tagged JSAtom* names, allocated beyond the end of the
         // struct.
-        BindingName names[1];
+        TrailingNamesArray trailingNames;
 
         void trace(JSTracer* trc);
     };
 
     static size_t sizeOfData(uint32_t length) {
         return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
     }
 
     static void getDataNamesAndLength(Data* data, BindingName** names, uint32_t* length) {
-        *names = data->names;
+        *names = data->trailingNames.start();
         *length = data->length;
     }
 
     static EvalScope* create(JSContext* cx, ScopeKind kind, Handle<Data*> data,
                              HandleScope enclosing);
 
     template <XDRMode mode>
     static XDRResult XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
@@ -912,28 +945,28 @@ class ModuleScope : public Scope
         uint32_t length;
 
         // Frame slots [0, nextFrameSlot) are live when this is the innermost
         // scope.
         uint32_t nextFrameSlot;
 
         // The array of tagged JSAtom* names, allocated beyond the end of the
         // struct.
-        BindingName names[1];
+        TrailingNamesArray trailingNames;
 
         void trace(JSTracer* trc);
         Zone* zone() const;
     };
 
     static size_t sizeOfData(uint32_t length) {
         return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
     }
 
     static void getDataNamesAndLength(Data* data, BindingName** names, uint32_t* length) {
-        *names = data->names;
+        *names = data->trailingNames.start();
         *length = data->length;
     }
 
     static ModuleScope* create(JSContext* cx, Handle<Data*> data,
                                Handle<ModuleObject*> module, HandleScope enclosing);
 
   private:
     static ModuleScope* createWithData(JSContext* cx, MutableHandle<UniquePtr<Data>> data,
@@ -973,17 +1006,17 @@ class WasmInstanceScope : public Scope
         uint32_t memoriesStart;
         uint32_t globalsStart;
         uint32_t length;
         uint32_t nextFrameSlot;
 
         // The wasm instance of the scope.
         GCPtr<WasmInstanceObject*> instance;
 
-        BindingName names[1];
+        TrailingNamesArray trailingNames;
 
         void trace(JSTracer* trc);
     };
 
     static WasmInstanceScope* create(JSContext* cx, WasmInstanceObject* instance);
 
     static size_t sizeOfData(uint32_t length) {
         return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);
@@ -1029,17 +1062,17 @@ class WasmFunctionScope : public Scope
 
   public:
     struct Data
     {
         uint32_t length;
         uint32_t nextFrameSlot;
         uint32_t funcIndex;
 
-        BindingName names[1];
+        TrailingNamesArray trailingNames;
 
         void trace(JSTracer* trc);
     };
 
     static WasmFunctionScope* create(JSContext* cx, HandleScope enclosing, uint32_t funcIndex);
 
     static size_t sizeOfData(uint32_t length) {
         return sizeof(Data) + (length ? length - 1 : 0) * sizeof(BindingName);