[bigint] Implement NoSideEffectsToString
When our various debugging and error reporting facilities want to perform a side effect free conversion of a value (which could be a BigInt) to a String, then the usual BigInt::ToString is not a great fit because it reacts to termination requests. This patch adds a method BigInt::NoSideEffectsToString, which uses a low upper bound instead of termination requests. Fixed: chromium:1406774 Change-Id: Ibc5d37027823e4a03c470f1dd0a63c16c552850c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4177099 Auto-Submit: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Clemens Backes <clemensb@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/main@{#85433}
This commit is contained in:
parent
11c4c8e3d2
commit
b4ae834223
@ -922,6 +922,26 @@ ComparisonResult BigInt::CompareToDouble(Handle<BigInt> x, double y) {
|
||||
return ComparisonResult::kEqual;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void RightTrimString(Isolate* isolate, Handle<SeqOneByteString> string,
|
||||
int chars_allocated, int chars_written) {
|
||||
DCHECK_LE(chars_written, chars_allocated);
|
||||
if (chars_written == chars_allocated) return;
|
||||
string->set_length(chars_written, kReleaseStore);
|
||||
int string_size =
|
||||
ALIGN_TO_ALLOCATION_ALIGNMENT(SeqOneByteString::SizeFor(chars_allocated));
|
||||
int needed_size =
|
||||
ALIGN_TO_ALLOCATION_ALIGNMENT(SeqOneByteString::SizeFor(chars_written));
|
||||
if (needed_size < string_size && !isolate->heap()->IsLargeObject(*string)) {
|
||||
isolate->heap()->NotifyObjectSizeChange(*string, string_size, needed_size,
|
||||
ClearRecordedSlots::kNo,
|
||||
UpdateInvalidatedObjectSize::kNo);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MaybeHandle<String> BigInt::ToString(Isolate* isolate, Handle<BigInt> bigint,
|
||||
int radix, ShouldThrow should_throw) {
|
||||
if (bigint->is_zero()) {
|
||||
@ -995,18 +1015,7 @@ MaybeHandle<String> BigInt::ToString(Isolate* isolate, Handle<BigInt> bigint,
|
||||
|
||||
// Right-trim any over-allocation (which can happen due to conservative
|
||||
// estimates).
|
||||
if (chars_written < chars_allocated) {
|
||||
result->set_length(chars_written, kReleaseStore);
|
||||
int string_size = ALIGN_TO_ALLOCATION_ALIGNMENT(
|
||||
SeqOneByteString::SizeFor(chars_allocated));
|
||||
int needed_size =
|
||||
ALIGN_TO_ALLOCATION_ALIGNMENT(SeqOneByteString::SizeFor(chars_written));
|
||||
if (needed_size < string_size && !isolate->heap()->IsLargeObject(*result)) {
|
||||
isolate->heap()->NotifyObjectSizeChange(*result, string_size, needed_size,
|
||||
ClearRecordedSlots::kNo,
|
||||
UpdateInvalidatedObjectSize::kNo);
|
||||
}
|
||||
}
|
||||
RightTrimString(isolate, result, chars_allocated, chars_written);
|
||||
#if DEBUG
|
||||
// Verify that all characters have been written.
|
||||
DCHECK(result->length() == chars_written);
|
||||
@ -1019,6 +1028,37 @@ MaybeHandle<String> BigInt::ToString(Isolate* isolate, Handle<BigInt> bigint,
|
||||
return result;
|
||||
}
|
||||
|
||||
Handle<String> BigInt::NoSideEffectsToString(Isolate* isolate,
|
||||
Handle<BigInt> bigint) {
|
||||
if (bigint->is_zero()) {
|
||||
return isolate->factory()->zero_string();
|
||||
}
|
||||
// The threshold is chosen such that the operation will be fast enough to
|
||||
// not need interrupt checks. This function is meant for producing human-
|
||||
// readable error messages, so super-long results aren't useful anyway.
|
||||
if (bigint->length() > 100) {
|
||||
return isolate->factory()->NewStringFromStaticChars(
|
||||
"<a very large BigInt>");
|
||||
}
|
||||
|
||||
int chars_allocated =
|
||||
bigint::ToStringResultLength(GetDigits(bigint), 10, bigint->sign());
|
||||
DCHECK_LE(chars_allocated, String::kMaxLength);
|
||||
Handle<SeqOneByteString> result = isolate->factory()
|
||||
->NewRawOneByteString(chars_allocated)
|
||||
.ToHandleChecked();
|
||||
int chars_written = chars_allocated;
|
||||
DisallowGarbageCollection no_gc;
|
||||
char* characters = reinterpret_cast<char*>(result->GetChars(no_gc));
|
||||
std::unique_ptr<bigint::Processor, bigint::Processor::Destroyer>
|
||||
non_interruptible_processor(
|
||||
bigint::Processor::New(new bigint::Platform()));
|
||||
non_interruptible_processor->ToString(characters, &chars_written,
|
||||
GetDigits(bigint), 10, bigint->sign());
|
||||
RightTrimString(isolate, result, chars_allocated, chars_written);
|
||||
return result;
|
||||
}
|
||||
|
||||
MaybeHandle<BigInt> BigInt::FromNumber(Isolate* isolate,
|
||||
Handle<Object> number) {
|
||||
DCHECK(number->IsNumber());
|
||||
|
@ -278,6 +278,12 @@ class BigInt : public BigIntBase {
|
||||
static MaybeHandle<String> ToString(Isolate* isolate, Handle<BigInt> bigint,
|
||||
int radix = 10,
|
||||
ShouldThrow should_throw = kThrowOnError);
|
||||
// Like the above, but adapted for the needs of producing error messages:
|
||||
// doesn't care about termination requests, and returns a default string
|
||||
// for inputs beyond a relatively low upper bound.
|
||||
static Handle<String> NoSideEffectsToString(Isolate* isolate,
|
||||
Handle<BigInt> bigint);
|
||||
|
||||
// "The Number value for x", see:
|
||||
// https://tc39.github.io/ecma262/#sec-ecmascript-language-types-number-type
|
||||
// Returns a Smi or HeapNumber.
|
||||
|
@ -487,14 +487,7 @@ MaybeHandle<String> Object::NoSideEffectsToMaybeString(Isolate* isolate,
|
||||
} while (currInput->IsJSProxy());
|
||||
return NoSideEffectsToString(isolate, currInput);
|
||||
} else if (input->IsBigInt()) {
|
||||
MaybeHandle<String> maybe_string =
|
||||
BigInt::ToString(isolate, Handle<BigInt>::cast(input), 10, kDontThrow);
|
||||
Handle<String> result;
|
||||
if (maybe_string.ToHandle(&result)) return result;
|
||||
// BigInt-to-String conversion can fail on 32-bit platforms where
|
||||
// String::kMaxLength is too small to fit this BigInt.
|
||||
return isolate->factory()->NewStringFromStaticChars(
|
||||
"<a very large BigInt>");
|
||||
return BigInt::NoSideEffectsToString(isolate, Handle<BigInt>::cast(input));
|
||||
} else if (input->IsFunction()) {
|
||||
// -- F u n c t i o n
|
||||
Handle<String> fun_str;
|
||||
|
23
test/mjsunit/regress/regress-crbug-1406774.js
Normal file
23
test/mjsunit/regress/regress-crbug-1406774.js
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2023 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --stack-size=100
|
||||
|
||||
function worker_code(arr) {
|
||||
postMessage("worker starting");
|
||||
const large_bigint = 2n ** 100_000n;
|
||||
function f() {
|
||||
// Run until near the stack limit.
|
||||
try { f(); } catch (e) {
|
||||
postMessage("stack limit reached");
|
||||
postMessage(arr[large_bigint]);
|
||||
}
|
||||
}
|
||||
onmessage = f;
|
||||
}
|
||||
let w = new Worker(worker_code, { "arguments": [], "type": "function" });
|
||||
assertEquals("worker starting", w.getMessage());
|
||||
w.postMessage("");
|
||||
assertEquals("stack limit reached", w.getMessage());
|
||||
w.terminate();
|
Loading…
Reference in New Issue
Block a user