JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { JSString* sVal = thisValue.toThisJSString(exec); const UString& s = sVal->value(); int sSize = s.size(); if (!sSize) return sVal; const UChar* sData = s.data(); Vector<UChar> buffer(sSize); UChar ored = 0; for (int i = 0; i < sSize; i++) { UChar c = sData[i]; ored |= c; buffer[i] = toASCIIUpper(c); } if (!(ored & ~0x7f)) return jsString(exec, UString(buffer.releaseBuffer(), sSize, false)); bool error; int length = Unicode::toUpper(buffer.data(), sSize, sData, sSize, &error); if (error) { buffer.resize(length); length = Unicode::toUpper(buffer.data(), length, sData, sSize, &error); if (error) return sVal; } if (length == sSize && memcmp(buffer.data(), sData, length * sizeof(UChar)) == 0) return sVal; return jsString(exec, UString(buffer.releaseBuffer(), length, false)); }
EncodedJSValue JSC_HOST_CALL symbolConstructorFor(ExecState* exec) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSString* stringKey = exec->argument(0).toString(exec); RETURN_IF_EXCEPTION(scope, encodedJSValue()); String string = stringKey->value(exec); RETURN_IF_EXCEPTION(scope, encodedJSValue()); return JSValue::encode(Symbol::create(exec->vm(), exec->vm().symbolRegistry().symbolForKey(string))); }
EncodedJSValue JSC_HOST_CALL regExpProtoFuncSearch(ExecState* exec) { JSValue thisValue = exec->thisValue(); if (!thisValue.inherits(RegExpObject::info())) return throwVMTypeError(exec); RegExp* regExp = asRegExpObject(thisValue)->regExp(); JSString* string = exec->argument(0).toString(exec); String s = string->value(exec); if (exec->hadException()) return JSValue::encode(jsUndefined()); RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); MatchResult result = regExpConstructor->performMatch(exec->vm(), regExp, string, s, 0); return JSValue::encode(result ? jsNumber(result.start) : jsNumber(-1)); }
inline RegExpFlags toFlags(ExecState* exec, JSValue flags) { if (flags.isUndefined()) return NoFlags; JSString* flagsString = flags.toString(exec); if (!flagsString) { ASSERT(exec->hadException()); return InvalidFlags; } RegExpFlags result = regExpFlags(flagsString->value(exec)); if (exec->hadException()) return InvalidFlags; if (result == InvalidFlags) throwSyntaxError(exec, ASCIILiteral("Invalid flags supplied to RegExp constructor.")); return result; }
inline RegExpFlags toFlags(ExecState* exec, JSValue flags) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (flags.isUndefined()) return NoFlags; JSString* flagsString = flags.toStringOrNull(exec); ASSERT(!!scope.exception() == !flagsString); if (UNLIKELY(!flagsString)) return InvalidFlags; RegExpFlags result = regExpFlags(flagsString->value(exec)); RETURN_IF_EXCEPTION(scope, InvalidFlags); if (result == InvalidFlags) throwSyntaxError(exec, scope, ASCIILiteral("Invalid flags supplied to RegExp constructor.")); return result; }
JSValue* stringProtoFuncToLocaleUpperCase(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&) { JSString* sVal = thisValue->toThisJSString(exec); const UString& s = sVal->value(); int ssize = s.size(); if (!ssize) return sVal; Vector<UChar> buffer(ssize); bool error; int length = Unicode::toUpper(buffer.data(), ssize, reinterpret_cast<const UChar*>(s.data()), ssize, &error); if (error) { buffer.resize(length); length = Unicode::toUpper(buffer.data(), length, reinterpret_cast<const UChar*>(s.data()), ssize, &error); if (error) return sVal; } if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) return sVal; return jsString(exec, UString(buffer.releaseBuffer(), length, false)); }
JSValue* stringProtoFuncToLocaleLowerCase(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList&) { // FIXME: See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt for locale-sensitive mappings that aren't implemented. JSString* sVal = thisValue->toThisJSString(exec); const UString& s = sVal->value(); int ssize = s.size(); if (!ssize) return sVal; Vector<UChar> buffer(ssize); bool error; int length = Unicode::toLower(buffer.data(), ssize, reinterpret_cast<const UChar*>(s.data()), ssize, &error); if (error) { buffer.resize(length); length = Unicode::toLower(buffer.data(), length, reinterpret_cast<const UChar*>(s.data()), ssize, &error); if (error) return sVal; } if (length == ssize && memcmp(buffer.data(), s.data(), length * sizeof(UChar)) == 0) return sVal; return jsString(exec, UString(buffer.releaseBuffer(), length, false)); }
JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { JSString* sourceVal = thisValue.toThisJSString(exec); const UString& source = sourceVal->value(); JSValue pattern = args.at(0); JSValue replacement = args.at(1); UString replacementString; CallData callData; CallType callType = replacement.getCallData(callData); if (callType == CallTypeNone) replacementString = replacement.toString(exec); if (pattern.isObject(&RegExpObject::info)) { RegExp* reg = asRegExpObject(pattern)->regExp(); bool global = reg->global(); RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); int lastIndex = 0; int startPosition = 0; Vector<UString::Range, 16> sourceRanges; Vector<UString, 16> replacements; // This is either a loop (if global is set) or a one-way (if not). if (global && callType == CallTypeJS) { // reg->numSubpatterns() + 1 for pattern args, + 2 for match start and sourceValue int argCount = reg->numSubpatterns() + 1 + 2; JSFunction* func = asFunction(replacement); CachedCall cachedCall(exec, func, argCount, exec->exceptionSlot()); if (exec->hadException()) return jsNull(); while (true) { int matchIndex; int matchLen; int* ovector; regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); if (matchIndex < 0) break; sourceRanges.append(UString::Range(lastIndex, matchIndex - lastIndex)); int completeMatchStart = ovector[0]; unsigned i = 0; for (; i < reg->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(exec, source, matchStart, matchLen)); } cachedCall.setArgument(i++, jsNumber(exec, completeMatchStart)); cachedCall.setArgument(i++, sourceVal); cachedCall.setThis(exec->globalThisValue()); replacements.append(cachedCall.call().toString(cachedCall.newCallFrame())); if (exec->hadException()) break; lastIndex = matchIndex + matchLen; startPosition = lastIndex; // special case of empty match if (matchLen == 0) { startPosition++; if (startPosition > source.size()) break; } } } else { do { int matchIndex; int matchLen; int* ovector; regExpConstructor->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); if (matchIndex < 0) break; sourceRanges.append(UString::Range(lastIndex, matchIndex - lastIndex)); if (callType != CallTypeNone) { int completeMatchStart = ovector[0]; MarkedArgumentBuffer args; for (unsigned i = 0; i < reg->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(exec, completeMatchStart)); args.append(sourceVal); replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec)); if (exec->hadException()) break; } else replacements.append(substituteBackreferences(replacementString, source, ovector, reg)); lastIndex = matchIndex + matchLen; startPosition = lastIndex; // special case of empty match if (matchLen == 0) { startPosition++; if (startPosition > source.size()) break; } } while (global); } if (!lastIndex && replacements.isEmpty()) return sourceVal; if (lastIndex < source.size()) sourceRanges.append(UString::Range(lastIndex, source.size() - lastIndex)); return jsString(exec, source.spliceSubstringsWithSeparators(sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size())); } // Not a regular expression, so treat the pattern as a string. UString patternString = pattern.toString(exec); int matchPos = source.find(patternString); if (matchPos == -1) return sourceVal; int matchLen = patternString.size(); if (callType != CallTypeNone) { MarkedArgumentBuffer args; args.append(jsSubstring(exec, source, matchPos, matchLen)); args.append(jsNumber(exec, matchPos)); args.append(sourceVal); replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args).toString(exec); } int ovector[2] = { matchPos, matchPos + matchLen }; return jsString(exec, source.replaceRange(matchPos, matchLen, substituteBackreferences(replacementString, source, ovector, 0))); }
// 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]]) EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec) { JSGlobalObject* globalObject = exec->callee()->globalObject(); // Let Target be the this value. JSValue target = exec->thisValue(); // If IsCallable(Target) is false, throw a TypeError exception. CallData callData; CallType callType = getCallData(target, callData); if (callType == CallTypeNone) return throwVMTypeError(exec); // Primitive values are not callable. ASSERT(target.isObject()); JSObject* targetObject = asObject(target); VM& vm = exec->vm(); // Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order. size_t numBoundArgs = exec->argumentCount() > 1 ? exec->argumentCount() - 1 : 0; JSArray* boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), numBoundArgs); if (!boundArgs) return JSValue::encode(throwOutOfMemoryError(exec)); for (size_t i = 0; i < numBoundArgs; ++i) boundArgs->initializeIndex(vm, i, exec->argument(i + 1)); // If the [[Class]] internal property of Target is "Function", then ... // Else set the length own property of F to 0. unsigned length = 0; if (targetObject->inherits(JSFunction::info())) { ASSERT(target.get(exec, exec->propertyNames().length).isNumber()); // a. Let L be the length property of Target minus the length of A. // b. Set the length own property of F to either 0 or L, whichever is larger. unsigned targetLength = (unsigned)target.get(exec, exec->propertyNames().length).asNumber(); if (targetLength > numBoundArgs) length = targetLength - numBoundArgs; } JSString* name = target.get(exec, exec->propertyNames().name).toString(exec); return JSValue::encode(JSBoundFunction::create(vm, globalObject, targetObject, exec->argument(0), boundArgs, length, name->value(exec))); }
JSValue* stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args) { JSString* sourceVal = thisValue->toThisJSString(exec); const UString& source = sourceVal->value(); JSValue* pattern = args.at(exec, 0); JSValue* replacement = args.at(exec, 1); UString replacementString; CallData callData; CallType callType = replacement->getCallData(callData); if (callType == CallTypeNone) replacementString = replacement->toString(exec); if (pattern->isObject(&RegExpObject::info)) { RegExp* reg = static_cast<RegExpObject*>(pattern)->regExp(); bool global = reg->global(); RegExpConstructor* regExpObj = exec->lexicalGlobalObject()->regExpConstructor(); int lastIndex = 0; int startPosition = 0; Vector<UString::Range, 16> sourceRanges; Vector<UString, 16> replacements; // This is either a loop (if global is set) or a one-way (if not). do { int matchIndex; int matchLen; int* ovector; regExpObj->performMatch(reg, source, startPosition, matchIndex, matchLen, &ovector); if (matchIndex < 0) break; sourceRanges.append(UString::Range(lastIndex, matchIndex - lastIndex)); if (callType != CallTypeNone) { int completeMatchStart = ovector[0]; ArgList args; for (unsigned i = 0; i < reg->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(exec, completeMatchStart)); args.append(sourceVal); replacements.append(call(exec, replacement, callType, callData, exec->globalThisValue(), args)->toString(exec)); } else replacements.append(substituteBackreferences(replacementString, source, ovector, reg)); lastIndex = matchIndex + matchLen; startPosition = lastIndex; // special case of empty match if (matchLen == 0) { startPosition++; if (startPosition > source.size()) break; } } while (global); if (lastIndex < source.size()) sourceRanges.append(UString::Range(lastIndex, source.size() - lastIndex)); UString result = source.spliceSubstringsWithSeparators(sourceRanges.data(), sourceRanges.size(), replacements.data(), replacements.size()); if (result == source) return sourceVal; return jsString(exec, result); } // First arg is a string UString patternString = pattern->toString(exec); int matchPos = source.find(patternString); int matchLen = patternString.size(); // Do the replacement if (matchPos == -1) return sourceVal; if (callType != CallTypeNone) { ArgList args; args.append(jsSubstring(exec, source, matchPos, matchLen)); args.append(jsNumber(exec, matchPos)); args.append(sourceVal); replacementString = call(exec, replacement, callType, callData, exec->globalThisValue(), args)->toString(exec); } return jsString(exec, source.substr(0, matchPos) + replacementString + source.substr(matchPos + matchLen)); }