[inspector] Side-effect free DateMirror descriptions.

Similar to what we did for FunctionMirror before in
https://crrev.com/c/2887508, we also need to avoid running user
JavaScript for DateMirrors.

This also refactors the ToDateString logic a bit.

Fixed: chromium:1311613
Change-Id: I793b86106765550a9aa449f85f0766840081cc58
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3571896
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79821}
This commit is contained in:
Benedikt Meurer 2022-04-06 09:56:09 +02:00 committed by V8 LUCI CQ
parent 0ff8205261
commit 25c69ecbc1
8 changed files with 174 additions and 83 deletions

View File

@ -24,11 +24,6 @@ namespace internal {
namespace {
const char* kShortWeekDays[] = {"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"};
const char* kShortMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
// ES6 section 20.3.1.16 Date Time String Format
double ParseDateTimeString(Isolate* isolate, Handle<String> str) {
str = String::Flatten(isolate, str);
@ -61,55 +56,6 @@ double ParseDateTimeString(Isolate* isolate, Handle<String> str) {
return DateCache::TimeClip(date);
}
enum ToDateStringMode { kDateOnly, kTimeOnly, kDateAndTime };
using DateBuffer = base::SmallVector<char, 128>;
template <class... Args>
DateBuffer FormatDate(const char* format, Args... args) {
DateBuffer buffer;
SmallStringOptimizedAllocator<DateBuffer::kInlineSize> allocator(&buffer);
StringStream sstream(&allocator);
sstream.Add(format, args...);
buffer.resize_no_init(sstream.length());
return buffer;
}
// ES6 section 20.3.4.41.1 ToDateString(tv)
DateBuffer ToDateString(double time_val, DateCache* date_cache,
ToDateStringMode mode = kDateAndTime) {
if (std::isnan(time_val)) {
return FormatDate("Invalid Date");
}
int64_t time_ms = static_cast<int64_t>(time_val);
int64_t local_time_ms = date_cache->ToLocal(time_ms);
int year, month, day, weekday, hour, min, sec, ms;
date_cache->BreakDownTime(local_time_ms, &year, &month, &day, &weekday, &hour,
&min, &sec, &ms);
int timezone_offset = -date_cache->TimezoneOffset(time_ms);
int timezone_hour = std::abs(timezone_offset) / 60;
int timezone_min = std::abs(timezone_offset) % 60;
const char* local_timezone = date_cache->LocalTimezone(time_ms);
switch (mode) {
case kDateOnly:
return FormatDate((year < 0) ? "%s %s %02d %05d" : "%s %s %02d %04d",
kShortWeekDays[weekday], kShortMonths[month], day,
year);
case kTimeOnly:
return FormatDate("%02d:%02d:%02d GMT%c%02d%02d (%s)", hour, min, sec,
(timezone_offset < 0) ? '-' : '+', timezone_hour,
timezone_min, local_timezone);
case kDateAndTime:
return FormatDate(
(year < 0) ? "%s %s %02d %05d %02d:%02d:%02d GMT%c%02d%02d (%s)"
: "%s %s %02d %04d %02d:%02d:%02d GMT%c%02d%02d (%s)",
kShortWeekDays[weekday], kShortMonths[month], day, year, hour, min,
sec, (timezone_offset < 0) ? '-' : '+', timezone_hour, timezone_min,
local_timezone);
}
UNREACHABLE();
}
Object SetLocalDateValue(Isolate* isolate, Handle<JSDate> date,
double time_val) {
if (time_val >= -DateCache::kMaxTimeBeforeUTCInMs &&
@ -128,7 +74,8 @@ BUILTIN(DateConstructor) {
HandleScope scope(isolate);
if (args.new_target()->IsUndefined(isolate)) {
double const time_val = JSDate::CurrentTimeValue(isolate);
DateBuffer buffer = ToDateString(time_val, isolate->date_cache());
DateBuffer buffer = ToDateString(time_val, isolate->date_cache(),
ToDateStringMode::kLocalDateAndTime);
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(base::VectorOf(buffer)));
}
@ -720,7 +667,8 @@ BUILTIN(DatePrototypeToDateString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toDateString");
DateBuffer buffer =
ToDateString(date->value().Number(), isolate->date_cache(), kDateOnly);
ToDateString(date->value().Number(), isolate->date_cache(),
ToDateStringMode::kLocalDate);
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(base::VectorOf(buffer)));
}
@ -757,7 +705,8 @@ BUILTIN(DatePrototypeToString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toString");
DateBuffer buffer =
ToDateString(date->value().Number(), isolate->date_cache());
ToDateString(date->value().Number(), isolate->date_cache(),
ToDateStringMode::kLocalDateAndTime);
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(base::VectorOf(buffer)));
}
@ -767,7 +716,8 @@ BUILTIN(DatePrototypeToTimeString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toTimeString");
DateBuffer buffer =
ToDateString(date->value().Number(), isolate->date_cache(), kTimeOnly);
ToDateString(date->value().Number(), isolate->date_cache(),
ToDateStringMode::kLocalTime);
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(base::VectorOf(buffer)));
}
@ -838,21 +788,11 @@ BUILTIN(DatePrototypeToLocaleTimeString) {
BUILTIN(DatePrototypeToUTCString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toUTCString");
double const time_val = date->value().Number();
if (std::isnan(time_val)) {
return *isolate->factory()->NewStringFromAsciiChecked("Invalid Date");
}
char buffer[128];
int64_t time_ms = static_cast<int64_t>(time_val);
int year, month, day, weekday, hour, min, sec, ms;
isolate->date_cache()->BreakDownTime(time_ms, &year, &month, &day, &weekday,
&hour, &min, &sec, &ms);
SNPrintF(base::ArrayVector(buffer),
(year < 0) ? "%s, %02d %s %05d %02d:%02d:%02d GMT"
: "%s, %02d %s %04d %02d:%02d:%02d GMT",
kShortWeekDays[weekday], day, kShortMonths[month], year, hour, min,
sec);
return *isolate->factory()->NewStringFromAsciiChecked(buffer);
DateBuffer buffer =
ToDateString(date->value().Number(), isolate->date_cache(),
ToDateStringMode::kUTCDateAndTime);
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(base::VectorOf(buffer)));
}
// ES6 section B.2.4.1 Date.prototype.getYear ( )

View File

@ -10,6 +10,7 @@
#ifdef V8_INTL_SUPPORT
#include "src/objects/intl-objects.h"
#endif
#include "src/strings/string-stream.h"
namespace v8 {
namespace internal {
@ -533,5 +534,65 @@ double MakeTime(double hour, double min, double sec, double ms) {
return std::numeric_limits<double>::quiet_NaN();
}
namespace {
const char* kShortWeekDays[] = {"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"};
const char* kShortMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
template <class... Args>
DateBuffer FormatDate(const char* format, Args... args) {
DateBuffer buffer;
SmallStringOptimizedAllocator<DateBuffer::kInlineSize> allocator(&buffer);
StringStream sstream(&allocator);
sstream.Add(format, args...);
buffer.resize_no_init(sstream.length());
return buffer;
}
} // namespace
DateBuffer ToDateString(double time_val, DateCache* date_cache,
ToDateStringMode mode) {
if (std::isnan(time_val)) {
return FormatDate("Invalid Date");
}
int64_t time_ms = static_cast<int64_t>(time_val);
int64_t local_time_ms = mode != ToDateStringMode::kUTCDateAndTime
? date_cache->ToLocal(time_ms)
: time_ms;
int year, month, day, weekday, hour, min, sec, ms;
date_cache->BreakDownTime(local_time_ms, &year, &month, &day, &weekday, &hour,
&min, &sec, &ms);
int timezone_offset = -date_cache->TimezoneOffset(time_ms);
int timezone_hour = std::abs(timezone_offset) / 60;
int timezone_min = std::abs(timezone_offset) % 60;
const char* local_timezone = date_cache->LocalTimezone(time_ms);
switch (mode) {
case ToDateStringMode::kLocalDate:
return FormatDate((year < 0) ? "%s %s %02d %05d" : "%s %s %02d %04d",
kShortWeekDays[weekday], kShortMonths[month], day,
year);
case ToDateStringMode::kLocalTime:
return FormatDate("%02d:%02d:%02d GMT%c%02d%02d (%s)", hour, min, sec,
(timezone_offset < 0) ? '-' : '+', timezone_hour,
timezone_min, local_timezone);
case ToDateStringMode::kLocalDateAndTime:
return FormatDate(
(year < 0) ? "%s %s %02d %05d %02d:%02d:%02d GMT%c%02d%02d (%s)"
: "%s %s %02d %04d %02d:%02d:%02d GMT%c%02d%02d (%s)",
kShortWeekDays[weekday], kShortMonths[month], day, year, hour, min,
sec, (timezone_offset < 0) ? '-' : '+', timezone_hour, timezone_min,
local_timezone);
case ToDateStringMode::kUTCDateAndTime:
return FormatDate((year < 0) ? "%s, %02d %s %05d %02d:%02d:%02d GMT"
: "%s, %02d %s %04d %02d:%02d:%02d GMT",
kShortWeekDays[weekday], day, kShortMonths[month], year,
hour, min, sec);
}
UNREACHABLE();
}
} // namespace internal
} // namespace v8

View File

@ -5,6 +5,7 @@
#ifndef V8_DATE_DATE_H_
#define V8_DATE_DATE_H_
#include "src/base/small-vector.h"
#include "src/base/timezone-cache.h"
#include "src/common/globals.h"
#include "src/objects/smi.h"
@ -247,6 +248,19 @@ double MakeDay(double year, double month, double date);
// ES6 section 20.3.1.12 MakeTime (hour, min, sec, ms)
double MakeTime(double hour, double min, double sec, double ms);
using DateBuffer = base::SmallVector<char, 128>;
enum class ToDateStringMode {
kLocalDate,
kLocalTime,
kLocalDateAndTime,
kUTCDateAndTime,
};
// ES6 section 20.3.4.41.1 ToDateString(tv)
DateBuffer ToDateString(double time_val, DateCache* date_cache,
ToDateStringMode mode);
} // namespace internal
} // namespace v8

