[inspector] fix timestamp formatting with non C locales

If current locale has "," as decimal separator then message for consoleAPICalled will be corrupted.

BUG=chromium:653424
R=dgozman@chromium.org

Review-Url: https://codereview.chromium.org/2410933002
Cr-Commit-Position: refs/heads/master@{#40190}
This commit is contained in:
kozyatinskiy 2016-10-11 16:21:52 -07:00 committed by Commit bot
parent d4c4618174
commit dde5ef75cb
7 changed files with 214 additions and 23 deletions

View File

@ -8,8 +8,10 @@
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <limits>
#include <locale>
#include <sstream>
#include <string>
#include "src/base/platform/platform.h"
@ -381,26 +383,19 @@ String16 String16::fromInteger(size_t number) {
// static
String16 String16::fromDouble(double number) {
const size_t kBufferSize = 100;
char buffer[kBufferSize];
v8::base::OS::SNPrintF(buffer, kBufferSize, "%f", number);
return String16(buffer);
std::ostringstream s;
s.imbue(std::locale("C"));
s << std::fixed << std::setprecision(std::numeric_limits<double>::digits10)
<< number;
return String16(s.str().c_str());
}
// static
String16 String16::fromDoublePrecision3(double number) {
const size_t kBufferSize = 100;
char buffer[kBufferSize];
v8::base::OS::SNPrintF(buffer, kBufferSize, "%.3g", number);
return String16(buffer);
}
// static
String16 String16::fromDoublePrecision6(double number) {
const size_t kBufferSize = 100;
char buffer[kBufferSize];
v8::base::OS::SNPrintF(buffer, kBufferSize, "%.6g", number);
return String16(buffer);
String16 String16::fromDouble(double number, int precision) {
std::ostringstream s;
s.imbue(std::locale("C"));
s << std::fixed << std::setprecision(precision) << number;
return String16(s.str().c_str());
}
int String16::toInteger(bool* ok) const {

View File

@ -35,8 +35,7 @@ class String16 {
static String16 fromInteger(int);
static String16 fromInteger(size_t);
static String16 fromDouble(double);
static String16 fromDoublePrecision3(double);
static String16 fromDoublePrecision6(double);
static String16 fromDouble(double, int precision);
int toInteger(bool* ok = nullptr) const;
String16 stripWhiteSpace() const;

View File

@ -93,8 +93,8 @@ class V8ValueStringBuilder {
if (value->IsSymbolObject())
return append(v8::Local<v8::SymbolObject>::Cast(value)->ValueOf());
if (value->IsNumberObject()) {
m_builder.append(String16::fromDoublePrecision6(
v8::Local<v8::NumberObject>::Cast(value)->ValueOf()));
m_builder.append(String16::fromDouble(
v8::Local<v8::NumberObject>::Cast(value)->ValueOf(), 6));
return true;
}
if (value->IsBooleanObject()) {

View File

@ -431,7 +431,7 @@ static void timeEndFunction(const v8::FunctionCallbackInfo<v8::Value>& info,
double elapsed = client->currentTimeMS() -
helper.getDoubleFromMap(timeMap, protocolTitle, 0.0);
String16 message =
protocolTitle + ": " + String16::fromDoublePrecision3(elapsed) + "ms";
protocolTitle + ": " + String16::fromDouble(elapsed, 3) + "ms";
helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message);
}
}

View File

@ -6,6 +6,8 @@
#include <unistd.h> // NOLINT
#endif // !defined(_WIN32) && !defined(_WIN64)
#include <locale.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
@ -29,7 +31,9 @@ class UtilsExtension : public v8::Extension {
public:
UtilsExtension()
: v8::Extension("v8_inspector/utils",
"native function print(); native function quit();") {}
"native function print();"
"native function quit();"
"native function setlocale();") {}
virtual v8::Local<v8::FunctionTemplate> GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Local<v8::String> name) {
v8::Local<v8::Context> context = isolate->GetCurrentContext();
@ -44,6 +48,12 @@ class UtilsExtension : public v8::Extension {
.ToLocalChecked())
.FromJust()) {
return v8::FunctionTemplate::New(isolate, UtilsExtension::Quit);
} else if (name->Equals(context,
v8::String::NewFromUtf8(isolate, "setlocale",
v8::NewStringType::kNormal)
.ToLocalChecked())
.FromJust()) {
return v8::FunctionTemplate::New(isolate, UtilsExtension::SetLocale);
}
return v8::Local<v8::FunctionTemplate>();
}
@ -83,6 +93,15 @@ class UtilsExtension : public v8::Extension {
}
static void Quit(const v8::FunctionCallbackInfo<v8::Value>& args) { Exit(); }
static void SetLocale(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1 || !args[0]->IsString()) {
fprintf(stderr, "Internal error: setlocale get one string argument.");
Exit();
}
v8::String::Utf8Value str(args[0]);
setlocale(LC_NUMERIC, *str);
}
};
class SetTimeoutTask : public TaskRunner::Task {

View File

@ -0,0 +1,138 @@
Running test: consoleLogWithDefaultLocale
{
method : Runtime.consoleAPICalled
params : {
args : [
[0] : {
description : 239
type : number
value : 239
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 8
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
]
}
timestamp : <timestamp>
type : log
}
}
Running test: consoleTimeWithCommaAsSeparator
set locale to fr_CA.UTF-8 (has comma as separator)
{
method : Runtime.consoleAPICalled
params : {
args : [
[0] : {
type : string
value : a: x.xms
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 27
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
]
}
timestamp : <timestamp>
type : debug
}
}
Running test: consoleLogWithCommaAsSeparator
set locale to fr_CA.UTF-8 (has comma as separator)
{
method : Runtime.consoleAPICalled
params : {
args : [
[0] : {
description : 239
type : number
value : 239
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 8
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
]
}
timestamp : <timestamp>
type : log
}
}
Running test: consoleTimeWithCommaAfterConsoleLog
set locale to fr_CA.UTF-8 (has comma as separator)
{
method : Runtime.consoleAPICalled
params : {
args : [
[0] : {
description : 239
type : number
value : 239
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 8
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
]
}
timestamp : <timestamp>
type : log
}
}
{
method : Runtime.consoleAPICalled
params : {
args : [
[0] : {
type : string
value : a: x.xms
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 27
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
]
}
timestamp : <timestamp>
type : debug
}
}

View File

@ -0,0 +1,40 @@
// Copyright 2016 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.
Protocol.Runtime.enable();
Protocol.Runtime.onConsoleAPICalled(dumpConsoleApiCalled);
InspectorTest.runTestSuite([
function consoleLogWithDefaultLocale(next) {
Protocol.Runtime.evaluate({ expression: "console.log(239) "}).then(next);
},
function consoleTimeWithCommaAsSeparator(next) {
InspectorTest.log("set locale to fr_CA.UTF-8 (has comma as separator)");
setlocale("fr_CA.UTF-8");
Protocol.Runtime.evaluate({ expression: "console.time(\"a\"); console.timeEnd(\"a\")"}).then(next);
},
function consoleLogWithCommaAsSeparator(next) {
InspectorTest.log("set locale to fr_CA.UTF-8 (has comma as separator)");
setlocale("fr_CA.UTF-8");
Protocol.Runtime.evaluate({ expression: "console.log(239) "}).then(next);
},
function consoleTimeWithCommaAfterConsoleLog(next) {
InspectorTest.log("set locale to fr_CA.UTF-8 (has comma as separator)");
setlocale("fr_CA.UTF-8");
Protocol.Runtime.evaluate({ expression: "console.log(239) "})
.then(() => Protocol.Runtime.evaluate({ expression: "console.time(\"a\"); console.timeEnd(\"a\")"}))
.then(next);
}
]);
function dumpConsoleApiCalled(message) {
var firstArg = message.params.args[0];
if (firstArg.type === "string")
firstArg.value = firstArg.value.replace(/[0-9]+/g, "x");
InspectorTest.logMessage(message);
}