Changeset 248829 in webkit


Ignore:
Timestamp:
Aug 17, 2019, 10:54:13 PM (6 years ago)
Author:
Ross Kirsling
Message:

[ESNext] Implement optional chaining
https://bugs.webkit.org/show_bug.cgi?id=200199

Reviewed by Yusuke Suzuki.

JSTests:

  • stress/nullish-coalescing.js:
  • stress/optional-chaining.js: Added.
  • stress/tail-call-recognize.js:

Source/JavaScriptCore:

Implement the optional chaining proposal, which has now reached Stage 3 at TC39.

This introduces a ?. operator which:

  • guards member access when the LHS is nullish, i.e. null?.foo and null?.['foo'] are undefined
  • guards function calls when the LHS is nullish, i.e. null?.() is undefined
  • short-circuits over a whole access/call chain, i.e. null?.a['b'](c++) is undefined and does not increment c

This feature can be naively viewed as a ternary in disguise, i.e. a?.b is like a == null ? undefined : a.b.
However, since we must be sure not to double-evaluate the LHS, it's actually rather akin to a try block --
namely, we have the bytecode generator keep an early-out label for use throughout the access and call chain.

(Also note that document.all behaves as an object, so "nullish" means *strictly* equal to null or undefined.)

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::pushOptionalChainTarget): Added.
(JSC::BytecodeGenerator::popOptionalChainTarget): Added.
(JSC::BytecodeGenerator::emitOptionalCheck): Added.

  • bytecompiler/BytecodeGenerator.h:

Implement early-out logic.

  • bytecompiler/NodesCodegen.cpp:

(JSC::BracketAccessorNode::emitBytecode):
(JSC::DotAccessorNode::emitBytecode):
(JSC::EvalFunctionCallNode::emitBytecode): Refactor so we can emitOptionalCheck in a single location.
(JSC::FunctionCallValueNode::emitBytecode):
(JSC::FunctionCallResolveNode::emitBytecode): Refactor so we can emitOptionalCheck in a single location.
(JSC::FunctionCallBracketNode::emitBytecode):
(JSC::FunctionCallDotNode::emitBytecode):
(JSC::CallFunctionCallDotNode::emitBytecode):
(JSC::ApplyFunctionCallDotNode::emitBytecode):
(JSC::DeleteBracketNode::emitBytecode):
(JSC::DeleteDotNode::emitBytecode):
(JSC::CoalesceNode::emitBytecode): Clean up.
(JSC::OptionalChainNode::emitBytecode): Added.
Implement ?. node and emit checks where needed.

  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:

Have OpIsUndefinedOrNull support constant registers.

  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createOptionalChain): Added.
(JSC::ASTBuilder::makeDeleteNode):
(JSC::ASTBuilder::makeFunctionCallNode):

  • parser/Lexer.cpp:

(JSC::Lexer<T>::lexWithoutClearingLineTerminator):

  • parser/NodeConstructors.h:

(JSC::OptionalChainNode::OptionalChainNode): Added.

  • parser/Nodes.h:

(JSC::ExpressionNode::isOptionalChain const): Added.
(JSC::ExpressionNode::isOptionalChainBase const): Added.
(JSC::ExpressionNode::setIsOptionalChainBase): Added.

  • parser/ParserTokens.h:
  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::makeFunctionCallNode):
(JSC::SyntaxChecker::createOptionalChain): Added.
Introduce new token and AST node, as well as an ExpressionNode field to mark LHSes with.

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseMemberExpression):
Parse optional chains by wrapping the access/call parse loop.

  • runtime/ExceptionHelpers.cpp:

(JSC::functionCallBase):
Ensure that TypeError messages don't include the '?.'.

  • runtime/Options.h:

Update feature flag, as ?. and ?? are a double feature of "nullish-aware" operators.

Tools:

  • Scripts/run-jsc-stress-tests:
