diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index b32993f8482..d7189411c2b 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -672,6 +672,7 @@ PHASE(All) #define DEFAULT_CONFIG_ESSymbolDescription (true) #define DEFAULT_CONFIG_ESArrayFindFromLast (false) #define DEFAULT_CONFIG_ESNullishCoalescingOperator (true) +#define DEFAULT_CONFIG_ESLogicalAssignmentOperator (true) #define DEFAULT_CONFIG_ESGlobalThis (true) #ifdef COMPILE_DISABLE_ES6RegExPrototypeProperties // If ES6RegExPrototypeProperties needs to be disabled by compile flag, DEFAULT_CONFIG_ES6RegExPrototypeProperties should be false @@ -1092,93 +1093,93 @@ FLAGNR(Boolean, Force32BitByteCode, "Force CC to generate 32bit bytecode intende FLAGNR(Boolean, CollectGarbage , "Enable CollectGarbage API", DEFAULT_CONFIG_CollectGarbage) -FLAGR (Boolean, Intl , "Intl object support", DEFAULT_CONFIG_Intl) -FLAGNR(Boolean, IntlBuiltIns , "Intl built-in function support", DEFAULT_CONFIG_IntlBuiltIns) -FLAGNR(Boolean, IntlPlatform , "Make the internal Intl native helper object visible to user code", DEFAULT_CONFIG_IntlPlatform) +FLAGR(Boolean, Intl, "Intl object support", DEFAULT_CONFIG_Intl) +FLAGNR(Boolean, IntlBuiltIns, "Intl built-in function support", DEFAULT_CONFIG_IntlBuiltIns) +FLAGNR(Boolean, IntlPlatform, "Make the internal Intl native helper object visible to user code", DEFAULT_CONFIG_IntlPlatform) -FLAGNR(Boolean, JsBuiltIn , "JS Built-in function support", DEFAULT_CONFIG_JsBuiltIn) -FLAGNR(Boolean, JitRepro , "Add Function.invokeJit to execute codegen on an encoded rpc buffer", DEFAULT_CONFIG_JitRepro) -FLAGNR(Boolean, EntryPointInfoRpcData , "Keep encoded rpc buffer for jitted function on EntryPointInfo until cleanup", DEFAULT_CONFIG_EntryPointInfoRpcData) +FLAGNR(Boolean, JsBuiltIn, "JS Built-in function support", DEFAULT_CONFIG_JsBuiltIn) +FLAGNR(Boolean, JitRepro, "Add Function.invokeJit to execute codegen on an encoded rpc buffer", DEFAULT_CONFIG_JitRepro) +FLAGNR(Boolean, EntryPointInfoRpcData, "Keep encoded rpc buffer for jitted function on EntryPointInfo until cleanup", DEFAULT_CONFIG_EntryPointInfoRpcData) -FLAGNR(Boolean, LdChakraLib , "Access to the Chakra internal library with the __chakraLibrary keyword", DEFAULT_CONFIG_LdChakraLib) -FLAGNR(Boolean, TestChakraLib , "Access to the Chakra internal library with the __chakraLibrary keyword without global access restriction", DEFAULT_CONFIG_TestChakraLib) +FLAGNR(Boolean, LdChakraLib, "Access to the Chakra internal library with the __chakraLibrary keyword", DEFAULT_CONFIG_LdChakraLib) +FLAGNR(Boolean, TestChakraLib, "Access to the Chakra internal library with the __chakraLibrary keyword without global access restriction", DEFAULT_CONFIG_TestChakraLib) // ES6 (BLUE+1) features/flags // Master ES6 flag to enable STABLE ES6 features/flags -FLAGR(Boolean, ES6 , "Enable ES6 stable features", DEFAULT_CONFIG_ES6) +FLAGR(Boolean, ES6, "Enable ES6 stable features", DEFAULT_CONFIG_ES6) // Master ES6 flag to enable ALL sub ES6 features/flags -FLAGNRC(Boolean, ES6All , "Enable all ES6 features, both stable and unstable", DEFAULT_CONFIG_ES6All) +FLAGNRC(Boolean, ES6All, "Enable all ES6 features, both stable and unstable", DEFAULT_CONFIG_ES6All) // Master ES6 flag to enable Threshold ES6 features/flags -FLAGNRC(Boolean, ES6Experimental , "Enable all experimental features", DEFAULT_CONFIG_ES6All) +FLAGNRC(Boolean, ES6Experimental, "Enable all experimental features", DEFAULT_CONFIG_ES6All) // Per ES6 feature/flag -FLAGPR (Boolean, ES6, ES7AsyncAwait , "Enable ES7 'async' and 'await' keywords" , DEFAULT_CONFIG_ES7AsyncAwait) -FLAGPR (Boolean, ES6, ES6DateParseFix , "Enable ES6 Date.parse fixes" , DEFAULT_CONFIG_ES6DateParseFix) -FLAGPR (Boolean, ES6, ES6FunctionNameFull , "Enable ES6 Full function.name" , DEFAULT_CONFIG_ES6FunctionNameFull) -FLAGPR (Boolean, ES6, ES6Generators , "Enable ES6 generators" , DEFAULT_CONFIG_ES6Generators) -FLAGPR (Boolean, ES6, ES7ExponentiationOperator, "Enable ES7 exponentiation operator (**)" , DEFAULT_CONFIG_ES7ExponentionOperator) +FLAGPR(Boolean, ES6, ES7AsyncAwait, "Enable ES7 'async' and 'await' keywords", DEFAULT_CONFIG_ES7AsyncAwait) +FLAGPR(Boolean, ES6, ES6DateParseFix, "Enable ES6 Date.parse fixes", DEFAULT_CONFIG_ES6DateParseFix) +FLAGPR(Boolean, ES6, ES6FunctionNameFull, "Enable ES6 Full function.name", DEFAULT_CONFIG_ES6FunctionNameFull) +FLAGPR(Boolean, ES6, ES6Generators, "Enable ES6 generators", DEFAULT_CONFIG_ES6Generators) +FLAGPR(Boolean, ES6, ES7ExponentiationOperator, "Enable ES7 exponentiation operator (**)", DEFAULT_CONFIG_ES7ExponentionOperator) -FLAGPR (Boolean, ES6, ES7ValuesEntries , "Enable ES7 Object.values and Object.entries" , DEFAULT_CONFIG_ES7ValuesEntries) -FLAGPR (Boolean, ES6, ES7TrailingComma , "Enable ES7 trailing comma in function" , DEFAULT_CONFIG_ES7TrailingComma) -FLAGPR (Boolean, ES6, ES6IsConcatSpreadable , "Enable ES6 isConcatSpreadable Symbol" , DEFAULT_CONFIG_ES6IsConcatSpreadable) -FLAGPR (Boolean, ES6, ES6Math , "Enable ES6 Math extensions" , DEFAULT_CONFIG_ES6Math) +FLAGPR(Boolean, ES6, ES7ValuesEntries, "Enable ES7 Object.values and Object.entries", DEFAULT_CONFIG_ES7ValuesEntries) +FLAGPR(Boolean, ES6, ES7TrailingComma, "Enable ES7 trailing comma in function", DEFAULT_CONFIG_ES7TrailingComma) +FLAGPR(Boolean, ES6, ES6IsConcatSpreadable, "Enable ES6 isConcatSpreadable Symbol", DEFAULT_CONFIG_ES6IsConcatSpreadable) +FLAGPR(Boolean, ES6, ES6Math, "Enable ES6 Math extensions", DEFAULT_CONFIG_ES6Math) #ifndef COMPILE_DISABLE_ESDynamicImport #define COMPILE_DISABLE_ESDynamicImport 0 #endif -FLAGPR_REGOVR_EXP(Boolean, ES6, ESDynamicImport , "Enable dynamic import" , DEFAULT_CONFIG_ESDynamicImport) - -FLAGPR (Boolean, ES6, ES6Module , "Enable ES6 Modules" , DEFAULT_CONFIG_ES6Module) -FLAGPR (Boolean, ES6, ES6Object , "Enable ES6 Object extensions" , DEFAULT_CONFIG_ES6Object) -FLAGPR (Boolean, ES6, ES6Number , "Enable ES6 Number extensions" , DEFAULT_CONFIG_ES6Number) -FLAGPR (Boolean, ES6, ES6ObjectLiterals , "Enable ES6 Object literal extensions" , DEFAULT_CONFIG_ES6ObjectLiterals) -FLAGPR (Boolean, ES6, ES6Proxy , "Enable ES6 Proxy feature" , DEFAULT_CONFIG_ES6Proxy) -FLAGPR (Boolean, ES6, ES6Rest , "Enable ES6 Rest parameters" , DEFAULT_CONFIG_ES6Rest) -FLAGPR (Boolean, ES6, ES6Spread , "Enable ES6 Spread support" , DEFAULT_CONFIG_ES6Spread) -FLAGPR (Boolean, ES6, ES6String , "Enable ES6 String extensions" , DEFAULT_CONFIG_ES6String) -FLAGPR (Boolean, ES6, ES6StringPrototypeFixes, "Enable ES6 String.prototype fixes" , DEFAULT_CONFIG_ES6StringPrototypeFixes) -FLAGPR (Boolean, ES6, ES2018ObjectRestSpread , "Enable ES2018 Object Rest/Spread" , DEFAULT_CONFIG_ES2018ObjectRestSpread) - -FLAGPR (Boolean, ES6, ES6PrototypeChain , "Enable ES6 prototypes (Example: Date prototype is object)", DEFAULT_CONFIG_ES6PrototypeChain) -FLAGPR (Boolean, ES6, ES6ToPrimitive , "Enable ES6 ToPrimitive symbol" , DEFAULT_CONFIG_ES6ToPrimitive) -FLAGPR (Boolean, ES6, ES6ToLength , "Enable ES6 ToLength fixes" , DEFAULT_CONFIG_ES6ToLength) -FLAGPR (Boolean, ES6, ES6ToStringTag , "Enable ES6 ToStringTag symbol" , DEFAULT_CONFIG_ES6ToStringTag) -FLAGPR (Boolean, ES6, ES6Unicode , "Enable ES6 Unicode 6.0 extensions" , DEFAULT_CONFIG_ES6Unicode) -FLAGPR (Boolean, ES6, ES6UnicodeVerbose , "Enable ES6 Unicode 6.0 verbose failure output" , DEFAULT_CONFIG_ES6UnicodeVerbose) -FLAGPR (Boolean, ES6, ES6Unscopables , "Enable ES6 With Statement Unscopables" , DEFAULT_CONFIG_ES6Unscopables) -FLAGPR (Boolean, ES6, ES6RegExSticky , "Enable ES6 RegEx sticky flag" , DEFAULT_CONFIG_ES6RegExSticky) -FLAGPR (Boolean, ES6, ES2018RegExDotAll , "Enable ES2018 RegEx dotAll flag" , DEFAULT_CONFIG_ES2018RegExDotAll) -FLAGPR (Boolean, ES6, ESExportNsAs , "Enable ES experimental export * as name" , DEFAULT_CONFIG_ESExportNsAs) -FLAGPR (Boolean, ES6, ES2018AsyncIteration , "Enable ES2018 Async Iteration" , DEFAULT_CONFIG_ES2018AsyncIteration) -FLAGPR (Boolean, ES6, ESTopLevelAwait , "Enable Top Level Await in modules" , DEFAULT_CONFIG_ESTopLevelAwait) +FLAGPR_REGOVR_EXP(Boolean, ES6, ESDynamicImport, "Enable dynamic import", DEFAULT_CONFIG_ESDynamicImport) + +FLAGPR(Boolean, ES6, ES6Module, "Enable ES6 Modules", DEFAULT_CONFIG_ES6Module) +FLAGPR(Boolean, ES6, ES6Object, "Enable ES6 Object extensions", DEFAULT_CONFIG_ES6Object) +FLAGPR(Boolean, ES6, ES6Number, "Enable ES6 Number extensions", DEFAULT_CONFIG_ES6Number) +FLAGPR(Boolean, ES6, ES6ObjectLiterals, "Enable ES6 Object literal extensions", DEFAULT_CONFIG_ES6ObjectLiterals) +FLAGPR(Boolean, ES6, ES6Proxy, "Enable ES6 Proxy feature", DEFAULT_CONFIG_ES6Proxy) +FLAGPR(Boolean, ES6, ES6Rest, "Enable ES6 Rest parameters", DEFAULT_CONFIG_ES6Rest) +FLAGPR(Boolean, ES6, ES6Spread, "Enable ES6 Spread support", DEFAULT_CONFIG_ES6Spread) +FLAGPR(Boolean, ES6, ES6String, "Enable ES6 String extensions", DEFAULT_CONFIG_ES6String) +FLAGPR(Boolean, ES6, ES6StringPrototypeFixes, "Enable ES6 String.prototype fixes", DEFAULT_CONFIG_ES6StringPrototypeFixes) +FLAGPR(Boolean, ES6, ES2018ObjectRestSpread, "Enable ES2018 Object Rest/Spread", DEFAULT_CONFIG_ES2018ObjectRestSpread) + +FLAGPR(Boolean, ES6, ES6PrototypeChain, "Enable ES6 prototypes (Example: Date prototype is object)", DEFAULT_CONFIG_ES6PrototypeChain) +FLAGPR(Boolean, ES6, ES6ToPrimitive, "Enable ES6 ToPrimitive symbol", DEFAULT_CONFIG_ES6ToPrimitive) +FLAGPR(Boolean, ES6, ES6ToLength, "Enable ES6 ToLength fixes", DEFAULT_CONFIG_ES6ToLength) +FLAGPR(Boolean, ES6, ES6ToStringTag, "Enable ES6 ToStringTag symbol", DEFAULT_CONFIG_ES6ToStringTag) +FLAGPR(Boolean, ES6, ES6Unicode, "Enable ES6 Unicode 6.0 extensions", DEFAULT_CONFIG_ES6Unicode) +FLAGPR(Boolean, ES6, ES6UnicodeVerbose, "Enable ES6 Unicode 6.0 verbose failure output", DEFAULT_CONFIG_ES6UnicodeVerbose) +FLAGPR(Boolean, ES6, ES6Unscopables, "Enable ES6 With Statement Unscopables", DEFAULT_CONFIG_ES6Unscopables) +FLAGPR(Boolean, ES6, ES6RegExSticky, "Enable ES6 RegEx sticky flag", DEFAULT_CONFIG_ES6RegExSticky) +FLAGPR(Boolean, ES6, ES2018RegExDotAll, "Enable ES2018 RegEx dotAll flag", DEFAULT_CONFIG_ES2018RegExDotAll) +FLAGPR(Boolean, ES6, ESExportNsAs, "Enable ES experimental export * as name", DEFAULT_CONFIG_ESExportNsAs) +FLAGPR(Boolean, ES6, ES2018AsyncIteration, "Enable ES2018 Async Iteration", DEFAULT_CONFIG_ES2018AsyncIteration) +FLAGPR(Boolean, ES6, ESTopLevelAwait, "Enable Top Level Await in modules", DEFAULT_CONFIG_ESTopLevelAwait) #ifndef COMPILE_DISABLE_ES6RegExPrototypeProperties - #define COMPILE_DISABLE_ES6RegExPrototypeProperties 0 +#define COMPILE_DISABLE_ES6RegExPrototypeProperties 0 #endif -FLAGPR_REGOVR_EXP(Boolean, ES6, ES6RegExPrototypeProperties, "Enable ES6 properties on the RegEx prototype" , DEFAULT_CONFIG_ES6RegExPrototypeProperties) +FLAGPR_REGOVR_EXP(Boolean, ES6, ES6RegExPrototypeProperties, "Enable ES6 properties on the RegEx prototype", DEFAULT_CONFIG_ES6RegExPrototypeProperties) #ifndef COMPILE_DISABLE_ES6RegExSymbols - #define COMPILE_DISABLE_ES6RegExSymbols 0 +#define COMPILE_DISABLE_ES6RegExSymbols 0 #endif // When we enable ES6RegExSymbols check all String and Regex built-ins which are inlined in JIT and make sure the helper // sets implicit call flag before calling into script // Also, the corresponding helpers in JnHelperMethodList.h should be marked as being reentrant -FLAGPR_REGOVR_EXP(Boolean, ES6, ES6RegExSymbols , "Enable ES6 RegExp symbols" , DEFAULT_CONFIG_ES6RegExSymbols) +FLAGPR_REGOVR_EXP(Boolean, ES6, ES6RegExSymbols, "Enable ES6 RegExp symbols", DEFAULT_CONFIG_ES6RegExSymbols) -FLAGPR (Boolean, ES6, ES6HasInstance , "Enable ES6 @@hasInstance symbol" , DEFAULT_CONFIG_ES6HasInstance) -FLAGPR (Boolean, ES6, ES6Verbose , "Enable ES6 verbose trace" , DEFAULT_CONFIG_ES6Verbose) -FLAGPR (Boolean, ES6, ESObjectGetOwnPropertyDescriptors, "Enable Object.getOwnPropertyDescriptors" , DEFAULT_CONFIG_ESObjectGetOwnPropertyDescriptors) +FLAGPR(Boolean, ES6, ES6HasInstance, "Enable ES6 @@hasInstance symbol", DEFAULT_CONFIG_ES6HasInstance) +FLAGPR(Boolean, ES6, ES6Verbose, "Enable ES6 verbose trace", DEFAULT_CONFIG_ES6Verbose) +FLAGPR(Boolean, ES6, ESObjectGetOwnPropertyDescriptors, "Enable Object.getOwnPropertyDescriptors", DEFAULT_CONFIG_ESObjectGetOwnPropertyDescriptors) #ifndef COMPILE_DISABLE_ESSharedArrayBuffer - #define COMPILE_DISABLE_ESSharedArrayBuffer 0 +#define COMPILE_DISABLE_ESSharedArrayBuffer 0 #endif -FLAGPR_REGOVR_EXP(Boolean, ES6, ESSharedArrayBuffer , "Enable SharedArrayBuffer" , DEFAULT_CONFIG_ESSharedArrayBuffer) +FLAGPR_REGOVR_EXP(Boolean, ES6, ESSharedArrayBuffer, "Enable SharedArrayBuffer", DEFAULT_CONFIG_ESSharedArrayBuffer) // Newer language feature flags @@ -1191,6 +1192,9 @@ FLAGR(Boolean, ESNumericSeparator, "Enable Numeric Separator flag", DEFAULT_CONF // ES Nullish coalescing operator support (??) FLAGR(Boolean, ESNullishCoalescingOperator, "Enable nullish coalescing operator", DEFAULT_CONFIG_ESNullishCoalescingOperator) +// ES Logical assignment operator support (||=, &&=, ??=) +FLAGR(Boolean, ESLogicalAssignmentOperator, "Enable logical assignment operator", DEFAULT_CONFIG_ESLogicalAssignmentOperator) + // ES Hashbang support for interpreter directive syntax FLAGR(Boolean, ESHashbang, "Enable Hashbang syntax", DEFAULT_CONFIG_ESHashbang) diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp index a820a432043..8e1ffcd9e41 100644 --- a/lib/Parser/Parse.cpp +++ b/lib/Parser/Parse.cpp @@ -281,6 +281,9 @@ LPCWSTR Parser::GetTokenString(tokens token) case tkLogOr: return _u("||"); case tkLogAnd: return _u("&&"); case tkCoalesce: return _u("??"); + case tkAsgLogOr: return _u("||="); + case tkAsgLogAnd: return _u("&&="); + case tkAsgLogCoalesce: return _u("??="); case tkOr: return _u("|"); case tkXor: return _u("^"); case tkAnd: return _u("&"); @@ -12748,6 +12751,9 @@ ParseNode* Parser::CopyPnode(ParseNode *pnode) { case knopLogOr: case knopLogAnd: case knopCoalesce: + case knopAsgLogAnd: + case knopAsgLogOr: + case knopAsgCoalesce: case knopLsh: case knopRsh: case knopRs2: @@ -14042,6 +14048,21 @@ void PrintPnodeWIndent(ParseNode *pnode, int indentAmt) { PrintPnodeWIndent(pnode->AsParseNodeBin()->pnode1, indentAmt + INDENT_SIZE); PrintPnodeWIndent(pnode->AsParseNodeBin()->pnode2, indentAmt + INDENT_SIZE); break; + case knopAsgLogAnd: + Indent(indentAmt); + Output::Print(_u("&&=\n")); + PrintPnodeWIndent(pnode->AsParseNodeBin()->pnode1, indentAmt + INDENT_SIZE); + PrintPnodeWIndent(pnode->AsParseNodeBin()->pnode2, indentAmt + INDENT_SIZE); + case knopAsgLogOr: + Indent(indentAmt); + Output::Print(_u("||=\n")); + PrintPnodeWIndent(pnode->AsParseNodeBin()->pnode1, indentAmt + INDENT_SIZE); + PrintPnodeWIndent(pnode->AsParseNodeBin()->pnode2, indentAmt + INDENT_SIZE); + case knopAsgCoalesce: + Indent(indentAmt); + Output::Print(_u("??=\n")); + PrintPnodeWIndent(pnode->AsParseNodeBin()->pnode1, indentAmt + INDENT_SIZE); + PrintPnodeWIndent(pnode->AsParseNodeBin()->pnode2, indentAmt + INDENT_SIZE); //PTNODE(knopLsh , "<<" ,Lsh ,Bin ,fnopBin) case knopLsh: Indent(indentAmt); diff --git a/lib/Parser/Parse.h b/lib/Parser/Parse.h index bcbc9413c13..eaa1b9df561 100644 --- a/lib/Parser/Parse.h +++ b/lib/Parser/Parse.h @@ -18,6 +18,9 @@ enum koplNo, // not an operator koplCma, // , koplSpr, // ... + koplLorAsg, // ||= + koplLanAsg, // &&= + koplLcoAsg, // ??= koplAsg, // = += etc koplQue, // ?: koplLor, // || diff --git a/lib/Parser/Scan.cpp b/lib/Parser/Scan.cpp index f1b9efa71c3..46a92bc441c 100644 --- a/lib/Parser/Scan.cpp +++ b/lib/Parser/Scan.cpp @@ -1778,7 +1778,20 @@ tokens Scanner::ScanCore(bool identifyKwds) { p++; token = tkCoalesce; - break; + } + if (m_scriptContext->GetConfig()->IsEsLogicalAssignmentOperatorEnabled()) + { + Assert(token == tkQMark || token == tkCoalesce); + if (token == tkCoalesce && this->PeekFirst(p, last) == '=') + { + p++; + token = tkAsgLogCoalesce; + } + else if (this->PeekFirst(p, last) == '?' && this->PeekFirst(p + 1, last) == '=') + { + p += 2; + token = tkAsgLogCoalesce; + } } break; @@ -2181,6 +2194,12 @@ tokens Scanner::ScanCore(bool identifyKwds) case '|': p++; token = tkLogOr; + if (m_scriptContext->GetConfig()->IsEsLogicalAssignmentOperatorEnabled() && this->PeekFirst(p, last) == '=') + { + p++; + token = tkAsgLogOr; + break; + } break; } break; @@ -2196,6 +2215,12 @@ tokens Scanner::ScanCore(bool identifyKwds) case '&': p++; token = tkLogAnd; + if (m_scriptContext->GetConfig()->IsEsLogicalAssignmentOperatorEnabled() && this->PeekFirst(p, last) == '=') + { + p++; + token = tkAsgLogAnd; + break; + } break; } break; diff --git a/lib/Parser/kwd-lsc.h b/lib/Parser/kwd-lsc.h index dba31b1c5d9..e5e53a8f342 100644 --- a/lib/Parser/kwd-lsc.h +++ b/lib/Parser/kwd-lsc.h @@ -146,6 +146,9 @@ TOK_DCL(tkColon , No, knopNone , No, knopNone ) // : TOK_DCL(tkLogOr ,Lor, knopLogOr , No, knopNone ) // || TOK_DCL(tkLogAnd ,Lan, knopLogAnd , No, knopNone ) // && TOK_DCL(tkCoalesce ,Lco, knopCoalesce,No, knopNone ) // ?? +TOK_DCL(tkAsgLogOr ,LorAsg ,knopAsgLogOr , No ,knopNone) // ||= +TOK_DCL(tkAsgLogAnd ,LanAsg ,knopAsgLogAnd , No ,knopNone) // &&= +TOK_DCL(tkAsgLogCoalesce,LcoAsg ,knopAsgCoalesce, No ,knopNone) // ??= TOK_DCL(tkOr ,Bor, knopOr , No, knopNone ) // | TOK_DCL(tkXor ,Xor, knopXor , No, knopNone ) // ^ TOK_DCL(tkAnd ,Ban, knopAnd , No, knopNone ) // & diff --git a/lib/Parser/ptlist.h b/lib/Parser/ptlist.h index a340d73f5bd..ebde042b672 100644 --- a/lib/Parser/ptlist.h +++ b/lib/Parser/ptlist.h @@ -110,6 +110,9 @@ PTNODE(knopAsgOr , "|=" , Or_A , Bin , fnopBin|fn PTNODE(knopAsgLsh , "<<=" , Shl_A , Bin , fnopBin|fnopAsg , "LeftShiftAssignExpr" ) PTNODE(knopAsgRsh , ">>=" , Shr_A , Bin , fnopBin|fnopAsg , "RightShiftAssignExpr" ) PTNODE(knopAsgRs2 , ">>>=" , ShrU_A , Bin , fnopBin|fnopAsg , "UnsignedRightShiftAssignExpr" ) +PTNODE(knopAsgLogOr , "||=" , Nop , Bin , fnopBin|fnopAsg , "LogicalOrAssignExpr") +PTNODE(knopAsgLogAnd , "&&=" , Nop , Bin , fnopBin|fnopAsg , "LogicalAndAssignExpr") +PTNODE(knopAsgCoalesce, "??=" , Nop , Bin , fnopBin|fnopAsg , "LogicalCoalesceAssignExpr") //___end range PTNODE(knopMember , ":" , Nop , Bin , fnopNotExprStmt|fnopBin, "MemberOper" ) diff --git a/lib/Runtime/Base/ThreadConfigFlagsList.h b/lib/Runtime/Base/ThreadConfigFlagsList.h index 98fbdef244e..9c7992e11ca 100644 --- a/lib/Runtime/Base/ThreadConfigFlagsList.h +++ b/lib/Runtime/Base/ThreadConfigFlagsList.h @@ -46,6 +46,7 @@ FLAG_RELEASE(IsESBigIntEnabled, ESBigInt) FLAG_RELEASE(IsESNumericSeparatorEnabled, ESNumericSeparator) FLAG_RELEASE(IsESHashbangEnabled, ESHashbang) FLAG_RELEASE(IsESNullishCoalescingOperatorEnabled, ESNullishCoalescingOperator) +FLAG_RELEASE(IsEsLogicalAssignmentOperatorEnabled, ESLogicalAssignmentOperator) FLAG_RELEASE(IsESExportNsAsEnabled, ESExportNsAs) FLAG_RELEASE(IsESSymbolDescriptionEnabled, ESSymbolDescription) FLAG_RELEASE(IsESArrayFindFromLastEnabled , ESArrayFindFromLast) diff --git a/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h b/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h index bba7110bfd0..70e204c48c0 100644 --- a/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h +++ b/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h @@ -6,7 +6,7 @@ // NOTE: If there is a merge conflict the correct fix is to make a new GUID. // This file was generated with tools/regenByteCode.py -// {ba0f47b7-ac35-42ef-8dfa-1935af336123} +// {9d9d79a0-458e-4480-9369-b9542c825ef3} const GUID byteCodeCacheReleaseFileVersion = -{ 0xba0f47b7, 0xac35, 0x42ef, {0x8d, 0xfa, 0x19, 0x35, 0xaf, 0x33, 0x61, 0x23 } }; +{ 0x9d9d79a0, 0x458e, 0x4480, {0x93, 0x69, 0xb9, 0x54, 0x2c, 0x82, 0x5e, 0xf3 } }; diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index 499051dc77c..46fe500561d 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -9400,6 +9400,7 @@ void EmitGeneratingBooleanExpression(ParseNode *expr, Js::ByteCodeLabel trueLabe { case knopLogOr: + case knopAsgLogOr: { byteCodeGenerator->StartStatement(expr); Js::ByteCodeLabel leftFalse = byteCodeGenerator->Writer()->DefineLabel(); @@ -9413,6 +9414,7 @@ void EmitGeneratingBooleanExpression(ParseNode *expr, Js::ByteCodeLabel trueLabe } case knopLogAnd: + case knopAsgLogAnd: { byteCodeGenerator->StartStatement(expr); Js::ByteCodeLabel leftTrue = byteCodeGenerator->Writer()->DefineLabel(); @@ -11710,26 +11712,38 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func // otherwise. (In other words, the "truth" of the right-hand expression is never tested.) // PTNODE(knopLogOr , "||" ,None ,Bin ,fnopBin) case knopLogOr: + case knopAsgLogOr: { STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode); Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel(); // We use a single dest here for the whole generating boolean expr, because we were poorly // optimizing the previous version where we had a dest for each level - funcInfo->AcquireLoc(pnode); + const Js::RegSlot result_location = funcInfo->AcquireLoc(pnode); EmitGeneratingBooleanExpression(pnode, doneLabel, true, doneLabel, true, pnode->location, false, byteCodeGenerator, funcInfo); + if (pnode->nop == knopAsgLogOr) + { + EmitAssignment(pnode, pnode->AsParseNodeBin()->pnode1, result_location, byteCodeGenerator, funcInfo); + } byteCodeGenerator->Writer()->MarkLabel(doneLabel); ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode); break; } // PTNODE(knopLogAnd , "&&" ,None ,Bin ,fnopBin) case knopLogAnd: + case knopAsgLogAnd: { STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode); Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel(); // We use a single dest here for the whole generating boolean expr, because we were poorly // optimizing the previous version where we had a dest for each level - funcInfo->AcquireLoc(pnode); + const Js::RegSlot result_location = funcInfo->AcquireLoc(pnode); EmitGeneratingBooleanExpression(pnode, doneLabel, true, doneLabel, true, pnode->location, false, byteCodeGenerator, funcInfo); + + if (pnode->nop == knopAsgLogAnd) + { + EmitAssignment(pnode, pnode->AsParseNodeBin()->pnode1, result_location, byteCodeGenerator, funcInfo); + } + byteCodeGenerator->Writer()->MarkLabel(doneLabel); ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode); break; @@ -11739,10 +11753,11 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func // If the left hand side is null or undefined it resolves to the right hand side // PTNODE(knopCoalesce , "??" ,None ,Bin ,fnopBin) case knopCoalesce: + case knopAsgCoalesce: { STARTSTATEMENET_IFTOPLEVEL(isTopLevel, pnode); Js::ByteCodeLabel doneLabel = byteCodeGenerator->Writer()->DefineLabel(); - funcInfo->AcquireLoc(pnode); + const Js::RegSlot result_location = funcInfo->AcquireLoc(pnode); // LHS Emit(pnode->AsParseNodeBin()->pnode1, byteCodeGenerator, funcInfo, false); @@ -11756,6 +11771,11 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A_ReuseLoc, pnode->location, pnode->AsParseNodeBin()->pnode2->location); funcInfo->ReleaseLoc(pnode->AsParseNodeBin()->pnode2); + if (pnode->nop == knopAsgCoalesce) + { + EmitAssignment(pnode, pnode->AsParseNodeBin()->pnode1, result_location, byteCodeGenerator, funcInfo); + } + byteCodeGenerator->Writer()->MarkLabel(doneLabel); ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode); break; @@ -11793,7 +11813,6 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func break; } - case knopAsgAdd: case knopAsgSub: case knopAsgMul: diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp index c859ef631fd..459148f0f4b 100644 --- a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp +++ b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp @@ -4955,7 +4955,9 @@ void AssignRegisters(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) byteCodeGenerator->EnregisterConstant(1); CheckMaybeEscapedUse(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator); break; + case knopCoalesce: + case knopAsgCoalesce: case knopObject: byteCodeGenerator->AssignNullConstRegister(); break; diff --git a/test/Operators/logicalAndAssignment.js b/test/Operators/logicalAndAssignment.js new file mode 100644 index 00000000000..36a982dc725 --- /dev/null +++ b/test/Operators/logicalAndAssignment.js @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft Corporation and contributors. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); + +assert.sameValue = function sameValue(expected, actual, message) { + assert.areEqual(actual, expected, message); +} + +const tests = [ + { + name: "lgcl and assignment operator", + body: function () { + var value = undefined; + assert.sameValue(value &&= 1, undefined, "(value &&= 1) === undefined; where value = undefined"); + + value = null; + assert.sameValue(value &&= 1, null, "(value &&= 1) === null where value = null"); + + value = false; + assert.sameValue(value &&= 1, false, "(value &&= 1) === false; where value = false"); + + value = 0; + assert.sameValue(value &&= 1, 0, "(value &&= 1) === 0; where value = 0"); + + value = -0; + assert.sameValue(value &&= 1, -0, "(value &&= 1) === -0; where value = -0"); + + value = NaN; + assert.sameValue(value &&= 1, NaN, "(value &&= 1) === NaN; where value = NaN"); + + value = ""; + assert.sameValue(value &&= 1, "", '(value &&= 1) === "" where value = ""'); + + + + value = true; + assert.sameValue(value &&= 1, 1, "(value &&= 1) === 1; where value = true"); + + value = 2; + assert.sameValue(value &&= 1, 1, "(value &&= 1) === 1; where value = 2"); + + value = "test"; + assert.sameValue(value &&= 1, 1, '(value &&= 1) === 1; where value = "test"'); + + var sym = Symbol(""); + value = sym; + assert.sameValue(value &&= 1, 1, "(value &&= 1) === 1; where value = Symbol()"); + + var obj = {}; + value = obj; + assert.sameValue(value &&= 1, 1, "(value &&= 1) === 1; where value = {}"); + } + }, + { + name: "lgcl and eval strict", + body: function () { + "use strict", + eval &&= 20; + } + }, + { + name: "lgcl and arguments strict", + body: function () { + "use strict", + arguments &&= 20; + } + }, + { + name: "lgcl and assignment lhs before rhs", + body: function () { + function DummyError() { } + + class TestError extends Error {} + + assert.throws(function () { + var base = null; + var prop = function () { + throw new DummyError(); + }; + var expr = function () { + throw new TestError("right-hand side expression evaluated"); + }; + + base[prop()] &&= expr(); + }, DummyError); + + // assert.throws(function () { + // var base = null; + // var prop = { + // toString: function () { + // throw new TestError("property key evaluated"); + // } + // }; + // var expr = function () { + // throw new TestError("right-hand side expression evaluated"); + // }; + + // base[prop] &&= expr(); + // }, TypeError); + + // var count = 0; + // var obj = {}; + // function incr() { + // return ++count; + // } + + // assert.sameValue(obj[incr()] &&= incr(), undefined, "obj[incr()] &&= incr()"); + // assert.sameValue(obj[1], undefined, "obj[1]"); + // assert.sameValue(count, 1, "count"); + + // obj[2] = 1; + // assert.sameValue(obj[incr()] &&= incr(), 3, "obj[incr()] &&= incr()"); + // assert.sameValue(obj[2], 3, "obj[2]"); + // assert.sameValue(count, 3, "count"); + } + } +] + +testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" }); diff --git a/test/Operators/rlexe.xml b/test/Operators/rlexe.xml index 30668c76d73..74a8b6b25cd 100644 --- a/test/Operators/rlexe.xml +++ b/test/Operators/rlexe.xml @@ -55,12 +55,6 @@ div.baseline - - - equals.js - equals.baseline - - instanceof.js @@ -83,6 +77,12 @@ logAnd.baseline + + + logicalAndAssignment.js + -args summary -endargs + + logOr.js