Bug 1770048: Improve self-hosted new_List r=jandem,tcampbell,a=dsmith
authorIain Ireland <iireland@mozilla.com>
Thu, 19 May 2022 14:30:10 +0000
changeset 688316 15984d19afe08fbef60bee95872f289396882f22
parent 688315 d6d9df73d017278bc8af1fd1b6f7445769a26ec0
child 688317 7ce9f0fe6cb4c4a2eb518c0add727a60d5672542
push id2856
push userdsmith@mozilla.com
push dateThu, 19 May 2022 19:58:01 +0000
treeherdermozilla-release@15984d19afe0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, tcampbell, dsmith
bugs1770048
milestone100.0.2
Bug 1770048: Improve self-hosted new_List r=jandem,tcampbell,a=dsmith Differential Revision: https://phabricator.services.mozilla.com/D146760
js/src/builtin/Array.cpp
js/src/builtin/Array.h
js/src/builtin/Module.js
js/src/builtin/RegExp.js
js/src/builtin/Utilities.js
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -4944,8 +4944,27 @@ JS_PUBLIC_API bool JS::GetArrayLength(JS
 JS_PUBLIC_API bool JS::SetArrayLength(JSContext* cx, Handle<JSObject*> obj,
                                       uint32_t length) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(obj);
 
   return SetLengthProperty(cx, obj, length);
 }
