Skip to content

Commit 3839eae

Browse files
committed
optimize JSON.stringify(string)
1 parent 2c5d510 commit 3839eae

File tree

2 files changed

+36
-19
lines changed

2 files changed

+36
-19
lines changed

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/builtins/JSONBuiltins.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
import com.oracle.truffle.js.runtime.objects.JSObject;
7878
import com.oracle.truffle.js.runtime.objects.JSObjectUtil;
7979
import com.oracle.truffle.js.runtime.objects.Undefined;
80+
import com.oracle.truffle.js.runtime.util.StringBuilderProfile;
8081

8182
/**
8283
* Contains builtins for {@linkplain JSON} function (constructor).
@@ -271,7 +272,22 @@ private static void addToReplacer(List<String> replacerList, String item) {
271272
}
272273

273274
@SuppressWarnings("unused")
274-
@Specialization(guards = {"!isCallable(replacer)", "!isArray(replacer)"})
275+
@Specialization(guards = {"isString(value)", "!isCallable(replacer)", "!isArray(replacer)"})
276+
// GR-24628: JSON.stringify is frequently called with (just) a String argument
277+
protected Object stringifyAStringNoReplacer(Object value, Object replacer, Object spaceParam,
278+
@Cached("createStringBuilderProfile()") StringBuilderProfile stringBuilderProfile) {
279+
String str = JSRuntime.toStringIsString(value);
280+
StringBuilder builder = new StringBuilder(str.length() + 8);
281+
JSONStringifyStringNode.jsonQuote(stringBuilderProfile, builder, str);
282+
return stringBuilderProfile.toString(builder);
283+
}
284+
285+
protected StringBuilderProfile createStringBuilderProfile() {
286+
return StringBuilderProfile.create(getContext().getStringLengthLimit());
287+
}
288+
289+
@SuppressWarnings("unused")
290+
@Specialization(guards = {"!isString(value)", "!isCallable(replacer)", "!isArray(replacer)"})
275291
protected Object stringifyNoReplacer(Object value, Object replacer, Object spaceParam) {
276292
return stringifyIntl(value, spaceParam, null, null);
277293
}

graal-js/src/com.oracle.truffle.js/src/com/oracle/truffle/js/builtins/helper/JSONStringifyStringNode.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -121,7 +121,7 @@ private void jsonStrExecute(StringBuilder builder, JSONData data, Object value)
121121
} else if (value instanceof Boolean) {
122122
stringBuilderProfile.append(builder, (boolean) value ? JSBoolean.TRUE_NAME : JSBoolean.FALSE_NAME);
123123
} else if (JSRuntime.isString(value)) {
124-
jsonQuote(builder, value.toString());
124+
jsonQuote(stringBuilderProfile, builder, value.toString());
125125
} else if (JSRuntime.isNumber(value)) {
126126
appendNumber(builder, (Number) value);
127127
} else if (JSRuntime.isBigInt(value)) {
@@ -138,7 +138,7 @@ private void jsonStrExecute(StringBuilder builder, JSONData data, Object value)
138138
jsonForeignObject(builder, data, value);
139139
} else if (JSRuntime.isJavaPrimitive(value)) {
140140
// call toString on Java objects, GR-3722
141-
jsonQuote(builder, value.toString());
141+
jsonQuote(stringBuilderProfile, builder, value.toString());
142142
} else {
143143
throw new RuntimeException("JSON.stringify: should never reach here, unknown type: " + value + " " + value.getClass());
144144
}
@@ -292,7 +292,7 @@ private boolean serializeJSONObjectProperties(StringBuilder builder, JSONData da
292292
} else {
293293
appendSeparator(builder, data, indent);
294294
}
295-
jsonQuote(builder, name);
295+
jsonQuote(stringBuilderProfile, builder, name);
296296
appendColon(builder, data);
297297
jsonStrExecute(builder, data, strPPrepared);
298298
hasContent = true;
@@ -335,7 +335,7 @@ private boolean serializeForeignObjectProperties(StringBuilder builder, JSONData
335335
} else {
336336
appendSeparator(builder, data, indent);
337337
}
338-
jsonQuote(builder, stringKey);
338+
jsonQuote(stringBuilderProfile, builder, stringKey);
339339
appendColon(builder, data);
340340
jsonStrExecute(builder, data, strPPrepared);
341341
hasContent = true;
@@ -453,7 +453,8 @@ private static void checkCycle(JSONData data, Object value) {
453453
}
454454
}
455455

456-
private void jsonQuote(StringBuilder builder, String value) {
456+
@TruffleBoundary
457+
public static void jsonQuote(StringBuilderProfile stringBuilderProfile, StringBuilder builder, String value) {
457458
stringBuilderProfile.append(builder, '"');
458459
for (int i = 0; i < value.length();) {
459460
char ch = value.charAt(i);
@@ -469,7 +470,7 @@ private void jsonQuote(StringBuilder builder, String value) {
469470
} else if (ch == '\t') {
470471
stringBuilderProfile.append(builder, "\\t");
471472
} else {
472-
jsonQuoteUnicode(builder, ch);
473+
jsonQuoteUnicode(stringBuilderProfile, builder, ch);
473474
}
474475
} else {
475476
if (ch == '\\') {
@@ -486,11 +487,11 @@ private void jsonQuote(StringBuilder builder, String value) {
486487
i++;
487488
} else {
488489
// unpaired high surrogate
489-
jsonQuoteSurrogate(builder, ch);
490+
jsonQuoteSurrogate(stringBuilderProfile, builder, ch);
490491
}
491492
} else {
492493
// unpaired low surrogate
493-
jsonQuoteSurrogate(builder, ch);
494+
jsonQuoteSurrogate(stringBuilderProfile, builder, ch);
494495
}
495496
} else {
496497
stringBuilderProfile.append(builder, ch);
@@ -501,17 +502,17 @@ private void jsonQuote(StringBuilder builder, String value) {
501502
stringBuilderProfile.append(builder, '"');
502503
}
503504

504-
private void jsonQuoteUnicode(StringBuilder builder, char c) {
505-
stringBuilderProfile.append(builder, "\\u00");
506-
stringBuilderProfile.append(builder, Character.forDigit((c >> 4) & 0xF, 16));
507-
stringBuilderProfile.append(builder, Character.forDigit(c & 0xF, 16));
505+
private static void jsonQuoteUnicode(StringBuilderProfile profile, StringBuilder builder, char c) {
506+
profile.append(builder, "\\u00");
507+
profile.append(builder, Character.forDigit((c >> 4) & 0xF, 16));
508+
profile.append(builder, Character.forDigit(c & 0xF, 16));
508509
}
509510

510-
private void jsonQuoteSurrogate(StringBuilder builder, char c) {
511-
stringBuilderProfile.append(builder, "\\ud");
512-
stringBuilderProfile.append(builder, Character.forDigit((c >> 8) & 0xF, 16));
513-
stringBuilderProfile.append(builder, Character.forDigit((c >> 4) & 0xF, 16));
514-
stringBuilderProfile.append(builder, Character.forDigit(c & 0xF, 16));
511+
private static void jsonQuoteSurrogate(StringBuilderProfile profile, StringBuilder builder, char c) {
512+
profile.append(builder, "\\ud");
513+
profile.append(builder, Character.forDigit((c >> 8) & 0xF, 16));
514+
profile.append(builder, Character.forDigit((c >> 4) & 0xF, 16));
515+
profile.append(builder, Character.forDigit(c & 0xF, 16));
515516
}
516517

517518
private Object truffleGetSize(Object obj) {

0 commit comments

Comments
 (0)