Location:
trunk
Files:
1 added
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r248826 r248829  
     12019-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
    1122019-08-17  Ross Kirsling  <ross.kirsling@sony.com>
    213
  • trunk/JSTests/stress/nullish-coalescing.js

    r248426 r248829  
    1 //@ runNullishCoalescingEnabled
     1//@ runNullishAwareOperatorsEnabled
    22
    33function shouldBe(actual, expected) {
  • trunk/JSTests/stress/tail-call-recognize.js

    r232314 r248829  
     1//@ runNullishAwareOperatorsEnabled
     2
    13function callerMustBeRun() {
    24    if (!Object.is(callerMustBeRun.caller, runTests))
     
    151153    })();
    152154
     155    (function tailCallCoalesce() {
     156        "use strict";
     157        return false ?? callerMustBeRun();
     158    })();
     159
    153160    (function memberTailCall() {
    154161        "use strict";
     
    159166        "use strict";
    160167        return callerMustBeRun.bind()();
     168    })();
     169
     170    (function optionalTailCall() {
     171        "use strict";
     172        return callerMustBeRun?.();
    161173    })();
    162174
  • trunk/Source/JavaScriptCore/ChangeLog

    r248826 r248829  
     12019-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
    1772019-08-17  Ross Kirsling  <ross.kirsling@sony.com>
    278
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r248426 r248829  
    49654965}
    49664966
     4967void BytecodeGenerator::pushOptionalChainTarget()
     4968{
     4969    m_optionalChainTargetStack.append(newLabel());
     4970}
     4971
     4972void 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
     4985void BytecodeGenerator::emitOptionalCheck(RegisterID* src)
     4986{
     4987    ASSERT(m_optionalChainTargetStack.size());
     4988    emitJumpIfTrue(emitIsUndefinedOrNull(newTemporary(), src), m_optionalChainTargetStack.last().get());
     4989}
     4990
    49674991void ForInContext::finalize(BytecodeGenerator& generator, UnlinkedCodeBlock* codeBlock, unsigned bodyBytecodeEndOffset)
    49684992{
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r245906 r248829  
    960960        void popFinallyControlFlowScope();
    961961
     962        void pushOptionalChainTarget();
     963        void popOptionalChainTarget(RegisterID* dst, bool isDelete = false);
     964        void emitOptionalCheck(RegisterID* src);
     965
    962966        void pushIndexedForInScope(RegisterID* local, RegisterID* index);
    963967        void popIndexedForInScope(RegisterID* local);
     
    12671271        SegmentedVector<TryData, 8> m_tryData;
    12681272
     1273        Vector<Ref<Label>> m_optionalChainTargetStack;
     1274
    12691275        int m_nextConstantOffset { 0 };
    12701276
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r247819 r248829  
    745745    RefPtr<RegisterID> finalDest = generator.finalDestination(dst);
    746746
    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) {
    749756        generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
    750757        ret = generator.emitGetById(finalDest.get(), base.get(), static_cast<StringNode*>(m_subscript)->value());
    751758    } else {
    752         RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator));
    753759        RegisterID* property = generator.emitNodeForProperty(m_subscript);
    754760        generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
     
    764770RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
    765771{
     772    RefPtr<RegisterID> finalDest = generator.finalDestination(dst);
    766773    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
    768784    generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
    769     RegisterID* finalDest = generator.finalDestination(dst);
    770785    RegisterID* ret;
    771786    if (baseIsSuper) {
    772787        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);
    774789    } 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());
    777792    return ret;
    778793}
     
    849864
    850865    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)
    855876        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);
    869892}
    870893
     
    900923        return ret;
    901924    }
     925
    902926    RefPtr<RegisterID> func = generator.emitNode(m_expr);
    903927    RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get());
     928    if (isOptionalChainBase())
     929        generator.emitOptionalCheck(func.get());
     930
    904931    CallArguments callArguments(generator, m_args);
    905932    generator.emitLoad(callArguments.thisRegister(), jsUndefined());
     
    921948
    922949    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) {
    928960        generator.emitLoad(callArguments.thisRegister(), jsUndefined());
    929961        // This passes NoExpectedFunction because we expect that if the function is in a
    930962        // 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
    937974    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
    947978    RegisterID* ret = generator.emitCallInTailPosition(returnValue.get(), func.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes);
    948979    generator.emitProfileType(returnValue.get(), divotStart(), divotEnd());
     
    12761307RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
    12771308{
     1309    RefPtr<RegisterID> function = generator.tempDestination(dst);
     1310    RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get());
    12781311    bool baseIsSuper = m_base->isSuperNode();
    12791312    bool subscriptIsNonIndexString = isNonIndexStringElement(*m_subscript);
     
    12871320        else
    12881321            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
    12921327    RefPtr<RegisterID> thisRegister;
    12931328    if (baseIsSuper) {
     
    12981333        generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
    12991334        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());
    13011336        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());
    13031338    } else {
    13041339        RefPtr<RegisterID> property = generator.emitNodeForProperty(m_subscript);
    13051340        generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
    13061341        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());
    13081343        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
    13131349    CallArguments callArguments(generator, m_args);
    13141350    if (baseIsSuper) {
     
    13321368    if (baseIsSuper)
    13331369        generator.move(callArguments.thisRegister(), generator.ensureThis());
    1334     else
     1370    else {
    13351371        generator.emitNode(callArguments.thisRegister(), m_base);
     1372        if (m_base->isOptionalChainBase())
     1373            generator.emitOptionalCheck(callArguments.thisRegister());
     1374    }
    13361375    generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
    13371376    if (baseIsSuper) {
     
    13401379    } else
    13411380        generator.emitGetById(function.get(), callArguments.thisRegister(), m_ident);
     1381
     1382    if (isOptionalChainBase())
     1383        generator.emitOptionalCheck(function.get());
     1384
    13421385    RegisterID* ret = generator.emitCallInTailPosition(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd(), DebuggableCall::Yes);
    13431386    generator.emitProfileType(returnValue.get(), divotStart(), divotEnd());
     
    13491392RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
    13501393{
     1394    RefPtr<RegisterID> returnValue = generator.finalDestination(dst);
    13511395    RefPtr<RegisterID> base = generator.emitNode(m_base);
     1396
     1397    if (m_base->isOptionalChainBase())
     1398        generator.emitOptionalCheck(base.get());
     1399
    13521400    generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
     1401
    13531402    RefPtr<RegisterID> function;
    1354     RefPtr<RegisterID> returnValue = generator.finalDestination(dst);
    1355 
    13561403    auto makeFunction = [&] {
    13571404        if (m_base->isSuperNode()) {
     
    13601407        } else
    13611408            function = generator.emitGetById(generator.tempDestination(dst), base.get(), generator.propertyNames().builtinNames().callPublicName());
     1409
     1410        if (isOptionalChainBase())
     1411            generator.emitOptionalCheck(function.get());
    13621412    };
    13631413
     
    14311481    bool mayBeCall = areTrivialApplyArguments(m_args);
    14321482
     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
    14331489    RefPtr<RegisterID> function;
    1434     RefPtr<RegisterID> base = generator.emitNode(m_base);
    1435     RefPtr<RegisterID> returnValue = generator.finalDestination(dst);
    14361490    auto makeFunction = [&] {
    14371491        if (m_base->isSuperNode()) {
     
    14401494        } else
    14411495            function = generator.emitGetById(generator.tempDestination(dst), base.get(), generator.propertyNames().builtinNames().applyPublicName());
     1496
     1497        if (isOptionalChainBase())
     1498            generator.emitOptionalCheck(function.get());
    14421499    };
    14431500
     
    16931750RegisterID* DeleteBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
    16941751{
     1752    RefPtr<RegisterID> finalDest = generator.finalDestination(dst);
    16951753    RefPtr<RegisterID> r0 = generator.emitNode(m_base);
     1754
     1755    if (m_base->isOptionalChainBase())
     1756        generator.emitOptionalCheck(r0.get());
     1757
    16961758    RefPtr<RegisterID> r1 = generator.emitNode(m_subscript);
    1697 
    16981759    generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
    16991760    if (m_base->isSuperNode())
    17001761        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());
    17021763}
    17031764
     
    17061767RegisterID* DeleteDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
    17071768{
     1769    RefPtr<RegisterID> finalDest = generator.finalDestination(dst);
    17081770    RefPtr<RegisterID> r0 = generator.emitNode(m_base);
     1771
     1772    if (m_base->isOptionalChainBase())
     1773        generator.emitOptionalCheck(r0.get());
    17091774
    17101775    generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
    17111776    if (m_base->isSuperNode())
    17121777        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);
    17141779}
    17151780
     
    23472412
    23482413    generator.emitNode(temp.get(), m_expr1);
    2349     generator.emitJumpIfFalse(generator.emitUnaryOp<OpIsUndefinedOrNull>(generator.newTemporary(), temp.get()), target.get());
     2414    generator.emitJumpIfFalse(generator.emitIsUndefinedOrNull(generator.newTemporary(), temp.get()), target.get());
    23502415    generator.emitNodeInTailPosition(temp.get(), m_expr2);
    23512416    generator.emitLabel(target.get());
    23522417
    23532418    return generator.move(dst, temp.get());
     2419}
     2420
     2421// ------------------------------ OptionalChainNode ----------------------------
     2422
     2423RegisterID* 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();
    23542434}
    23552435
  • trunk/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm

    r248426 r248829  
    876876llintOpWithReturn(op_is_undefined_or_null, OpIsUndefinedOrNull, macro (size, get, dispatch, return)
    877877    get(m_operand, t0)
    878     assertNotConstant(size, t0)
    879     loadi TagOffset[cfr, t0, 8], t1
     878    loadConstantOrVariableTag(size, t0, t1)
    880879    ori 1, t1
    881880    cieq t1, NullTag, t1
  • trunk/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm

    r248426 r248829  
    828828
    829829llintOpWithReturn(op_is_undefined_or_null, OpIsUndefinedOrNull, macro (size, get, dispatch, return)
    830     get(m_operand, t0)
    831     loadq [cfr, t0, 8], t0
     830    get(m_operand, t1)
     831    loadConstantOrVariable(size, t1, t0)
    832832    andq ~TagBitUndefined, t0
    833833    cqeq t0, ValueNull, t0
  • trunk/Source/JavaScriptCore/parser/ASTBuilder.h

    r247819 r248829  
    130130
    131131    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);
    133133
    134134    JSC::SourceElements* createSourceElements() { return new (m_parserArena) JSC::SourceElements(); }
     
    361361    }
    362362
     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
    363369    ExpressionNode* createConditionalExpr(const JSTokenLocation& location, ExpressionNode* condition, ExpressionNode* lhs, ExpressionNode* rhs)
    364370    {
     
    11491155ExpressionNode* ASTBuilder::makeDeleteNode(const JSTokenLocation& location, ExpressionNode* expr, const JSTextPosition& start, const JSTextPosition& divot, const JSTextPosition& end)
    11501156{
     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
    11511167    if (!expr->isLocation())
    11521168        return new (m_parserArena) DeleteValueNode(location, expr);
     
    13461362}
    13471363
    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)
     1364ExpressionNode* 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)
    13491365{
    13501366    ASSERT(divot.offset >= divot.lineStartOffset);
     
    13531369
    13541370    if (func->isBytecodeIntrinsicNode()) {
     1371        ASSERT(!isOptionalCall);
    13551372        BytecodeIntrinsicNode* intrinsic = static_cast<BytecodeIntrinsicNode*>(func);
    13561373        if (intrinsic->type() == BytecodeIntrinsicNode::Type::Constant)
    13571374            return new (m_parserArena) BytecodeIntrinsicNode(BytecodeIntrinsicNode::Type::Function, location, intrinsic->emitter(), intrinsic->identifier(), args, divot, divotStart, divotEnd);
    13581375    }
     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
    13591389    if (!func->isLocation())
    13601390        return new (m_parserArena) FunctionCallValueNode(location, func, args, divot, divotStart, divotEnd);
  • trunk/Source/JavaScriptCore/parser/Lexer.cpp

    r248826 r248829  
    21592159    case CharacterQuestion:
    21602160        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            }
    21652172        }
    21662173        token = QUESTION;
  • trunk/Source/JavaScriptCore/parser/NodeConstructors.h

    r247819 r248829  
    677677    }
    678678
     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
    679686    inline ConditionalNode::ConditionalNode(const JSTokenLocation& location, ExpressionNode* logical, ExpressionNode* expr1, ExpressionNode* expr2)
    680687        : ExpressionNode(location)
  • trunk/Source/JavaScriptCore/parser/Nodes.h

    r247819 r248829  
    206206        virtual bool isBinaryOpNode() const { return false; }
    207207        virtual bool isFunctionCall() const { return false; }
     208        virtual bool isOptionalChain() const { return false; }
    208209
    209210        virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label&, Label&, FallThroughMode);
     
    213214        ResultType resultDescriptor() const { return m_resultType; }
    214215
     216        bool isOptionalChainBase() const { return m_isOptionalChainBase; }
     217        void setIsOptionalChainBase() { m_isOptionalChainBase = true; }
     218
    215219    private:
    216220        ResultType m_resultType;
     221        bool m_isOptionalChainBase { false };
    217222    };
    218223
     
    13111316
    13121317    private:
    1313         RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
     1318        RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = nullptr) final;
    13141319
    13151320        ExpressionNode* m_expr1;
    13161321        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 };
    13171341    };
    13181342
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r248711 r248829  
    48094809
    48104810    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();
    48174824            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++;
    48364838                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++;
    48424908                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;
    48754916            }
    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        }
     4936endOfChain:
     4937        if (optionalChainBase)
     4938            base = context.createOptionalChain(optionalChainLocation, optionalChainBase, base, !match(QUESTIONDOT));
     4939    } while (match(QUESTIONDOT));
     4940
    49084941    semanticFailIfTrue(baseIsSuper, "super is not valid in this context");
    49094942    while (newCount--)
  • trunk/Source/JavaScriptCore/parser/ParserTokens.h

    r247819 r248829  
    137137    DOTDOTDOT,
    138138    ARROWFUNCTION,
     139    QUESTIONDOT,
    139140    LastUntaggedToken,
    140141
  • trunk/Source/JavaScriptCore/parser/SyntaxChecker.h

    r245406 r248829  
    7474        ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr,
    7575        FunctionExpr, ClassExpr, SuperExpr, ImportExpr, BracketExpr, DotExpr, CallExpr,
    76         NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr,
     76        NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr, OptionalChain,
    7777        ConditionalExpr, AssignmentExpr, TypeofExpr,
    7878        DeleteExpr, ArrayLiteralExpr, BindingDestructuring, RestParameter,
     
    148148
    149149    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; }
    151151    ExpressionType createCommaExpr(const JSTokenLocation&, ExpressionType expr) { return expr; }
    152152    ExpressionType appendToCommaExpr(const JSTokenLocation&, ExpressionType& head, ExpressionType, ExpressionType next) { head = next; return next; }
     
    185185    ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int, int, int) { return NewExpr; }
    186186    ExpressionType createNewExpr(const JSTokenLocation&, ExpressionType, int, int) { return NewExpr; }
     187    ExpressionType createOptionalChain(const JSTokenLocation&, ExpressionType, ExpressionType, bool) { return OptionalChain; }
    187188    ExpressionType createConditionalExpr(const JSTokenLocation&, ExpressionType, ExpressionType, ExpressionType) { return ConditionalExpr; }
    188189    ExpressionType createAssignResolve(const JSTokenLocation&, const Identifier&, ExpressionType, int, int, int, AssignmentContext) { return AssignmentExpr; }
  • trunk/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp

    r243955 r248829  
    177177    }
    178178
     179    // Don't display the ?. of an optional call.
     180    if (idx > 1 && sourceText[idx] == '.' && sourceText[idx - 1] == '?')
     181        idx -= 2;
     182
    179183    return sourceText.left(idx + 1);
    180184}
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r248824 r248829  
    500500    v(bool, useWeakRefs, false, Normal, "Expose the WeakRef constructor.") \
    501501    v(bool, useBigInt, false, Normal, "If true, we will enable BigInt support.") \
    502     v(bool, useNullishCoalescing, false, Normal, "Enable support for the ?? operator.") \
     502    v(bool, useNullishAwareOperators, false, Normal, "Enable support for ?. and ?? operators.") \
    503503    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.") \
    504504    v(bool, forcePolyProto, false, Normal, "If true, create_this will always create an object with a poly proto structure.") \
  • trunk/Tools/ChangeLog

    r248828 r248829  
     12019-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
    1102019-08-17  Tim Horton  <timothy_horton@apple.com>
    211
  • trunk/Tools/Scripts/run-jsc-stress-tests

    r247819 r248829  
    700700end
    701701
    702 def runNullishCoalescingEnabled(*optionalTestSpecificOptions)
    703     run("nullish-coalescing-enabled", "--useNullishCoalescing=true" , *(FTL_OPTIONS + optionalTestSpecificOptions))
     702def runNullishAwareOperatorsEnabled(*optionalTestSpecificOptions)
     703    run("nullish-aware-operators-enabled", "--useNullishAwareOperators=true" , *(FTL_OPTIONS + optionalTestSpecificOptions))
    704704end
    705705
Note: See TracChangeset for help on using the changeset viewer.