Changeset 282468 in webkit
- Timestamp:
- Sep 15, 2021 1:17:05 PM (10 months ago)
- Location:
- trunk
- Files:
-
- 2 added
- 6 edited
-
JSTests/ChangeLog (modified) (1 diff)
-
JSTests/microbenchmarks/json-parse-leaf-object.js (added)
-
JSTests/stress/json-parse-syntax.js (added)
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/js/dom/JSON-parse-expected.txt (modified) (1 diff)
-
Source/JavaScriptCore/ChangeLog (modified) (1 diff)
-
Source/JavaScriptCore/runtime/LiteralParser.cpp (modified) (10 diffs)
-
Source/JavaScriptCore/runtime/LiteralParser.h (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r282373 r282468 1 2021-09-15 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] Optimize leaf object creation in JSON.parse 4 https://bugs.webkit.org/show_bug.cgi?id=230298 5 6 Reviewed by Keith Miller. 7 8 * microbenchmarks/json-parse-leaf-object.js: Added. 9 1 10 2021-09-13 Yusuke Suzuki <ysuzuki@apple.com> 2 11 -
trunk/LayoutTests/ChangeLog
r282467 r282468 1 2021-09-15 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] Optimize leaf object creation in JSON.parse 4 https://bugs.webkit.org/show_bug.cgi?id=230298 5 6 Reviewed by Keith Miller. 7 8 * js/dom/JSON-parse-expected.txt: 9 1 10 2021-09-15 Chris Dumez <cdumez@apple.com> 2 11 -
trunk/LayoutTests/js/dom/JSON-parse-expected.txt
r246162 r282468 86 86 return jsonObject.parse('{"a":5,"a",}'); 87 87 } 88 PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Expected ':' .88 PASS tests[i](nativeJSON) threw exception SyntaxError: JSON Parse error: Expected ':' before value in object property definition. 89 89 function (jsonObject){ 90 90 return jsonObject.parse('{"a":(5,"a"),}'); -
trunk/Source/JavaScriptCore/ChangeLog
r282430 r282468 1 2021-09-15 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] Optimize leaf object creation in JSON.parse 4 https://bugs.webkit.org/show_bug.cgi?id=230298 5 6 Reviewed by Keith Miller. 7 8 This patch optimizes JSON.parse. 9 10 1. Use table in isJSONWhiteSpace. 11 2. Extract primitive value creation as parsePrimitiveValue function to use it in different place. 12 3. Add leaf-object creation fast path. Previously, when creating a leaf-object from JSON.parse we 13 are too generic and jumping around the code. Instead we add a fast path that does not perform 14 unnecessary operations and code gets tight. 15 16 It offers 3-4% improvement in microbenchmarks. 17 ToT Patched 18 19 vanilla-es2015-babel-webpack-todomvc-json-parse 20 104.7169+-0.1113 ^ 101.4836+-0.2168 ^ definitely 1.0319x faster 21 flight-todomvc-json-parse 53.9074+-0.0957 ^ 52.1347+-0.0802 ^ definitely 1.0340x faster 22 vanilla-es2015-todomvc-json-parse 104.9373+-0.1631 ^ 101.4978+-0.1073 ^ definitely 1.0339x faster 23 vanilla-todomvc-json-parse 79.1330+-0.0963 ^ 76.7568+-0.1606 ^ definitely 1.0310x faster 24 25 This offers 0.2% improvement in Speedometer2. 26 27 ---------------------------------------------------------------------------------------------------------------------------------- 28 | subtest | ms | ms | b / a | pValue (significance using False Discovery Rate) | 29 ---------------------------------------------------------------------------------------------------------------------------------- 30 | Elm-TodoMVC |116.860000 |116.825000 |0.999700 | 0.901070 | 31 | VueJS-TodoMVC |24.658333 |24.763333 |1.004258 | 0.571728 | 32 | EmberJS-TodoMVC |126.666667 |126.335000 |0.997382 | 0.289517 | 33 | BackboneJS-TodoMVC |48.435000 |48.523333 |1.001824 | 0.455638 | 34 | Preact-TodoMVC |17.585000 |17.368333 |0.987679 | 0.247658 | 35 | AngularJS-TodoMVC |129.576667 |129.398333 |0.998624 | 0.625634 | 36 | Vanilla-ES2015-TodoMVC |62.746667 |62.241667 |0.991952 | 0.000019 (significant) | 37 | Inferno-TodoMVC |63.741667 |63.495000 |0.996130 | 0.448861 | 38 | Flight-TodoMVC |78.021667 |77.306667 |0.990836 | 0.087137 | 39 | Angular2-TypeScript-TodoMVC |39.823333 |39.923333 |1.002511 | 0.736279 | 40 | VanillaJS-TodoMVC |50.073333 |49.791667 |0.994375 | 0.136495 | 41 | jQuery-TodoMVC |221.300000 |221.586667 |1.001295 | 0.418008 | 42 | EmberJS-Debug-TodoMVC |340.145000 |339.965000 |0.999471 | 0.691490 | 43 | React-TodoMVC |85.698333 |85.650000 |0.999436 | 0.761586 | 44 | React-Redux-TodoMVC |140.510000 |140.785000 |1.001957 | 0.285922 | 45 | Vanilla-ES2015-Babel-Webpack-TodoMVC |60.928333 |60.500000 |0.992970 | 0.000069 (significant) | 46 ---------------------------------------------------------------------------------------------------------------------------------- 47 a mean = 262.15844 48 b mean = 262.72261 49 pValue = 0.0428052487 50 (Bigger means are better.) 51 1.002 times better 52 Results ARE significant 53 54 * runtime/LiteralParser.cpp: 55 (JSC::LiteralParser<CharType>::makeIdentifier): 56 (JSC::isJSONWhiteSpace): 57 (JSC::LiteralParser<CharType>::Lexer::lex): 58 (JSC::LiteralParser<CharType>::parsePrimitiveValue): 59 (JSC::LiteralParser<CharType>::parse): 60 * runtime/LiteralParser.h: 61 1 62 2021-09-14 Don Olmstead <don.olmstead@sony.com> 2 63 -
trunk/Source/JavaScriptCore/runtime/LiteralParser.cpp
r279393 r282468 60 60 61 61 template <typename CharType> 62 static ALWAYS_INLINE bool isJSONWhiteSpace(const CharType& c)63 {64 // The JSON RFC 4627 defines a list of allowed characters to be considered65 // insignificant white space: http://www.ietf.org/rfc/rfc4627.txt (2. JSON Grammar).66 return c == ' ' || c == 0x9 || c == 0xA || c == 0xD;67 }68 69 template <typename CharType>70 62 bool LiteralParser<CharType>::tryJSONPParse(Vector<JSONPData>& results, bool needsFullSourceInfo) 71 63 { … … 150 142 return m_lexer.currentToken()->type == TokEnd; 151 143 } 144 145 template <typename CharType> 146 ALWAYS_INLINE Identifier LiteralParser<CharType>::makeIdentifier(typename Lexer::LiteralParserTokenPtr token) 147 { 148 if (token->stringIs8Bit) 149 return makeIdentifier(token->stringToken8, token->stringLength); 150 return makeIdentifier(token->stringToken16, token->stringLength); 151 } 152 152 153 153 154 template <typename CharType> … … 201 202 202 203 // 256 Latin-1 codes 204 // The JSON RFC 4627 defines a list of allowed characters to be considered 205 // insignificant white space: http://www.ietf.org/rfc/rfc4627.txt (2. JSON Grammar). 203 206 static constexpr const TokenType tokenTypesOfLatin1Characters[256] = { 204 207 /* 0 - Null */ TokError, … … 211 214 /* 7 - Bell */ TokError, 212 215 /* 8 - Back Space */ TokError, 213 /* 9 - Horizontal Tab */ TokError ,214 /* 10 - Line Feed */ TokError ,216 /* 9 - Horizontal Tab */ TokErrorSpace, 217 /* 10 - Line Feed */ TokErrorSpace, 215 218 /* 11 - Vertical Tab */ TokError, 216 219 /* 12 - Form Feed */ TokError, 217 /* 13 - Carriage Return */ TokError ,220 /* 13 - Carriage Return */ TokErrorSpace, 218 221 /* 14 - Shift Out */ TokError, 219 222 /* 15 - Shift In */ TokError, … … 234 237 /* 30 - Record Separator */ TokError, 235 238 /* 31 - Unit Separator */ TokError, 236 /* 32 - Space */ TokError ,239 /* 32 - Space */ TokErrorSpace, 237 240 /* 33 - ! */ TokError, 238 241 /* 34 - " */ TokString, … … 721 724 722 725 template <typename CharType> 726 static ALWAYS_INLINE bool isJSONWhiteSpace(const CharType& c) 727 { 728 return isLatin1(c) && tokenTypesOfLatin1Characters[c] == TokErrorSpace; 729 } 730 731 template <typename CharType> 723 732 ALWAYS_INLINE TokenType LiteralParser<CharType>::Lexer::lex(LiteralParserToken<CharType>& token) 724 733 { … … 784 793 785 794 case TokError: 795 case TokErrorSpace: 786 796 break; 787 797 … … 1112 1122 1113 1123 template <typename CharType> 1124 void LiteralParser<CharType>::setErrorMessageForToken(TokenType tokenType) 1125 { 1126 switch (tokenType) { 1127 case TokRBrace: 1128 m_parseErrorMessage = "Expected '}'"_s; 1129 break; 1130 case TokRBracket: 1131 m_parseErrorMessage = "Expected ']'"_s; 1132 break; 1133 case TokColon: 1134 m_parseErrorMessage = "Expected ':' before value in object property definition"_s; 1135 break; 1136 default: { 1137 RELEASE_ASSERT_NOT_REACHED(); 1138 } 1139 } 1140 } 1141 1142 template <typename CharType> 1143 ALWAYS_INLINE JSValue LiteralParser<CharType>::parsePrimitiveValue(VM& vm) 1144 { 1145 switch (m_lexer.currentToken()->type) { 1146 case TokString: { 1147 JSValue result = jsString(vm, makeIdentifier(m_lexer.currentToken()).string()); 1148 m_lexer.next(); 1149 return result; 1150 } 1151 case TokNumber: { 1152 JSValue result = jsNumber(m_lexer.currentToken()->numberToken); 1153 m_lexer.next(); 1154 return result; 1155 } 1156 case TokNull: 1157 m_lexer.next(); 1158 return jsNull(); 1159 case TokTrue: 1160 m_lexer.next(); 1161 return jsBoolean(true); 1162 case TokFalse: 1163 m_lexer.next(); 1164 return jsBoolean(false); 1165 case TokRBracket: 1166 m_parseErrorMessage = "Unexpected token ']'"_s; 1167 return { }; 1168 case TokRBrace: 1169 m_parseErrorMessage = "Unexpected token '}'"_s; 1170 return { }; 1171 case TokIdentifier: { 1172 auto token = m_lexer.currentToken(); 1173 1174 auto tryMakeErrorString = [&] (unsigned length) -> String { 1175 bool addEllipsis = length != token->stringLength; 1176 if (token->stringIs8Bit) 1177 return tryMakeString("Unexpected identifier \"", StringView { token->stringToken8, length }, addEllipsis ? "..." : "", '"'); 1178 return tryMakeString("Unexpected identifier \"", StringView { token->stringToken16, length }, addEllipsis ? "..." : "", '"'); 1179 }; 1180 1181 constexpr unsigned maxLength = 200; 1182 1183 String errorString = tryMakeErrorString(std::min(token->stringLength, maxLength)); 1184 if (!errorString) { 1185 constexpr unsigned shortLength = 10; 1186 if (token->stringLength > shortLength) 1187 errorString = tryMakeErrorString(shortLength); 1188 if (!errorString) 1189 errorString = "Unexpected identifier"; 1190 } 1191 1192 m_parseErrorMessage = errorString; 1193 return { }; 1194 } 1195 case TokColon: 1196 m_parseErrorMessage = "Unexpected token ':'"_s; 1197 return { }; 1198 case TokLParen: 1199 m_parseErrorMessage = "Unexpected token '('"_s; 1200 return { }; 1201 case TokRParen: 1202 m_parseErrorMessage = "Unexpected token ')'"_s; 1203 return { }; 1204 case TokComma: 1205 m_parseErrorMessage = "Unexpected token ','"_s; 1206 return { }; 1207 case TokDot: 1208 m_parseErrorMessage = "Unexpected token '.'"_s; 1209 return { }; 1210 case TokAssign: 1211 m_parseErrorMessage = "Unexpected token '='"_s; 1212 return { }; 1213 case TokSemi: 1214 m_parseErrorMessage = "Unexpected token ';'"_s; 1215 return { }; 1216 case TokEnd: 1217 m_parseErrorMessage = "Unexpected EOF"_s; 1218 return { }; 1219 case TokError: 1220 default: 1221 // Error 1222 m_parseErrorMessage = "Could not parse value expression"_s; 1223 return { }; 1224 } 1225 } 1226 1227 template <typename CharType> 1114 1228 JSValue LiteralParser<CharType>::parse(ParserState initialState) 1115 1229 { … … 1124 1238 while (1) { 1125 1239 switch(state) { 1126 startParseArray: 1127 case StartParseArray: { 1128 JSArray* array = constructEmptyArray(m_globalObject, nullptr); 1129 RETURN_IF_EXCEPTION(scope, JSValue()); 1130 objectStack.appendWithCrashOnOverflow(array); 1131 } 1132 doParseArrayStartExpression: 1133 FALLTHROUGH; 1134 case DoParseArrayStartExpression: { 1135 TokenType lastToken = m_lexer.currentToken()->type; 1136 if (m_lexer.next() == TokRBracket) { 1137 if (lastToken == TokComma) { 1138 m_parseErrorMessage = "Unexpected comma at the end of array expression"_s; 1139 return JSValue(); 1140 } 1141 m_lexer.next(); 1142 lastValue = objectStack.takeLast(); 1143 break; 1144 } 1145 1146 stateStack.append(DoParseArrayEndExpression); 1147 goto startParseExpression; 1148 } 1149 case DoParseArrayEndExpression: { 1150 JSArray* array = asArray(objectStack.last()); 1151 array->putDirectIndex(m_globalObject, array->length(), lastValue); 1152 RETURN_IF_EXCEPTION(scope, JSValue()); 1153 1154 if (m_lexer.currentToken()->type == TokComma) 1155 goto doParseArrayStartExpression; 1156 1157 if (m_lexer.currentToken()->type != TokRBracket) { 1158 m_parseErrorMessage = "Expected ']'"_s; 1159 return JSValue(); 1160 } 1161 1162 m_lexer.next(); 1163 lastValue = objectStack.takeLast(); 1164 break; 1165 } 1166 startParseObject: 1167 case StartParseObject: { 1168 JSObject* object = constructEmptyObject(m_globalObject); 1169 objectStack.appendWithCrashOnOverflow(object); 1170 1171 TokenType type = m_lexer.next(); 1172 if (type == TokString || (m_mode != StrictJSON && type == TokIdentifier)) { 1173 typename Lexer::LiteralParserTokenPtr identifierToken = m_lexer.currentToken(); 1174 if (identifierToken->stringIs8Bit) 1175 identifierStack.append(makeIdentifier(identifierToken->stringToken8, identifierToken->stringLength)); 1176 else 1177 identifierStack.append(makeIdentifier(identifierToken->stringToken16, identifierToken->stringLength)); 1178 1179 // Check for colon 1180 if (m_lexer.next() != TokColon) { 1181 m_parseErrorMessage = "Expected ':' before value in object property definition"_s; 1182 return JSValue(); 1183 } 1184 1185 m_lexer.next(); 1186 stateStack.append(DoParseObjectEndExpression); 1187 goto startParseExpression; 1188 } 1189 if (type != TokRBrace) { 1190 m_parseErrorMessage = "Expected '}'"_s; 1191 return JSValue(); 1240 startParseArray: 1241 case StartParseArray: { 1242 JSArray* array = constructEmptyArray(m_globalObject, nullptr); 1243 RETURN_IF_EXCEPTION(scope, { }); 1244 objectStack.appendWithCrashOnOverflow(array); 1245 } 1246 doParseArrayStartExpression: 1247 FALLTHROUGH; 1248 case DoParseArrayStartExpression: { 1249 TokenType lastToken = m_lexer.currentToken()->type; 1250 if (m_lexer.next() == TokRBracket) { 1251 if (UNLIKELY(lastToken == TokComma)) { 1252 m_parseErrorMessage = "Unexpected comma at the end of array expression"_s; 1253 return { }; 1192 1254 } 1193 1255 m_lexer.next(); … … 1195 1257 break; 1196 1258 } 1197 doParseObjectStartExpression: 1198 case DoParseObjectStartExpression: { 1199 TokenType type = m_lexer.next(); 1200 if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier)) { 1201 m_parseErrorMessage = "Property name must be a string literal"_s; 1202 return JSValue(); 1259 1260 stateStack.append(DoParseArrayEndExpression); 1261 goto startParseExpression; 1262 } 1263 case DoParseArrayEndExpression: { 1264 JSArray* array = asArray(objectStack.last()); 1265 array->putDirectIndex(m_globalObject, array->length(), lastValue); 1266 RETURN_IF_EXCEPTION(scope, { }); 1267 1268 if (m_lexer.currentToken()->type == TokComma) 1269 goto doParseArrayStartExpression; 1270 1271 if (UNLIKELY(m_lexer.currentToken()->type != TokRBracket)) { 1272 setErrorMessageForToken(TokRBracket); 1273 return { }; 1274 } 1275 1276 m_lexer.next(); 1277 lastValue = objectStack.takeLast(); 1278 break; 1279 } 1280 startParseObject: 1281 case StartParseObject: { 1282 JSObject* object = constructEmptyObject(m_globalObject); 1283 1284 TokenType type = m_lexer.next(); 1285 if (type == TokString || (m_mode != StrictJSON && type == TokIdentifier)) { 1286 while (true) { 1287 Identifier ident = makeIdentifier(m_lexer.currentToken()); 1288 1289 if (UNLIKELY(m_lexer.next() != TokColon)) { 1290 setErrorMessageForToken(TokColon); 1291 return { }; 1292 } 1293 1294 TokenType nextType = m_lexer.next(); 1295 if (nextType == TokLBrace || nextType == TokLBracket) { 1296 objectStack.appendWithCrashOnOverflow(object); 1297 identifierStack.append(WTFMove(ident)); 1298 stateStack.append(DoParseObjectEndExpression); 1299 if (nextType == TokLBrace) 1300 goto startParseObject; 1301 ASSERT(nextType == TokLBracket); 1302 goto startParseArray; 1303 } 1304 1305 // Leaf object construction fast path. 1306 JSValue primitive = parsePrimitiveValue(vm); 1307 if (UNLIKELY(!primitive)) 1308 return { }; 1309 1310 if (m_mode != StrictJSON && ident == vm.propertyNames->underscoreProto) { 1311 if (UNLIKELY(!visitedUnderscoreProto.add(object).isNewEntry)) { 1312 m_parseErrorMessage = "Attempted to redefine __proto__ property"_s; 1313 return { }; 1314 } 1315 PutPropertySlot slot(object, m_nullOrCodeBlock ? m_nullOrCodeBlock->ownerExecutable()->isInStrictContext() : false); 1316 JSValue(object).put(m_globalObject, ident, primitive, slot); 1317 RETURN_IF_EXCEPTION(scope, { }); 1318 } else { 1319 if (std::optional<uint32_t> index = parseIndex(ident)) { 1320 object->putDirectIndex(m_globalObject, index.value(), primitive); 1321 RETURN_IF_EXCEPTION(scope, { }); 1322 } else 1323 object->putDirect(vm, ident, primitive); 1324 } 1325 1326 if (m_lexer.currentToken()->type != TokComma) 1327 break; 1328 1329 nextType = m_lexer.next(); 1330 if (UNLIKELY(nextType != TokString && (m_mode == StrictJSON || nextType != TokIdentifier))) { 1331 m_parseErrorMessage = "Property name must be a string literal"_s; 1332 return { }; 1333 } 1203 1334 } 1204 typename Lexer::LiteralParserTokenPtr identifierToken = m_lexer.currentToken(); 1205 if (identifierToken->stringIs8Bit) 1206 identifierStack.append(makeIdentifier(identifierToken->stringToken8, identifierToken->stringLength)); 1207 else 1208 identifierStack.append(makeIdentifier(identifierToken->stringToken16, identifierToken->stringLength)); 1209 1210 // Check for colon 1211 if (m_lexer.next() != TokColon) { 1212 m_parseErrorMessage = "Expected ':'"_s; 1213 return JSValue(); 1214 } 1215 1216 m_lexer.next(); 1217 stateStack.append(DoParseObjectEndExpression); 1218 goto startParseExpression; 1219 } 1220 case DoParseObjectEndExpression: 1221 { 1222 JSObject* object = asObject(objectStack.last()); 1223 Identifier ident = identifierStack.takeLast(); 1224 if (m_mode != StrictJSON && ident == vm.propertyNames->underscoreProto) { 1225 if (!visitedUnderscoreProto.add(object).isNewEntry) { 1226 m_parseErrorMessage = "Attempted to redefine __proto__ property"_s; 1227 return JSValue(); 1228 } 1229 PutPropertySlot slot(object, m_nullOrCodeBlock ? m_nullOrCodeBlock->ownerExecutable()->isInStrictContext() : false); 1230 objectStack.last().put(m_globalObject, ident, lastValue, slot); 1231 } else { 1232 if (std::optional<uint32_t> index = parseIndex(ident)) 1233 object->putDirectIndex(m_globalObject, index.value(), lastValue); 1234 else 1235 object->putDirect(vm, ident, lastValue); 1236 } 1237 RETURN_IF_EXCEPTION(scope, JSValue()); 1238 if (m_lexer.currentToken()->type == TokComma) 1239 goto doParseObjectStartExpression; 1240 if (m_lexer.currentToken()->type != TokRBrace) { 1241 m_parseErrorMessage = "Expected '}'"_s; 1242 return JSValue(); 1335 1336 if (UNLIKELY(m_lexer.currentToken()->type != TokRBrace)) { 1337 setErrorMessageForToken(TokRBrace); 1338 return { }; 1243 1339 } 1244 1340 m_lexer.next(); 1245 lastValue = object Stack.takeLast();1341 lastValue = object; 1246 1342 break; 1247 1343 } 1248 startParseExpression: 1249 case StartParseExpression: { 1250 switch (m_lexer.currentToken()->type) { 1251 case TokLBracket: 1252 goto startParseArray; 1253 case TokLBrace: 1254 goto startParseObject; 1255 case TokString: { 1256 typename Lexer::LiteralParserTokenPtr stringToken = m_lexer.currentToken(); 1257 if (stringToken->stringIs8Bit) 1258 lastValue = jsString(vm, makeIdentifier(stringToken->stringToken8, stringToken->stringLength).string()); 1259 else 1260 lastValue = jsString(vm, makeIdentifier(stringToken->stringToken16, stringToken->stringLength).string()); 1261 m_lexer.next(); 1262 break; 1263 } 1264 case TokNumber: { 1265 typename Lexer::LiteralParserTokenPtr numberToken = m_lexer.currentToken(); 1266 lastValue = jsNumber(numberToken->numberToken); 1267 m_lexer.next(); 1268 break; 1269 } 1270 case TokNull: 1271 m_lexer.next(); 1272 lastValue = jsNull(); 1273 break; 1274 1275 case TokTrue: 1276 m_lexer.next(); 1277 lastValue = jsBoolean(true); 1278 break; 1279 1280 case TokFalse: 1281 m_lexer.next(); 1282 lastValue = jsBoolean(false); 1283 break; 1284 case TokRBracket: 1285 m_parseErrorMessage = "Unexpected token ']'"_s; 1286 return JSValue(); 1287 case TokRBrace: 1288 m_parseErrorMessage = "Unexpected token '}'"_s; 1289 return JSValue(); 1290 case TokIdentifier: { 1291 auto token = m_lexer.currentToken(); 1292 1293 auto tryMakeErrorString = [&] (unsigned length) -> String { 1294 bool addEllipsis = length != token->stringLength; 1295 if (token->stringIs8Bit) 1296 return tryMakeString("Unexpected identifier \"", StringView { token->stringToken8, length }, addEllipsis ? "..." : "", '"'); 1297 return tryMakeString("Unexpected identifier \"", StringView { token->stringToken16, length }, addEllipsis ? "..." : "", '"'); 1298 }; 1299 1300 constexpr unsigned maxLength = 200; 1301 1302 String errorString = tryMakeErrorString(std::min(token->stringLength, maxLength)); 1303 if (!errorString) { 1304 constexpr unsigned shortLength = 10; 1305 if (token->stringLength > shortLength) 1306 errorString = tryMakeErrorString(shortLength); 1307 if (!errorString) 1308 errorString = "Unexpected identifier"; 1309 } 1310 1311 m_parseErrorMessage = errorString; 1312 return JSValue(); 1313 } 1314 case TokColon: 1315 m_parseErrorMessage = "Unexpected token ':'"_s; 1316 return JSValue(); 1317 case TokLParen: 1318 m_parseErrorMessage = "Unexpected token '('"_s; 1319 return JSValue(); 1320 case TokRParen: 1321 m_parseErrorMessage = "Unexpected token ')'"_s; 1322 return JSValue(); 1323 case TokComma: 1324 m_parseErrorMessage = "Unexpected token ','"_s; 1325 return JSValue(); 1326 case TokDot: 1327 m_parseErrorMessage = "Unexpected token '.'"_s; 1328 return JSValue(); 1329 case TokAssign: 1330 m_parseErrorMessage = "Unexpected token '='"_s; 1331 return JSValue(); 1332 case TokSemi: 1333 m_parseErrorMessage = "Unexpected token ';'"_s; 1334 return JSValue(); 1335 case TokEnd: 1336 m_parseErrorMessage = "Unexpected EOF"_s; 1337 return JSValue(); 1338 case TokError: 1339 default: 1340 // Error 1341 m_parseErrorMessage = "Could not parse value expression"_s; 1342 return JSValue(); 1344 1345 if (UNLIKELY(type != TokRBrace)) { 1346 setErrorMessageForToken(TokRBrace); 1347 return { }; 1348 } 1349 1350 m_lexer.next(); 1351 lastValue = object; 1352 break; 1353 } 1354 doParseObjectStartExpression: 1355 case DoParseObjectStartExpression: { 1356 TokenType type = m_lexer.next(); 1357 if (UNLIKELY(type != TokString && (m_mode == StrictJSON || type != TokIdentifier))) { 1358 m_parseErrorMessage = "Property name must be a string literal"_s; 1359 return { }; 1360 } 1361 identifierStack.append(makeIdentifier(m_lexer.currentToken())); 1362 1363 // Check for colon 1364 if (UNLIKELY(m_lexer.next() != TokColon)) { 1365 setErrorMessageForToken(TokColon); 1366 return { }; 1367 } 1368 1369 m_lexer.next(); 1370 stateStack.append(DoParseObjectEndExpression); 1371 goto startParseExpression; 1372 } 1373 case DoParseObjectEndExpression: 1374 { 1375 JSObject* object = asObject(objectStack.last()); 1376 Identifier ident = identifierStack.takeLast(); 1377 if (m_mode != StrictJSON && ident == vm.propertyNames->underscoreProto) { 1378 if (UNLIKELY(!visitedUnderscoreProto.add(object).isNewEntry)) { 1379 m_parseErrorMessage = "Attempted to redefine __proto__ property"_s; 1380 return { }; 1343 1381 } 1382 PutPropertySlot slot(object, m_nullOrCodeBlock ? m_nullOrCodeBlock->ownerExecutable()->isInStrictContext() : false); 1383 JSValue(object).put(m_globalObject, ident, lastValue, slot); 1384 RETURN_IF_EXCEPTION(scope, { }); 1385 } else { 1386 if (std::optional<uint32_t> index = parseIndex(ident)) { 1387 object->putDirectIndex(m_globalObject, index.value(), lastValue); 1388 RETURN_IF_EXCEPTION(scope, { }); 1389 } else 1390 object->putDirect(vm, ident, lastValue); 1391 } 1392 if (m_lexer.currentToken()->type == TokComma) 1393 goto doParseObjectStartExpression; 1394 if (UNLIKELY(m_lexer.currentToken()->type != TokRBrace)) { 1395 setErrorMessageForToken(TokRBrace); 1396 return { }; 1397 } 1398 m_lexer.next(); 1399 lastValue = objectStack.takeLast(); 1400 break; 1401 } 1402 startParseExpression: 1403 case StartParseExpression: { 1404 TokenType type = m_lexer.currentToken()->type; 1405 if (type == TokLBracket) 1406 goto startParseArray; 1407 if (type == TokLBrace) 1408 goto startParseObject; 1409 lastValue = parsePrimitiveValue(vm); 1410 if (UNLIKELY(!lastValue)) 1411 return { }; 1412 break; 1413 } 1414 case StartParseStatement: { 1415 switch (m_lexer.currentToken()->type) { 1416 case TokLBracket: 1417 case TokNumber: 1418 case TokString: { 1419 lastValue = parsePrimitiveValue(vm); 1420 if (UNLIKELY(!lastValue)) 1421 return { }; 1344 1422 break; 1345 1423 } 1346 case StartParseStatement: { 1347 switch (m_lexer.currentToken()->type) { 1348 case TokLBracket: 1349 case TokNumber: 1350 case TokString: 1351 goto startParseExpression; 1352 1353 case TokLParen: { 1354 m_lexer.next(); 1355 stateStack.append(StartParseStatementEndStatement); 1356 goto startParseExpression; 1357 } 1358 case TokRBracket: 1359 m_parseErrorMessage = "Unexpected token ']'"_s; 1360 return JSValue(); 1361 case TokLBrace: 1362 m_parseErrorMessage = "Unexpected token '{'"_s; 1363 return JSValue(); 1364 case TokRBrace: 1365 m_parseErrorMessage = "Unexpected token '}'"_s; 1366 return JSValue(); 1367 case TokIdentifier: 1368 m_parseErrorMessage = "Unexpected identifier"_s; 1369 return JSValue(); 1370 case TokColon: 1371 m_parseErrorMessage = "Unexpected token ':'"_s; 1372 return JSValue(); 1373 case TokRParen: 1374 m_parseErrorMessage = "Unexpected token ')'"_s; 1375 return JSValue(); 1376 case TokComma: 1377 m_parseErrorMessage = "Unexpected token ','"_s; 1378 return JSValue(); 1379 case TokTrue: 1380 m_parseErrorMessage = "Unexpected token 'true'"_s; 1381 return JSValue(); 1382 case TokFalse: 1383 m_parseErrorMessage = "Unexpected token 'false'"_s; 1384 return JSValue(); 1385 case TokNull: 1386 m_parseErrorMessage = "Unexpected token 'null'"_s; 1387 return JSValue(); 1388 case TokEnd: 1389 m_parseErrorMessage = "Unexpected EOF"_s; 1390 return JSValue(); 1391 case TokDot: 1392 m_parseErrorMessage = "Unexpected token '.'"_s; 1393 return JSValue(); 1394 case TokAssign: 1395 m_parseErrorMessage = "Unexpected token '='"_s; 1396 return JSValue(); 1397 case TokSemi: 1398 m_parseErrorMessage = "Unexpected token ';'"_s; 1399 return JSValue(); 1400 case TokError: 1401 default: 1402 m_parseErrorMessage = "Could not parse statement"_s; 1403 return JSValue(); 1404 } 1405 } 1406 case StartParseStatementEndStatement: { 1407 ASSERT(stateStack.isEmpty()); 1408 if (m_lexer.currentToken()->type != TokRParen) 1409 return JSValue(); 1410 if (m_lexer.next() == TokEnd) 1411 return lastValue; 1412 m_parseErrorMessage = "Unexpected content at end of JSON literal"_s; 1413 return JSValue(); 1414 } 1424 1425 case TokLParen: { 1426 m_lexer.next(); 1427 stateStack.append(StartParseStatementEndStatement); 1428 goto startParseExpression; 1429 } 1430 case TokRBracket: 1431 m_parseErrorMessage = "Unexpected token ']'"_s; 1432 return { }; 1433 case TokLBrace: 1434 m_parseErrorMessage = "Unexpected token '{'"_s; 1435 return { }; 1436 case TokRBrace: 1437 m_parseErrorMessage = "Unexpected token '}'"_s; 1438 return { }; 1439 case TokIdentifier: 1440 m_parseErrorMessage = "Unexpected identifier"_s; 1441 return { }; 1442 case TokColon: 1443 m_parseErrorMessage = "Unexpected token ':'"_s; 1444 return { }; 1445 case TokRParen: 1446 m_parseErrorMessage = "Unexpected token ')'"_s; 1447 return { }; 1448 case TokComma: 1449 m_parseErrorMessage = "Unexpected token ','"_s; 1450 return { }; 1451 case TokTrue: 1452 m_parseErrorMessage = "Unexpected token 'true'"_s; 1453 return { }; 1454 case TokFalse: 1455 m_parseErrorMessage = "Unexpected token 'false'"_s; 1456 return { }; 1457 case TokNull: 1458 m_parseErrorMessage = "Unexpected token 'null'"_s; 1459 return { }; 1460 case TokEnd: 1461 m_parseErrorMessage = "Unexpected EOF"_s; 1462 return { }; 1463 case TokDot: 1464 m_parseErrorMessage = "Unexpected token '.'"_s; 1465 return { }; 1466 case TokAssign: 1467 m_parseErrorMessage = "Unexpected token '='"_s; 1468 return { }; 1469 case TokSemi: 1470 m_parseErrorMessage = "Unexpected token ';'"_s; 1471 return { }; 1472 case TokError: 1415 1473 default: 1416 RELEASE_ASSERT_NOT_REACHED(); 1474 m_parseErrorMessage = "Could not parse statement"_s; 1475 return { }; 1476 } 1477 break; 1478 } 1479 case StartParseStatementEndStatement: { 1480 ASSERT(stateStack.isEmpty()); 1481 if (m_lexer.currentToken()->type != TokRParen) 1482 return { }; 1483 if (m_lexer.next() == TokEnd) 1484 return lastValue; 1485 m_parseErrorMessage = "Unexpected content at end of JSON literal"_s; 1486 return { }; 1487 } 1488 default: 1489 RELEASE_ASSERT_NOT_REACHED(); 1417 1490 } 1418 1491 if (stateStack.isEmpty()) -
trunk/Source/JavaScriptCore/runtime/LiteralParser.h
r278971 r282468 53 53 TokString, TokIdentifier, TokNumber, TokColon, 54 54 TokLParen, TokRParen, TokComma, TokTrue, TokFalse, 55 TokNull, TokEnd, TokDot, TokAssign, TokSemi, TokError };55 TokNull, TokEnd, TokDot, TokAssign, TokSemi, TokError, TokErrorSpace }; 56 56 57 57 struct JSONPPathEntry { … … 139 139 140 140 #if !ASSERT_ENABLED 141 typedef const LiteralParserToken<CharType>* LiteralParserTokenPtr;141 using LiteralParserTokenPtr = const LiteralParserToken<CharType>*; 142 142 143 143 LiteralParserTokenPtr currentToken() … … 196 196 JSValue parse(ParserState); 197 197 198 JSValue parsePrimitiveValue(VM&); 199 200 ALWAYS_INLINE Identifier makeIdentifier(typename Lexer::LiteralParserTokenPtr); 198 201 template<typename LiteralCharType> 199 202 ALWAYS_INLINE Identifier makeIdentifier(const LiteralCharType* characters, size_t length); 203 204 void setErrorMessageForToken(TokenType); 200 205 201 206 JSGlobalObject* m_globalObject;
Note: See TracChangeset
for help on using the changeset viewer.