View File

@ -9,6 +9,7 @@
#include "src/base/utils/random-number-generator.h"
#include "src/codegen/compiler.h"
#include "src/codegen/script-details.h"
#include "src/date/date.h"
#include "src/debug/debug-coverage.h"
#include "src/debug/debug-evaluate.h"
#include "src/debug/debug-property-iterator.h"
@ -49,6 +50,17 @@ v8_inspector::V8Inspector* GetInspector(Isolate* isolate) {
return reinterpret_cast<i::Isolate*>(isolate)->inspector();
}
Local<String> GetDateDescription(Local<Date> date) {
auto receiver = Utils::OpenHandle(*date);
i::Handle<i::JSDate> jsdate = i::Handle<i::JSDate>::cast(receiver);
i::Isolate* isolate = jsdate->GetIsolate();
auto buffer = i::ToDateString(jsdate->value().Number(), isolate->date_cache(),
i::ToDateStringMode::kLocalDateAndTime);
return Utils::ToLocal(isolate->factory()
->NewStringFromUtf8(base::VectorOf(buffer))
.ToHandleChecked());
}
Local<String> GetFunctionDescription(Local<Function> function) {
auto receiver = Utils::OpenHandle(*function);
if (receiver->IsJSBoundFunction()) {

View File

@ -8,6 +8,7 @@
#include <memory>
#include "include/v8-callbacks.h"
#include "include/v8-date.h"
#include "include/v8-debug.h"
#include "include/v8-embedder-heap.h"
#include "include/v8-local-handle.h"
@ -49,6 +50,9 @@ int GetContextId(Local<Context> context);
void SetInspector(Isolate* isolate, v8_inspector::V8Inspector*);
v8_inspector::V8Inspector* GetInspector(Isolate* isolate);
// Returns a debug string representation of the date.
Local<String> GetDateDescription(Local<Date> date);
// Returns a debug string representation of the function.
Local<String> GetFunctionDescription(Local<Function> function);

View File

@ -309,11 +309,7 @@ String16 descriptionForObject(v8::Isolate* isolate,
String16 descriptionForDate(v8::Local<v8::Context> context,
v8::Local<v8::Date> date) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::String> description;
if (!date->ToString(context).ToLocal(&description)) {
return descriptionForObject(isolate, date);
}
v8::Local<v8::String> description = v8::debug::GetDateDescription(date);
return toProtocolString(isolate, description);
}

View File

@ -626,6 +626,47 @@ Running test: testDate
type : object
}
}
'a = new Date(2018, 9, 31); a.toString = date => 'bar'; a', returnByValue: false, generatePreview: true
{
result : {
className : Date
description : <expected description>
objectId : <objectId>
preview : {
description : <expected description>
overflow : false
properties : [
[0] : {
name : toString
type : function
value :
}
]
subtype : date
type : object
}
subtype : date
type : object
}
}
'a = new Date(2018, 9, 31); a[Symbol.toPrimitive] = date => 'bar'; a', returnByValue: false, generatePreview: true
{
result : {
className : Date
description : <expected description>
objectId : <objectId>
preview : {
description : <expected description>
overflow : false
properties : [
]
subtype : date
type : object
}
subtype : date
type : object
}
}
Running test: testMap
'new Map()', returnByValue: false, generatePreview: true
@ -1224,7 +1265,7 @@ Running test: testWeakMap
[0] : {
name : setTimeout
type : function
value :
value :
}
[1] : {
name : inspector
@ -1308,7 +1349,7 @@ Running test: testWeakSet
[0] : {
name : setTimeout
type : function
value :
value :
}
[1] : {
name : inspector
@ -2693,7 +2734,7 @@ Running test: testOtherObjects
[0] : {
name : a
type : function
value :
value :
}
]
type : object
@ -2883,12 +2924,12 @@ Running test: testOtherObjects
[0] : {
name : a1
type : function
value :
value :
}
[1] : {
name : a2
type : function
value :
value :
}
]
type : object

View File

@ -278,6 +278,29 @@ InspectorTest.runAsyncTestSuite([
if (result.result.preview.description === new Date(2018, 9, 31) + '')
result.result.preview.description = '<expected description>';
InspectorTest.logMessage(result);
result = (await evaluate({
expression:
`a = new Date(2018, 9, 31); a.toString = date => 'bar'; a`,
generatePreview: true
})).result;
if (result.result.description === new Date(2018, 9, 31) + '')
result.result.description = '<expected description>';
if (result.result.preview.description === new Date(2018, 9, 31) + '')
result.result.preview.description = '<expected description>';
InspectorTest.logMessage(result);
result =
(await evaluate({
expression:
`a = new Date(2018, 9, 31); a[Symbol.toPrimitive] = date => 'bar'; a`,
generatePreview: true
})).result;
if (result.result.description === new Date(2018, 9, 31) + '')
result.result.description = '<expected description>';
if (result.result.preview.description === new Date(2018, 9, 31) + '')
result.result.preview.description = '<expected description>';
InspectorTest.logMessage(result);
},
async function testMap() {
InspectorTest.logMessage((await evaluate({