Give JSString a StringView getter and start using it.
authorakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 May 2015 17:06:23 +0000 (17:06 +0000)
committerakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 19 May 2015 17:06:23 +0000 (17:06 +0000)
<https://webkit.org/b/145131>

Reviewed by Anders Carlsson.

When JSString is a substring internally, calling value(ExecState*) on it
will reify the baseString/start/length tuple into a new StringImpl.

For clients that only want to look at the characters of a JSString, but
don't actually need a reffable StringImpl, adding a light-weight StringView
getter lets them avoid constructing anything.

This patch adds JSString::view(ExecState*) and uses it in a few places.
There are many more opportunities to use this API, but let's do a few things
at a time.

* runtime/FunctionConstructor.cpp:
(JSC::constructFunctionSkippingEvalEnabledCheck):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::decode):
(JSC::parseInt):
(JSC::jsToNumber):
(JSC::parseFloat):
(JSC::globalFuncParseInt):
(JSC::globalFuncParseFloat):
(JSC::globalFuncEscape):
(JSC::globalFuncUnescape):
* runtime/JSGlobalObjectFunctions.h:
* runtime/JSONObject.cpp:
(JSC::JSONProtoFuncParse):
* runtime/JSString.cpp:
(JSC::JSString::getPrimitiveNumber):
(JSC::JSString::toNumber):
* runtime/JSString.h:
(JSC::JSRopeString::view):
(JSC::JSString::view):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@184575 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/FunctionConstructor.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
Source/JavaScriptCore/runtime/JSONObject.cpp
Source/JavaScriptCore/runtime/JSString.cpp
Source/JavaScriptCore/runtime/JSString.h

index a64c2a8..052d1ee 100644 (file)
@@ -1,3 +1,42 @@
+2015-05-19  Andreas Kling  <akling@apple.com>
+
+        Give JSString a StringView getter and start using it.
+        <https://webkit.org/b/145131>
+
+        Reviewed by Anders Carlsson.
+
+        When JSString is a substring internally, calling value(ExecState*) on it
+        will reify the baseString/start/length tuple into a new StringImpl.
+
+        For clients that only want to look at the characters of a JSString, but
+        don't actually need a reffable StringImpl, adding a light-weight StringView
+        getter lets them avoid constructing anything.
+
+        This patch adds JSString::view(ExecState*) and uses it in a few places.
+        There are many more opportunities to use this API, but let's do a few things
+        at a time.
+
+        * runtime/FunctionConstructor.cpp:
+        (JSC::constructFunctionSkippingEvalEnabledCheck):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::decode):
+        (JSC::parseInt):
+        (JSC::jsToNumber):
+        (JSC::parseFloat):
+        (JSC::globalFuncParseInt):
+        (JSC::globalFuncParseFloat):
+        (JSC::globalFuncEscape):
+        (JSC::globalFuncUnescape):
+        * runtime/JSGlobalObjectFunctions.h:
+        * runtime/JSONObject.cpp:
+        (JSC::JSONProtoFuncParse):
+        * runtime/JSString.cpp:
+        (JSC::JSString::getPrimitiveNumber):
+        (JSC::JSString::toNumber):
+        * runtime/JSString.h:
+        (JSC::JSRopeString::view):
+        (JSC::JSString::view):
+
 2015-05-18  Filip Pizlo  <fpizlo@apple.com>
 
         Better optimize 'if' with ternaries conditional tests.
index 4448d6f..dbe42fa 100644 (file)
@@ -103,13 +103,13 @@ JSObject* constructFunctionSkippingEvalEnabledCheck(
         builder.appendLiteral("{function ");
         builder.append(functionName.string());
         builder.append('(');
-        builder.append(args.at(0).toString(exec)->value(exec));
+        builder.append(args.at(0).toString(exec)->view(exec));
         for (size_t i = 1; i < args.size() - 1; i++) {
             builder.appendLiteral(", ");
-            builder.append(args.at(i).toString(exec)->value(exec));
+            builder.append(args.at(i).toString(exec)->view(exec));
         }
         builder.appendLiteral(") {\n");
-        builder.append(args.at(args.size() - 1).toString(exec)->value(exec));
+        builder.append(args.at(args.size() - 1).toString(exec)->view(exec));
         builder.appendLiteral("\n}}");
         program = builder.toString();
     }
