Skip to content

Commit 0ca38a6

Browse files
mi-acCommit bot
authored andcommitted
Revert of Replace PushIfAbsent by a Stack object and move StringBuilderJoin to JS (patchset v8#6 id:100001 of https://codereview.chromium.org/1775403008/ )
Reason for revert: [Sheriff] This lets a gc stress test time out: https://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20gc%20stress/builds/2337/steps/Mjsunit%20%28flakes%29/logs/regress-542823 The test ran in <2sec before this. Please fix the test as well on a reland. Original issue's description: > Replace PushIfAbsent by a Stack object and move StringBuilderJoin to JS > > This significantly speeds up String(array). > BUG= > > Committed: https://crrev.com/c91faa0b39b62025460eb9f8b578e20d88f3549e > Cr-Commit-Position: refs/heads/master@{#34696} TBR=cbruni@chromium.org,adamk@chromium.org,bmeurer@chromium.org,verwaest@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG= Review URL: https://codereview.chromium.org/1785003004 Cr-Commit-Position: refs/heads/master@{#34706}
1 parent 6a7ec6a commit 0ca38a6

6 files changed

Lines changed: 122 additions & 73 deletions

File tree

src/js/array.js

Lines changed: 8 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ function DefineIndexedProperty(array, i, value) {
7474
}
7575

7676

77+
// Global list of arrays visited during toString, toLocaleString and
78+
// join invocations.
79+
var visited_arrays = new InternalArray();
80+
81+
7782
// Gets a sorted array of array keys. Useful for operations on sparse
7883
// arrays. Dupes have not been removed.
7984
function GetSortedArrayKeys(array, indices) {
@@ -145,17 +150,6 @@ function SparseJoin(array, len, convert) {
145150
}
146151

147152

148-
function StringBuilderJoin(elements, length, separator) {
149-
length = MinSimple(elements.length, length);
150-
if (length == 0) return "";
151-
var result = elements[0];
152-
for (var i = 1; i < length; i++) {
153-
result = result + separator + elements[i];
154-
}
155-
return result;
156-
}
157-
158-
159153
function UseSparseVariant(array, length, is_array, touched) {
160154
// Only use the sparse variant on arrays that are likely to be sparse and the
161155
// number of elements touched in the operation is relatively small compared to
@@ -173,36 +167,6 @@ function UseSparseVariant(array, length, is_array, touched) {
173167
(touched > estimated_elements * 4);
174168
}
175169

176-
function Stack() {
177-
this.length = 0;
178-
this.values = new InternalArray();
179-
}
180-
181-
// Predeclare the instance variables on the prototype. Otherwise setting them in
182-
// the constructor will leak the instance through settings on Object.prototype.
183-
Stack.prototype.length = null;
184-
Stack.prototype.values = null;
185-
186-
function StackPush(stack, value) {
187-
stack.values[stack.length++] = value;
188-
}
189-
190-
function StackPop(stack) {
191-
stack.values[--stack.length] = null
192-
}
193-
194-
function StackHas(stack, v) {
195-
var length = stack.length;
196-
var values = stack.values;
197-
for (var i = 0; i < length; i++) {
198-
if (values[i] === v) return true;
199-
}
200-
return false;
201-
}
202-
203-
// Global list of arrays visited during toString, toLocaleString and
204-
// join invocations.
205-
var visited_arrays = new Stack();
206170

207171
function Join(array, length, separator, convert) {
208172
if (length == 0) return '';
@@ -212,8 +176,7 @@ function Join(array, length, separator, convert) {
212176
if (is_array) {
213177
// If the array is cyclic, return the empty string for already
214178
// visited arrays.
215-
if (StackHas(visited_arrays, array)) return '';
216-
StackPush(visited_arrays, array);
179+
if (!%PushIfAbsent(visited_arrays, array)) return '';
217180
}
218181

219182
// Attempt to convert the elements.
@@ -272,11 +235,11 @@ function Join(array, length, separator, convert) {
272235
elements[i] = e;
273236
}
274237
}
275-
return StringBuilderJoin(elements, length, separator);
238+
return %StringBuilderJoin(elements, length, separator);
276239
} finally {
277240
// Make sure to remove the last element of the visited array no
278241
// matter what happens.
279-
if (is_array) StackPop(visited_arrays);
242+
if (is_array) visited_arrays.length = visited_arrays.length - 1;
280243
}
281244
}
282245

@@ -1996,10 +1959,6 @@ utils.Export(function(to) {
19961959
to.InnerArraySort = InnerArraySort;
19971960
to.InnerArrayToLocaleString = InnerArrayToLocaleString;
19981961
to.PackedArrayReverse = PackedArrayReverse;
1999-
to.Stack = Stack;
2000-
to.StackHas = StackHas;
2001-
to.StackPush = StackPush;
2002-
to.StackPop = StackPop;
20031962
});
20041963

20051964
%InstallToContext([

src/js/json.js

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,13 @@ var MakeTypeError;
1919
var MaxSimple;
2020
var MinSimple;
2121
var ObjectHasOwnProperty;
22-
var Stack;
23-
var StackHas;
24-
var StackPop;
25-
var StackPush;
2622
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
2723

2824
utils.Import(function(from) {
2925
MakeTypeError = from.MakeTypeError;
3026
MaxSimple = from.MaxSimple;
3127
MinSimple = from.MinSimple;
3228
ObjectHasOwnProperty = from.ObjectHasOwnProperty;
33-
Stack = from.Stack;
34-
StackHas = from.StackHas;
35-
StackPop = from.StackPop;
36-
StackPush = from.StackPush;
3729
});
3830

3931
// -------------------------------------------------------------------
@@ -86,8 +78,7 @@ function JSONParse(text, reviver) {
8678

8779

8880
function SerializeArray(value, replacer, stack, indent, gap) {
89-
if (StackHas(stack, value)) throw MakeTypeError(kCircularStructure);
90-
StackPush(stack, value);
81+
if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
9182
var stepback = indent;
9283
indent += gap;
9384
var partial = new InternalArray();
@@ -110,14 +101,13 @@ function SerializeArray(value, replacer, stack, indent, gap) {
110101
} else {
111102
final = "[]";
112103
}
113-
StackPop(stack);
104+
stack.pop();
114105
return final;
115106
}
116107

117108

118109
function SerializeObject(value, replacer, stack, indent, gap) {
119-
if (StackHas(stack, value)) throw MakeTypeError(kCircularStructure);
120-
StackPush(stack, value);
110+
if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
121111
var stepback = indent;
122112
indent += gap;
123113
var partial = new InternalArray();
@@ -156,7 +146,7 @@ function SerializeObject(value, replacer, stack, indent, gap) {
156146
} else {
157147
final = "{}";
158148
}
159-
StackPop(stack);
149+
stack.pop();
160150
return final;
161151
}
162152

@@ -251,7 +241,7 @@ function JSONStringify(value, replacer, space) {
251241
if (!IS_CALLABLE(replacer) && !property_list && !gap && !IS_PROXY(value)) {
252242
return %BasicJSONStringify(value);
253243
}
254-
return JSONSerialize('', {'': value}, replacer, new Stack(), "", gap);
244+
return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
255245
}
256246

257247
// -------------------------------------------------------------------
@@ -289,7 +279,7 @@ function JsonSerializeAdapter(key, object) {
289279
var holder = {};
290280
holder[key] = object;
291281
// No need to pass the actual holder since there is no replacer function.
292-
return JSONSerialize(key, holder, UNDEFINED, new Stack(), "", "");
282+
return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", "");
293283
}
294284

295285
%InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);

src/runtime/runtime-array.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,29 @@ RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
8888
}
8989

9090

91+
// Push an object unto an array of objects if it is not already in the
92+
// array. Returns true if the element was pushed on the stack and
93+
// false otherwise.
94+
RUNTIME_FUNCTION(Runtime_PushIfAbsent) {
95+
HandleScope scope(isolate);
96+
DCHECK(args.length() == 2);
97+
CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
98+
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, element, 1);
99+
RUNTIME_ASSERT(array->HasFastSmiOrObjectElements());
100+
int length = Smi::cast(array->length())->value();
101+
FixedArray* elements = FixedArray::cast(array->elements());
102+
for (int i = 0; i < length; i++) {
103+
if (elements->get(i) == *element) return isolate->heap()->false_value();
104+
}
105+
106+
// Strict not needed. Used for cycle detection in Array join implementation.
107+
RETURN_FAILURE_ON_EXCEPTION(
108+
isolate, JSObject::AddDataElement(array, length, element, NONE));
109+
JSObject::ValidateElements(array);
110+
return isolate->heap()->true_value();
111+
}
112+
113+
91114
// Moves all own elements of an object, that are below a limit, to positions
92115
// starting at zero. All undefined values are placed after non-undefined values,
93116
// and are followed by non-existing element. Does not change the length

src/runtime/runtime-strings.cc

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,89 @@ RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
497497
}
498498

499499

500+
RUNTIME_FUNCTION(Runtime_StringBuilderJoin) {
501+
HandleScope scope(isolate);
502+
DCHECK(args.length() == 3);
503+
CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
504+
int32_t array_length;
505+
if (!args[1]->ToInt32(&array_length)) {
506+
THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
507+
}
508+
CONVERT_ARG_HANDLE_CHECKED(String, separator, 2);
509+
RUNTIME_ASSERT(array->HasFastObjectElements());
510+
RUNTIME_ASSERT(array_length >= 0);
511+
512+
Handle<FixedArray> fixed_array(FixedArray::cast(array->elements()));
513+
if (fixed_array->length() < array_length) {
514+
array_length = fixed_array->length();
515+
}
516+
517+
if (array_length == 0) {
518+
return isolate->heap()->empty_string();
519+
} else if (array_length == 1) {
520+
Object* first = fixed_array->get(0);
521+
RUNTIME_ASSERT(first->IsString());
522+
return first;
523+
}
524+
525+
int separator_length = separator->length();
526+
RUNTIME_ASSERT(separator_length > 0);
527+
int max_nof_separators =
528+
(String::kMaxLength + separator_length - 1) / separator_length;
529+
if (max_nof_separators < (array_length - 1)) {
530+
THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError());
531+
}
532+
int length = (array_length - 1) * separator_length;
533+
for (int i = 0; i < array_length; i++) {
534+
Object* element_obj = fixed_array->get(i);
535+
RUNTIME_ASSERT(element_obj->IsString());
536+
String* element = String::cast(element_obj);
537+
int increment = element->length();
538+
if (increment > String::kMaxLength - length) {
539+
STATIC_ASSERT(String::kMaxLength < kMaxInt);
540+
length = kMaxInt; // Provoke exception;
541+
break;
542+
}
543+
length += increment;
544+
}
545+
546+
Handle<SeqTwoByteString> answer;
547+
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
548+
isolate, answer, isolate->factory()->NewRawTwoByteString(length));
549+
550+
DisallowHeapAllocation no_gc;
551+
552+
uc16* sink = answer->GetChars();
553+
#ifdef DEBUG
554+
uc16* end = sink + length;
555+
#endif
556+
557+
RUNTIME_ASSERT(fixed_array->get(0)->IsString());
558+
String* first = String::cast(fixed_array->get(0));
559+
String* separator_raw = *separator;
560+
int first_length = first->length();
561+
String::WriteToFlat(first, sink, 0, first_length);
562+
sink += first_length;
563+
564+
for (int i = 1; i < array_length; i++) {
565+
DCHECK(sink + separator_length <= end);
566+
String::WriteToFlat(separator_raw, sink, 0, separator_length);
567+
sink += separator_length;
568+
569+
RUNTIME_ASSERT(fixed_array->get(i)->IsString());
570+
String* element = String::cast(fixed_array->get(i));
571+
int element_length = element->length();
572+
DCHECK(sink + element_length <= end);
573+
String::WriteToFlat(element, sink, 0, element_length);
574+
sink += element_length;
575+
}
576+
DCHECK(sink == end);
577+
578+
// Use %_FastOneByteArrayJoin instead.
579+
DCHECK(!answer->IsOneByteRepresentation());
580+
return *answer;
581+
}
582+
500583
template <typename Char>
501584
static void JoinSparseArrayWithSeparator(FixedArray* elements,
502585
int elements_length,

src/runtime/runtime.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ namespace internal {
3535
F(FinishArrayPrototypeSetup, 1, 1) \
3636
F(SpecialArrayFunctions, 0, 1) \
3737
F(TransitionElementsKind, 2, 1) \
38+
F(PushIfAbsent, 2, 1) \
3839
F(RemoveArrayHoles, 2, 1) \
3940
F(MoveArrayContents, 2, 1) \
4041
F(EstimateNumberOfElements, 1, 1) \
@@ -850,6 +851,7 @@ namespace internal {
850851
F(StringCharCodeAtRT, 2, 1) \
851852
F(StringCompare, 2, 1) \
852853
F(StringBuilderConcat, 3, 1) \
854+
F(StringBuilderJoin, 3, 1) \
853855
F(SparseJoinWithSeparator, 3, 1) \
854856
F(StringToArray, 2, 1) \
855857
F(StringToLowerCase, 1, 1) \

test/mjsunit/json-stringify-stack.js

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)