WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-150812-20151102134557.patch (text/plain), 48.31 KB, created by
Keith Miller
on 2015-11-02 13:46:53 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Keith Miller
Created:
2015-11-02 13:46:53 PST
Size:
48.31 KB
patch
obsolete
>Subversion Revision: 191908 >diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog >index 7e84d93d7bb1b5ea3e779f1c884832a2bece94ca..623d0bc2fc211b7dfab4827e35bb89c6b5e2aabc 100644 >--- a/Source/JavaScriptCore/ChangeLog >+++ b/Source/JavaScriptCore/ChangeLog >@@ -1,3 +1,46 @@ >+2015-11-02 Keith Miller <keith_miller@apple.com> >+ >+ [ES6] Add support for Symbol.replace. >+ https://bugs.webkit.org/show_bug.cgi?id=150812 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch adds support for Symbol.replace. This patch changes >+ String.prototype.replace so it now calls the Symbol.replace property >+ on the first argument. The underlying behavior for both a string >+ first argument and a non-string first argument remains unchanged. >+ In a future patch we need to update the code to reflectthe ES6 spec. >+ Finally, most of the replace helper code has been moved to >+ RegExpPrototype.cpp; partly, because the code was no longer relevent >+ but also because some of the code is shared but inherently tied to >+ regular expressions. >+ >+ * runtime/CommonIdentifiers.h: >+ * runtime/JSGlobalObject.cpp: >+ (JSC::JSGlobalObject::init): >+ * runtime/RegExpPrototype.cpp: >+ (JSC::RegExpPrototype::finishCreation): >+ (JSC::StringRange::StringRange): >+ (JSC::substituteBackreferencesSlow): >+ (JSC::jsSpliceSubstrings): >+ (JSC::jsSpliceSubstringsWithSeparators): >+ (JSC::removeUsingRegExpSearch): >+ (JSC::regExpProtoFuncReplaceSymbol): >+ * runtime/RegExpPrototype.h: >+ (JSC::RegExpPrototype::create): >+ (JSC::substituteBackreferences): >+ * runtime/StringPrototype.cpp: >+ (JSC::replaceUsingStringSearch): >+ (JSC::stringProtoFuncReplace): >+ (JSC::substituteBackreferencesSlow): Deleted. >+ (JSC::substituteBackreferences): Deleted. >+ (JSC::StringRange::StringRange): Deleted. >+ (JSC::jsSpliceSubstrings): Deleted. >+ (JSC::jsSpliceSubstringsWithSeparators): Deleted. >+ (JSC::removeUsingRegExpSearch): Deleted. >+ (JSC::replaceUsingRegExpSearch): Deleted. >+ * tests/es6.yaml: >+ > 2015-11-02 Mark Lam <mark.lam@apple.com> > > Snippefy op_add for the baseline JIT. >diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >index 9572bc1f4bdf2ad62745a24702511a64c79f905d..aa64f1c5a0cd630e32bf9e1117d5ec83a6b8d310 100644 >--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h >+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h >@@ -252,7 +252,6 @@ > macro(hasInstance) \ > macro(isConcatSpreadable) \ > macro(match) \ >- macro(replace) \ > macro(search) \ > macro(species) \ > macro(split) \ >@@ -261,6 +260,7 @@ > #define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \ > macro(iterator) \ > macro(unscopables) \ >+ macro(replace) \ > macro(toStringTag) > > #define JSC_COMMON_BYTECODE_INTRINSICS_EACH_NAME(macro) \ >diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >index b30ebf3ceef136376aa533b14635ac324343e78c..e4a9b5f4a5f04598cee0accfbb83c22410e4f7e0 100644 >--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp >@@ -354,7 +354,7 @@ void JSGlobalObject::init(VM& vm) > > RegExp* emptyRegex = RegExp::create(vm, "", NoFlags); > >- m_regExpPrototype.set(vm, this, RegExpPrototype::create(vm, RegExpPrototype::createStructure(vm, this, m_objectPrototype.get()), emptyRegex)); >+ m_regExpPrototype.set(vm, this, RegExpPrototype::create(vm, this, RegExpPrototype::createStructure(vm, this, m_objectPrototype.get()), emptyRegex)); > m_regExpStructure.set(vm, this, RegExpObject::createStructure(vm, this, m_regExpPrototype.get())); > m_regExpMatchesArrayStructure.set(vm, this, createRegExpMatchesArrayStructure(vm, *this)); > >diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp >index fb6fc5f4aaf19907569f441adb9573f53b9ad101..9c44794d2aacc53bcbd0a444fe7ea46bc2447baf 100644 >--- a/Source/JavaScriptCore/runtime/RegExpPrototype.cpp >+++ b/Source/JavaScriptCore/runtime/RegExpPrototype.cpp >@@ -22,6 +22,7 @@ > #include "RegExpPrototype.h" > > #include "ArrayPrototype.h" >+#include "CachedCall.h" > #include "Error.h" > #include "JSArray.h" > #include "JSCJSValue.h" >@@ -32,6 +33,7 @@ > #include "Lexer.h" > #include "ObjectPrototype.h" > #include "JSCInlines.h" >+#include "RegExpConstructor.h" > #include "RegExpObject.h" > #include "RegExp.h" > #include "RegExpCache.h" >@@ -43,6 +45,7 @@ static EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState*); > static EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState*); > static EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState*); > static EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState*); >+static EncodedJSValue JSC_HOST_CALL regExpProtoFuncReplaceSymbol(ExecState*); > static EncodedJSValue JSC_HOST_CALL regExpProtoGetterGlobal(ExecState*); > static EncodedJSValue JSC_HOST_CALL regExpProtoGetterIgnoreCase(ExecState*); > static EncodedJSValue JSC_HOST_CALL regExpProtoGetterMultiline(ExecState*); >@@ -81,8 +84,468 @@ bool RegExpPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, Prop > return getStaticFunctionSlot<Base>(exec, regExpPrototypeTable, jsCast<RegExpPrototype*>(object), propertyName, slot); > } > >+void RegExpPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) >+{ >+ Base::finishCreation(vm); >+ >+ JSC_NATIVE_FUNCTION(vm.propertyNames->replaceSymbol, regExpProtoFuncReplaceSymbol, DontEnum | DontDelete | ReadOnly, 2); >+} >+ > // ------------------------------ Functions --------------------------- > >+ >+struct StringRange { >+ StringRange(int pos, int len) >+ : position(pos) >+ , length(len) >+ { >+ } >+ >+ StringRange() >+ { >+ } >+ >+ int position; >+ int length; >+}; >+ >+String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i) >+{ >+ StringBuilder substitutedReplacement; >+ int offset = 0; >+ do { >+ if (i + 1 == replacement.length()) >+ break; >+ >+ UChar ref = replacement[i + 1]; >+ if (ref == '$') { >+ // "$$" -> "$" >+ ++i; >+ substitutedReplacement.append(replacement.substring(offset, i - offset)); >+ offset = i + 1; >+ continue; >+ } >+ >+ int backrefStart; >+ int backrefLength; >+ int advance = 0; >+ if (ref == '&') { >+ backrefStart = ovector[0]; >+ backrefLength = ovector[1] - backrefStart; >+ } else if (ref == '`') { >+ backrefStart = 0; >+ backrefLength = ovector[0]; >+ } else if (ref == '\'') { >+ backrefStart = ovector[1]; >+ backrefLength = source.length() - backrefStart; >+ } else if (reg && ref >= '0' && ref <= '9') { >+ // 1- and 2-digit back references are allowed >+ unsigned backrefIndex = ref - '0'; >+ if (backrefIndex > reg->numSubpatterns()) >+ continue; >+ if (replacement.length() > i + 2) { >+ ref = replacement[i + 2]; >+ if (ref >= '0' && ref <= '9') { >+ backrefIndex = 10 * backrefIndex + ref - '0'; >+ if (backrefIndex > reg->numSubpatterns()) >+ backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference >+ else >+ advance = 1; >+ } >+ } >+ if (!backrefIndex) >+ continue; >+ backrefStart = ovector[2 * backrefIndex]; >+ backrefLength = ovector[2 * backrefIndex + 1] - backrefStart; >+ } else >+ continue; >+ >+ if (i - offset) >+ substitutedReplacement.append(replacement.substring(offset, i - offset)); >+ i += 1 + advance; >+ offset = i + 1; >+ if (backrefStart >= 0) >+ substitutedReplacement.append(source.substring(backrefStart, backrefLength)); >+ } while ((i = replacement.find('$', i + 1)) != notFound); >+ >+ if (replacement.length() - offset) >+ substitutedReplacement.append(replacement.substring(offset)); >+ >+ return substitutedReplacement.toString(); >+} >+ >+static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount) >+{ >+ if (rangeCount == 1) { >+ int sourceSize = source.length(); >+ int position = substringRanges[0].position; >+ int length = substringRanges[0].length; >+ if (position <= 0 && length >= sourceSize) >+ return sourceVal; >+ // We could call String::substringSharingImpl(), but this would result in redundant checks. >+ return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length))); >+ } >+ >+ int totalLength = 0; >+ for (int i = 0; i < rangeCount; i++) >+ totalLength += substringRanges[i].length; >+ >+ if (!totalLength) >+ return jsEmptyString(exec); >+ >+ if (source.is8Bit()) { >+ LChar* buffer; >+ const LChar* sourceData = source.characters8(); >+ RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); >+ if (!impl) >+ return throwOutOfMemoryError(exec); >+ >+ int bufferPos = 0; >+ for (int i = 0; i < rangeCount; i++) { >+ if (int srcLen = substringRanges[i].length) { >+ StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); >+ bufferPos += srcLen; >+ } >+ } >+ >+ return jsString(exec, impl.release()); >+ } >+ >+ UChar* buffer; >+ const UChar* sourceData = source.characters16(); >+ >+ RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); >+ if (!impl) >+ return throwOutOfMemoryError(exec); >+ >+ int bufferPos = 0; >+ for (int i = 0; i < rangeCount; i++) { >+ if (int srcLen = substringRanges[i].length) { >+ StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); >+ bufferPos += srcLen; >+ } >+ } >+ >+ return jsString(exec, impl.release()); >+} >+ >+static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount) >+{ >+ if (rangeCount == 1 && !separatorCount) { >+ int sourceSize = source.length(); >+ int position = substringRanges[0].position; >+ int length = substringRanges[0].length; >+ if (position <= 0 && length >= sourceSize) >+ return sourceVal; >+ // We could call String::substringSharingImpl(), but this would result in redundant checks. >+ return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length))); >+ } >+ >+ Checked<int, RecordOverflow> totalLength = 0; >+ bool allSeparators8Bit = true; >+ for (int i = 0; i < rangeCount; i++) >+ totalLength += substringRanges[i].length; >+ for (int i = 0; i < separatorCount; i++) { >+ totalLength += separators[i].length(); >+ if (separators[i].length() && !separators[i].is8Bit()) >+ allSeparators8Bit = false; >+ } >+ if (totalLength.hasOverflowed()) >+ return throwOutOfMemoryError(exec); >+ >+ if (!totalLength) >+ return jsEmptyString(exec); >+ >+ if (source.is8Bit() && allSeparators8Bit) { >+ LChar* buffer; >+ const LChar* sourceData = source.characters8(); >+ >+ RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer); >+ if (!impl) >+ return throwOutOfMemoryError(exec); >+ >+ int maxCount = std::max(rangeCount, separatorCount); >+ int bufferPos = 0; >+ for (int i = 0; i < maxCount; i++) { >+ if (i < rangeCount) { >+ if (int srcLen = substringRanges[i].length) { >+ StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); >+ bufferPos += srcLen; >+ } >+ } >+ if (i < separatorCount) { >+ if (int sepLen = separators[i].length()) { >+ StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen); >+ bufferPos += sepLen; >+ } >+ } >+ } >+ >+ return jsString(exec, impl.release()); >+ } >+ >+ UChar* buffer; >+ RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer); >+ if (!impl) >+ return throwOutOfMemoryError(exec); >+ >+ int maxCount = std::max(rangeCount, separatorCount); >+ int bufferPos = 0; >+ for (int i = 0; i < maxCount; i++) { >+ if (i < rangeCount) { >+ if (int srcLen = substringRanges[i].length) { >+ if (source.is8Bit()) >+ StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen); >+ else >+ StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen); >+ bufferPos += srcLen; >+ } >+ } >+ if (i < separatorCount) { >+ if (int sepLen = separators[i].length()) { >+ if (separators[i].is8Bit()) >+ StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen); >+ else >+ StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen); >+ bufferPos += sepLen; >+ } >+ } >+ } >+ >+ return jsString(exec, impl.release()); >+} >+ >+static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp) >+{ >+ size_t lastIndex = 0; >+ unsigned startPosition = 0; >+ >+ Vector<StringRange, 16> sourceRanges; >+ VM* vm = &exec->vm(); >+ RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); >+ unsigned sourceLen = source.length(); >+ >+ while (true) { >+ MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition); >+ if (!result) >+ break; >+ >+ if (lastIndex < result.start) >+ sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >+ >+ lastIndex = result.end; >+ startPosition = lastIndex; >+ >+ // special case of empty match >+ if (result.empty()) { >+ startPosition++; >+ if (startPosition > sourceLen) >+ break; >+ } >+ } >+ >+ if (!lastIndex) >+ return JSValue::encode(string); >+ >+ if (static_cast<unsigned>(lastIndex) < sourceLen) >+ sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); >+ >+ return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size())); >+} >+ >+EncodedJSValue JSC_HOST_CALL regExpProtoFuncReplaceSymbol(ExecState* exec) >+{ >+ // FIXME: This function should probably be reimplemented in JS when updating to ES6 as it will >+ // need to perform a lot of [[Get]]s. >+ >+ JSValue searchValue = exec->thisValue(); >+ if (!jsDynamicCast<RegExpObject*>(searchValue)) >+ return throwVMTypeError(exec, "RegExp.prototype[Symbol.replace] expected |this| to be a RegExp object."); >+ JSString* string = exec->argument(0).toString(exec); >+ JSValue replaceValue = exec->argument(1); >+ >+ String replacementString; >+ CallData callData; >+ CallType callType = getCallData(replaceValue, callData); >+ if (callType == CallTypeNone) >+ replacementString = replaceValue.toString(exec)->value(exec); >+ >+ const String& source = string->value(exec); >+ unsigned sourceLen = source.length(); >+ if (exec->hadException()) >+ return JSValue::encode(JSValue()); >+ RegExpObject* regExpObject = asRegExpObject(searchValue); >+ RegExp* regExp = regExpObject->regExp(); >+ bool global = regExp->global(); >+ >+ if (global) { >+ // ES5.1 15.5.4.10 step 8.a. >+ regExpObject->setLastIndex(exec, 0); >+ if (exec->hadException()) >+ return JSValue::encode(JSValue()); >+ >+ if (callType == CallTypeNone && !replacementString.length()) >+ return removeUsingRegExpSearch(exec, string, source, regExp); >+ } >+ >+ RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); >+ >+ size_t lastIndex = 0; >+ unsigned startPosition = 0; >+ >+ Vector<StringRange, 16> sourceRanges; >+ Vector<String, 16> replacements; >+ >+ // This is either a loop (if global is set) or a one-way (if not). >+ if (global && callType == CallTypeJS) { >+ // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string >+ int argCount = regExp->numSubpatterns() + 1 + 2; >+ JSFunction* func = jsCast<JSFunction*>(replaceValue); >+ CachedCall cachedCall(exec, func, argCount); >+ if (exec->hadException()) >+ return JSValue::encode(jsNull()); >+ VM* vm = &exec->vm(); >+ if (source.is8Bit()) { >+ while (true) { >+ int* ovector; >+ MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); >+ if (!result) >+ break; >+ >+ sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >+ >+ unsigned i = 0; >+ for (; i < regExp->numSubpatterns() + 1; ++i) { >+ int matchStart = ovector[i * 2]; >+ int matchLen = ovector[i * 2 + 1] - matchStart; >+ >+ if (matchStart < 0) >+ cachedCall.setArgument(i, jsUndefined()); >+ else >+ cachedCall.setArgument(i, jsSubstring8(vm, source, matchStart, matchLen)); >+ } >+ >+ cachedCall.setArgument(i++, jsNumber(result.start)); >+ cachedCall.setArgument(i++, string); >+ >+ cachedCall.setThis(jsUndefined()); >+ JSValue jsResult = cachedCall.call(); >+ replacements.append(jsResult.toString(exec)->value(exec)); >+ if (exec->hadException()) >+ break; >+ >+ lastIndex = result.end; >+ startPosition = lastIndex; >+ >+ // special case of empty match >+ if (result.empty()) { >+ startPosition++; >+ if (startPosition > sourceLen) >+ break; >+ } >+ } >+ } else { >+ while (true) { >+ int* ovector; >+ MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); >+ if (!result) >+ break; >+ >+ sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >+ >+ unsigned i = 0; >+ for (; i < regExp->numSubpatterns() + 1; ++i) { >+ int matchStart = ovector[i * 2]; >+ int matchLen = ovector[i * 2 + 1] - matchStart; >+ >+ if (matchStart < 0) >+ cachedCall.setArgument(i, jsUndefined()); >+ else >+ cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen)); >+ } >+ >+ cachedCall.setArgument(i++, jsNumber(result.start)); >+ cachedCall.setArgument(i++, string); >+ >+ cachedCall.setThis(jsUndefined()); >+ JSValue jsResult = cachedCall.call(); >+ replacements.append(jsResult.toString(exec)->value(exec)); >+ if (exec->hadException()) >+ break; >+ >+ lastIndex = result.end; >+ startPosition = lastIndex; >+ >+ // special case of empty match >+ if (result.empty()) { >+ startPosition++; >+ if (startPosition > sourceLen) >+ break; >+ } >+ } >+ } >+ } else { >+ VM* vm = &exec->vm(); >+ do { >+ int* ovector; >+ MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); >+ if (!result) >+ break; >+ >+ if (callType != CallTypeNone) { >+ sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >+ >+ MarkedArgumentBuffer args; >+ >+ for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) { >+ int matchStart = ovector[i * 2]; >+ int matchLen = ovector[i * 2 + 1] - matchStart; >+ >+ if (matchStart < 0) >+ args.append(jsUndefined()); >+ else >+ args.append(jsSubstring(exec, source, matchStart, matchLen)); >+ } >+ >+ args.append(jsNumber(result.start)); >+ args.append(string); >+ >+ replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec)); >+ if (exec->hadException()) >+ break; >+ } else { >+ int replLen = replacementString.length(); >+ if (lastIndex < result.start || replLen) { >+ sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >+ >+ if (replLen) >+ replacements.append(substituteBackreferences(replacementString, source, ovector, regExp)); >+ else >+ replacements.append(String()); >+ } >+ } >+ >+ lastIndex = result.end; >+ startPosition = lastIndex; >+ >+ // special case of empty match >+ if (result.empty()) { >+ startPosition++; >+ if (startPosition > sourceLen) >+ break; >+ } >+ } while (global); >+ } >+ >+ if (!lastIndex && replacements.isEmpty()) >+ return JSValue::encode(string); >+ >+ if (static_cast<unsigned>(lastIndex) < sourceLen) >+ sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); >+ >+ return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); >+} >+ > EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec) > { > JSValue thisValue = exec->thisValue(); >diff --git a/Source/JavaScriptCore/runtime/RegExpPrototype.h b/Source/JavaScriptCore/runtime/RegExpPrototype.h >index cb6ee6974a47cecbe101976a3dd4b8e73fe391b3..f8c04be01b58bd810ee05e2fbf9f397b5c3ce963 100644 >--- a/Source/JavaScriptCore/runtime/RegExpPrototype.h >+++ b/Source/JavaScriptCore/runtime/RegExpPrototype.h >@@ -31,10 +31,10 @@ public: > typedef RegExpObject Base; > static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot; > >- static RegExpPrototype* create(VM& vm, Structure* structure, RegExp* regExp) >+ static RegExpPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure, RegExp* regExp) > { > RegExpPrototype* prototype = new (NotNull, allocateCell<RegExpPrototype>(vm.heap)) RegExpPrototype(vm, structure, regExp); >- prototype->finishCreation(vm); >+ prototype->finishCreation(vm, globalObject); > return prototype; > } > >@@ -50,8 +50,20 @@ protected: > > private: > static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); >+ void finishCreation(VM&, JSGlobalObject*); > }; > >+String NEVER_INLINE substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp*, size_t); >+ >+inline String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg) >+{ >+ size_t i = replacement.find('$'); >+ if (UNLIKELY(i != notFound)) >+ return substituteBackreferencesSlow(replacement, source, ovector, reg, i); >+ >+ return replacement; >+} >+ > } // namespace JSC > > #endif // RegExpPrototype_h >diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp >index a1e78a81e2e9178d41706c35ff72453fe749eb66..fde53fc6e3552eba4b60369c60180832a62fd368 100644 >--- a/Source/JavaScriptCore/runtime/StringPrototype.cpp >+++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp >@@ -41,6 +41,7 @@ > #include "RegExpConstructor.h" > #include "RegExpMatchesArray.h" > #include "RegExpObject.h" >+#include "RegExpPrototype.h" > #include <algorithm> > #include <unicode/uconfig.h> > #include <unicode/unorm.h> >@@ -189,460 +190,7 @@ static inline JSString* jsSubstring(ExecState* exec, JSValue originalValue, cons > return jsSubstring(exec, string, offset, length); > } > >-static NEVER_INLINE String substituteBackreferencesSlow(StringView replacement, StringView source, const int* ovector, RegExp* reg, size_t i) >-{ >- StringBuilder substitutedReplacement; >- int offset = 0; >- do { >- if (i + 1 == replacement.length()) >- break; >- >- UChar ref = replacement[i + 1]; >- if (ref == '$') { >- // "$$" -> "$" >- ++i; >- substitutedReplacement.append(replacement.substring(offset, i - offset)); >- offset = i + 1; >- continue; >- } >- >- int backrefStart; >- int backrefLength; >- int advance = 0; >- if (ref == '&') { >- backrefStart = ovector[0]; >- backrefLength = ovector[1] - backrefStart; >- } else if (ref == '`') { >- backrefStart = 0; >- backrefLength = ovector[0]; >- } else if (ref == '\'') { >- backrefStart = ovector[1]; >- backrefLength = source.length() - backrefStart; >- } else if (reg && ref >= '0' && ref <= '9') { >- // 1- and 2-digit back references are allowed >- unsigned backrefIndex = ref - '0'; >- if (backrefIndex > reg->numSubpatterns()) >- continue; >- if (replacement.length() > i + 2) { >- ref = replacement[i + 2]; >- if (ref >= '0' && ref <= '9') { >- backrefIndex = 10 * backrefIndex + ref - '0'; >- if (backrefIndex > reg->numSubpatterns()) >- backrefIndex = backrefIndex / 10; // Fall back to the 1-digit reference >- else >- advance = 1; >- } >- } >- if (!backrefIndex) >- continue; >- backrefStart = ovector[2 * backrefIndex]; >- backrefLength = ovector[2 * backrefIndex + 1] - backrefStart; >- } else >- continue; >- >- if (i - offset) >- substitutedReplacement.append(replacement.substring(offset, i - offset)); >- i += 1 + advance; >- offset = i + 1; >- if (backrefStart >= 0) >- substitutedReplacement.append(source.substring(backrefStart, backrefLength)); >- } while ((i = replacement.find('$', i + 1)) != notFound); >- >- if (replacement.length() - offset) >- substitutedReplacement.append(replacement.substring(offset)); >- >- return substitutedReplacement.toString(); >-} >- >-static inline String substituteBackreferences(const String& replacement, StringView source, const int* ovector, RegExp* reg) >-{ >- size_t i = replacement.find('$'); >- if (UNLIKELY(i != notFound)) >- return substituteBackreferencesSlow(replacement, source, ovector, reg, i); >- >- return replacement; >-} >- >-struct StringRange { >- StringRange(int pos, int len) >- : position(pos) >- , length(len) >- { >- } >- >- StringRange() >- { >- } >- >- int position; >- int length; >-}; >- >-static ALWAYS_INLINE JSValue jsSpliceSubstrings(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount) >-{ >- if (rangeCount == 1) { >- int sourceSize = source.length(); >- int position = substringRanges[0].position; >- int length = substringRanges[0].length; >- if (position <= 0 && length >= sourceSize) >- return sourceVal; >- // We could call String::substringSharingImpl(), but this would result in redundant checks. >- return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length))); >- } >- >- int totalLength = 0; >- for (int i = 0; i < rangeCount; i++) >- totalLength += substringRanges[i].length; >- >- if (!totalLength) >- return jsEmptyString(exec); >- >- if (source.is8Bit()) { >- LChar* buffer; >- const LChar* sourceData = source.characters8(); >- RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); >- if (!impl) >- return throwOutOfMemoryError(exec); >- >- int bufferPos = 0; >- for (int i = 0; i < rangeCount; i++) { >- if (int srcLen = substringRanges[i].length) { >- StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); >- bufferPos += srcLen; >- } >- } >- >- return jsString(exec, impl.release()); >- } >- >- UChar* buffer; >- const UChar* sourceData = source.characters16(); >- >- RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength, buffer); >- if (!impl) >- return throwOutOfMemoryError(exec); >- >- int bufferPos = 0; >- for (int i = 0; i < rangeCount; i++) { >- if (int srcLen = substringRanges[i].length) { >- StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); >- bufferPos += srcLen; >- } >- } >- >- return jsString(exec, impl.release()); >-} >- >-static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, JSString* sourceVal, const String& source, const StringRange* substringRanges, int rangeCount, const String* separators, int separatorCount) >-{ >- if (rangeCount == 1 && separatorCount == 0) { >- int sourceSize = source.length(); >- int position = substringRanges[0].position; >- int length = substringRanges[0].length; >- if (position <= 0 && length >= sourceSize) >- return sourceVal; >- // We could call String::substringSharingImpl(), but this would result in redundant checks. >- return jsString(exec, StringImpl::createSubstringSharingImpl(source.impl(), std::max(0, position), std::min(sourceSize, length))); >- } >- >- Checked<int, RecordOverflow> totalLength = 0; >- bool allSeparators8Bit = true; >- for (int i = 0; i < rangeCount; i++) >- totalLength += substringRanges[i].length; >- for (int i = 0; i < separatorCount; i++) { >- totalLength += separators[i].length(); >- if (separators[i].length() && !separators[i].is8Bit()) >- allSeparators8Bit = false; >- } >- if (totalLength.hasOverflowed()) >- return throwOutOfMemoryError(exec); >- >- if (!totalLength) >- return jsEmptyString(exec); >- >- if (source.is8Bit() && allSeparators8Bit) { >- LChar* buffer; >- const LChar* sourceData = source.characters8(); >- >- RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer); >- if (!impl) >- return throwOutOfMemoryError(exec); >- >- int maxCount = std::max(rangeCount, separatorCount); >- int bufferPos = 0; >- for (int i = 0; i < maxCount; i++) { >- if (i < rangeCount) { >- if (int srcLen = substringRanges[i].length) { >- StringImpl::copyChars(buffer + bufferPos, sourceData + substringRanges[i].position, srcLen); >- bufferPos += srcLen; >- } >- } >- if (i < separatorCount) { >- if (int sepLen = separators[i].length()) { >- StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen); >- bufferPos += sepLen; >- } >- } >- } >- >- return jsString(exec, impl.release()); >- } >- >- UChar* buffer; >- RefPtr<StringImpl> impl = StringImpl::tryCreateUninitialized(totalLength.unsafeGet(), buffer); >- if (!impl) >- return throwOutOfMemoryError(exec); >- >- int maxCount = std::max(rangeCount, separatorCount); >- int bufferPos = 0; >- for (int i = 0; i < maxCount; i++) { >- if (i < rangeCount) { >- if (int srcLen = substringRanges[i].length) { >- if (source.is8Bit()) >- StringImpl::copyChars(buffer + bufferPos, source.characters8() + substringRanges[i].position, srcLen); >- else >- StringImpl::copyChars(buffer + bufferPos, source.characters16() + substringRanges[i].position, srcLen); >- bufferPos += srcLen; >- } >- } >- if (i < separatorCount) { >- if (int sepLen = separators[i].length()) { >- if (separators[i].is8Bit()) >- StringImpl::copyChars(buffer + bufferPos, separators[i].characters8(), sepLen); >- else >- StringImpl::copyChars(buffer + bufferPos, separators[i].characters16(), sepLen); >- bufferPos += sepLen; >- } >- } >- } >- >- return jsString(exec, impl.release()); >-} >- >-static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const String& source, RegExp* regExp) >-{ >- size_t lastIndex = 0; >- unsigned startPosition = 0; >- >- Vector<StringRange, 16> sourceRanges; >- VM* vm = &exec->vm(); >- RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); >- unsigned sourceLen = source.length(); >- >- while (true) { >- MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition); >- if (!result) >- break; >- >- if (lastIndex < result.start) >- sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >- >- lastIndex = result.end; >- startPosition = lastIndex; >- >- // special case of empty match >- if (result.empty()) { >- startPosition++; >- if (startPosition > sourceLen) >- break; >- } >- } >- >- if (!lastIndex) >- return JSValue::encode(string); >- >- if (static_cast<unsigned>(lastIndex) < sourceLen) >- sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); >- >- return JSValue::encode(jsSpliceSubstrings(exec, string, source, sourceRanges.data(), sourceRanges.size())); >-} >- >-static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSString* string, JSValue searchValue) >-{ >- JSValue replaceValue = exec->argument(1); >- String replacementString; >- CallData callData; >- CallType callType = getCallData(replaceValue, callData); >- if (callType == CallTypeNone) >- replacementString = replaceValue.toString(exec)->value(exec); >- >- const String& source = string->value(exec); >- unsigned sourceLen = source.length(); >- if (exec->hadException()) >- return JSValue::encode(JSValue()); >- RegExpObject* regExpObject = asRegExpObject(searchValue); >- RegExp* regExp = regExpObject->regExp(); >- bool global = regExp->global(); >- >- if (global) { >- // ES5.1 15.5.4.10 step 8.a. >- regExpObject->setLastIndex(exec, 0); >- if (exec->hadException()) >- return JSValue::encode(JSValue()); >- >- if (callType == CallTypeNone && !replacementString.length()) >- return removeUsingRegExpSearch(exec, string, source, regExp); >- } >- >- RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); >- >- size_t lastIndex = 0; >- unsigned startPosition = 0; >- >- Vector<StringRange, 16> sourceRanges; >- Vector<String, 16> replacements; >- >- // This is either a loop (if global is set) or a one-way (if not). >- if (global && callType == CallTypeJS) { >- // regExp->numSubpatterns() + 1 for pattern args, + 2 for match start and string >- int argCount = regExp->numSubpatterns() + 1 + 2; >- JSFunction* func = jsCast<JSFunction*>(replaceValue); >- CachedCall cachedCall(exec, func, argCount); >- if (exec->hadException()) >- return JSValue::encode(jsNull()); >- VM* vm = &exec->vm(); >- if (source.is8Bit()) { >- while (true) { >- int* ovector; >- MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); >- if (!result) >- break; >- >- sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >- >- unsigned i = 0; >- for (; i < regExp->numSubpatterns() + 1; ++i) { >- int matchStart = ovector[i * 2]; >- int matchLen = ovector[i * 2 + 1] - matchStart; >- >- if (matchStart < 0) >- cachedCall.setArgument(i, jsUndefined()); >- else >- cachedCall.setArgument(i, jsSubstring8(vm, source, matchStart, matchLen)); >- } >- >- cachedCall.setArgument(i++, jsNumber(result.start)); >- cachedCall.setArgument(i++, string); >- >- cachedCall.setThis(jsUndefined()); >- JSValue jsResult = cachedCall.call(); >- replacements.append(jsResult.toString(exec)->value(exec)); >- if (exec->hadException()) >- break; >- >- lastIndex = result.end; >- startPosition = lastIndex; >- >- // special case of empty match >- if (result.empty()) { >- startPosition++; >- if (startPosition > sourceLen) >- break; >- } >- } >- } else { >- while (true) { >- int* ovector; >- MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); >- if (!result) >- break; >- >- sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >- >- unsigned i = 0; >- for (; i < regExp->numSubpatterns() + 1; ++i) { >- int matchStart = ovector[i * 2]; >- int matchLen = ovector[i * 2 + 1] - matchStart; >- >- if (matchStart < 0) >- cachedCall.setArgument(i, jsUndefined()); >- else >- cachedCall.setArgument(i, jsSubstring(vm, source, matchStart, matchLen)); >- } >- >- cachedCall.setArgument(i++, jsNumber(result.start)); >- cachedCall.setArgument(i++, string); >- >- cachedCall.setThis(jsUndefined()); >- JSValue jsResult = cachedCall.call(); >- replacements.append(jsResult.toString(exec)->value(exec)); >- if (exec->hadException()) >- break; >- >- lastIndex = result.end; >- startPosition = lastIndex; >- >- // special case of empty match >- if (result.empty()) { >- startPosition++; >- if (startPosition > sourceLen) >- break; >- } >- } >- } >- } else { >- VM* vm = &exec->vm(); >- do { >- int* ovector; >- MatchResult result = regExpConstructor->performMatch(*vm, regExp, string, source, startPosition, &ovector); >- if (!result) >- break; >- >- if (callType != CallTypeNone) { >- sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >- >- MarkedArgumentBuffer args; >- >- for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) { >- int matchStart = ovector[i * 2]; >- int matchLen = ovector[i * 2 + 1] - matchStart; >- >- if (matchStart < 0) >- args.append(jsUndefined()); >- else >- args.append(jsSubstring(exec, source, matchStart, matchLen)); >- } >- >- args.append(jsNumber(result.start)); >- args.append(string); >- >- replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec)); >- if (exec->hadException()) >- break; >- } else { >- int replLen = replacementString.length(); >- if (lastIndex < result.start || replLen) { >- sourceRanges.append(StringRange(lastIndex, result.start - lastIndex)); >- >- if (replLen) >- replacements.append(substituteBackreferences(replacementString, source, ovector, regExp)); >- else >- replacements.append(String()); >- } >- } >- >- lastIndex = result.end; >- startPosition = lastIndex; >- >- // special case of empty match >- if (result.empty()) { >- startPosition++; >- if (startPosition > sourceLen) >- break; >- } >- } while (global); >- } >- >- if (!lastIndex && replacements.isEmpty()) >- return JSValue::encode(string); >- >- if (static_cast<unsigned>(lastIndex) < sourceLen) >- sourceRanges.append(StringRange(lastIndex, sourceLen - lastIndex)); >- >- return JSValue::encode(jsSpliceSubstringsWithSeparators(exec, string, source, sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); >-} >- >-static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue) >+static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* jsString, JSValue searchValue, JSValue replaceValue) > { > const String& string = jsString->value(exec); > String searchString = searchValue.toString(exec)->value(exec); >@@ -654,7 +202,6 @@ static inline EncodedJSValue replaceUsingStringSearch(ExecState* exec, JSString* > if (matchStart == notFound) > return JSValue::encode(jsString); > >- JSValue replaceValue = exec->argument(1); > CallData callData; > CallType callType = getCallData(replaceValue, callData); > if (callType != CallTypeNone) { >@@ -762,12 +309,32 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec) > JSValue thisValue = exec->thisValue(); > if (!checkObjectCoercible(thisValue)) > return throwVMTypeError(exec); >- JSString* string = thisValue.toString(exec); > JSValue searchValue = exec->argument(0); >+ JSValue replaceValue = exec->argument(1); >+ >+ if (!searchValue.isUndefinedOrNull()) { >+ JSValue replaceFunction = searchValue.get(exec, exec->propertyNames().replaceSymbol); >+ if (exec->hadException()) >+ return JSValue::encode(jsUndefined()); >+ >+ if (!replaceFunction.isUndefinedOrNull()) { >+ CallData callData; >+ CallType callType = getCallData(replaceFunction, callData); >+ if (callType == CallTypeNone) >+ return throwVMTypeError(exec, "Symbol.replace on the first argument of String.prototype.replace was not a function"); >+ >+ MarkedArgumentBuffer callArgs; >+ callArgs.append(thisValue); >+ callArgs.append(replaceValue); >+ return JSValue::encode(call(exec, replaceFunction, callType, callData, searchValue, callArgs)); >+ } >+ } >+ >+ JSString* string = thisValue.toString(exec); >+ if (exec->hadException()) >+ return JSValue::encode(jsUndefined()); > >- if (searchValue.inherits(RegExpObject::info())) >- return replaceUsingRegExpSearch(exec, string, searchValue); >- return replaceUsingStringSearch(exec, string, searchValue); >+ return replaceUsingStringSearch(exec, string, searchValue, replaceValue); > } > > EncodedJSValue JSC_HOST_CALL stringProtoFuncToString(ExecState* exec) >diff --git a/Source/JavaScriptCore/tests/es6.yaml b/Source/JavaScriptCore/tests/es6.yaml >index 285c5f98f4953928e67433f8c266a9bba150f7a7..3cb1bc3a133b1a1010f108c9e2aa1809f343fbbe 100644 >--- a/Source/JavaScriptCore/tests/es6.yaml >+++ b/Source/JavaScriptCore/tests/es6.yaml >@@ -1067,7 +1067,7 @@ > - path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.match].js > cmd: runES6 :fail > - path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.replace].js >- cmd: runES6 :fail >+ cmd: runES6 :normal > - path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.search].js > cmd: runES6 :fail > - path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.split].js >@@ -1177,7 +1177,7 @@ > - path: es6/well-known_symbols_Symbol.match.js > cmd: runES6 :fail > - path: es6/well-known_symbols_Symbol.replace.js >- cmd: runES6 :fail >+ cmd: runES6 :normal > - path: es6/well-known_symbols_Symbol.search.js > cmd: runES6 :fail > - path: es6/well-known_symbols_Symbol.species_Array.prototype.concat.js >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index b4d603c11511bcd7d034b869bf6be358717838b7..1a7d950ed735ac4e0942cf3c6f12809fff92d429 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,15 @@ >+2015-11-02 Keith Miller <keith_miller@apple.com> >+ >+ [ES6] Add support for Symbol.replace. >+ https://bugs.webkit.org/show_bug.cgi?id=150812 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Fix test as Symbol now has a replace property. >+ >+ * js/Object-getOwnPropertyNames-expected.txt: >+ * js/script-tests/Object-getOwnPropertyNames.js: >+ > 2015-11-02 Ryan Haddad <ryanhaddad@apple.com> > > Marking fast/text/multiple-feature-properties.html as flaky on win >diff --git a/LayoutTests/js/Object-getOwnPropertyNames-expected.txt b/LayoutTests/js/Object-getOwnPropertyNames-expected.txt >index cae219baf92b5950be77f9457da02cc51967f983..13dc2dff99958c9824b953bf91a3dc40c7a98093 100644 >--- a/LayoutTests/js/Object-getOwnPropertyNames-expected.txt >+++ b/LayoutTests/js/Object-getOwnPropertyNames-expected.txt >@@ -61,7 +61,7 @@ PASS getSortedOwnPropertyNames(Error) is ['length', 'name', 'prototype'] > PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'name', 'toString'] > PASS getSortedOwnPropertyNames(Math) is ['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc'] > PASS getSortedOwnPropertyNames(JSON) is ['parse', 'stringify'] >-PASS getSortedOwnPropertyNames(Symbol) is ['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'toStringTag', 'unscopables'] >+PASS getSortedOwnPropertyNames(Symbol) is ['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'replace', 'toStringTag', 'unscopables'] > PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'toString', 'valueOf'] > PASS globalPropertyNames.indexOf('NaN') != -1 is true > PASS globalPropertyNames.indexOf('Infinity') != -1 is true >diff --git a/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js b/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js >index cb5ec24faf907f80237985f8c8ace838dd68e089..9e17143195d41bd0ce28319c1f1877ae79ed3b38 100644 >--- a/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js >+++ b/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js >@@ -70,7 +70,7 @@ var expectedPropertyNamesSet = { > "Error.prototype": "['constructor', 'message', 'name', 'toString']", > "Math": "['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']", > "JSON": "['parse', 'stringify']", >- "Symbol": "['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'toStringTag', 'unscopables']", >+ "Symbol": "['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'replace', 'toStringTag', 'unscopables']", > "Symbol.prototype": "['constructor', 'toString', 'valueOf']" > }; >
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
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 150812
:
264619
|
264623
|
264629
|
264630
|
264632
|
265636