index 23239a8..d42804d 100644 (file)
@@ -150,7 +150,7 @@ static JSValue decode(ExecState* exec, const CharType* characters, int length, c
 
 static JSValue decode(ExecState* exec, const Bitmap<256>& doNotUnescape, bool strict)
 {
-    String str = exec->argument(0).toString(exec)->value(exec);
+    StringView str = exec->argument(0).toString(exec)->view(exec);
     
     if (str.is8Bit())
         return decode(exec, str.characters8(), str.length(), doNotUnescape, strict);
@@ -248,7 +248,7 @@ static double parseIntOverflow(StringView string, int radix)
 // ES5.1 15.1.2.2
 template <typename CharType>
 ALWAYS_INLINE
-static double parseInt(const String& s, const CharType* data, int radix)
+static double parseInt(StringView s, const CharType* data, int radix)
 {
     // 1. Let inputString be ToString(string).
     // 2. Let S be a newly created substring of inputString consisting of the first character that is not a
@@ -320,16 +320,16 @@ static double parseInt(const String& s, const CharType* data, int radix)
     if (number >= mantissaOverflowLowerBound) {
         if (radix == 10) {
             size_t parsedLength;
-            number = parseDouble(StringView(s).substring(firstDigitPosition, p - firstDigitPosition), parsedLength);
+            number = parseDouble(s.substring(firstDigitPosition, p - firstDigitPosition), parsedLength);
         } else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32)
-            number = parseIntOverflow(StringView(s).substring(firstDigitPosition, p - firstDigitPosition), radix);
+            number = parseIntOverflow(s.substring(firstDigitPosition, p - firstDigitPosition), radix);
     }
 
     // 15. Return sign x number.
     return sign * number;
 }
 
-static double parseInt(const String& s, int radix)
+static double parseInt(StringView s, int radix)
 {
     if (s.is8Bit())
         return parseInt(s, s.characters8(), radix);
@@ -499,7 +499,7 @@ static double toDouble(const CharType* characters, unsigned size)
 }
 
 // See ecma-262 6th 11.8.3
-double jsToNumber(const String& s)
+double jsToNumber(StringView s)
 {
     unsigned size = s.length();
 
@@ -517,7 +517,7 @@ double jsToNumber(const String& s)
     return toDouble(s.characters16(), size);
 }
 
-static double parseFloat(const String& s)
+static double parseFloat(StringView s)
 {
     unsigned size = s.length();
 
@@ -610,7 +610,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec)
     }
 
     // If ToString throws, we shouldn't call ToInt32.
-    String s = value.toString(exec)->value(exec);
+    StringView s = value.toString(exec)->view(exec);
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
 
@@ -619,7 +619,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec)
 {
-    return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)->value(exec))));
+    return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)->view(exec))));
 }
 
 EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec)
@@ -682,7 +682,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec)
     );
 
     JSStringBuilder builder;
-    String str = exec->argument(0).toString(exec)->value(exec);
+    StringView str = exec->argument(0).toString(exec)->view(exec);
     if (str.is8Bit()) {
         const LChar* c = str.characters8();
         for (unsigned k = 0; k < str.length(); k++, c++) {
@@ -720,7 +720,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec)
 EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec)
 {
     StringBuilder builder;
-    String str = exec->argument(0).toString(exec)->value(exec);
+    StringView str = exec->argument(0).toString(exec)->view(exec);
     int k = 0;
     int len = str.length();
     
index 69902df..8d0d2d1 100644 (file)
@@ -60,7 +60,7 @@ EncodedJSValue JSC_HOST_CALL globalPrivateFuncFloor(ExecState*);
 static const double mantissaOverflowLowerBound = 9007199254740992.0;
 double parseIntOverflow(const LChar*, unsigned length, int radix);
 bool isStrWhiteSpace(UChar);
-double jsToNumber(const WTF::String&);
+double jsToNumber(StringView);
 
 } // namespace JSC
 
index 8c69ee9..61dc046 100644 (file)
@@ -719,7 +719,7 @@ EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec)
 {
     if (!exec->argumentCount())
         return throwVMError(exec, createError(exec, ASCIILiteral("JSON.parse requires at least one parameter")));
-    String source = exec->uncheckedArgument(0).toString(exec)->value(exec);
+    StringView source = exec->uncheckedArgument(0).toString(exec)->view(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
 
index 13b90ef..15780a4 100644 (file)
@@ -385,7 +385,7 @@ JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const
 bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
 {
     result = this;
-    number = jsToNumber(value(exec));
+    number = jsToNumber(view(exec));
     return false;
 }
 
@@ -396,7 +396,7 @@ bool JSString::toBoolean() const
 
 double JSString::toNumber(ExecState* exec) const
 {
-    return jsToNumber(value(exec));
+    return jsToNumber(view(exec));
 }
 
 inline StringObject* StringObject::create(VM& vm, JSGlobalObject* globalObject, JSString* string)
index a22e6fb..e48d05a 100644 (file)
@@ -30,6 +30,7 @@
 #include "PropertySlot.h"
 #include "Structure.h"
 #include <array>
+#include <wtf/text/StringView.h>
 
 namespace JSC {
 
@@ -143,6 +144,7 @@ public:
     Identifier toIdentifier(ExecState*) const;
     AtomicString toAtomicString(ExecState*) const;
     AtomicStringImpl* toExistingAtomicString(ExecState*) const;
+    StringView view(ExecState*) const;
     const String& value(ExecState*) const;
     const String& tryGetValue() const;
     const StringImpl* tryGetValueImpl() const;
@@ -369,6 +371,7 @@ private:
     void resolveRopeInternal16(UChar*) const;
     void resolveRopeInternal16NoSubstring(UChar*) const;
     void clearFibers() const;
+    StringView view(ExecState*) const;
 
     JS_EXPORT_PRIVATE JSString* getIndexSlowCase(ExecState*, unsigned);
 
@@ -698,6 +701,24 @@ ALWAYS_INLINE String JSValue::toWTFStringInline(ExecState* exec) const
     return inlineJSValueNotStringtoString(*this, exec);
 }
 
+ALWAYS_INLINE StringView JSRopeString::view(ExecState* exec) const
+{
+    if (isSubstring()) {
+        if (is8Bit())
+            return StringView(substringBase()->m_value.characters8() + substringOffset(), m_length);
+        return StringView(substringBase()->m_value.characters16() + substringOffset(), m_length);
+    }
+    resolveRope(exec);
+    return StringView(m_value);
+}
+
+ALWAYS_INLINE StringView JSString::view(ExecState* exec) const
+{
+    if (isRope())
+        return static_cast<const JSRopeString*>(this)->view(exec);
+    return StringView(m_value);
+}
+
 } // namespace JSC
 
 #endif // JSString_h