Changeset 248829 in webkit
- Timestamp:
- Aug 17, 2019, 10:54:13 PM (6 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 20 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r248826 r248829 1 2019-08-17 Ross Kirsling <ross.kirsling@sony.com> 2 3 [ESNext] Implement optional chaining 4 https://bugs.webkit.org/show_bug.cgi?id=200199 5 6 Reviewed by Yusuke Suzuki. 7 8 * stress/nullish-coalescing.js: 9 * stress/optional-chaining.js: Added. 10 * stress/tail-call-recognize.js: 11 1 12 2019-08-17 Ross Kirsling <ross.kirsling@sony.com> 2 13 -
trunk/JSTests/stress/nullish-coalescing.js
r248426 r248829 1 //@ runNullish CoalescingEnabled1 //@ runNullishAwareOperatorsEnabled 2 2 3 3 function shouldBe(actual, expected) { -
trunk/JSTests/stress/tail-call-recognize.js
r232314 r248829 1 //@ runNullishAwareOperatorsEnabled 2 1 3 function callerMustBeRun() { 2 4 if (!Object.is(callerMustBeRun.caller, runTests)) … … 151 153 })(); 152 154 155 (function tailCallCoalesce() { 156 "use strict"; 157 return false ?? callerMustBeRun(); 158 })(); 159 153 160 (function memberTailCall() { 154 161 "use strict"; … … 159 166 "use strict"; 160 167 return callerMustBeRun.bind()(); 168 })(); 169 170 (function optionalTailCall() { 171 "use strict"; 172 return callerMustBeRun?.(); 161 173 })(); 162 174 -
trunk/Source/JavaScriptCore/ChangeLog
r248826 r248829 1 2019-08-17 Ross Kirsling <ross.kirsling@sony.com> 2 3 [ESNext] Implement optional chaining 4 https://bugs.webkit.org/show_bug.cgi?id=200199 5 6 Reviewed by Yusuke Suzuki. 7 8 Implement the optional chaining proposal, which has now reached Stage 3 at TC39. 9 10 This introduces a ?. operator which: 11 - guards member access when the LHS is nullish, i.e. `null?.foo` and `null?.['foo']` are undefined 12 - guards function calls when the LHS is nullish, i.e. `null?.()` is undefined 13 - short-circuits over a whole access/call chain, i.e. `null?.a['b'](c++)` is undefined and does not increment c 14 15 This feature can be naively viewed as a ternary in disguise, i.e. `a?.b` is like `a == null ? undefined : a.b`. 16 However, since we must be sure not to double-evaluate the LHS, it's actually rather akin to a try block -- 17 namely, we have the bytecode generator keep an early-out label for use throughout the access and call chain. 18 19 (Also note that document.all behaves as an object, so "nullish" means *strictly* equal to null or undefined.) 20 21 * bytecompiler/BytecodeGenerator.cpp: 22 (JSC::BytecodeGenerator::pushOptionalChainTarget): Added. 23 (JSC::BytecodeGenerator::popOptionalChainTarget): Added. 24 (JSC::BytecodeGenerator::emitOptionalCheck): Added. 25 * bytecompiler/BytecodeGenerator.h: 26 Implement early-out logic. 27 28 * bytecompiler/NodesCodegen.cpp: 29 (JSC::BracketAccessorNode::emitBytecode): 30 (JSC::DotAccessorNode::emitBytecode): 31 (JSC::EvalFunctionCallNode::emitBytecode): Refactor so we can emitOptionalCheck in a single location. 32 (JSC::FunctionCallValueNode::emitBytecode): 33 (JSC::FunctionCallResolveNode::emitBytecode): Refactor so we can emitOptionalCheck in a single location. 34 (JSC::FunctionCallBracketNode::emitBytecode): 35 (JSC::FunctionCallDotNode::emitBytecode): 36 (JSC::CallFunctionCallDotNode::emitBytecode): 37 (JSC::ApplyFunctionCallDotNode::emitBytecode): 38 (JSC::DeleteBracketNode::emitBytecode): 39 (JSC::DeleteDotNode::emitBytecode): 40 (JSC::CoalesceNode::emitBytecode): Clean up. 41 (JSC::OptionalChainNode::emitBytecode): Added. 42 Implement ?. node and emit checks where needed. 43 44 * llint/LowLevelInterpreter32_64.asm: 45 * llint/LowLevelInterpreter64.asm: 46 Have OpIsUndefinedOrNull support constant registers. 47 48 * parser/ASTBuilder.h: 49 (JSC::ASTBuilder::createOptionalChain): Added. 50 (JSC::ASTBuilder::makeDeleteNode): 51 (JSC::ASTBuilder::makeFunctionCallNode): 52 * parser/Lexer.cpp: 53 (JSC::Lexer<T>::lexWithoutClearingLineTerminator): 54 * parser/NodeConstructors.h: 55 (JSC::OptionalChainNode::OptionalChainNode): Added. 56 * parser/Nodes.h: 57 (JSC::ExpressionNode::isOptionalChain const): Added. 58 (JSC::ExpressionNode::isOptionalChainBase const): Added. 59 (JSC::ExpressionNode::setIsOptionalChainBase): Added. 60 * parser/ParserTokens.h: 61 * parser/SyntaxChecker.h: 62 (JSC::SyntaxChecker::makeFunctionCallNode): 63 (JSC::SyntaxChecker::createOptionalChain): Added. 64 Introduce new token and AST node, as well as an ExpressionNode field to mark LHSes with. 65 66 * parser/Parser.cpp: 67 (JSC::Parser<LexerType>::parseMemberExpression): 68 Parse optional chains by wrapping the access/call parse loop. 69 70 * runtime/ExceptionHelpers.cpp: 71 (JSC::functionCallBase): 72 Ensure that TypeError messages don't include the '?.'. 73 74 * runtime/Options.h: 75 Update feature flag, as ?. and ?? are a double feature of "nullish-aware" operators. 76 1 77 2019-08-17 Ross Kirsling <ross.kirsling@sony.com> 2 78 -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
r248426 r248829 4965 4965 } 4966 4966 4967 void BytecodeGenerator::pushOptionalChainTarget() 4968 { 4969 m_optionalChainTargetStack.append(newLabel()); 4970 } 4971 4972 void BytecodeGenerator::popOptionalChainTarget(RegisterID* dst, bool isDelete) 4973 { 4974 ASSERT(m_optionalChainTargetStack.size()); 4975 4976 Ref<Label> endLabel = newLabel(); 4977 emitJump(endLabel.get()); 4978 4979 emitLabel(m_optionalChainTargetStack.takeLast().get()); 4980 emitLoad(dst, isDelete ? jsBoolean(true) : jsUndefined()); 4981 4982 emitLabel(endLabel.get()); 4983 } 4984 4985 void BytecodeGenerator::emitOptionalCheck(RegisterID* src) 4986 { 4987 ASSERT(m_optionalChainTargetStack.size()); 4988 emitJumpIfTrue(emitIsUndefinedOrNull(newTemporary(), src), m_optionalChainTargetStack.last().get()); 4989 } 4990 4967 4991 void ForInContext::finalize(BytecodeGenerator& generator, UnlinkedCodeBlock* codeBlock, unsigned bodyBytecodeEndOffset) 4968 4992 { -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
r245906 r248829 960 960 void popFinallyControlFlowScope(); 961 961 962 void pushOptionalChainTarget(); 963 void popOptionalChainTarget(RegisterID* dst, bool isDelete = false); 964 void emitOptionalCheck(RegisterID* src); 965 962 966 void pushIndexedForInScope(RegisterID* local, RegisterID* index); 963 967 void popIndexedForInScope(RegisterID* local); … … 1267 1271 SegmentedVector<TryData, 8> m_tryData; 1268 1272 1273 Vector<Ref<Label>> m_optionalChainTargetStack; 1274 1269 1275 int m_nextConstantOffset { 0 }; 1270 1276 -
trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
r247819 r248829 745 745 RefPtr<RegisterID> finalDest = generator.finalDestination(dst); 746 746 747 if (isNonIndexStringElement(*m_subscript)) { 748 RefPtr<RegisterID> base = generator.emitNode(m_base); 747 bool subscriptIsNonIndexString = isNonIndexStringElement(*m_subscript); 748 RefPtr<RegisterID> base = subscriptIsNonIndexString 749 ? generator.emitNode(m_base) 750 : generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); 751 752 if (m_base->isOptionalChainBase()) 753 generator.emitOptionalCheck(base.get()); 754 755 if (subscriptIsNonIndexString) { 749 756 generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); 750 757 ret = generator.emitGetById(finalDest.get(), base.get(), static_cast<StringNode*>(m_subscript)->value()); 751 758 } else { 752 RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator));753 759 RegisterID* property = generator.emitNodeForProperty(m_subscript); 754 760 generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); … … 764 770 RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) 765 771 { 772 RefPtr<RegisterID> finalDest = generator.finalDestination(dst); 766 773 bool baseIsSuper = m_base->isSuperNode(); 767 RefPtr<RegisterID> base = baseIsSuper ? emitSuperBaseForCallee(generator) : generator.emitNode(m_base); 774 775 RefPtr<RegisterID> base; 776 if (baseIsSuper) 777 base = emitSuperBaseForCallee(generator); 778 else { 779 base = generator.emitNode(m_base); 780 if (m_base->isOptionalChainBase()) 781 generator.emitOptionalCheck(base.get()); 782 } 783 768 784 generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); 769 RegisterID* finalDest = generator.finalDestination(dst);770 785 RegisterID* ret; 771 786 if (baseIsSuper) { 772 787 RefPtr<RegisterID> thisValue = generator.ensureThis(); 773 ret = generator.emitGetById(finalDest , base.get(), thisValue.get(), m_ident);788 ret = generator.emitGetById(finalDest.get(), base.get(), thisValue.get(), m_ident); 774 789 } else 775 ret = generator.emitGetById(finalDest , base.get(), m_ident);776 generator.emitProfileType(finalDest , divotStart(), divotEnd());790 ret = generator.emitGetById(finalDest.get(), base.get(), m_ident); 791 generator.emitProfileType(finalDest.get(), divotStart(), divotEnd()); 777 792 return ret; 778 793 } … … 849 864 850 865 Variable var = generator.variable(generator.propertyNames().eval); 851 if (RegisterID* local = var.local()) { 852 generator.emitTDZCheckIfNecessary(var, local, nullptr); 853 RefPtr<RegisterID> func = generator.move(generator.tempDestination(dst), local); 854 CallArguments callArguments(generator, m_args); 866 RefPtr<RegisterID> local = var.local(); 867 RefPtr<RegisterID> func; 868 if (local) { 869 generator.emitTDZCheckIfNecessary(var, local.get(), nullptr); 870 func = generator.move(generator.tempDestination(dst), local.get()); 871 } else 872 func = generator.newTemporary(); 873 CallArguments callArguments(generator, m_args); 874 875 if (local) 855 876 generator.emitLoad(callArguments.thisRegister(), jsUndefined()); 856 return generator.emitCallEval(generator.finalDestination(dst, func.get()), func.get(), callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::No); 857 } 858 859 RefPtr<RegisterID> func = generator.newTemporary(); 860 CallArguments callArguments(generator, m_args); 861 JSTextPosition newDivot = divotStart() + 4; 862 generator.emitExpressionInfo(newDivot, divotStart(), newDivot); 863 generator.move( 864 callArguments.thisRegister(), 865 generator.emitResolveScope(callArguments.thisRegister(), var)); 866 generator.emitGetFromScope(func.get(), callArguments.thisRegister(), var, ThrowIfNotFound); 867 generator.emitTDZCheckIfNecessary(var, func.get(), nullptr); 868 return generator.emitCallEval(generator.finalDestination(dst, func.get()), func.get(), callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::No); 877 else { 878 JSTextPosition newDivot = divotStart() + 4; 879 generator.emitExpressionInfo(newDivot, divotStart(), newDivot); 880 generator.move( 881 callArguments.thisRegister(), 882 generator.emitResolveScope(callArguments.thisRegister(), var)); 883 generator.emitGetFromScope(func.get(), callArguments.thisRegister(), var, ThrowIfNotFound); 884 generator.emitTDZCheckIfNecessary(var, func.get(), nullptr); 885 } 886 887 RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get()); 888 if (isOptionalChainBase()) 889 generator.emitOptionalCheck(func.get()); 890 891 return generator.emitCallEval(returnValue.get(), func.get(), callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::No); 869 892 } 870 893 … … 900 923 return ret; 901 924 } 925 902 926 RefPtr<RegisterID> func = generator.emitNode(m_expr); 903 927 RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get()); 928 if (isOptionalChainBase()) 929 generator.emitOptionalCheck(func.get()); 930 904 931 CallArguments callArguments(generator, m_args); 905 932 generator.emitLoad(callArguments.thisRegister(), jsUndefined()); … … 921 948 922 949 Variable var = generator.variable(m_ident); 923 if (RegisterID* local = var.local()) { 924 generator.emitTDZCheckIfNecessary(var, local, nullptr); 925 RefPtr<RegisterID> func = generator.move(generator.tempDestination(dst), local); 926 RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get()); 927 CallArguments callArguments(generator, m_args); 950 RefPtr<RegisterID> local = var.local(); 951 RefPtr<RegisterID> func; 952 if (local) { 953 generator.emitTDZCheckIfNecessary(var, local.get(), nullptr); 954 func = generator.move(generator.tempDestination(dst), local.get()); 955 } else 956 func = generator.newTemporary(); 957 CallArguments callArguments(generator, m_args); 958 959 if (local) { 928 960 generator.emitLoad(callArguments.thisRegister(), jsUndefined()); 929 961 // This passes NoExpectedFunction because we expect that if the function is in a 930 962 // local variable, then it's not one of our built-in constructors. 931 RegisterID* ret = generator.emitCallInTailPosition(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes); 932 generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); 933 return ret; 934 } 935 936 RefPtr<RegisterID> func = generator.newTemporary(); 963 expectedFunction = NoExpectedFunction; 964 } else { 965 JSTextPosition newDivot = divotStart() + m_ident.length(); 966 generator.emitExpressionInfo(newDivot, divotStart(), newDivot); 967 generator.move( 968 callArguments.thisRegister(), 969 generator.emitResolveScope(callArguments.thisRegister(), var)); 970 generator.emitGetFromScope(func.get(), callArguments.thisRegister(), var, ThrowIfNotFound); 971 generator.emitTDZCheckIfNecessary(var, func.get(), nullptr); 972 } 973 937 974 RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get()); 938 CallArguments callArguments(generator, m_args); 939 940 JSTextPosition newDivot = divotStart() + m_ident.length(); 941 generator.emitExpressionInfo(newDivot, divotStart(), newDivot); 942 generator.move( 943 callArguments.thisRegister(), 944 generator.emitResolveScope(callArguments.thisRegister(), var)); 945 generator.emitGetFromScope(func.get(), callArguments.thisRegister(), var, ThrowIfNotFound); 946 generator.emitTDZCheckIfNecessary(var, func.get(), nullptr); 975 if (isOptionalChainBase()) 976 generator.emitOptionalCheck(func.get()); 977 947 978 RegisterID* ret = generator.emitCallInTailPosition(returnValue.get(), func.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes); 948 979 generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); … … 1276 1307 RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) 1277 1308 { 1309 RefPtr<RegisterID> function = generator.tempDestination(dst); 1310 RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get()); 1278 1311 bool baseIsSuper = m_base->isSuperNode(); 1279 1312 bool subscriptIsNonIndexString = isNonIndexStringElement(*m_subscript); … … 1287 1320 else 1288 1321 base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); 1289 } 1290 1291 RefPtr<RegisterID> function; 1322 1323 if (m_base->isOptionalChainBase()) 1324 generator.emitOptionalCheck(base.get()); 1325 } 1326 1292 1327 RefPtr<RegisterID> thisRegister; 1293 1328 if (baseIsSuper) { … … 1298 1333 generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); 1299 1334 if (baseIsSuper) 1300 function = generator.emitGetById(generator.tempDestination(dst), base.get(), thisRegister.get(), static_cast<StringNode*>(m_subscript)->value());1335 generator.emitGetById(function.get(), base.get(), thisRegister.get(), static_cast<StringNode*>(m_subscript)->value()); 1301 1336 else 1302 function = generator.emitGetById(generator.tempDestination(dst), base.get(), static_cast<StringNode*>(m_subscript)->value());1337 generator.emitGetById(function.get(), base.get(), static_cast<StringNode*>(m_subscript)->value()); 1303 1338 } else { 1304 1339 RefPtr<RegisterID> property = generator.emitNodeForProperty(m_subscript); 1305 1340 generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); 1306 1341 if (baseIsSuper) 1307 function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), thisRegister.get(), property.get());1342 generator.emitGetByVal(function.get(), base.get(), thisRegister.get(), property.get()); 1308 1343 else 1309 function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get()); 1310 } 1311 1312 RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get()); 1344 generator.emitGetByVal(function.get(), base.get(), property.get()); 1345 } 1346 if (isOptionalChainBase()) 1347 generator.emitOptionalCheck(function.get()); 1348 1313 1349 CallArguments callArguments(generator, m_args); 1314 1350 if (baseIsSuper) { … … 1332 1368 if (baseIsSuper) 1333 1369 generator.move(callArguments.thisRegister(), generator.ensureThis()); 1334 else 1370 else { 1335 1371 generator.emitNode(callArguments.thisRegister(), m_base); 1372 if (m_base->isOptionalChainBase()) 1373 generator.emitOptionalCheck(callArguments.thisRegister()); 1374 } 1336 1375 generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); 1337 1376 if (baseIsSuper) { … … 1340 1379 } else 1341 1380 generator.emitGetById(function.get(), callArguments.thisRegister(), m_ident); 1381 1382 if (isOptionalChainBase()) 1383 generator.emitOptionalCheck(function.get()); 1384 1342 1385 RegisterID* ret = generator.emitCallInTailPosition(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes); 1343 1386 generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); … … 1349 1392 RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) 1350 1393 { 1394 RefPtr<RegisterID> returnValue = generator.finalDestination(dst); 1351 1395 RefPtr<RegisterID> base = generator.emitNode(m_base); 1396 1397 if (m_base->isOptionalChainBase()) 1398 generator.emitOptionalCheck(base.get()); 1399 1352 1400 generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); 1401 1353 1402 RefPtr<RegisterID> function; 1354 RefPtr<RegisterID> returnValue = generator.finalDestination(dst);1355 1356 1403 auto makeFunction = [&] { 1357 1404 if (m_base->isSuperNode()) { … … 1360 1407 } else 1361 1408 function = generator.emitGetById(generator.tempDestination(dst), base.get(), generator.propertyNames().builtinNames().callPublicName()); 1409 1410 if (isOptionalChainBase()) 1411 generator.emitOptionalCheck(function.get()); 1362 1412 }; 1363 1413 … … 1431 1481 bool mayBeCall = areTrivialApplyArguments(m_args); 1432 1482 1483 RefPtr<RegisterID> returnValue = generator.finalDestination(dst); 1484 RefPtr<RegisterID> base = generator.emitNode(m_base); 1485 1486 if (m_base->isOptionalChainBase()) 1487 generator.emitOptionalCheck(base.get()); 1488 1433 1489 RefPtr<RegisterID> function; 1434 RefPtr<RegisterID> base = generator.emitNode(m_base);1435 RefPtr<RegisterID> returnValue = generator.finalDestination(dst);1436 1490 auto makeFunction = [&] { 1437 1491 if (m_base->isSuperNode()) { … … 1440 1494 } else 1441 1495 function = generator.emitGetById(generator.tempDestination(dst), base.get(), generator.propertyNames().builtinNames().applyPublicName()); 1496 1497 if (isOptionalChainBase()) 1498 generator.emitOptionalCheck(function.get()); 1442 1499 }; 1443 1500 … … 1693 1750 RegisterID* DeleteBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) 1694 1751 { 1752 RefPtr<RegisterID> finalDest = generator.finalDestination(dst); 1695 1753 RefPtr<RegisterID> r0 = generator.emitNode(m_base); 1754 1755 if (m_base->isOptionalChainBase()) 1756 generator.emitOptionalCheck(r0.get()); 1757 1696 1758 RefPtr<RegisterID> r1 = generator.emitNode(m_subscript); 1697 1698 1759 generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); 1699 1760 if (m_base->isSuperNode()) 1700 1761 return emitThrowReferenceError(generator, "Cannot delete a super property"); 1701 return generator.emitDeleteByVal( generator.finalDestination(dst), r0.get(), r1.get());1762 return generator.emitDeleteByVal(finalDest.get(), r0.get(), r1.get()); 1702 1763 } 1703 1764 … … 1706 1767 RegisterID* DeleteDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) 1707 1768 { 1769 RefPtr<RegisterID> finalDest = generator.finalDestination(dst); 1708 1770 RefPtr<RegisterID> r0 = generator.emitNode(m_base); 1771 1772 if (m_base->isOptionalChainBase()) 1773 generator.emitOptionalCheck(r0.get()); 1709 1774 1710 1775 generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); 1711 1776 if (m_base->isSuperNode()) 1712 1777 return emitThrowReferenceError(generator, "Cannot delete a super property"); 1713 return generator.emitDeleteById( generator.finalDestination(dst), r0.get(), m_ident);1778 return generator.emitDeleteById(finalDest.get(), r0.get(), m_ident); 1714 1779 } 1715 1780 … … 2347 2412 2348 2413 generator.emitNode(temp.get(), m_expr1); 2349 generator.emitJumpIfFalse(generator.emit UnaryOp<OpIsUndefinedOrNull>(generator.newTemporary(), temp.get()), target.get());2414 generator.emitJumpIfFalse(generator.emitIsUndefinedOrNull(generator.newTemporary(), temp.get()), target.get()); 2350 2415 generator.emitNodeInTailPosition(temp.get(), m_expr2); 2351 2416 generator.emitLabel(target.get()); 2352 2417 2353 2418 return generator.move(dst, temp.get()); 2419 } 2420 2421 // ------------------------------ OptionalChainNode ---------------------------- 2422 2423 RegisterID* OptionalChainNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) 2424 { 2425 RefPtr<RegisterID> finalDest = generator.finalDestination(dst); 2426 2427 if (m_isOutermost) 2428 generator.pushOptionalChainTarget(); 2429 generator.emitNodeInTailPosition(finalDest.get(), m_expr); 2430 if (m_isOutermost) 2431 generator.popOptionalChainTarget(finalDest.get(), m_isDelete); 2432 2433 return finalDest.get(); 2354 2434 } 2355 2435 -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
r248426 r248829 876 876 llintOpWithReturn(op_is_undefined_or_null, OpIsUndefinedOrNull, macro (size, get, dispatch, return) 877 877 get(m_operand, t0) 878 assertNotConstant(size, t0) 879 loadi TagOffset[cfr, t0, 8], t1 878 loadConstantOrVariableTag(size, t0, t1) 880 879 ori 1, t1 881 880 cieq t1, NullTag, t1 -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
r248426 r248829 828 828 829 829 llintOpWithReturn(op_is_undefined_or_null, OpIsUndefinedOrNull, macro (size, get, dispatch, return) 830 get(m_operand, t 0)831 load q [cfr, t0, 8], t0830 get(m_operand, t1) 831 loadConstantOrVariable(size, t1, t0) 832 832 andq ~TagBitUndefined, t0 833 833 cqeq t0, ValueNull, t0 -
trunk/Source/JavaScriptCore/parser/ASTBuilder.h
r247819 r248829 130 130 131 131 ExpressionNode* makeBinaryNode(const JSTokenLocation&, int token, std::pair<ExpressionNode*, BinaryOpInfo>, std::pair<ExpressionNode*, BinaryOpInfo>); 132 ExpressionNode* makeFunctionCallNode(const JSTokenLocation&, ExpressionNode* func, bool previousBaseWasSuper, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth );132 ExpressionNode* makeFunctionCallNode(const JSTokenLocation&, ExpressionNode* func, bool previousBaseWasSuper, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth, bool isOptionalCall); 133 133 134 134 JSC::SourceElements* createSourceElements() { return new (m_parserArena) JSC::SourceElements(); } … … 361 361 } 362 362 363 ExpressionNode* createOptionalChain(const JSTokenLocation& location, ExpressionNode* base, ExpressionNode* expr, bool isOutermost) 364 { 365 base->setIsOptionalChainBase(); 366 return new (m_parserArena) OptionalChainNode(location, expr, isOutermost); 367 } 368 363 369 ExpressionNode* createConditionalExpr(const JSTokenLocation& location, ExpressionNode* condition, ExpressionNode* lhs, ExpressionNode* rhs) 364 370 { … … 1149 1155 ExpressionNode* ASTBuilder::makeDeleteNode(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end) 1150 1156 { 1157 if (expr->isOptionalChain()) { 1158 OptionalChainNode* optionalChain = static_cast<OptionalChainNode*>(expr); 1159 if (optionalChain->expr()->isLocation()) { 1160 ASSERT(!optionalChain->expr()->isResolveNode()); 1161 optionalChain->setExpr(makeDeleteNode(location, optionalChain->expr(), start, divot, end)); 1162 optionalChain->setIsDelete(); 1163 return optionalChain; 1164 } 1165 } 1166 1151 1167 if (!expr->isLocation()) 1152 1168 return new (m_parserArena) DeleteValueNode(location, expr); … … 1346 1362 } 1347 1363 1348 ExpressionNode* ASTBuilder::makeFunctionCallNode(const JSTokenLocation& location, ExpressionNode* func, bool previousBaseWasSuper, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth )1364 ExpressionNode* ASTBuilder::makeFunctionCallNode(const JSTokenLocation& location, ExpressionNode* func, bool previousBaseWasSuper, ArgumentsNode* args, const JSTextPosition& divotStart, const JSTextPosition& divot, const JSTextPosition& divotEnd, size_t callOrApplyChildDepth, bool isOptionalCall) 1349 1365 { 1350 1366 ASSERT(divot.offset >= divot.lineStartOffset); … … 1353 1369 1354 1370 if (func->isBytecodeIntrinsicNode()) { 1371 ASSERT(!isOptionalCall); 1355 1372 BytecodeIntrinsicNode* intrinsic = static_cast<BytecodeIntrinsicNode*>(func); 1356 1373 if (intrinsic->type() == BytecodeIntrinsicNode::Type::Constant) 1357 1374 return new (m_parserArena) BytecodeIntrinsicNode(BytecodeIntrinsicNode::Type::Function, location, intrinsic->emitter(), intrinsic->identifier(), args, divot, divotStart, divotEnd); 1358 1375 } 1376 1377 if (func->isOptionalChain()) { 1378 OptionalChainNode* optionalChain = static_cast<OptionalChainNode*>(func); 1379 if (optionalChain->expr()->isLocation()) { 1380 ASSERT(!optionalChain->expr()->isResolveNode()); 1381 // We must take care to preserve our `this` value in cases like `a?.b?.()` and `(a?.b)()`, respectively. 1382 if (isOptionalCall) 1383 return makeFunctionCallNode(location, optionalChain->expr(), previousBaseWasSuper, args, divotStart, divot, divotEnd, callOrApplyChildDepth, isOptionalCall); 1384 optionalChain->setExpr(makeFunctionCallNode(location, optionalChain->expr(), previousBaseWasSuper, args, divotStart, divot, divotEnd, callOrApplyChildDepth, isOptionalCall)); 1385 return optionalChain; 1386 } 1387 } 1388 1359 1389 if (!func->isLocation()) 1360 1390 return new (m_parserArena) FunctionCallValueNode(location, func, args, divot, divotStart, divotEnd); -
trunk/Source/JavaScriptCore/parser/Lexer.cpp
r248826 r248829 2159 2159 case CharacterQuestion: 2160 2160 shift(); 2161 if (Options::useNullishCoalescing() && m_current == '?') { 2162 shift(); 2163 token = COALESCE; 2164 break; 2161 if (Options::useNullishAwareOperators()) { 2162 if (m_current == '?') { 2163 shift(); 2164 token = COALESCE; 2165 break; 2166 } 2167 if (m_current == '.' && !isASCIIDigit(peek(1))) { 2168 shift(); 2169 token = QUESTIONDOT; 2170 break; 2171 } 2165 2172 } 2166 2173 token = QUESTION; -
trunk/Source/JavaScriptCore/parser/NodeConstructors.h
r247819 r248829 677 677 } 678 678 679 inline OptionalChainNode::OptionalChainNode(const JSTokenLocation& location, ExpressionNode* expr, bool isOutermost) 680 : ExpressionNode(location) 681 , m_expr(expr) 682 , m_isOutermost(isOutermost) 683 { 684 } 685 679 686 inline ConditionalNode::ConditionalNode(const JSTokenLocation& location, ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2) 680 687 : ExpressionNode(location) -
trunk/Source/JavaScriptCore/parser/Nodes.h
r247819 r248829 206 206 virtual bool isBinaryOpNode() const { return false; } 207 207 virtual bool isFunctionCall() const { return false; } 208 virtual bool isOptionalChain() const { return false; } 208 209 209 210 virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label&, Label&, FallThroughMode); … … 213 214 ResultType resultDescriptor() const { return m_resultType; } 214 215 216 bool isOptionalChainBase() const { return m_isOptionalChainBase; } 217 void setIsOptionalChainBase() { m_isOptionalChainBase = true; } 218 215 219 private: 216 220 ResultType m_resultType; 221 bool m_isOptionalChainBase { false }; 217 222 }; 218 223 … … 1311 1316 1312 1317 private: 1313 RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;1318 RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = nullptr) final; 1314 1319 1315 1320 ExpressionNode* m_expr1; 1316 1321 ExpressionNode* m_expr2; 1322 }; 1323 1324 class OptionalChainNode final : public ExpressionNode { 1325 public: 1326 OptionalChainNode(const JSTokenLocation&, ExpressionNode*, bool); 1327 1328 void setExpr(ExpressionNode* expr) { m_expr = expr; } 1329 ExpressionNode* expr() const { return m_expr; } 1330 1331 void setIsDelete() { m_isDelete = true; } 1332 1333 private: 1334 RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = nullptr) final; 1335 1336 bool isOptionalChain() const final { return true; } 1337 1338 ExpressionNode* m_expr; 1339 bool m_isOutermost; 1340 bool m_isDelete { false }; 1317 1341 }; 1318 1342 -
trunk/Source/JavaScriptCore/parser/Parser.cpp
r248711 r248829 4809 4809 4810 4810 failIfFalse(base, "Cannot parse base expression"); 4811 while (true) { 4812 location = tokenLocation(); 4813 switch (m_token.m_type) { 4814 case OPENBRACKET: { 4815 m_parserState.nonTrivialExpressionCount++; 4816 JSTextPosition expressionEnd = lastTokenEndPosition(); 4811 4812 do { 4813 TreeExpression optionalChainBase = 0; 4814 JSTokenLocation optionalChainLocation; 4815 JSTokenType type = m_token.m_type; 4816 4817 if (match(QUESTIONDOT)) { 4818 semanticFailIfTrue(newCount, "Cannot call constructor in an optional chain"); 4819 semanticFailIfTrue(baseIsSuper, "Cannot use super as the base of an optional chain"); 4820 optionalChainBase = base; 4821 optionalChainLocation = tokenLocation(); 4822 4823 SavePoint savePoint = createSavePoint(); 4817 4824 next(); 4818 int nonLHSCount = m_parserState.nonLHSCount; 4819 int initialAssignments = m_parserState.assignmentCount; 4820 TreeExpression property = parseExpression(context); 4821 failIfFalse(property, "Cannot parse subscript expression"); 4822 base = context.createBracketAccess(startLocation, base, property, initialAssignments != m_parserState.assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); 4823 4824 if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) 4825 currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); 4826 4827 handleProductionOrFail(CLOSEBRACKET, "]", "end", "subscript expression"); 4828 m_parserState.nonLHSCount = nonLHSCount; 4829 break; 4830 } 4831 case OPENPAREN: { 4832 m_parserState.nonTrivialExpressionCount++; 4833 int nonLHSCount = m_parserState.nonLHSCount; 4834 if (newCount) { 4835 newCount--; 4825 if (match(OPENBRACKET) || match(OPENPAREN) || match(BACKQUOTE)) 4826 type = m_token.m_type; 4827 else { 4828 type = DOT; 4829 restoreSavePoint(savePoint); 4830 } 4831 } 4832 4833 while (true) { 4834 location = tokenLocation(); 4835 switch (type) { 4836 case OPENBRACKET: { 4837 m_parserState.nonTrivialExpressionCount++; 4836 4838 JSTextPosition expressionEnd = lastTokenEndPosition(); 4837 TreeArguments arguments = parseArguments(context); 4838 failIfFalse(arguments, "Cannot parse call arguments"); 4839 base = context.createNewExpr(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); 4840 } else { 4841 size_t usedVariablesSize = currentScope()->currentUsedVariablesSize(); 4839 next(); 4840 int nonLHSCount = m_parserState.nonLHSCount; 4841 int initialAssignments = m_parserState.assignmentCount; 4842 TreeExpression property = parseExpression(context); 4843 failIfFalse(property, "Cannot parse subscript expression"); 4844 base = context.createBracketAccess(startLocation, base, property, initialAssignments != m_parserState.assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); 4845 4846 if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) 4847 currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); 4848 4849 handleProductionOrFail(CLOSEBRACKET, "]", "end", "subscript expression"); 4850 m_parserState.nonLHSCount = nonLHSCount; 4851 break; 4852 } 4853 case OPENPAREN: { 4854 m_parserState.nonTrivialExpressionCount++; 4855 int nonLHSCount = m_parserState.nonLHSCount; 4856 if (newCount) { 4857 newCount--; 4858 JSTextPosition expressionEnd = lastTokenEndPosition(); 4859 TreeArguments arguments = parseArguments(context); 4860 failIfFalse(arguments, "Cannot parse call arguments"); 4861 base = context.createNewExpr(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); 4862 } else { 4863 size_t usedVariablesSize = currentScope()->currentUsedVariablesSize(); 4864 JSTextPosition expressionEnd = lastTokenEndPosition(); 4865 Optional<CallOrApplyDepthScope> callOrApplyDepthScope; 4866 recordCallOrApplyDepth<TreeBuilder>(this, *m_vm, callOrApplyDepthScope, base); 4867 4868 TreeArguments arguments = parseArguments(context); 4869 4870 if (baseIsAsyncKeyword && (!arguments || match(ARROWFUNCTION))) { 4871 currentScope()->revertToPreviousUsedVariables(usedVariablesSize); 4872 forceClassifyExpressionError(ErrorIndicatesAsyncArrowFunction); 4873 failDueToUnexpectedToken(); 4874 } 4875 4876 failIfFalse(arguments, "Cannot parse call arguments"); 4877 if (baseIsSuper) { 4878 ScopeRef functionScope = currentFunctionScope(); 4879 if (!functionScope->setHasDirectSuper()) { 4880 // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error 4881 // in case of arrow function because during reparsing we don't know whether we currently parse the arrow function 4882 // inside of the constructor or method. 4883 if (!m_lexer->isReparsingFunction()) { 4884 ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); 4885 ConstructorKind functionConstructorKind = !functionScope->isArrowFunction() && !closestOrdinaryFunctionScope->isEvalContext() 4886 ? functionScope->constructorKind() 4887 : closestOrdinaryFunctionScope->constructorKind(); 4888 semanticFailIfTrue(functionConstructorKind == ConstructorKind::None, "super is not valid in this context"); 4889 semanticFailIfTrue(functionConstructorKind != ConstructorKind::Extends, "super is not valid in this context"); 4890 } 4891 } 4892 if (currentScope()->isArrowFunction()) 4893 functionScope->setInnerArrowFunctionUsesSuperCall(); 4894 } 4895 4896 bool isOptionalCall = optionalChainLocation.endOffset == static_cast<unsigned>(expressionEnd.offset); 4897 base = context.makeFunctionCallNode(startLocation, base, previousBaseWasSuper, arguments, expressionStart, 4898 expressionEnd, lastTokenEndPosition(), callOrApplyDepthScope ? callOrApplyDepthScope->distanceToInnermostChild() : 0, isOptionalCall); 4899 4900 if (isOptionalCall) 4901 optionalChainBase = base; 4902 } 4903 m_parserState.nonLHSCount = nonLHSCount; 4904 break; 4905 } 4906 case DOT: { 4907 m_parserState.nonTrivialExpressionCount++; 4842 4908 JSTextPosition expressionEnd = lastTokenEndPosition(); 4843 Optional<CallOrApplyDepthScope> callOrApplyDepthScope; 4844 recordCallOrApplyDepth<TreeBuilder>(this, *m_vm, callOrApplyDepthScope, base); 4845 4846 TreeArguments arguments = parseArguments(context); 4847 4848 if (baseIsAsyncKeyword && (!arguments || match(ARROWFUNCTION))) { 4849 currentScope()->revertToPreviousUsedVariables(usedVariablesSize); 4850 forceClassifyExpressionError(ErrorIndicatesAsyncArrowFunction); 4851 failDueToUnexpectedToken(); 4852 } 4853 4854 failIfFalse(arguments, "Cannot parse call arguments"); 4855 if (baseIsSuper) { 4856 ScopeRef functionScope = currentFunctionScope(); 4857 if (!functionScope->setHasDirectSuper()) { 4858 // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error 4859 // in case of arrow function because during reparsing we don't know whether we currently parse the arrow function 4860 // inside of the constructor or method. 4861 if (!m_lexer->isReparsingFunction()) { 4862 ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); 4863 ConstructorKind functionConstructorKind = !functionScope->isArrowFunction() && !closestOrdinaryFunctionScope->isEvalContext() 4864 ? functionScope->constructorKind() 4865 : closestOrdinaryFunctionScope->constructorKind(); 4866 semanticFailIfTrue(functionConstructorKind == ConstructorKind::None, "super is not valid in this context"); 4867 semanticFailIfTrue(functionConstructorKind != ConstructorKind::Extends, "super is not valid in this context"); 4868 } 4869 } 4870 if (currentScope()->isArrowFunction()) 4871 functionScope->setInnerArrowFunctionUsesSuperCall(); 4872 } 4873 base = context.makeFunctionCallNode(startLocation, base, previousBaseWasSuper, arguments, expressionStart, 4874 expressionEnd, lastTokenEndPosition(), callOrApplyDepthScope ? callOrApplyDepthScope->distanceToInnermostChild() : 0); 4909 nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); 4910 matchOrFail(IDENT, "Expected a property name after ", optionalChainBase ? "'?.'" : "'.'"); 4911 base = context.createDotAccess(startLocation, base, m_token.m_data.ident, expressionStart, expressionEnd, tokenEndPosition()); 4912 if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) 4913 currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); 4914 next(); 4915 break; 4875 4916 } 4876 m_parserState.nonLHSCount = nonLHSCount; 4877 break; 4878 } 4879 case DOT: { 4880 m_parserState.nonTrivialExpressionCount++; 4881 JSTextPosition expressionEnd = lastTokenEndPosition(); 4882 nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords); 4883 matchOrFail(IDENT, "Expected a property name after '.'"); 4884 base = context.createDotAccess(startLocation, base, m_token.m_data.ident, expressionStart, expressionEnd, tokenEndPosition()); 4885 if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) 4886 currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); 4887 next(); 4888 break; 4889 } 4890 case BACKQUOTE: { 4891 semanticFailIfTrue(baseIsSuper, "Cannot use super as tag for tagged templates"); 4892 JSTextPosition expressionEnd = lastTokenEndPosition(); 4893 int nonLHSCount = m_parserState.nonLHSCount; 4894 typename TreeBuilder::TemplateLiteral templateLiteral = parseTemplateLiteral(context, LexerType::RawStringsBuildMode::BuildRawStrings); 4895 failIfFalse(templateLiteral, "Cannot parse template literal"); 4896 base = context.createTaggedTemplate(startLocation, base, templateLiteral, expressionStart, expressionEnd, lastTokenEndPosition()); 4897 m_parserState.nonLHSCount = nonLHSCount; 4898 m_seenTaggedTemplate = true; 4899 break; 4900 } 4901 default: 4902 goto endMemberExpression; 4903 } 4904 previousBaseWasSuper = baseIsSuper; 4905 baseIsSuper = false; 4906 } 4907 endMemberExpression: 4917 case BACKQUOTE: { 4918 semanticFailIfTrue(optionalChainBase, "Cannot use tagged templates in an optional chain"); 4919 semanticFailIfTrue(baseIsSuper, "Cannot use super as tag for tagged templates"); 4920 JSTextPosition expressionEnd = lastTokenEndPosition(); 4921 int nonLHSCount = m_parserState.nonLHSCount; 4922 typename TreeBuilder::TemplateLiteral templateLiteral = parseTemplateLiteral(context, LexerType::RawStringsBuildMode::BuildRawStrings); 4923 failIfFalse(templateLiteral, "Cannot parse template literal"); 4924 base = context.createTaggedTemplate(startLocation, base, templateLiteral, expressionStart, expressionEnd, lastTokenEndPosition()); 4925 m_parserState.nonLHSCount = nonLHSCount; 4926 m_seenTaggedTemplate = true; 4927 break; 4928 } 4929 default: 4930 goto endOfChain; 4931 } 4932 previousBaseWasSuper = baseIsSuper; 4933 baseIsSuper = false; 4934 type = m_token.m_type; 4935 } 4936 endOfChain: 4937 if (optionalChainBase) 4938 base = context.createOptionalChain(optionalChainLocation, optionalChainBase, base, !match(QUESTIONDOT)); 4939 } while (match(QUESTIONDOT)); 4940 4908 4941 semanticFailIfTrue(baseIsSuper, "super is not valid in this context"); 4909 4942 while (newCount--) -
trunk/Source/JavaScriptCore/parser/ParserTokens.h
r247819 r248829 137 137 DOTDOTDOT, 138 138 ARROWFUNCTION, 139 QUESTIONDOT, 139 140 LastUntaggedToken, 140 141 -
trunk/Source/JavaScriptCore/parser/SyntaxChecker.h
r245406 r248829 74 74 ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr, 75 75 FunctionExpr, ClassExpr, SuperExpr, ImportExpr, BracketExpr, DotExpr, CallExpr, 76 NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr, 76 NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr, OptionalChain, 77 77 ConditionalExpr, AssignmentExpr, TypeofExpr, 78 78 DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter, … … 148 148 149 149 int createSourceElements() { return SourceElementsResult; } 150 ExpressionType makeFunctionCallNode(const JSTokenLocation&, int, bool, int, int, int, int, size_t) { return CallExpr; }150 ExpressionType makeFunctionCallNode(const JSTokenLocation&, ExpressionType, bool, int, int, int, int, size_t, bool) { return CallExpr; } 151 151 ExpressionType createCommaExpr(const JSTokenLocation&, ExpressionType expr) { return expr; } 152 152 ExpressionType appendToCommaExpr(const JSTokenLocation&, ExpressionType& head, ExpressionType, ExpressionType next) { head = next; return next; } … … 185 185 ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int, int, int) { return NewExpr; } 186 186 ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int) { return NewExpr; } 187 ExpressionType createOptionalChain(const JSTokenLocation&, ExpressionType, ExpressionType, bool) { return OptionalChain; } 187 188 ExpressionType createConditionalExpr(const JSTokenLocation&, ExpressionType, ExpressionType, ExpressionType) { return ConditionalExpr; } 188 189 ExpressionType createAssignResolve(const JSTokenLocation&, const Identifier&, ExpressionType, int, int, int, AssignmentContext) { return AssignmentExpr; } -
trunk/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
r243955 r248829 177 177 } 178 178 179 // Don't display the ?. of an optional call. 180 if (idx > 1 && sourceText[idx] == '.' && sourceText[idx - 1] == '?') 181 idx -= 2; 182 179 183 return sourceText.left(idx + 1); 180 184 } -
trunk/Source/JavaScriptCore/runtime/Options.h
r248824 r248829 500 500 v(bool, useWeakRefs, false, Normal, "Expose the WeakRef constructor.") \ 501 501 v(bool, useBigInt, false, Normal, "If true, we will enable BigInt support.") \ 502 v(bool, useNullish Coalescing, false, Normal, "Enable support for the ?? operator.") \502 v(bool, useNullishAwareOperators, false, Normal, "Enable support for ?. and ?? operators.") \ 503 503 v(bool, useArrayAllocationProfiling, true, Normal, "If true, we will use our normal array allocation profiling. If false, the allocation profile will always claim to be undecided.") \ 504 504 v(bool, forcePolyProto, false, Normal, "If true, create_this will always create an object with a poly proto structure.") \ -
trunk/Tools/ChangeLog
r248828 r248829 1 2019-08-17 Ross Kirsling <ross.kirsling@sony.com> 2 3 [ESNext] Implement optional chaining 4 https://bugs.webkit.org/show_bug.cgi?id=200199 5 6 Reviewed by Yusuke Suzuki. 7 8 * Scripts/run-jsc-stress-tests: 9 1 10 2019-08-17 Tim Horton <timothy_horton@apple.com> 2 11 -
trunk/Tools/Scripts/run-jsc-stress-tests
r247819 r248829 700 700 end 701 701 702 def runNullish CoalescingEnabled(*optionalTestSpecificOptions)703 run("nullish- coalescing-enabled", "--useNullishCoalescing=true" , *(FTL_OPTIONS + optionalTestSpecificOptions))702 def runNullishAwareOperatorsEnabled(*optionalTestSpecificOptions) 703 run("nullish-aware-operators-enabled", "--useNullishAwareOperators=true" , *(FTL_OPTIONS + optionalTestSpecificOptions)) 704 704 end 705 705
Note:
See TracChangeset
for help on using the changeset viewer.