Commit c1a9e556 authored by littledan's avatar littledan Committed by Commit bot

Reland of [date] Add ICU backend for timezone info behind a flag (patchset #1...

Reland of [date] Add ICU backend for timezone info behind a flag (patchset #1 id:1 of https://codereview.chromium.org/2811103002/ )

Reason for revert:
Reland with tests marked as off in no-i18n mode

Original issue's description:
> Revert of [date] Add ICU backend for timezone info behind a flag (patchset #17 id:320001 of https://codereview.chromium.org/2724373002/ )
>
> Reason for revert:
> Breaks noi18n:
> https://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20noi18n%20-%20debug/builds/13314
>
> Original issue's description:
> > [date] Add ICU backend for timezone info behind a flag
> >
> > This patch implements a timezone backend which is based on ICU, rather
> > than operating system calls. It can be turned on by passing the
> > --icu-timezone-data flag. The goal here is to take advantage of ICU's
> > data, which is more complete than the data that some system calls expose.
> > For example, without any special code, this patch fixes the time zone
> > of Lord Howe Island to have a correct 30 minute DST offset, rather than
> > 60 minutes as the OS backends assume it to have.
> >
> > Unfortunately, the parenthized timezone name in Date.prototype.toString()
> > differs across platforms. This patch chooses the long timezone name,
> > which matches Windows behavior and might be the most intelligible, but
> > the web compatibility impact is unclear.
> >
> > BUG=v8:6031,v8:2137,v8:6076
> >
> > Review-Url: https://codereview.chromium.org/2724373002
> > Cr-Commit-Position: refs/heads/master@{#44562}
> > Committed: https://chromium.googlesource.com/v8/v8/+/b213f2399038a615cdfbfa0201cddc113d304018
>
> TBR=ulan@chromium.org,jshin@chromium.org,jgruber@chromium.org,littledan@chromium.org
> # Skipping CQ checks because original CL landed less than 1 days ago.
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=v8:6031,v8:2137,v8:6076
>
> Review-Url: https://codereview.chromium.org/2811103002
> Cr-Commit-Position: refs/heads/master@{#44565}
> Committed: https://chromium.googlesource.com/v8/v8/+/13ad50811024ace5623d5d4d13cea4ef21f4affd

TBR=ulan@chromium.org,jshin@chromium.org,jgruber@chromium.org,machenbach@chromium.org
BUG=v8:6031,v8:2137,v8:6076

Review-Url: https://codereview.chromium.org/2813863002
Cr-Commit-Position: refs/heads/master@{#44575}
parent bb61aa4a
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
#include "src/objects.h" #include "src/objects.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
#ifdef V8_I18N_SUPPORT
#include "src/i18n.h"
#endif
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -21,6 +25,18 @@ static const int kYearsOffset = 400000; ...@@ -21,6 +25,18 @@ static const int kYearsOffset = 400000;
static const char kDaysInMonths[] = static const char kDaysInMonths[] =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
DateCache::DateCache()
: stamp_(0),
tz_cache_(
#ifdef V8_I18N_SUPPORT
FLAG_icu_timezone_data ? new ICUTimezoneCache()
: base::OS::CreateTimezoneCache()
#else
base::OS::CreateTimezoneCache()
#endif
) {
ResetDateCache();
}
void DateCache::ResetDateCache() { void DateCache::ResetDateCache() {
static const int kMaxStamp = Smi::kMaxValue; static const int kMaxStamp = Smi::kMaxValue;
......
...@@ -39,9 +39,7 @@ class DateCache { ...@@ -39,9 +39,7 @@ class DateCache {
// It is an invariant of DateCache that cache stamp is non-negative. // It is an invariant of DateCache that cache stamp is non-negative.
static const int kInvalidStamp = -1; static const int kInvalidStamp = -1;
DateCache() : stamp_(0), tz_cache_(base::OS::CreateTimezoneCache()) { DateCache();
ResetDateCache();
}
virtual ~DateCache() { virtual ~DateCache() {
delete tz_cache_; delete tz_cache_;
......
...@@ -254,6 +254,11 @@ HARMONY_STAGED(FLAG_STAGED_FEATURES) ...@@ -254,6 +254,11 @@ HARMONY_STAGED(FLAG_STAGED_FEATURES)
HARMONY_SHIPPING(FLAG_SHIPPING_FEATURES) HARMONY_SHIPPING(FLAG_SHIPPING_FEATURES)
#undef FLAG_SHIPPING_FEATURES #undef FLAG_SHIPPING_FEATURES
#ifdef V8_I18N_SUPPORT
DEFINE_BOOL(icu_timezone_data, false,
"get information about timezones from ICU")
#endif
#ifdef V8_ENABLE_FUTURE #ifdef V8_ENABLE_FUTURE
#define FUTURE_BOOL true #define FUTURE_BOOL true
#else #else
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "src/objects-inl.h" #include "src/objects-inl.h"
#include "src/string-case.h" #include "src/string-case.h"
#include "unicode/brkiter.h" #include "unicode/brkiter.h"
#include "unicode/bytestream.h"
#include "unicode/calendar.h" #include "unicode/calendar.h"
#include "unicode/coll.h" #include "unicode/coll.h"
#include "unicode/curramt.h" #include "unicode/curramt.h"
...@@ -1195,5 +1196,57 @@ MUST_USE_RESULT Object* ConvertCase(Handle<String> s, bool is_upper, ...@@ -1195,5 +1196,57 @@ MUST_USE_RESULT Object* ConvertCase(Handle<String> s, bool is_upper,
return is_upper ? ConvertToUpper(s, isolate) : ConvertToLower(s, isolate); return is_upper ? ConvertToUpper(s, isolate) : ConvertToLower(s, isolate);
} }
ICUTimezoneCache::ICUTimezoneCache() : timezone_(nullptr) { Clear(); }
ICUTimezoneCache::~ICUTimezoneCache() { Clear(); }
const char* ICUTimezoneCache::LocalTimezone(double time_ms) {
bool is_dst = DaylightSavingsOffset(time_ms) != 0;
char* name = is_dst ? dst_timezone_name_ : timezone_name_;
if (name[0] == '\0') {
icu::UnicodeString result;
GetTimeZone()->getDisplayName(is_dst, icu::TimeZone::LONG, result);
result += '\0';
icu::CheckedArrayByteSink byte_sink(name, kMaxTimezoneChars);
result.toUTF8(byte_sink);
CHECK(!byte_sink.Overflowed());
}
return const_cast<const char*>(name);
}
icu::TimeZone* ICUTimezoneCache::GetTimeZone() {
if (timezone_ == nullptr) {
timezone_ = icu::TimeZone::createDefault();
}
return timezone_;
}
bool ICUTimezoneCache::GetOffsets(double time_ms, int32_t* raw_offset,
int32_t* dst_offset) {
UErrorCode status = U_ZERO_ERROR;
GetTimeZone()->getOffset(time_ms, false, *raw_offset, *dst_offset, status);
return U_SUCCESS(status);
}
double ICUTimezoneCache::DaylightSavingsOffset(double time_ms) {
int32_t raw_offset, dst_offset;
if (!GetOffsets(time_ms, &raw_offset, &dst_offset)) return 0;
return dst_offset;
}
double ICUTimezoneCache::LocalTimeOffset() {
int32_t raw_offset, dst_offset;
if (!GetOffsets(icu::Calendar::getNow(), &raw_offset, &dst_offset)) return 0;
return raw_offset;
}
void ICUTimezoneCache::Clear() {
delete timezone_;
timezone_ = nullptr;
timezone_name_[0] = '\0';
dst_timezone_name_[0] = '\0';
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#ifndef V8_I18N_H_ #ifndef V8_I18N_H_
#define V8_I18N_H_ #define V8_I18N_H_
#include "src/base/timezone-cache.h"
#include "src/objects.h" #include "src/objects.h"
#include "unicode/uversion.h" #include "unicode/uversion.h"
...@@ -14,6 +15,7 @@ class BreakIterator; ...@@ -14,6 +15,7 @@ class BreakIterator;
class Collator; class Collator;
class DecimalFormat; class DecimalFormat;
class SimpleDateFormat; class SimpleDateFormat;
class TimeZone;
} }
namespace v8 { namespace v8 {
...@@ -138,6 +140,34 @@ MUST_USE_RESULT Object* ConvertToUpper(Handle<String> s, Isolate* isolate); ...@@ -138,6 +140,34 @@ MUST_USE_RESULT Object* ConvertToUpper(Handle<String> s, Isolate* isolate);
MUST_USE_RESULT Object* ConvertCase(Handle<String> s, bool is_upper, MUST_USE_RESULT Object* ConvertCase(Handle<String> s, bool is_upper,
Isolate* isolate); Isolate* isolate);
// ICUTimezoneCache calls out to ICU for TimezoneCache
// functionality in a straightforward way.
class ICUTimezoneCache : public base::TimezoneCache {
public:
ICUTimezoneCache();
~ICUTimezoneCache() override;
const char* LocalTimezone(double time_ms) override;
double DaylightSavingsOffset(double time_ms) override;
double LocalTimeOffset() override;
void Clear() override;
private:
icu::TimeZone* GetTimeZone();
bool GetOffsets(double time_ms, int32_t* raw_offset, int32_t* dst_offset);
icu::TimeZone* timezone_;
static const int32_t kMaxTimezoneChars = 100;
char timezone_name_[kMaxTimezoneChars];
char dst_timezone_name_[kMaxTimezoneChars];
};
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
// Copyright 2017 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: --icu-timezone-data
// Environment Variables: TZ=Australia/Lord_Howe LC_ALL=en
assertEquals(
"Mon Jan 01 1990 11:00:00 GMT+1100 (Lord Howe Daylight Time)",
new Date("1990-01-01").toString());
assertEquals(
"Fri Jun 01 1990 10:30:00 GMT+1030 (Lord Howe Standard Time)",
new Date("1990-06-01").toString());
// Copyright 2017 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: --icu-timezone-data
// Environment Variables: TZ=Europe/Madrid LC_ALL=de
assertEquals(
"Sun Dec 31 1989 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit)",
new Date(1990, 0, 0).toString());
assertEquals(
"Sat Jun 30 1990 00:00:00 GMT+0200 (Mitteleuropäische Sommerzeit)",
new Date(1990, 6, 0).toString());
...@@ -171,6 +171,10 @@ ...@@ -171,6 +171,10 @@
# noi18n build cannot parse characters in supplementary plane. # noi18n build cannot parse characters in supplementary plane.
'harmony/regexp-named-captures': [PASS, ['no_i18n == True', FAIL]], 'harmony/regexp-named-captures': [PASS, ['no_i18n == True', FAIL]],
# noi18n cannot turn on ICU backend for Date
'icu-date-to-string': [PASS, ['no_i18n == True', SKIP]],
'icu-date-lord-howe': [PASS, ['no_i18n == True', SKIP]],
# Allocates a large array buffer, which TSAN sometimes cannot handle. # Allocates a large array buffer, which TSAN sometimes cannot handle.
'regress/regress-599717': [PASS, ['tsan', SKIP]], 'regress/regress-599717': [PASS, ['tsan', SKIP]],
...@@ -543,6 +547,10 @@ ...@@ -543,6 +547,10 @@
# BUG(v8:4495). # BUG(v8:4495).
'es6/collections': [PASS, ['arch == ia32', FAST_VARIANTS]], 'es6/collections': [PASS, ['arch == ia32', FAST_VARIANTS]],
# Setting the timezone and locale with environment variables unavailable
'icu-date-to-string': [SKIP],
'icu-date-lord-howe': [SKIP],
}], # 'system == windows' }], # 'system == windows'
############################################################################## ##############################################################################
......
...@@ -33,6 +33,7 @@ from testrunner.objects import testcase ...@@ -33,6 +33,7 @@ from testrunner.objects import testcase
FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)") FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)")
FILES_PATTERN = re.compile(r"//\s+Files:(.*)") FILES_PATTERN = re.compile(r"//\s+Files:(.*)")
ENV_PATTERN = re.compile(r"//\s+Environment Variables:(.*)")
SELF_SCRIPT_PATTERN = re.compile(r"//\s+Env: TEST_FILE_NAME") SELF_SCRIPT_PATTERN = re.compile(r"//\s+Env: TEST_FILE_NAME")
MODULE_PATTERN = re.compile(r"^// MODULE$", flags=re.MULTILINE) MODULE_PATTERN = re.compile(r"^// MODULE$", flags=re.MULTILINE)
NO_HARNESS_PATTERN = re.compile(r"^// NO HARNESS$", flags=re.MULTILINE) NO_HARNESS_PATTERN = re.compile(r"^// NO HARNESS$", flags=re.MULTILINE)
...@@ -94,6 +95,12 @@ class MjsunitTestSuite(testsuite.TestSuite): ...@@ -94,6 +95,12 @@ class MjsunitTestSuite(testsuite.TestSuite):
flags.append("--isolate") flags.append("--isolate")
flags += files flags += files
env_match = ENV_PATTERN.search(source)
if env_match:
for env_pair in env_match.group(1).strip().split():
var, value = env_pair.split('=')
testcase.env[var] = value
return testcase.flags + flags return testcase.flags + flags
def GetSourceForTest(self, testcase): def GetSourceForTest(self, testcase):
......
...@@ -50,7 +50,7 @@ def Win32SetErrorMode(mode): ...@@ -50,7 +50,7 @@ def Win32SetErrorMode(mode):
return prev_error_mode return prev_error_mode
def RunProcess(verbose, timeout, args, **rest): def RunProcess(verbose, timeout, args, additional_env, **rest):
if verbose: print "#", " ".join(args) if verbose: print "#", " ".join(args)
popen_args = args popen_args = args
prev_error_mode = SEM_INVALID_VALUE prev_error_mode = SEM_INVALID_VALUE
...@@ -64,6 +64,7 @@ def RunProcess(verbose, timeout, args, **rest): ...@@ -64,6 +64,7 @@ def RunProcess(verbose, timeout, args, **rest):
Win32SetErrorMode(error_mode | prev_error_mode) Win32SetErrorMode(error_mode | prev_error_mode)
env = os.environ.copy() env = os.environ.copy()
env.update(additional_env)
# GTest shard information is read by the V8 tests runner. Make sure it # GTest shard information is read by the V8 tests runner. Make sure it
# doesn't leak into the execution of gtests we're wrapping. Those might # doesn't leak into the execution of gtests we're wrapping. Those might
# otherwise apply a second level of sharding and as a result skip tests. # otherwise apply a second level of sharding and as a result skip tests.
...@@ -126,6 +127,6 @@ def RunProcess(verbose, timeout, args, **rest): ...@@ -126,6 +127,6 @@ def RunProcess(verbose, timeout, args, **rest):
) )
def Execute(args, verbose=False, timeout=None): def Execute(args, verbose=False, timeout=None, env=None):
args = [ c for c in args if c != "" ] args = [ c for c in args if c != "" ]
return RunProcess(verbose, timeout, args=args) return RunProcess(verbose, timeout, args, env or {})
...@@ -49,11 +49,12 @@ TEST_DIR = os.path.join(BASE_DIR, "test") ...@@ -49,11 +49,12 @@ TEST_DIR = os.path.join(BASE_DIR, "test")
class Instructions(object): class Instructions(object):
def __init__(self, command, test_id, timeout, verbose): def __init__(self, command, test_id, timeout, verbose, env):
self.command = command self.command = command
self.id = test_id self.id = test_id
self.timeout = timeout self.timeout = timeout
self.verbose = verbose self.verbose = verbose
self.env = env
# Structure that keeps global information per worker process. # Structure that keeps global information per worker process.
...@@ -111,7 +112,7 @@ def _GetInstructions(test, context): ...@@ -111,7 +112,7 @@ def _GetInstructions(test, context):
# the like. # the like.
if statusfile.IsSlow(test.outcomes or [statusfile.PASS]): if statusfile.IsSlow(test.outcomes or [statusfile.PASS]):
timeout *= 2 timeout *= 2
return Instructions(command, test.id, timeout, context.verbose) return Instructions(command, test.id, timeout, context.verbose, test.env)
class Job(object): class Job(object):
...@@ -178,7 +179,8 @@ class TestJob(Job): ...@@ -178,7 +179,8 @@ class TestJob(Job):
return SetupProblem(e, self.test) return SetupProblem(e, self.test)
start_time = time.time() start_time = time.time()
output = commands.Execute(instr.command, instr.verbose, instr.timeout) output = commands.Execute(instr.command, instr.verbose, instr.timeout,
instr.env)
self._rename_coverage_data(output, process_context.context) self._rename_coverage_data(output, process_context.context)
return (instr.id, output, time.time() - start_time) return (instr.id, output, time.time() - start_time)
......
...@@ -41,11 +41,13 @@ class TestCase(object): ...@@ -41,11 +41,13 @@ class TestCase(object):
self.id = None # int, used to map result back to TestCase instance self.id = None # int, used to map result back to TestCase instance
self.duration = None # assigned during execution self.duration = None # assigned during execution
self.run = 1 # The nth time this test is executed. self.run = 1 # The nth time this test is executed.
self.env = {}
def CopyAddingFlags(self, variant, flags): def CopyAddingFlags(self, variant, flags):
copy = TestCase(self.suite, self.path, variant, self.flags + flags, copy = TestCase(self.suite, self.path, variant, self.flags + flags,
self.override_shell) self.override_shell)
copy.outcomes = self.outcomes copy.outcomes = self.outcomes
copy.env = self.env
return copy return copy
def PackTask(self): def PackTask(self):
...@@ -56,7 +58,7 @@ class TestCase(object): ...@@ -56,7 +58,7 @@ class TestCase(object):
assert self.id is not None assert self.id is not None
return [self.suitename(), self.path, self.variant, self.flags, return [self.suitename(), self.path, self.variant, self.flags,
self.override_shell, list(self.outcomes or []), self.override_shell, list(self.outcomes or []),
self.id] self.id, self.env]
@staticmethod @staticmethod
def UnpackTask(task): def UnpackTask(task):
...@@ -66,6 +68,7 @@ class TestCase(object): ...@@ -66,6 +68,7 @@ class TestCase(object):
test.outcomes = frozenset(task[5]) test.outcomes = frozenset(task[5])
test.id = task[6] test.id = task[6]
test.run = 1 test.run = 1
test.env = task[7]
return test return test
def SetSuiteObject(self, suites): def SetSuiteObject(self, suites):
......
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