WebKit Bugzilla
Attachment 358590 Details for
Bug 193127
: Array.prototype.flat/flatMap have a minor bug in ArraySpeciesCreate
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-193127-20190108080033.patch (text/plain), 36.27 KB, created by
Yusuke Suzuki
on 2019-01-08 08:00:34 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Yusuke Suzuki
Created:
2019-01-08 08:00:34 PST
Size:
36.27 KB
patch
obsolete
>Subversion Revision: 239711 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 3063c6d563e6c7336cdc252dbd376fb56849cbe9..bb1795b68793d26c2b5bc38a8e64e9f5a55a718a 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,95 @@ >+2019-01-08 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ Array.prototype.flat/flatMap have a minor bug in ArraySpeciesCreate >+ https://bugs.webkit.org/show_bug.cgi?id=193127 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ `== null` is frequently used idiom to check `null` or `undefined` in JS. >+ However, it has a problem in terms of the builtin JS implementation: it >+ returns true if masquerade-as-undefined objects (e.g. document.all) come. >+ >+ In this patch, we introduce a convenient builtin intrinsic @isUndefinedOrNull, >+ which is equivalent to C++ `JSValue::isUndefinedOrNull`. It does not consider >+ about masquerade-as-undefined objects, so that we can use it instead of >+ `value === null || value === @undefined`. We introduce is_undefined_or_null >+ bytecode, IsUndefinedOrNull DFG node and its DFG and FTL backends. Since >+ Null and Undefined have some bit patterns, we can implement this query >+ very efficiently. >+ >+ * builtins/ArrayIteratorPrototype.js: >+ (next): >+ * builtins/ArrayPrototype.js: >+ (globalPrivate.arraySpeciesCreate): >+ * builtins/GlobalOperations.js: >+ (globalPrivate.speciesConstructor): >+ (globalPrivate.copyDataProperties): >+ (globalPrivate.copyDataPropertiesNoExclusions): >+ * builtins/MapIteratorPrototype.js: >+ (next): >+ * builtins/SetIteratorPrototype.js: >+ (next): >+ * builtins/StringIteratorPrototype.js: >+ (next): >+ * builtins/StringPrototype.js: >+ (match): >+ (repeat): >+ (padStart): >+ (padEnd): >+ (intrinsic.StringPrototypeReplaceIntrinsic.replace): >+ (search): >+ (split): >+ (concat): >+ (globalPrivate.createHTML): >+ * builtins/TypedArrayPrototype.js: >+ (globalPrivate.typedArraySpeciesConstructor): >+ (map): >+ (filter): >+ * bytecode/BytecodeIntrinsicRegistry.h: >+ * bytecode/BytecodeList.rb: >+ * bytecode/BytecodeUseDef.h: >+ (JSC::computeUsesForBytecodeOffset): >+ (JSC::computeDefsForBytecodeOffset): >+ * bytecompiler/BytecodeGenerator.cpp: >+ (JSC::BytecodeGenerator::emitIsUndefinedOrNull): >+ * bytecompiler/BytecodeGenerator.h: >+ * bytecompiler/NodesCodegen.cpp: >+ (JSC::BytecodeIntrinsicNode::emit_intrinsic_isUndefinedOrNull): >+ * dfg/DFGAbstractInterpreterInlines.h: >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): >+ * dfg/DFGByteCodeParser.cpp: >+ (JSC::DFG::ByteCodeParser::parseBlock): >+ * dfg/DFGCapabilities.cpp: >+ (JSC::DFG::capabilityLevel): >+ * dfg/DFGClobberize.h: >+ (JSC::DFG::clobberize): >+ * dfg/DFGDoesGC.cpp: >+ (JSC::DFG::doesGC): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGNodeType.h: >+ * dfg/DFGPredictionPropagationPhase.cpp: >+ * dfg/DFGSafeToExecute.h: >+ (JSC::DFG::safeToExecute): >+ * dfg/DFGSpeculativeJIT32_64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * ftl/FTLCapabilities.cpp: >+ (JSC::FTL::canCompile): >+ * ftl/FTLLowerDFGToB3.cpp: >+ (JSC::FTL::DFG::LowerDFGToB3::compileNode): >+ (JSC::FTL::DFG::LowerDFGToB3::compileIsUndefinedOrNull): >+ * jit/JIT.cpp: >+ (JSC::JIT::privateCompileMainPass): >+ * jit/JIT.h: >+ * jit/JITOpcodes.cpp: >+ (JSC::JIT::emit_op_is_undefined_or_null): >+ * jit/JITOpcodes32_64.cpp: >+ (JSC::JIT::emit_op_is_undefined_or_null): >+ * llint/LowLevelInterpreter32_64.asm: >+ * llint/LowLevelInterpreter64.asm: >+ > 2019-01-07 Devin Rousso <drousso@apple.com> > > Web Inspector: extend XHR breakpoints to work with fetch >diff --git a/Source/JavaScriptCore/builtins/ArrayIteratorPrototype.js b/Source/JavaScriptCore/builtins/ArrayIteratorPrototype.js >index f21a0ab6287bc8d6eefe363a7a660c333d760afe..94eb5f43ea8fcc0c10f1b555b56249c733fa820d 100644 >--- a/Source/JavaScriptCore/builtins/ArrayIteratorPrototype.js >+++ b/Source/JavaScriptCore/builtins/ArrayIteratorPrototype.js >@@ -28,7 +28,7 @@ function next() > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("%ArrayIteratorPrototype%.next requires that |this| not be null or undefined"); > > let next = @getByIdDirectPrivate(this, "arrayIteratorNext"); >diff --git a/Source/JavaScriptCore/builtins/ArrayPrototype.js b/Source/JavaScriptCore/builtins/ArrayPrototype.js >index bb67a722bd2d4ba0084b86b0f718ff90fce7b949..1b518f61cd3a89dd7b721d895b4598251f7e6d1c 100644 >--- a/Source/JavaScriptCore/builtins/ArrayPrototype.js >+++ b/Source/JavaScriptCore/builtins/ArrayPrototype.js >@@ -761,7 +761,7 @@ function arraySpeciesCreate(array, length) > > if (@isObject(constructor)) { > constructor = constructor.@speciesSymbol; >- if (constructor == null) >+ if (@isUndefinedOrNull(constructor)) > return @newArrayWithSize(length); > } > >diff --git a/Source/JavaScriptCore/builtins/GlobalOperations.js b/Source/JavaScriptCore/builtins/GlobalOperations.js >index 62c29d5bca7abee4e508a663e497dc7a267fc36e..362d204652876ad1aa07b480262141b608cb835b 100644 >--- a/Source/JavaScriptCore/builtins/GlobalOperations.js >+++ b/Source/JavaScriptCore/builtins/GlobalOperations.js >@@ -77,7 +77,7 @@ function speciesConstructor(obj, defaultConstructor) > if (!@isObject(constructor)) > @throwTypeError("|this|.constructor is not an Object or undefined"); > constructor = constructor.@speciesSymbol; >- if (constructor == null) >+ if (@isUndefinedOrNull(constructor)) > return defaultConstructor; > if (@isConstructor(constructor)) > return constructor; >@@ -92,7 +92,7 @@ function copyDataProperties(target, source, excludedSet) > if (!@isObject(target)) > @throwTypeError("target needs to be an object"); > >- if (source == null) >+ if (@isUndefinedOrNull(source)) > return target; > > let from = @toObject(source); >@@ -119,7 +119,7 @@ function copyDataPropertiesNoExclusions(target, source) > if (!@isObject(target)) > @throwTypeError("target needs to be an object"); > >- if (source == null) >+ if (@isUndefinedOrNull(source)) > return target; > > let from = @toObject(source); >diff --git a/Source/JavaScriptCore/builtins/MapIteratorPrototype.js b/Source/JavaScriptCore/builtins/MapIteratorPrototype.js >index 31ca95b68bb1e168c070f16008d5c7744cdc2f5d..41cd408c7acf32d70b99a149cac15d16607c7d01 100644 >--- a/Source/JavaScriptCore/builtins/MapIteratorPrototype.js >+++ b/Source/JavaScriptCore/builtins/MapIteratorPrototype.js >@@ -48,7 +48,7 @@ function next() > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("%MapIteratorPrototype%.next requires that |this| not be null or undefined"); > > var bucket = @getByIdDirectPrivate(this, "mapBucket"); >diff --git a/Source/JavaScriptCore/builtins/SetIteratorPrototype.js b/Source/JavaScriptCore/builtins/SetIteratorPrototype.js >index b80b932894bb5f8721f0840a591a3c5425520cfa..183ed674f736082b2c1e20407a143cd36015540d 100644 >--- a/Source/JavaScriptCore/builtins/SetIteratorPrototype.js >+++ b/Source/JavaScriptCore/builtins/SetIteratorPrototype.js >@@ -45,7 +45,7 @@ function next() > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("%SetIteratorPrototype%.next requires that |this| not be null or undefined"); > > var bucket = @getByIdDirectPrivate(this, "setBucket"); >diff --git a/Source/JavaScriptCore/builtins/StringIteratorPrototype.js b/Source/JavaScriptCore/builtins/StringIteratorPrototype.js >index fc77b523995fb98fc4e6365cac02354e97d5a37b..d122de8b270ccf9a945150c41a2d200eb8aa97e8 100644 >--- a/Source/JavaScriptCore/builtins/StringIteratorPrototype.js >+++ b/Source/JavaScriptCore/builtins/StringIteratorPrototype.js >@@ -27,7 +27,7 @@ function next() > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("%StringIteratorPrototype%.next requires that |this| not be null or undefined"); > > var position = @getByIdDirectPrivate(this, "stringIteratorNextIndex"); >diff --git a/Source/JavaScriptCore/builtins/StringPrototype.js b/Source/JavaScriptCore/builtins/StringPrototype.js >index 4028f80105c3690587999d2ede0784ee50987faf..80fc826318949a02a0657803be64cf82369e5888 100644 >--- a/Source/JavaScriptCore/builtins/StringPrototype.js >+++ b/Source/JavaScriptCore/builtins/StringPrototype.js >@@ -29,7 +29,7 @@ function match(regexp) > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.match requires that |this| not be null or undefined"); > > if (regexp != null) { >@@ -101,7 +101,7 @@ function repeat(count) > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.repeat requires that |this| not be null or undefined"); > > var string = @toString(this); >@@ -120,7 +120,7 @@ function padStart(maxLength/*, fillString*/) > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.padStart requires that |this| not be null or undefined"); > > var string = @toString(this); >@@ -157,7 +157,7 @@ function padEnd(maxLength/*, fillString*/) > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.padEnd requires that |this| not be null or undefined"); > > var string = @toString(this); >@@ -221,7 +221,7 @@ function replace(search, replace) > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.replace requires that |this| not be null or undefined"); > > if (search != null) { >@@ -254,7 +254,7 @@ function localeCompare(that/*, locales, options */) > // http://ecma-international.org/publications/standards/Ecma-402.htm > > // 1. Let O be RequireObjectCoercible(this value). >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.localeCompare requires that |this| not be null or undefined"); > > // 2. Let S be ToString(O). >@@ -283,7 +283,7 @@ function search(regexp) > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.search requires that |this| not be null or undefined"); > > if (regexp != null) { >@@ -301,7 +301,7 @@ function split(separator, limit) > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.split requires that |this| not be null or undefined"); > > if (separator != null) { >@@ -328,7 +328,7 @@ function concat(arg /* ... */) > { > "use strict"; > >- if (this == null) >+ if (@isUndefinedOrNull(this)) > @throwTypeError("String.prototype.concat requires that |this| not be null or undefined"); > > if (@argumentCount() === 1) >@@ -340,7 +340,7 @@ function concat(arg /* ... */) > function createHTML(func, string, tag, attribute, value) > { > "use strict"; >- if (string == null) >+ if (@isUndefinedOrNull(string)) > @throwTypeError(`${func} requires that |this| not be null or undefined`); > let S = @toString(string); > let p1 = "<" + tag; >diff --git a/Source/JavaScriptCore/builtins/TypedArrayPrototype.js b/Source/JavaScriptCore/builtins/TypedArrayPrototype.js >index 989aa606414e647e34cf46d24fffabb480423ead..f5faa6d3beca0e4f998e32f28bce0fca71e969b2 100644 >--- a/Source/JavaScriptCore/builtins/TypedArrayPrototype.js >+++ b/Source/JavaScriptCore/builtins/TypedArrayPrototype.js >@@ -43,7 +43,7 @@ function typedArraySpeciesConstructor(value) > @throwTypeError("|this|.constructor is not an Object or undefined"); > > constructor = constructor.@speciesSymbol; >- if (constructor == null) >+ if (@isUndefinedOrNull(constructor)) > return @typedArrayGetOriginalConstructor(value); > // The lack of an @isConstructor(constructor) check here is not observable because > // the first thing we will do with the value is attempt to construct the result with it. >@@ -340,7 +340,7 @@ function map(callback /*, thisArg */) > result = new (@typedArrayGetOriginalConstructor(this))(length); > else { > var speciesConstructor = constructor.@speciesSymbol; >- if (speciesConstructor === null || speciesConstructor === @undefined) >+ if (@isUndefinedOrNull(speciesConstructor)) > result = new (@typedArrayGetOriginalConstructor(this))(length); > else { > result = new speciesConstructor(length); >@@ -381,7 +381,7 @@ function filter(callback /*, thisArg */) > result = new (@typedArrayGetOriginalConstructor(this))(resultLength); > else { > var speciesConstructor = constructor.@speciesSymbol; >- if (speciesConstructor === null || speciesConstructor === @undefined) >+ if (@isUndefinedOrNull(speciesConstructor)) > result = new (@typedArrayGetOriginalConstructor(this))(resultLength); > else { > result = new speciesConstructor(resultLength); >diff --git a/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h b/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h >index eda34ecd73bd77ac97d65888a987051c03b9d1e9..e97bd95b917f60ac21d9c82a70b58d54c1e874a8 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h >+++ b/Source/JavaScriptCore/bytecode/BytecodeIntrinsicRegistry.h >@@ -50,6 +50,7 @@ class Identifier; > macro(isRegExpObject) \ > macro(isMap) \ > macro(isSet) \ >+ macro(isUndefinedOrNull) \ > macro(tailCallForwardArguments) \ > macro(throwTypeError) \ > macro(throwRangeError) \ >diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.rb b/Source/JavaScriptCore/bytecode/BytecodeList.rb >index 211532d0f6602dd178c019c6adae43fc682bff1e..3c8b312e69a25cff115dc33e2586e27809566f89 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeList.rb >+++ b/Source/JavaScriptCore/bytecode/BytecodeList.rb >@@ -286,6 +286,7 @@ > :unsigned, > :is_empty, > :is_undefined, >+ :is_undefined_or_null, > :is_boolean, > :is_number, > :is_object, >diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >index 22792277c53b3cff99c879738ef64a124692ecd7..bba02b116b166bd9928e35fb6fe41e17cd8c0031 100644 >--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h >@@ -168,6 +168,7 @@ void computeUsesForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, const Ins > USES(OpTypeof, value) > USES(OpIsEmpty, operand) > USES(OpIsUndefined, operand) >+ USES(OpIsUndefinedOrNull, operand) > USES(OpIsBoolean, operand) > USES(OpIsNumber, operand) > USES(OpIsObject, operand) >@@ -403,6 +404,7 @@ void computeDefsForBytecodeOffset(Block* codeBlock, OpcodeID opcodeID, const Ins > DEFS(OpIdentityWithProfile, srcDst) > DEFS(OpIsEmpty, dst) > DEFS(OpIsUndefined, dst) >+ USES(OpIsUndefinedOrNull, dst) > DEFS(OpIsBoolean, dst) > DEFS(OpIsNumber, dst) > DEFS(OpIsObject, dst) >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >index 71eff15247eb8f976da169f43b17e984a6fcaf1b..51ee8efff35633ee608935e9c8f2d009447eab6c 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp >@@ -4193,6 +4193,12 @@ RegisterID* BytecodeGenerator::emitIsUndefined(RegisterID* dst, RegisterID* src) > return dst; > } > >+RegisterID* BytecodeGenerator::emitIsUndefinedOrNull(RegisterID* dst, RegisterID* src) >+{ >+ OpIsUndefinedOrNull::emit(this, dst, src); >+ return dst; >+} >+ > RegisterID* BytecodeGenerator::emitIsEmpty(RegisterID* dst, RegisterID* src) > { > OpIsEmpty::emit(this, dst, src); >diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >index 968eb09efa5263e94f53ed1703a0637f8a8decc2..6ad716eebd28fad7ca02affeb344026eedb94376 100644 >--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h >@@ -859,6 +859,7 @@ namespace JSC { > RegisterID* emitIsObject(RegisterID* dst, RegisterID* src); > RegisterID* emitIsNumber(RegisterID* dst, RegisterID* src); > RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src); >+ RegisterID* emitIsUndefinedOrNull(RegisterID* dst, RegisterID* src); > RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src); > RegisterID* emitIsDerivedArray(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, DerivedArrayType); } > void emitRequireObjectCoercible(RegisterID* value, const String& error); >diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >index 4c09459aa0d26bed42bdb245c27e6600559a847f..90b6d49bb9954d2e6b8c75f531b6eeddb958bf9f 100644 >--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp >@@ -1221,6 +1221,15 @@ RegisterID* BytecodeIntrinsicNode::emit_intrinsic_isSet(JSC::BytecodeGenerator& > return generator.move(dst, generator.emitIsSet(generator.tempDestination(dst), src.get())); > } > >+RegisterID* BytecodeIntrinsicNode::emit_intrinsic_isUndefinedOrNull(JSC::BytecodeGenerator& generator, JSC::RegisterID* dst) >+{ >+ ArgumentListNode* node = m_args->m_listNode; >+ RefPtr<RegisterID> src = generator.emitNode(node); >+ ASSERT(!node->m_next); >+ >+ return generator.move(dst, generator.emitIsUndefinedOrNull(generator.tempDestination(dst), src.get())); >+} >+ > RegisterID* BytecodeIntrinsicNode::emit_intrinsic_newArrayWithSize(JSC::BytecodeGenerator& generator, JSC::RegisterID* dst) > { > ArgumentListNode* node = m_args->m_listNode; >diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >index a8113f31380ce12d6603fd753e6b380d18e60127..a0bfa6f2e0f8b967dd64d214bb24e4e572e7784c 100644 >--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >@@ -1281,6 +1281,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi > > case IsEmpty: > case IsUndefined: >+ case IsUndefinedOrNull: > case IsBoolean: > case IsNumber: > case NumberIsInteger: >@@ -1302,6 +1303,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi > ? child.value().asCell()->structure(m_vm)->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)) > : child.value().isUndefined())); > break; >+ case IsUndefinedOrNull: >+ setConstant(node, jsBoolean(child.value().isUndefinedOrNull())); >+ break; > case IsBoolean: > setConstant(node, jsBoolean(child.value().isBoolean())); > break; >@@ -1389,6 +1393,13 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi > break; > } > >+ break; >+ case IsUndefinedOrNull: >+ if (!(child.m_type & SpecOther)) { >+ setConstant(node, jsBoolean(false)); >+ constantWasSet = true; >+ break; >+ } > break; > case IsBoolean: > if (!(child.m_type & ~SpecBoolean)) { >diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >index 93529db0f13f5a48333e80bb723d74e6a22e90f7..a6d3128f755ce8748a249bebb91f990000ecb0f5 100644 >--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp >@@ -5197,6 +5197,12 @@ void ByteCodeParser::parseBlock(unsigned limit) > set(bytecode.dst, addToGraph(IsUndefined, value)); > NEXT_OPCODE(op_is_undefined); > } >+ case op_is_undefined_or_null: { >+ auto bytecode = currentInstruction->as<OpIsUndefinedOrNull>(); >+ Node* value = get(bytecode.operand); >+ set(bytecode.dst, addToGraph(IsUndefinedOrNull, value)); >+ NEXT_OPCODE(op_is_undefined_or_null); >+ } > > case op_is_boolean: { > auto bytecode = currentInstruction->as<OpIsBoolean>(); >diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >index c3de14b7c67d8a0a12b771fd78c926993c68a980..405336384266cfc66ee437ed3c5180c97daa5e00 100644 >--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp >@@ -143,6 +143,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, const I > case op_instanceof_custom: > case op_is_empty: > case op_is_undefined: >+ case op_is_undefined_or_null: > case op_is_boolean: > case op_is_number: > case op_is_object: >diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h >index fcb3c6b6bcd0b00ad9eb369a4832e19836d690cc..58ec03035053a8d9e31aff33ccedf46ebc049331 100644 >--- a/Source/JavaScriptCore/dfg/DFGClobberize.h >+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h >@@ -176,6 +176,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu > case SameValue: > case IsEmpty: > case IsUndefined: >+ case IsUndefinedOrNull: > case IsBoolean: > case IsNumber: > case NumberIsInteger: >diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >index ddbfa5d83cf5977a07dc37af8d2890222d33f1db..4f79af6c4cc5c1d9a9581e712441ad703994cb2d 100644 >--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp >@@ -185,6 +185,7 @@ bool doesGC(Graph& graph, Node* node) > case InstanceOfCustom: > case IsEmpty: > case IsUndefined: >+ case IsUndefinedOrNull: > case IsBoolean: > case IsNumber: > case NumberIsInteger: >diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >index d7860ea9c1334324f2efd201350b34c23eaf4103..e6bac2df2ae6e36df3966be95353bbd1b1b69825 100644 >--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >@@ -2356,6 +2356,7 @@ class FixupPhase : public Phase { > case IsTypedArrayView: > case IsEmpty: > case IsUndefined: >+ case IsUndefinedOrNull: > case IsBoolean: > case IsNumber: > case IsObjectOrNull: >diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h >index 03fe46b95184da166aeff5dce1ec157107e693d9..ded0e2680b8e82018c3d4868335b4d03e4ac8fd2 100644 >--- a/Source/JavaScriptCore/dfg/DFGNodeType.h >+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h >@@ -368,6 +368,7 @@ namespace JSC { namespace DFG { > macro(IsCellWithType, NodeResultBoolean) \ > macro(IsEmpty, NodeResultBoolean) \ > macro(IsUndefined, NodeResultBoolean) \ >+ macro(IsUndefinedOrNull, NodeResultBoolean) \ > macro(IsBoolean, NodeResultBoolean) \ > macro(IsNumber, NodeResultBoolean) \ > macro(NumberIsInteger, NodeResultBoolean) \ >diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >index 1ba4eff373c8a570ea88cd71ae11210f493ff6d1..a5869b3c24334bd7d135a28e721d2af43cd00acc 100644 >--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >@@ -930,6 +930,7 @@ class PredictionPropagationPhase : public Phase { > case InstanceOfCustom: > case IsEmpty: > case IsUndefined: >+ case IsUndefinedOrNull: > case IsBoolean: > case IsNumber: > case NumberIsInteger: >diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >index 06da94f441f85d2456100cb18547104c58951b0f..48444a1409c49a188c5b0b926a27137824548909 100644 >--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h >@@ -327,6 +327,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node, bool igno > case InstanceOfCustom: > case IsEmpty: > case IsUndefined: >+ case IsUndefinedOrNull: > case IsBoolean: > case IsNumber: > case NumberIsInteger: >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >index 10bddf84252969dcf5dae46238f80595850b64f0..c299bccd61322c2ebbe8136447f34d15f6b97545 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >@@ -3564,6 +3564,23 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >+ case IsUndefinedOrNull: { >+ JSValueOperand value(this, node->child1()); >+ GPRTemporary result(this, Reuse, value, TagWord); >+ >+ GPRReg valueTagGPR = value.tagGPR(); >+ GPRReg resultGPR = result.gpr(); >+ >+ m_jit.move(valueTagGPR, resultGPR); >+ static_assert((JSValue::UndefinedTag + 1 == JSValue::NullTag) && (JSValue::NullTag & 0x1), ""); >+ m_jit.or32(CCallHelpers::TrustedImm32(1), resultGPR); >+ m_jit.compare32(CCallHelpers::Equal, resultGPR, CCallHelpers::TrustedImm32(JSValue::NullTag), resultGPR); >+ >+ booleanResult(resultGPR, node); >+ break; >+ } >+ >+ > case IsBoolean: { > JSValueOperand value(this, node->child1()); > GPRTemporary result(this, Reuse, value, TagWord); >diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >index 5d58fda3e76dd6b47f7e6d7ef826c46bad5565d8..32e08c99f465f006c19731bd2bb0a02b4c1488ba 100644 >--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >@@ -3873,6 +3873,21 @@ void SpeculativeJIT::compile(Node* node) > jsValueResult(result.gpr(), node, DataFormatJSBoolean); > break; > } >+ >+ case IsUndefinedOrNull: { >+ JSValueOperand value(this, node->child1()); >+ GPRTemporary result(this, Reuse, value); >+ >+ GPRReg valueGPR = value.gpr(); >+ GPRReg resultGPR = result.gpr(); >+ >+ m_jit.move(valueGPR, resultGPR); >+ m_jit.and64(CCallHelpers::TrustedImm32(~TagBitUndefined), resultGPR); >+ m_jit.compare64(CCallHelpers::Equal, resultGPR, CCallHelpers::TrustedImm32(ValueNull), resultGPR); >+ >+ unblessedBooleanResult(resultGPR, node); >+ break; >+ } > > case IsBoolean: { > JSValueOperand value(this, node->child1()); >diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >index 6b7d0fc989fb6a52d3d2fe77211d996b4fd7f5f0..7db5b56d326ddad666ec69cfa4d8d186d224ff4a 100644 >--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp >@@ -236,6 +236,7 @@ inline CapabilityLevel canCompile(Node* node) > case WeakMapSet: > case IsEmpty: > case IsUndefined: >+ case IsUndefinedOrNull: > case IsBoolean: > case IsNumber: > case NumberIsInteger: >diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >index 9156a4838ce9dbc469747e7a5a59c9ce2e33e65f..73836a8aad901e4324a7eea8e40e1ad9368d5137 100644 >--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp >@@ -1117,6 +1117,9 @@ class LowerDFGToB3 { > case IsUndefined: > compileIsUndefined(); > break; >+ case IsUndefinedOrNull: >+ compileIsUndefinedOrNull(); >+ break; > case IsBoolean: > compileIsBoolean(); > break; >@@ -9323,6 +9326,11 @@ class LowerDFGToB3 { > { > setBoolean(equalNullOrUndefined(m_node->child1(), AllCellsAreFalse, EqualUndefined)); > } >+ >+ void compileIsUndefinedOrNull() >+ { >+ setBoolean(isOther(lowJSValue(m_node->child1()), provenType(m_node->child1()))); >+ } > > void compileIsBoolean() > { >diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp >index 39bfd64c0405b589b6cbc0bac81757f9e6a6576f..5d6e07773dcbef5272d57151115a3d4dcfd168a2 100644 >--- a/Source/JavaScriptCore/jit/JIT.cpp >+++ b/Source/JavaScriptCore/jit/JIT.cpp >@@ -355,6 +355,7 @@ void JIT::privateCompileMainPass() > DEFINE_OP(op_instanceof_custom) > DEFINE_OP(op_is_empty) > DEFINE_OP(op_is_undefined) >+ DEFINE_OP(op_is_undefined_or_null) > DEFINE_OP(op_is_boolean) > DEFINE_OP(op_is_number) > DEFINE_OP(op_is_object) >diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h >index a1ca2ea8ba07bcfe185afe186f6e41ad95d224e9..6c9d3bb41b0931a99fd7ba6b6b0f2cfc354004a0 100644 >--- a/Source/JavaScriptCore/jit/JIT.h >+++ b/Source/JavaScriptCore/jit/JIT.h >@@ -545,6 +545,7 @@ namespace JSC { > void emit_op_instanceof_custom(const Instruction*); > void emit_op_is_empty(const Instruction*); > void emit_op_is_undefined(const Instruction*); >+ void emit_op_is_undefined_or_null(const Instruction*); > void emit_op_is_boolean(const Instruction*); > void emit_op_is_number(const Instruction*); > void emit_op_is_object(const Instruction*); >diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp >index 801a6a8221527d8a82f2e351ec1818395d73660f..6ec0749ac2246bd052d72084a7e5e5d16972128b 100644 >--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp >+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp >@@ -243,6 +243,21 @@ void JIT::emit_op_is_undefined(const Instruction* currentInstruction) > emitPutVirtualRegister(dst); > } > >+void JIT::emit_op_is_undefined_or_null(const Instruction* currentInstruction) >+{ >+ auto bytecode = currentInstruction->as<OpIsUndefinedOrNull>(); >+ int dst = bytecode.dst.offset(); >+ int value = bytecode.operand.offset(); >+ >+ emitGetVirtualRegister(value, regT0); >+ >+ and64(TrustedImm32(~TagBitUndefined), regT0); >+ compare64(Equal, regT0, TrustedImm32(ValueNull), regT0); >+ >+ boxBoolean(regT0, JSValueRegs { regT0 }); >+ emitPutVirtualRegister(dst); >+} >+ > void JIT::emit_op_is_boolean(const Instruction* currentInstruction) > { > auto bytecode = currentInstruction->as<OpIsBoolean>(); >diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp >index 87c1fa0729e10a2b8dee4a55732ad40a446418ad..acdc007b06b7d44be3a394923f9b7bbb17acdccd 100644 >--- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp >+++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp >@@ -252,6 +252,19 @@ void JIT::emit_op_is_undefined(const Instruction* currentInstruction) > emitStoreBool(dst, regT0); > } > >+void JIT::emit_op_is_undefined_or_null(const Instruction* currentInstruction) >+{ >+ auto bytecode = currentInstruction->as<OpIsUndefinedOrNull>(); >+ int dst = bytecode.dst.offset(); >+ int value = bytecode.operand.offset(); >+ >+ emitLoadTag(value, regT0); >+ static_assert((JSValue::UndefinedTag + 1 == JSValue::NullTag) && (JSValue::NullTag & 0x1), ""); >+ or32(TrustedImm32(1), regT0); >+ compare32(Equal, regT0, TrustedImm32(JSValue::NullTag), regT0); >+ emitStoreBool(dst, regT0); >+} >+ > void JIT::emit_op_is_boolean(const Instruction* currentInstruction) > { > auto bytecode = currentInstruction->as<OpIsBoolean>(); >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >index bb22057deaddb2c865e26965a3750a535c3549c3..05b3a69556d7da2b44f3c5ab6fe1a3bcbe150e44 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm >@@ -859,6 +859,16 @@ equalNullComparisonOp(op_neq_null, OpNeqNull, > macro (value) xori 1, value end) > > >+llintOpWithReturn(op_is_undefined_or_null, OpIsUndefinedOrNull, macro (size, get, dispatch, return) >+ get(operand, t0) >+ assertNotConstant(size, t0) >+ loadi TagOffset[cfr, t0, 8], t1 >+ ori 1, t1 >+ cieq t1, NullTag, t1 >+ return(BooleanTag, t1) >+end) >+ >+ > macro strictEqOp(name, op, equalityOperation) > llintOpWithReturn(op_%name%, op, macro (size, get, dispatch, return) > get(rhs, t2) >diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >index 2e7ed9ecb6c4d62762565f40c20f4cea083600f7..2a0f2b14fc6a5f579e40a173bf3303584f4fe2d2 100644 >--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm >@@ -790,6 +790,16 @@ equalNullComparisonOp(op_neq_null, OpNeqNull, > macro (value) xorq ValueTrue, value end) > > >+llintOpWithReturn(op_is_undefined_or_null, OpIsUndefinedOrNull, macro (size, get, dispatch, return) >+ get(operand, t0) >+ loadq [cfr, t0, 8], t0 >+ andq ~TagBitUndefined, t0 >+ cqeq t0, ValueNull, t0 >+ orq ValueFalse, t0 >+ return(t0) >+end) >+ >+ > macro strictEqOp(name, op, equalityOperation) > llintOpWithReturn(op_%name%, op, macro (size, get, dispatch, return) > get(rhs, t0) >diff --git a/JSTests/ChangeLog b/JSTests/ChangeLog >index 12c1b7814fd12982d1b51de168fe824785acc29e..edcecd30f0ab819140f119e4eb73362167008a80 100644 >--- a/JSTests/ChangeLog >+++ b/JSTests/ChangeLog >@@ -1,3 +1,16 @@ >+2019-01-08 Yusuke Suzuki <yusukesuzuki@slowstart.org> >+ >+ Array.prototype.flat/flatMap have a minor bug in ArraySpeciesCreate >+ https://bugs.webkit.org/show_bug.cgi?id=193127 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * stress/array-species-create-should-handle-masquerader.js: Added. >+ (shouldThrow): >+ * stress/is-undefined-or-null-builtin.js: Added. >+ (shouldBe): >+ (isUndefinedOrNull.vm.createBuiltin): >+ > 2019-01-04 Tadeu Zagallo <tzagallo@apple.com> > > Baseline version of get_by_id may corrupt metadata >diff --git a/JSTests/stress/array-species-create-should-handle-masquerader.js b/JSTests/stress/array-species-create-should-handle-masquerader.js >new file mode 100644 >index 0000000000000000000000000000000000000000..6caaccf9dcecc7470078c8be1a7201161dbd7d24 >--- /dev/null >+++ b/JSTests/stress/array-species-create-should-handle-masquerader.js >@@ -0,0 +1,21 @@ >+function shouldThrow(func, errorMessage) { >+ var errorThrown = false; >+ var error = null; >+ try { >+ func(); >+ } catch (e) { >+ errorThrown = true; >+ error = e; >+ } >+ if (!errorThrown) >+ throw new Error('not thrown'); >+ if (String(error) !== errorMessage) >+ throw new Error(`bad error: ${String(error)}`); >+} >+noInline(shouldThrow); >+ >+for (var i = 0; i < 1e5; ++i) { >+ shouldThrow(() => { >+ new (class extends Array { static get [Symbol.species]() { return makeMasquerader(); } })(1, 2, 3).flat().constructor >+ }, `TypeError: Masquerader is not a constructor`); >+} >diff --git a/JSTests/stress/is-undefined-or-null-builtin.js b/JSTests/stress/is-undefined-or-null-builtin.js >new file mode 100644 >index 0000000000000000000000000000000000000000..0f5a8c518ec282281675767ffd0a542900e3eeb4 >--- /dev/null >+++ b/JSTests/stress/is-undefined-or-null-builtin.js >@@ -0,0 +1,26 @@ >+function shouldBe(actual, expected) { >+ if (actual !== expected) >+ throw new Error('bad value: ' + actual); >+} >+noInline(shouldBe); >+ >+var isUndefinedOrNull = $vm.createBuiltin(`(function (value) { return @isUndefinedOrNull(value); })`); >+noInline(isUndefinedOrNull); >+ >+var masquerader = makeMasquerader(); >+for (var i = 0; i < 1e5; ++i) { >+ shouldBe(isUndefinedOrNull(null), true); >+ shouldBe(isUndefinedOrNull(undefined), true); >+ shouldBe(isUndefinedOrNull("Hello"), false); >+ shouldBe(isUndefinedOrNull(Symbol("Hello")), false); >+ shouldBe(isUndefinedOrNull(42), false); >+ shouldBe(isUndefinedOrNull(-42), false); >+ shouldBe(isUndefinedOrNull(0), false); >+ shouldBe(isUndefinedOrNull(-0), false); >+ shouldBe(isUndefinedOrNull(42.2), false); >+ shouldBe(isUndefinedOrNull(-42.2), false); >+ shouldBe(isUndefinedOrNull({}), false); >+ shouldBe(isUndefinedOrNull([]), false); >+ shouldBe(isUndefinedOrNull(true), false); >+ shouldBe(isUndefinedOrNull(masquerader), false); >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
saam
:
review+
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 193127
:
358571
|
358581
| 358590