+
+bool js::intrinsic_newList(JSContext* cx, unsigned argc, js::Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+  MOZ_ASSERT(args.length() == 0);
+
+  RootedShape shape(cx, GetArrayShapeWithProto(cx, nullptr));
+  if (!shape) {
+    return false;
+  }
+
+  uint32_t length = 0;
+  ArrayObject* list = ::NewArrayWithShape<0>(cx, shape, length, GenericObject);
+  if (!list) {
+    return false;
+  }
+
+  args.rval().setObject(*list);
+  return true;
+}
--- a/js/src/builtin/Array.h
+++ b/js/src/builtin/Array.h
@@ -122,16 +122,18 @@ extern void ArrayShiftMoveElements(Array
 
 extern bool array_shift(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern bool array_slice(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern JSObject* ArraySliceDense(JSContext* cx, HandleObject obj, int32_t begin,
                                  int32_t end, HandleObject result);
 
+extern bool intrinsic_newList(JSContext* cx, unsigned argc, js::Value* vp);
+
 /*
  * Append the given (non-hole) value to the end of an array.  The array must be
  * a newborn array -- that is, one which has not been exposed to script for
  * arbitrary manipulation.  (This method optimizes on the assumption that
  * extending the array to accommodate the element will never make the array
  * sparse, which requires that the array be completely filled.)
  */
 extern bool NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v);
--- a/js/src/builtin/Module.js
+++ b/js/src/builtin/Module.js
@@ -9,35 +9,35 @@ function CallModuleResolveHook(module, m
     if (requestedModule.status < expectedMinimumStatus)
         ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
 
     return requestedModule;
 }
 
 // https://tc39.es/ecma262/#sec-getexportednames
 // ES2020 15.2.1.17.2 GetExportedNames
-function ModuleGetExportedNames(exportStarSet = [])
+function ModuleGetExportedNames(exportStarSet = new_List())
 {
     if (!IsObject(this) || !IsModule(this)) {
         return callFunction(CallModuleMethodIfWrapped, this, exportStarSet,
                             "ModuleGetExportedNames");
     }
 
     // Step 3
     let module = this;
 
     // Step 4
     if (callFunction(std_Array_includes, exportStarSet, module))
-        return [];
+        return new_List();
 
     // Step 5
     DefineDataProperty(exportStarSet, exportStarSet.length, module);
 
     // Step 6
-    let exportedNames = [];
+    let exportedNames = new_List();
     let namesCount = 0;
 
     // Step 7
     let localExportEntries = module.localExportEntries;
     for (let i = 0; i < localExportEntries.length; i++) {
         let e = localExportEntries[i];
         DefineDataProperty(exportedNames, namesCount++, e.exportName);
     }
@@ -94,17 +94,17 @@ function ModuleSetStatus(module, newStat
 // There are two failure cases:
 //
 //  - If no definition was found or the request is found to be circular, *null*
 //    is returned.
 //
 //  - If the request is found to be ambiguous, the string `"ambiguous"` is
 //    returned.
 //
-function ModuleResolveExport(exportName, resolveSet = [])
+function ModuleResolveExport(exportName, resolveSet = new_List())
 {
     assert(typeof exportName === "string", "ModuleResolveExport");
 
     if (!IsObject(this) || !IsModule(this)) {
         return callFunction(CallModuleMethodIfWrapped, this, exportName, resolveSet,
                             "ModuleResolveExport");
     }
 
@@ -201,17 +201,17 @@ function GetModuleNamespace(module)
            "Bad module state in GetModuleNamespace");
 
     // Step 3
     let namespace = module.namespace;
 
     // Step 4
     if (typeof namespace === "undefined") {
         let exportedNames = callFunction(module.getExportedNames, module);
-        let unambiguousNames = [];
+        let unambiguousNames = new_List();
         for (let i = 0; i < exportedNames.length; i++) {
             let name = exportedNames[i];
             let resolution = callFunction(module.resolveExport, module, name);
             if (IsResolvedBinding(resolution))
                 DefineDataProperty(unambiguousNames, unambiguousNames.length, name);
         }
         namespace = ModuleNamespaceCreate(module, unambiguousNames);
     }
@@ -307,17 +307,17 @@ function ModuleInstantiate()
     // Step 2
     if (module.status === MODULE_STATUS_LINKING ||
         module.status === MODULE_STATUS_EVALUATING)
     {
         ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
     }
 
     // Step 3
-    let stack = [];
+    let stack = new_List();
 
     // Steps 4-5
     try {
         InnerModuleLinking(module, stack, 0);
     } catch (error) {
         for (let i = 0; i < stack.length; i++) {
             let m = stack[i];
             if (m.status === MODULE_STATUS_LINKING) {
@@ -596,17 +596,17 @@ function ModuleEvaluate()
     // Top-level Await Step 5
     if (module.topLevelCapability) {
       return module.topLevelCapability;
     }
 
     const capability = CreateTopLevelCapability(module);
 
     // Step 4
-    let stack = [];
+    let stack = new_List();
 
     // Steps 5-6
     try {
         InnerModuleEvaluation(module, stack, 0);
         if (!IsAsyncEvaluating(module)) {
           ModuleTopLevelCapabilityResolve(module);
         }
         // Steps 7-8
@@ -741,17 +741,17 @@ function InnerModuleEvaluation(module, s
         } while (requiredModule !== module);
     }
 
     // Step 15
     return index;
 }
 
 // https://tc39.es/proposal-top-level-await/#sec-gather-async-parent-completions
-function GatherAsyncParentCompletions(module, execList = []) {
+function GatherAsyncParentCompletions(module, execList = new_List()) {
   assert(module.status == MODULE_STATUS_EVALUATED, "bad status for async module");
 
   // Step 5.
   // asyncParentModules is a list, and doesn't have a .length. Might be worth changing
   // later on.
   let i = 0;
   while (module.asyncParentModules[i]) {
     const m = module.asyncParentModules[i];
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -344,17 +344,17 @@ function RegExpReplaceSlowPath(rx, S, le
         // Step 8.a.
         fullUnicode = !!rx.unicode;
 
         // Step 8.b.
         rx.lastIndex = 0;
     }
 
     // Step 9.
-    var results = [];
+    var results = new_List();
     var nResults = 0;
 
     // Step 11.
     while (true) {
         // Step 11.a.
         var result = RegExpExec(rx, S, false);
 
         // Step 11.b.
@@ -451,17 +451,17 @@ function RegExpReplaceSlowPath(rx, S, le
 // Calculates functional/substitution replacement from match result.
 // Used in the following functions:
 //   * RegExpReplaceSlowPath
 function RegExpGetComplexReplacement(result, matched, S, position,
                                      nCaptures, replaceValue,
                                      functionalReplace, firstDollarIndex)
 {
     // Step 14.h.
-    var captures = [];
+    var captures = new_List();
     var capturesLength = 0;
 
     // Step 14.k.i (reordered).
     DefineDataProperty(captures, capturesLength++, matched);
 
     // Step 14.g, 14.i, 14.i.iv.
     for (var n = 1; n <= nCaptures; n++) {
         // Step 14.i.i.
@@ -541,17 +541,17 @@ function RegExpGetFunctionalReplacement(
           case 3:
             return ToString(replaceValue(SPREAD(result, 4), position, S));
           case 4:
             return ToString(replaceValue(SPREAD(result, 5), position, S));
         }
     }
 
     // Steps 14.g-i, 14.k.i-ii.
-    var captures = [];
+    var captures = new_List();
     for (var n = 0; n <= nCaptures; n++) {
         assert(typeof result[n] === "string" || result[n] === undefined,
                "RegExpMatcher returns only strings and undefined");
         DefineDataProperty(captures, n, result[n]);
     }
 
     // Step 14.k.iii.
     DefineDataProperty(captures, nCaptures + 1, position);
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -22,26 +22,17 @@
 #define assert(b, info) ; // Elided assertion.
 #define dbg(msg) ; // Elided debugging output.
 #endif
 
 // All C++-implemented standard builtins library functions used in self-hosted
 // code are installed via the std_functions JSFunctionSpec[] in
 // SelfHosting.cpp.
 
-/********** List / Record specification types **********/
-
-// A "List" is an internal type used in the ECMAScript spec to define a simple
-// ordered list of values. It is never exposed to user script, but we use a
-// simple Object (with null prototype) as a convenient implementation.
-//
-// NOTE: This does not track a `length` field.
-function new_List() {
-    return std_Object_create(null);
-}
+/********** Specification types **********/
 
 
 // A "Record" is an internal type used in the ECMAScript spec to define a struct
 // made up of key / values. It is never exposed to user script, but we use a
 // simple Object (with null prototype) as a convenient implementation.
 function new_Record() {
     return std_Object_create(null);
 }
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2490,16 +2490,17 @@ static const JSFunctionSpec intrinsic_fu
           intl_resolveDateTimeFormatComponents, 3, 0),
     JS_FN("intl_supportedLocaleOrFallback", intl_supportedLocaleOrFallback, 1,
           0),
     JS_FN("intl_toLocaleLowerCase", intl_toLocaleLowerCase, 2, 0),
     JS_FN("intl_toLocaleUpperCase", intl_toLocaleUpperCase, 2, 0),
 #endif  // JS_HAS_INTL_API
 
     // Standard builtins used by self-hosting.
+    JS_FN("new_List", intrinsic_newList, 0, 0),
     JS_INLINABLE_FN("std_Array", array_construct, 1, 0, Array),
     JS_FN("std_Array_includes", array_includes, 1, 0),
     JS_FN("std_Array_indexOf", array_indexOf, 1, 0),
     JS_FN("std_Array_lastIndexOf", array_lastIndexOf, 1, 0),
     JS_INLINABLE_FN("std_Array_pop", array_pop, 0, 0, ArrayPop),
     JS_INLINABLE_FN("std_Array_push", array_push, 1, 0, ArrayPush),
     JS_FN("std_BigInt_valueOf", BigIntObject::valueOf, 0, 0),
     JS_FN("std_Date_now", date_now, 0, 0),