Commit cc71837f authored by bmeurer's avatar bmeurer Committed by Commit bot

[builtins] Migrate String.fromCharCode to C++.

The previous JavaScript version created way too many ConsStrings for
longer strings, i.e. when using String.fromCharCode together with
Function.prototype.apply and arrays of char codes.

This version now always allocates sequential strings and therefore uses
way less memory when turning longer character sequences into strings,
and therefore fixes the memory regression on Google Maps.

BUG=chromium:609831
R=yangguo@chromium.org

Review-Url: https://codereview.chromium.org/2004733002
Cr-Commit-Position: refs/heads/master@{#36427}
parent 438629ba
......@@ -1320,6 +1320,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
string_map->AppendDescriptor(&d);
}
// Install the String.fromCharCode function.
SimpleInstallFunction(string_fun, "fromCharCode",
Builtins::kStringFromCharCode, 1, false);
// Create the %StringPrototype%
Handle<JSValue> prototype =
Handle<JSValue>::cast(factory->NewJSObject(string_fun, TENURED));
......
......@@ -4298,6 +4298,69 @@ BUILTIN(ObjectProtoToString) {
// -----------------------------------------------------------------------------
// ES6 section 21.1 String Objects
namespace {
bool ToUint16(Handle<Object> value, uint16_t* result) {
if (value->IsNumber() || Object::ToNumber(value).ToHandle(&value)) {
*result = DoubleToUint32(value->Number());
return true;
}
return false;
}
} // namespace
// ES6 21.1.2.1 String.fromCharCode ( ...codeUnits )
BUILTIN(StringFromCharCode) {
HandleScope scope(isolate);
// Check resulting string length.
int index = 0;
Handle<String> result;
int const length = args.length() - 1;
if (length == 0) return isolate->heap()->empty_string();
DCHECK_LT(0, length);
// Load the first character code.
uint16_t code;
if (!ToUint16(args.at<Object>(1), &code)) return isolate->heap()->exception();
// Assume that the resulting String contains only one byte characters.
if (code <= String::kMaxOneByteCharCodeU) {
// Check for single one-byte character fast case.
if (length == 1) {
return *isolate->factory()->LookupSingleCharacterStringFromCode(code);
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawOneByteString(length));
do {
Handle<SeqOneByteString>::cast(result)->Set(index, code);
if (++index == length) break;
if (!ToUint16(args.at<Object>(1 + index), &code)) {
return isolate->heap()->exception();
}
} while (code <= String::kMaxOneByteCharCodeU);
}
// Check if all characters fit into the one byte range.
if (index < length) {
// Fallback to two byte string.
Handle<String> new_result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, new_result, isolate->factory()->NewRawTwoByteString(length));
for (int new_index = 0; new_index < index; ++new_index) {
uint16_t new_code =
Handle<SeqOneByteString>::cast(result)->Get(new_index);
Handle<SeqTwoByteString>::cast(new_result)->Set(new_index, new_code);
}
while (true) {
Handle<SeqTwoByteString>::cast(new_result)->Set(index, code);
if (++index == length) break;
if (!ToUint16(args.at<Object>(1 + index), &code)) {
return isolate->heap()->exception();
}
}
result = new_result;
}
return *result;
}
// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
void Builtins::Generate_StringPrototypeCharAt(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
......
......@@ -167,6 +167,8 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
V(ReflectSet, kNone) \
V(ReflectSetPrototypeOf, kNone) \
\
V(StringFromCharCode, kNone) \
\
V(SymbolConstructor, kNone) \
V(SymbolConstructor_ConstructStub, kTarget) \
\
......
......@@ -540,18 +540,6 @@ function StringTrimRight() {
}
// ECMA-262, section 15.5.3.2
function StringFromCharCode(_) { // length == 1
"use strict";
var s = "";
var n = arguments.length;
for (var i = 0; i < n; ++i) {
s += %_StringCharFromCode(arguments[i] & 0xffff);
}
return s;
}
// ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1
function HtmlEscape(str) {
return %_Call(StringReplace, TO_STRING(str), /"/g, "&quot;");
......@@ -835,7 +823,6 @@ function StringRaw(callSite) {
// Set up the non-enumerable functions on the String object.
utils.InstallFunctions(GlobalString, DONT_ENUM, [
"fromCharCode", StringFromCharCode,
"fromCodePoint", StringFromCodePoint,
"raw", StringRaw
]);
......
......@@ -1508,3 +1508,25 @@ TEST(FormatMessage) {
"'arg0' returned for property 'arg1' of object 'arg2' is not a function");
CHECK(String::Equals(result, expected));
}
TEST(Regress609831) {
CcTest::InitializeVM();
LocalContext context;
Isolate* isolate = CcTest::i_isolate();
{
HandleScope scope(isolate);
v8::Local<v8::Value> result = CompileRun(
"String.fromCharCode(32, 32, 32, 32, 32, "
"32, 32, 32, 32, 32, 32, 32, 32, 32, 32, "
"32, 32, 32, 32, 32, 32, 32, 32, 32, 32)");
CHECK(v8::Utils::OpenHandle(*result)->IsSeqOneByteString());
}
{
HandleScope scope(isolate);
v8::Local<v8::Value> result = CompileRun(
"String.fromCharCode(432, 432, 432, 432, 432, "
"432, 432, 432, 432, 432, 432, 432, 432, 432, "
"432, 432, 432, 432, 432, 432, 432, 432, 432)");
CHECK(v8::Utils::OpenHandle(*result)->IsSeqTwoByteString());
}
}
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax
String.fromCharCode(0xFFF, 0xFFF);
String.fromCharCode(0x7C, 0x7C);
%OptimizeFunctionOnNextCall(String.fromCharCode);
String.fromCharCode(0x7C, 0x7C);
String.fromCharCode(0xFFF, 0xFFF);
......@@ -103,11 +103,6 @@ for (var i = 0; i < 10; i++) {
test(i);
}
assertEquals("AAAA", String.fromCharCode(65, 65, 65, 65));
assertEquals("AAAA", String.fromCharCode(65, 65, 65, 65));
%OptimizeFunctionOnNextCall(String.fromCharCode);
assertEquals("AAAA", String.fromCharCode(65, 65, 65, 65));
// Test the custom IC works correctly when the map changes.
for (var i = 0; i < 10; i++) {
var expected = (i < 5) ? " " : 42;
......
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