Commit 1658dc82 authored by Anton Bikineev's avatar Anton Bikineev Committed by Commit Bot

[builtins] Prevent result truncation when formatting dates

The ToDateString builtin now uses StringStream to format dates
instead of SNPrintF. The patch also implements a new allocator
based on SSO that's able to expand automatically.

Bug: v8:7770
Change-Id: I23e03ec06fcfc7bda1e5abb1ac82637e5c9ddc95
Reviewed-on: https://chromium-review.googlesource.com/c/1425905
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59119}
parent 3145505a
......@@ -51,6 +51,7 @@ Andreas Anyuru <andreas.anyuru@gmail.com>
Andrew Paprocki <andrew@ishiboo.com>
Andrei Kashcha <anvaka@gmail.com>
Anna Henningsen <anna@addaleax.net>
Anton Bikineev <ant.bikineev@gmail.com>
Bangfu Tao <bangfu.tao@samsung.com>
Daniel Shelton <d1.shelton@samsung.com>
Ben Coe <ben@npmjs.com>
......
......@@ -15,7 +15,7 @@ namespace base {
// Minimal SmallVector implementation. Uses inline storage first, switches to
// malloc when it overflows.
template <typename T, size_t kInlineSize>
template <typename T, size_t kSize>
class SmallVector {
// Currently only support trivially copyable and trivially destructible data
// types, as it uses memcpy to copy elements and never calls destructors.
......@@ -23,6 +23,8 @@ class SmallVector {
STATIC_ASSERT(std::is_trivially_destructible<T>::value);
public:
static constexpr size_t kInlineSize = kSize;
SmallVector() = default;
SmallVector(const SmallVector& other) V8_NOEXCEPT { *this = other; }
SmallVector(SmallVector&& other) V8_NOEXCEPT { *this = std::move(other); }
......@@ -62,9 +64,15 @@ class SmallVector {
return *this;
}
T* data() const { return begin_; }
T* begin() const { return begin_; }
T* end() const { return end_; }
T* data() { return begin_; }
const T* data() const { return begin_; }
T* begin() { return begin_; }
const T* begin() const { return begin_; }
T* end() { return end_; }
const T* end() const { return end_; }
size_t size() const { return end_ - begin_; }
bool empty() const { return end_ == begin_; }
size_t capacity() const { return end_of_storage_ - begin_; }
......
......@@ -14,6 +14,7 @@
#include "src/objects/intl-objects.h"
#include "src/objects/js-date-time-format.h"
#endif
#include "src/string-stream.h"
namespace v8 {
namespace internal {
......@@ -142,12 +143,23 @@ double ParseDateTimeString(Isolate* isolate, Handle<String> str) {
enum ToDateStringMode { kDateOnly, kTimeOnly, kDateAndTime };
typedef base::SmallVector<char, 128> DateBuffer;
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)
void ToDateString(double time_val, Vector<char> str, DateCache* date_cache,
ToDateStringMode mode = kDateAndTime) {
DateBuffer ToDateString(double time_val, DateCache* date_cache,
ToDateStringMode mode = kDateAndTime) {
if (std::isnan(time_val)) {
SNPrintF(str, "Invalid Date");
return;
return FormatDate("Invalid Date");
}
int64_t time_ms = static_cast<int64_t>(time_val);
int64_t local_time_ms = date_cache->ToLocal(time_ms);
......@@ -160,22 +172,17 @@ void ToDateString(double time_val, Vector<char> str, DateCache* date_cache,
const char* local_timezone = date_cache->LocalTimezone(time_ms);
switch (mode) {
case kDateOnly:
SNPrintF(str, "%s %s %02d %04d", kShortWeekDays[weekday],
kShortMonths[month], day, year);
return;
return FormatDate("%s %s %02d %04d", kShortWeekDays[weekday],
kShortMonths[month], day, year);
case kTimeOnly:
// TODO(842085): str may be silently truncated.
SNPrintF(str, "%02d:%02d:%02d GMT%c%02d%02d (%s)", hour, min, sec,
(timezone_offset < 0) ? '-' : '+', timezone_hour, timezone_min,
local_timezone);
return;
return FormatDate("%02d:%02d:%02d GMT%c%02d%02d (%s)", hour, min, sec,
(timezone_offset < 0) ? '-' : '+', timezone_hour,
timezone_min, local_timezone);
case kDateAndTime:
// TODO(842085): str may be silently truncated.
SNPrintF(str, "%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);
return;
return FormatDate("%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();
}
......@@ -198,10 +205,9 @@ BUILTIN(DateConstructor) {
HandleScope scope(isolate);
if (args.new_target()->IsUndefined(isolate)) {
double const time_val = JSDate::CurrentTimeValue(isolate);
char buffer[128];
ToDateString(time_val, ArrayVector(buffer), isolate->date_cache());
DateBuffer buffer = ToDateString(time_val, isolate->date_cache());
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(CStrVector(buffer)));
isolate, isolate->factory()->NewStringFromUtf8(VectorOf(buffer)));
}
// [Construct]
int const argc = args.length() - 1;
......@@ -786,11 +792,10 @@ BUILTIN(DatePrototypeSetUTCSeconds) {
BUILTIN(DatePrototypeToDateString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toDateString");
char buffer[128];
ToDateString(date->value()->Number(), ArrayVector(buffer),
isolate->date_cache(), kDateOnly);
DateBuffer buffer =
ToDateString(date->value()->Number(), isolate->date_cache(), kDateOnly);
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(CStrVector(buffer)));
isolate, isolate->factory()->NewStringFromUtf8(VectorOf(buffer)));
}
// ES6 section 20.3.4.36 Date.prototype.toISOString ( )
......@@ -824,22 +829,20 @@ BUILTIN(DatePrototypeToISOString) {
BUILTIN(DatePrototypeToString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toString");
char buffer[128];
ToDateString(date->value()->Number(), ArrayVector(buffer),
isolate->date_cache());
DateBuffer buffer =
ToDateString(date->value()->Number(), isolate->date_cache());
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(CStrVector(buffer)));
isolate, isolate->factory()->NewStringFromUtf8(VectorOf(buffer)));
}
// ES6 section 20.3.4.42 Date.prototype.toTimeString ( )
BUILTIN(DatePrototypeToTimeString) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSDate, date, "Date.prototype.toTimeString");
char buffer[128];
ToDateString(date->value()->Number(), ArrayVector(buffer),
isolate->date_cache(), kTimeOnly);
DateBuffer buffer =
ToDateString(date->value()->Number(), isolate->date_cache(), kTimeOnly);
RETURN_RESULT_OR_FAILURE(
isolate, isolate->factory()->NewStringFromUtf8(CStrVector(buffer)));
isolate, isolate->factory()->NewStringFromUtf8(VectorOf(buffer)));
}
#ifdef V8_INTL_SUPPORT
......
......@@ -6,6 +6,7 @@
#define V8_STRING_STREAM_H_
#include "src/allocation.h"
#include "src/base/small-vector.h"
#include "src/handles.h"
#include "src/objects/heap-object.h"
#include "src/vector.h"
......@@ -56,6 +57,34 @@ class FixedStringAllocator final : public StringAllocator {
DISALLOW_COPY_AND_ASSIGN(FixedStringAllocator);
};
template <std::size_t kInlineSize>
class SmallStringOptimizedAllocator final : public StringAllocator {
public:
typedef base::SmallVector<char, kInlineSize> SmallVector;
explicit SmallStringOptimizedAllocator(SmallVector* vector) V8_NOEXCEPT
: vector_(vector) {}
char* allocate(unsigned bytes) override {
vector_->resize_no_init(bytes);
return vector_->data();
}
char* grow(unsigned* bytes) override {
unsigned new_bytes = *bytes * 2;
// Check for overflow.
if (new_bytes <= *bytes) {
return vector_->data();
}
vector_->resize_no_init(new_bytes);
*bytes = new_bytes;
return vector_->data();
}
private:
SmallVector* vector_;
};
class StringStream final {
class FmtElm final {
public:
......@@ -105,8 +134,8 @@ class StringStream final {
public:
enum ObjectPrintMode { kPrintObjectConcise, kPrintObjectVerbose };
StringStream(StringAllocator* allocator,
ObjectPrintMode object_print_mode = kPrintObjectVerbose)
explicit StringStream(StringAllocator* allocator,
ObjectPrintMode object_print_mode = kPrintObjectVerbose)
: allocator_(allocator),
object_print_mode_(object_print_mode),
capacity_(kInitialCapacity),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment