JSValue JSStringJoiner::join(ExecState* exec) { if (!m_isValid) return throwOutOfMemoryError(exec); if (!m_strings.size()) return jsEmptyString(exec); Checked<size_t, RecordOverflow> separatorLength = m_separator.length(); // FIXME: add special cases of joinStrings() for (separatorLength == 0) and (separatorLength == 1). ASSERT(m_strings.size() > 0); Checked<size_t, RecordOverflow> totalSeparactorsLength = separatorLength * (m_strings.size() - 1); Checked<size_t, RecordOverflow> outputStringSize = totalSeparactorsLength + m_accumulatedStringsLength; size_t finalSize; if (outputStringSize.safeGet(finalSize) == CheckedState::DidOverflow) return throwOutOfMemoryError(exec); if (!outputStringSize) return jsEmptyString(exec); RefPtr<StringImpl> outputStringImpl; if (m_is8Bits) outputStringImpl = joinStrings<LChar>(m_strings, m_separator, finalSize); else outputStringImpl = joinStrings<UChar>(m_strings, m_separator, finalSize); if (!outputStringImpl) return throwOutOfMemoryError(exec); return JSString::create(exec->vm(), outputStringImpl.release()); }
JSArray* createEmptyRegExpMatchesArray(JSGlobalObject* globalObject, JSString* input, RegExp* regExp) { VM& vm = globalObject->vm(); JSArray* array; // FIXME: This should handle array allocation errors gracefully. // https://bugs.webkit.org/show_bug.cgi?id=155144 GCDeferralContext deferralContext(vm.heap); if (UNLIKELY(globalObject->isHavingABadTime())) { array = JSArray::tryCreateForInitializationPrivate(vm, &deferralContext, globalObject->regExpMatchesArrayStructure(), regExp->numSubpatterns() + 1); array->initializeIndexWithoutBarrier(0, jsEmptyString(&vm)); if (unsigned numSubpatterns = regExp->numSubpatterns()) { for (unsigned i = 1; i <= numSubpatterns; ++i) array->initializeIndexWithoutBarrier(i, jsUndefined()); } } else { array = tryCreateUninitializedRegExpMatchesArray(vm, &deferralContext, globalObject->regExpMatchesArrayStructure(), regExp->numSubpatterns() + 1); RELEASE_ASSERT(array); array->initializeIndexWithoutBarrier(0, jsEmptyString(&vm), ArrayWithContiguous); if (unsigned numSubpatterns = regExp->numSubpatterns()) { for (unsigned i = 1; i <= numSubpatterns; ++i) array->initializeIndexWithoutBarrier(i, jsUndefined(), ArrayWithContiguous); } } array->putDirectWithoutBarrier(RegExpMatchesArrayIndexPropertyOffset, jsNumber(-1)); array->putDirectWithoutBarrier(RegExpMatchesArrayInputPropertyOffset, input); return array; }
JSValue JSStringJoiner::build(ExecState* exec) { if (!m_isValid) return throwOutOfMemoryError(exec); if (!m_strings.size()) return jsEmptyString(exec); size_t separatorLength = m_separator.length(); // FIXME: add special cases of joinStrings() for (separatorLength == 0) and (separatorLength == 1). ASSERT(m_strings.size() > 0); size_t totalSeparactorsLength = separatorLength * (m_strings.size() - 1); size_t outputStringSize = totalSeparactorsLength + m_cumulatedStringsLength; if (!outputStringSize) return jsEmptyString(exec); RefPtr<StringImpl> outputStringImpl; if (m_is8Bits) outputStringImpl = joinStrings<LChar>(m_strings, m_separator, outputStringSize); else outputStringImpl = joinStrings<UChar>(m_strings, m_separator, outputStringSize); if (!outputStringImpl) return throwOutOfMemoryError(exec); return JSString::create(exec->globalData(), outputStringImpl.release()); }
EncodedJSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec) { JSValue thisValue = exec->thisValue(); if (!thisValue.inherits(DateInstance::info())) return throwVMTypeError(exec); DateInstance* thisDateObj = asDateInstance(thisValue); if (!std::isfinite(thisDateObj->internalNumber())) return throwVMError(exec, createRangeError(exec, ASCIILiteral("Invalid Date"))); const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); if (!gregorianDateTime) return JSValue::encode(jsNontrivialString(exec, String(ASCIILiteral("Invalid Date")))); // Maximum amount of space we need in buffer: 7 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + 4 (. + 3 digits for milliseconds) // 6 for formatting and one for null termination = 28. We add one extra character to allow us to force null termination. char buffer[28]; // If the year is outside the bounds of 0 and 9999 inclusive we want to use the extended year format (ES 15.9.1.15.1). int ms = static_cast<int>(fmod(thisDateObj->internalNumber(), msPerSecond)); if (ms < 0) ms += msPerSecond; int charactersWritten; if (gregorianDateTime->year() > 9999 || gregorianDateTime->year() < 0) charactersWritten = snprintf(buffer, sizeof(buffer), "%+07d-%02d-%02dT%02d:%02d:%02d.%03dZ", gregorianDateTime->year(), gregorianDateTime->month() + 1, gregorianDateTime->monthDay(), gregorianDateTime->hour(), gregorianDateTime->minute(), gregorianDateTime->second(), ms); else charactersWritten = snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", gregorianDateTime->year(), gregorianDateTime->month() + 1, gregorianDateTime->monthDay(), gregorianDateTime->hour(), gregorianDateTime->minute(), gregorianDateTime->second(), ms); ASSERT(charactersWritten > 0 && static_cast<unsigned>(charactersWritten) < sizeof(buffer)); if (static_cast<unsigned>(charactersWritten) >= sizeof(buffer)) return JSValue::encode(jsEmptyString(exec)); return JSValue::encode(jsNontrivialString(exec, String(buffer, charactersWritten))); }
JSValue JSC_HOST_CALL stringProtoFuncCharAt(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); unsigned len = s.size(); JSValue a0 = args.at(0); if (a0.isUInt32Fast()) { uint32_t i = a0.getUInt32Fast(); if (i < len) return jsSingleCharacterSubstring(exec, s, i); return jsEmptyString(exec); } double dpos = a0.toInteger(exec); if (dpos >= 0 && dpos < len) return jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos)); return jsEmptyString(exec); }
void ErrorPrototype::finishCreation(VM& vm) { Base::finishCreation(vm); ASSERT(inherits(vm, info())); putDirect(vm, vm.propertyNames->name, jsNontrivialString(&vm, String(ASCIILiteral("Error"))), DontEnum); putDirect(vm, vm.propertyNames->message, jsEmptyString(&vm), DontEnum); }
void NativeErrorPrototype::finishCreation(VM& vm, const WTF::String& nameAndMessage, NativeErrorConstructor* constructor) { Base::finishCreation(vm); putDirect(vm, vm.propertyNames->name, jsString(&vm, nameAndMessage), DontEnum); putDirect(vm, vm.propertyNames->message, jsEmptyString(&vm), DontEnum); putDirect(vm, vm.propertyNames->constructor, constructor, DontEnum); }
JSValue* stringProtoFuncCharAt(ExecState* exec, JSObject*, JSValue* thisValue, const ArgList& args) { UString s = thisValue->toThisString(exec); unsigned len = s.size(); JSValue* a0 = args.at(exec, 0); if (JSImmediate::isNumber(a0)) { uint32_t i; if (JSImmediate::getUInt32(a0, i) && i < len) return jsSingleCharacterSubstring(exec, s, i); return jsEmptyString(exec); } double dpos = a0->toInteger(exec); if (dpos >= 0 && dpos < len) return jsSingleCharacterSubstring(exec, s, static_cast<unsigned>(dpos)); return jsEmptyString(exec); }
JSValue JSDOMWindow::atob(ExecState* exec, const ArgList& args) { if (args.size() < 1) return throwError(exec, SyntaxError, "Not enough arguments"); JSValue v = args.at(0); if (v.isNull()) return jsEmptyString(exec); UString s = v.toString(exec); if (!s.is8Bit()) { setDOMException(exec, INVALID_CHARACTER_ERR); return jsUndefined(); } Vector<char> in(s.size()); for (int i = 0; i < s.size(); ++i) in[i] = static_cast<char>(s.data()[i]); Vector<char> out; if (!base64Decode(in, out)) return throwError(exec, GeneralError, "Cannot decode base64"); return jsString(exec, String(out.data(), out.size())); }
JSString* RegExpMatchesArray::rightContext(ExecState* exec) { unsigned length = m_input->length(); if (m_result.end == length) return jsEmptyString(exec); return jsSubstring(exec, m_input.get(), m_result.end, length - m_result.end); }
void NativeErrorPrototype::finishCreation(ExecState* exec, JSGlobalObject* globalObject, const WTF::String& nameAndMessage, NativeErrorConstructor* constructor) { Base::finishCreation(exec, globalObject); putDirect(exec->vm(), exec->propertyNames().name, jsString(exec, nameAndMessage), DontEnum); putDirect(exec->vm(), exec->propertyNames().message, jsEmptyString(exec), DontEnum); putDirect(exec->vm(), exec->propertyNames().constructor, constructor, DontEnum); }
static EncodedJSValue JSC_HOST_CALL callStringConstructor(ExecState* exec) { if (!exec->argumentCount()) return JSValue::encode(jsEmptyString(exec)); JSValue argument = exec->uncheckedArgument(0); if (argument.isSymbol()) return JSValue::encode(jsString(exec, asSymbol(argument)->descriptiveString())); return JSValue::encode(argument.toString(exec)); }
JSValuePtr RegExpConstructor::getBackref(ExecState* exec, unsigned i) const { if (d->lastOvector && i <= d->lastNumSubPatterns) { int start = d->lastOvector[2 * i]; if (start >= 0) return jsSubstring(exec, d->lastInput, start, d->lastOvector[2 * i + 1] - start); } return jsEmptyString(exec); }
static JSCell* formatLocaleDate(ExecState* exec, const GregorianDateTime& gdt, LocaleDateTimeFormat format) { #if HAVE(LANGINFO_H) static const nl_item formats[] = { D_T_FMT, D_FMT, T_FMT }; #elif PLATFORM(WINCE) && !PLATFORM(QT) // strftime() we are using does not support # static const char* const formatStrings[] = { "%c", "%x", "%X" }; #else static const char* const formatStrings[] = { "%#c", "%#x", "%X" }; #endif // Offset year if needed struct tm localTM = gdt; int year = gdt.year + 1900; bool yearNeedsOffset = year < 1900 || year > 2038; if (yearNeedsOffset) localTM.tm_year = equivalentYearForDST(year) - 1900; #if HAVE(LANGINFO_H) // We do not allow strftime to generate dates with 2-digits years, // both to avoid ambiguity, and a crash in strncpy, for years that // need offset. char* formatString = strdup(nl_langinfo(formats[format])); char* yPos = strchr(formatString, 'y'); if (yPos) *yPos = 'Y'; #endif // Do the formatting const int bufsize = 128; char timebuffer[bufsize]; #if HAVE(LANGINFO_H) size_t ret = strftime(timebuffer, bufsize, formatString, &localTM); free(formatString); #else size_t ret = strftime(timebuffer, bufsize, formatStrings[format], &localTM); #endif if (ret == 0) return jsEmptyString(exec); // Copy original into the buffer if (yearNeedsOffset && format != LocaleTime) { static const int yearLen = 5; // FIXME will be a problem in the year 10,000 char yearString[yearLen]; snprintf(yearString, yearLen, "%d", localTM.tm_year + 1900); char* yearLocation = strstr(timebuffer, yearString); snprintf(yearString, yearLen, "%d", year); strncpy(yearLocation, yearString, yearLen - 1); } return jsNontrivialString(exec, timebuffer); }
static JSCell* formatLocaleDate(ExecState* exec, DateInstance*, double timeInMilliseconds, LocaleDateTimeFormat format) { UDateFormatStyle timeStyle = (format != LocaleDate ? UDAT_LONG : UDAT_NONE); UDateFormatStyle dateStyle = (format != LocaleTime ? UDAT_LONG : UDAT_NONE); UErrorCode status = U_ZERO_ERROR; UDateFormat* df = udat_open(timeStyle, dateStyle, 0, 0, -1, 0, 0, &status); if (!df) return jsEmptyString(exec); UChar buffer[128]; int32_t length; length = udat_format(df, timeInMilliseconds, buffer, 128, 0, &status); udat_close(df); if (status != U_ZERO_ERROR) return jsEmptyString(exec); return jsNontrivialString(exec, String(buffer, length)); }
JSValuePtr RegExpConstructor::getLastParen(ExecState* exec) const { unsigned i = d->lastNumSubPatterns; if (i > 0) { ASSERT(d->lastOvector); int start = d->lastOvector[2 * i]; if (start >= 0) return jsSubstring(exec, d->lastInput, start, d->lastOvector[2 * i + 1] - start); } return jsEmptyString(exec); }
JSString* JSRopeString::getIndexSlowCase(ExecState* exec, unsigned i) { ASSERT(isRope()); resolveRope(exec); // Return a safe no-value result, this should never be used, since the excetion will be thrown. if (exec->exception()) return jsEmptyString(exec); ASSERT(!isRope()); RELEASE_ASSERT(i < m_value.length()); return jsSingleCharacterSubstring(exec, m_value, i); }
JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) { JSArray* array = m_cachedResult.lastResult(exec, this); if (i < array->length()) { JSValue result = JSValue(array).get(exec, i); ASSERT(result.isString() || result.isUndefined()); if (!result.isUndefined()) return result; } return jsEmptyString(exec); }
JSValue RegExpConstructor::getLastParen(ExecState* exec) { JSArray* array = m_cachedResult.lastResult(exec, this); unsigned length = array->length(); if (length > 1) { JSValue result = JSValue(array).get(exec, length - 1); ASSERT(result.isString() || result.isUndefined()); if (!result.isUndefined()) return result; } return jsEmptyString(exec); }
// 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]]) EncodedJSValue JSC_HOST_CALL functionProtoFuncBind(ExecState* exec) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); 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 == CallType::None) return throwVMTypeError(exec, scope); // Primitive values are not callable. ASSERT(target.isObject()); JSObject* targetObject = asObject(target); // 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; if (numBoundArgs) { boundArgs = JSArray::tryCreateUninitialized(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), numBoundArgs); if (!boundArgs) return JSValue::encode(throwOutOfMemoryError(exec, scope)); for (size_t i = 0; i < numBoundArgs; ++i) boundArgs->initializeIndex(vm, i, exec->argument(i + 1)); } else boundArgs = nullptr; // 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->hasOwnProperty(exec, exec->propertyNames().length)) { if (exec->hadException()) return JSValue::encode(jsUndefined()); // 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. JSValue lengthValue = target.get(exec, exec->propertyNames().length); if (lengthValue.isNumber()) { unsigned targetLength = (unsigned)lengthValue.asNumber(); if (targetLength > numBoundArgs) length = targetLength - numBoundArgs; } } JSValue nameProp = target.get(exec, exec->propertyNames().name); JSString* name = nameProp.isString() ? nameProp.toString(exec) : jsEmptyString(exec); return JSValue::encode(JSBoundFunction::create(vm, exec, globalObject, targetObject, exec->argument(0), boundArgs, length, name->value(exec))); }
JSString* JSValue::toStringSlowCase(ExecState* exec, bool returnEmptyStringOnError) const { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto errorValue = [&] () -> JSString* { if (returnEmptyStringOnError) return jsEmptyString(exec); return nullptr; }; ASSERT(!isString()); if (isInt32()) { auto integer = asInt32(); if (static_cast<unsigned>(integer) <= 9) return vm.smallStrings.singleCharacterString(integer + '0'); return jsNontrivialString(&vm, vm.numericStrings.add(integer)); } if (isDouble()) return jsString(&vm, vm.numericStrings.add(asDouble())); if (isTrue()) return vm.smallStrings.trueString(); if (isFalse()) return vm.smallStrings.falseString(); if (isNull()) return vm.smallStrings.nullString(); if (isUndefined()) return vm.smallStrings.undefinedString(); if (isSymbol()) { throwTypeError(exec, scope, ASCIILiteral("Cannot convert a symbol to a string")); return errorValue(); } ASSERT(isCell()); JSValue value = asCell()->toPrimitive(exec, PreferString); if (vm.exception()) return errorValue(); ASSERT(!value.isObject()); JSString* result = value.toString(exec); if (vm.exception()) return errorValue(); return result; }
JSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); int len = s.size(); JSValue a0 = args.at(0); JSValue a1 = args.at(1); double start = a0.toInteger(exec); double length = a1.isUndefined() ? len : a1.toInteger(exec); if (start >= len || length <= 0) return jsEmptyString(exec); if (start < 0) { start += len; if (start < 0) start = 0; } if (start + length > len) length = len - start; return jsSubstring(exec, s, static_cast<unsigned>(start), static_cast<unsigned>(length)); }
JSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { UString s = thisValue.toThisString(exec); int len = s.size(); JSValue a0 = args.at(0); JSValue a1 = args.at(1); // The arg processing is very much like ArrayProtoFunc::Slice double start = a0.toInteger(exec); double end = a1.isUndefined() ? len : a1.toInteger(exec); double from = start < 0 ? len + start : start; double to = end < 0 ? len + end : end; if (to > from && to > 0 && from < len) { if (from < 0) from = 0; if (to > len) to = len; return jsSubstring(exec, s, static_cast<unsigned>(from), static_cast<unsigned>(to) - static_cast<unsigned>(from)); } return jsEmptyString(exec); }
JSString* RegExpCachedResult::rightContext(ExecState* exec, JSObject* owner) { // Make sure we're reified. lastResult(exec, owner); if (!m_reifiedRightContext) { unsigned length = m_reifiedInput->length(); m_reifiedRightContext.set(exec->vm(), owner, m_result.end != length ? jsSubstring(exec, m_reifiedInput.get(), m_result.end, length - m_result.end) : jsEmptyString(exec)); } return m_reifiedRightContext.get(); }
JSValuePtr RegExpConstructor::getRightContext(ExecState* exec) const { if (d->lastOvector) return jsSubstring(exec, d->lastInput, d->lastOvector[1], d->lastInput.size() - d->lastOvector[1]); return jsEmptyString(exec); }
JSValuePtr RegExpConstructor::getLeftContext(ExecState* exec) const { if (d->lastOvector) return jsSubstring(exec, d->lastInput, 0, d->lastOvector[0]); return jsEmptyString(exec); }
// ECMA 15.5.1 static EncodedJSValue JSC_HOST_CALL callStringConstructor(ExecState* exec) { if (!exec->argumentCount()) return JSValue::encode(jsEmptyString(exec)); return JSValue::encode(jsString(exec, exec->argument(0).toString(exec))); }
JSValue RegExpConstructor::getRightContext(ExecState* exec) const { if (!d.lastOvector().isEmpty()) return jsSubstring(exec, d.lastInput, d.lastOvector()[1], d.lastInput.length() - d.lastOvector()[1]); return jsEmptyString(exec); }
JSValue RegExpConstructor::getLeftContext(ExecState* exec) const { if (!d.lastOvector().isEmpty()) return jsSubstring(exec, d.lastInput, 0, d.lastOvector()[0]); return jsEmptyString(exec); }
JSString* RegExpMatchesArray::leftContext(ExecState* exec) { if (!m_result.start) return jsEmptyString(exec); return jsSubstring(exec, m_input.get(), 0, m_result.start); }