escargot/src/builtins/BuiltinString.cpp
HyukWoo Park 14089804d5 Fix remaining exception handlings
Signed-off-by: HyukWoo Park <hyukwoo.park@samsung.com>
2023-09-01 14:11:02 +09:00

1878 lines
90 KiB
C++

/*
* Copyright (c) 2016-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "Escargot.h"
#include "runtime/GlobalObject.h"
#include "runtime/Context.h"
#include "runtime/VMInstance.h"
#include "runtime/StringObject.h"
#include "runtime/ErrorObject.h"
#include "runtime/RegExpObject.h"
#include "runtime/ArrayObject.h"
#include "runtime/NativeFunctionObject.h"
#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
#include "intl/Intl.h"
#include "intl/IntlCollator.h"
#endif
#include "WTFBridge.h"
#include "Yarr.h"
#include "YarrPattern.h"
namespace Escargot {
static Value builtinStringConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
String* s = String::emptyString;
if (argc > 0) {
Value value = argv[0];
if (!newTarget.hasValue() && value.isSymbol()) {
return value.asSymbol()->symbolDescriptiveString();
} else {
s = value.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
}
}
if (!newTarget.hasValue()) {
return s;
}
// StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, "%StringPrototype%")).
Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* {
return constructorRealm->globalObject()->stringPrototype();
});
RETURN_VALUE_IF_PENDING_EXCEPTION
return new StringObject(state, proto, s);
}
static Value builtinStringToString(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (thisValue.isObject() && thisValue.asObject()->isStringObject()) {
return thisValue.asObject()->asStringObject()->primitiveValue();
}
if (thisValue.isString())
return thisValue.toString(state);
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().String.string(), true, state.context()->staticStrings().toString.string(), ErrorObject::Messages::GlobalObject_ThisNotString);
RELEASE_ASSERT_NOT_REACHED();
}
static Value builtinStringIndexOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, indexOf);
String* searchStr = argv[0].toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value val;
if (argc > 1) {
val = argv[1];
}
double pos;
if (val.isUndefined()) {
pos = 0;
} else {
pos = val.toInteger(state);
}
if (pos == std::numeric_limits<double>::infinity() || std::isnan(pos)) {
return Value(-1);
}
if (pos == -std::numeric_limits<double>::infinity()) {
pos = 0;
}
size_t len = str->length();
size_t start = std::min(std::max(pos, 0.0), (double)len);
size_t result = str->find(searchStr, start);
if (result == SIZE_MAX)
return Value(-1);
else
return Value(result);
}
static Value builtinStringLastIndexOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let S be ToString(O).
RESOLVE_THIS_BINDING_TO_STRING(S, String, lastIndexOf);
String* searchStr = argv[0].toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
double numPos;
if (argc > 1) {
numPos = argv[1].toNumber(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
} else {
numPos = Value().toNumber(state);
}
double pos;
// If numPos is NaN, let pos be +∞; otherwise, let pos be ToInteger(numPos).
if (std::isnan(numPos))
pos = std::numeric_limits<double>::infinity();
else
pos = numPos;
double len = S->length();
double start = std::min(std::max(pos, 0.0), len);
size_t result = S->rfind(searchStr, start);
if (result == SIZE_MAX) {
return Value(-1);
}
return Value(result);
}
static Value builtinStringLocaleCompare(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(S, String, localeCompare);
#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
String* That = argv[0].toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value locales, options;
if (argc >= 2) {
locales = argv[1];
}
if (argc >= 3) {
options = argv[2];
}
Object* collator = IntlCollator::create(state, state.context(), locales, options);
return Value(IntlCollator::compare(state, collator, S, That));
#else
String* That = argv[0].toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
return Value(stringCompare(*S, *That));
#endif
}
static Value builtinStringSubstring(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, substring);
if (argc == 0) {
return str;
} else {
size_t len = str->length();
double doubleStart = argv[0].toNumber(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value end = argv[1];
double doubleEnd = (argc < 2 || end.isUndefined()) ? len : end.toNumber(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
doubleStart = (std::isnan(doubleStart)) ? 0 : doubleStart;
doubleEnd = (std::isnan(doubleEnd)) ? 0 : doubleEnd;
double finalStart = (int)trunc(std::min(std::max(doubleStart, 0.0), (double)len));
double finalEnd = (int)trunc(std::min(std::max(doubleEnd, 0.0), (double)len));
size_t from = std::min(finalStart, finalEnd);
size_t to = std::max(finalStart, finalEnd);
ASSERT(from <= to);
if (to - from == 1) {
char16_t c = str->charAt(from);
return state.context()->staticStrings().charCodeToString(c);
}
return str->substring(from, to);
}
}
static Value builtinStringMatch(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (thisValue.isUndefinedOrNull()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().String.string(), true, state.context()->staticStrings().match.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
}
Value regexp = argv[0];
if (!regexp.isUndefinedOrNull()) {
Value matcher = Object::getMethod(state, regexp, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().match));
RETURN_VALUE_IF_PENDING_EXCEPTION
if (!matcher.isUndefined()) {
Value args[1] = { thisValue };
return Object::call(state, matcher, regexp, 1, args);
}
}
String* S = thisValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString : regexp.toString(state), String::emptyString);
RETURN_VALUE_IF_PENDING_EXCEPTION
RETURN_VALUE_IF_PENDING_EXCEPTION
Value func = rx->get(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().match)).value(state, rx);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value args[1] = { Value(S) };
return Object::call(state, func, rx, 1, args);
}
static Value builtinStringMatchAll(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (thisValue.isUndefinedOrNull()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().String.string(), true, state.context()->staticStrings().match.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
}
Value regexp = argv[0];
if (!regexp.isUndefinedOrNull()) {
if (regexp.isObject() && regexp.asObject()->isRegExpObject()) {
String* flags = regexp.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().flags)).value(state, regexp).toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
if (flags->find("g") == SIZE_MAX) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().String.string(), true, state.context()->staticStrings().match.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
}
}
Value matcher = Object::getMethod(state, regexp, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().matchAll));
RETURN_VALUE_IF_PENDING_EXCEPTION
if (!matcher.isUndefined()) {
Value args[1] = { thisValue };
return Object::call(state, matcher, regexp, 1, args);
}
}
String* S = thisValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
StringBuilder builder;
builder.appendChar('g');
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString : regexp.toString(state), builder.finalize());
RETURN_VALUE_IF_PENDING_EXCEPTION
RETURN_VALUE_IF_PENDING_EXCEPTION
Value func = rx->get(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().matchAll)).value(state, rx);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value args[1] = { Value(S) };
return Object::call(state, func, rx, 1, args);
}
#if defined(ENABLE_ICU)
static Value builtinStringNormalize(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
enum NormalizationForm {
NFC,
NFD,
NFKC,
NFKD
};
RESOLVE_THIS_BINDING_TO_STRING(str, String, normalize);
Value argument = Value();
if (argc > 0) {
argument = argv[0];
}
NormalizationForm form = NFC;
if (LIKELY(!argument.isUndefined())) {
String* formString = argument.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
if (formString->equals("NFC")) {
form = NFC;
} else if (formString->equals("NFD")) {
form = NFD;
} else if (formString->equals("NFKC")) {
form = NFKC;
} else if (formString->equals("NFKD")) {
form = NFKD;
} else {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::RangeError, "invalid normalization form");
return Value();
}
}
if (str->length() == 0) {
return Value(str);
}
auto utf16Str = str->toUTF16StringData();
UErrorCode status = U_ZERO_ERROR;
const UNormalizer2* normalizer = nullptr;
switch (form) {
case NFC:
normalizer = unorm2_getNFCInstance(&status);
break;
case NFD:
normalizer = unorm2_getNFDInstance(&status);
break;
case NFKC:
normalizer = unorm2_getNFKCInstance(&status);
break;
case NFKD:
normalizer = unorm2_getNFKDInstance(&status);
break;
default:
break;
}
if (!normalizer || U_FAILURE(status)) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, "normalization fails");
return Value();
}
int32_t normalizedStringLength = unorm2_normalize(normalizer, (const UChar*)utf16Str.data(), utf16Str.length(), nullptr, 0, &status);
if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
// when normalize fails.
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, "normalization fails");
return Value();
}
UTF16StringData ret;
ret.resizeWithUninitializedValues(normalizedStringLength);
status = U_ZERO_ERROR;
unorm2_normalize(normalizer, (const UChar*)utf16Str.data(), utf16Str.length(), (UChar*)ret.data(), normalizedStringLength, &status);
if (U_FAILURE(status)) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, "normalization fails");
return Value();
}
return new UTF16String(std::move(ret));
}
#endif // ENABLE_ICU
static Value builtinStringRepeat(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, repeat);
Value argument = argv[0];
int32_t repeatCount;
double count = argument.toInteger(state);
double newStringLength = str->length() * count;
if (count < 0 || count == std::numeric_limits<double>::infinity() || newStringLength > STRING_MAXIMUM_LENGTH) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::RangeError, "invalid count number of String repeat method");
}
if (newStringLength == 0) {
return String::emptyString;
}
repeatCount = static_cast<int32_t>(count);
StringBuilder builder;
for (int i = 0; i < repeatCount; i++) {
builder.appendString(str);
}
return builder.finalize();
}
static Value stringReplaceFastPathHelper(ExecutionState& state, String* string, String* replaceString, RegexMatchResult& result)
{
ASSERT(string && replaceString);
bool hasDollar = false;
for (size_t i = 0; i < replaceString->length(); i++) {
if (replaceString->charAt(i) == '$') {
hasDollar = true;
break;
}
}
StringBuilder builder;
if (!hasDollar) {
// flat replace
int32_t matchCount = result.m_matchResults.size();
builder.appendSubString(string, 0, result.m_matchResults[0][0].m_start);
for (int32_t i = 0; i < matchCount; i++) {
String* res = replaceString;
builder.appendString(res);
if (i < matchCount - 1) {
builder.appendSubString(string, result.m_matchResults[i][0].m_end, result.m_matchResults[i + 1][0].m_start);
}
}
builder.appendSubString(string, result.m_matchResults[matchCount - 1][0].m_end, string->length());
} else {
// dollar replace
int32_t matchCount = result.m_matchResults.size();
builder.appendSubString(string, 0, result.m_matchResults[0][0].m_start);
for (int32_t i = 0; i < matchCount; i++) {
for (unsigned j = 0; j < replaceString->length(); j++) {
if (replaceString->charAt(j) == '$' && (j + 1) < replaceString->length()) {
char16_t c = replaceString->charAt(j + 1);
if (c == '$') {
builder.appendChar(replaceString->charAt(j));
} else if (c == '&') {
builder.appendSubString(string, result.m_matchResults[i][0].m_start, result.m_matchResults[i][0].m_end);
} else if (c == '\'') {
builder.appendSubString(string, result.m_matchResults[i][0].m_end, string->length());
} else if (c == '`') {
builder.appendSubString(string, 0, result.m_matchResults[i][0].m_start);
} else if ('0' <= c && c <= '9') {
size_t idx = c - '0';
bool usePeek = false;
if (j + 2 < replaceString->length()) {
int peek = replaceString->charAt(j + 2) - '0';
if (0 <= peek && peek <= 9) {
idx *= 10;
idx += peek;
usePeek = true;
}
}
if (idx < result.m_matchResults[i].size() && idx != 0) {
builder.appendSubString(string, result.m_matchResults[i][idx].m_start, result.m_matchResults[i][idx].m_end);
if (usePeek)
j++;
} else {
idx = c - '0';
if (idx < result.m_matchResults[i].size() && idx != 0) {
builder.appendSubString(string, result.m_matchResults[i][idx].m_start, result.m_matchResults[i][idx].m_end);
} else {
builder.appendChar('$');
builder.appendChar(c);
}
}
} else {
builder.appendChar('$');
builder.appendChar(c);
}
j++;
} else {
builder.appendChar(replaceString->charAt(j));
}
}
if (i < matchCount - 1) {
builder.appendSubString(string, result.m_matchResults[i][0].m_end, result.m_matchResults[i + 1][0].m_start);
}
}
builder.appendSubString(string, result.m_matchResults[matchCount - 1][0].m_end, string->length());
}
return builder.finalize(&state);
}
static Value builtinStringReplace(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (thisValue.isUndefinedOrNull()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().object.string(), true, state.context()->staticStrings().replace.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
}
Value searchValue = argv[0];
Value replaceValue = argv[1];
bool isSearchValueRegExp = searchValue.isPointerValue() && searchValue.asPointerValue()->isRegExpObject();
// we should keep fast-path while performace issue is unresolved
bool canUseFastPath = searchValue.isString() || (isSearchValueRegExp && searchValue.asPointerValue()->asRegExpObject()->yarrPatern()->m_captureGroupNames.size() == 0);
if (!searchValue.isUndefinedOrNull()) {
Value replacer = Object::getMethod(state, searchValue, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().replace));
RETURN_VALUE_IF_PENDING_EXCEPTION
if (canUseFastPath && isSearchValueRegExp && replacer.isPointerValue() && replacer.asPointerValue() == state.context()->globalObject()->regexpReplaceMethod()) {
auto exec = searchValue.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().exec));
if (!exec.hasValue() || exec.value(state, searchValue) != state.context()->globalObject()->regexpExecMethod()) {
// this means we cannot use fast path
Value parameters[2] = { thisValue, replaceValue };
return Object::call(state, replacer, searchValue, 2, parameters);
}
} else {
// this means we cannot use fast path
if (!replacer.isUndefined()) {
Value parameters[2] = { thisValue, replaceValue };
return Object::call(state, replacer, searchValue, 2, parameters);
}
}
}
String* string = thisValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
String* searchString = searchValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
bool functionalReplace = replaceValue.isCallable();
if (canUseFastPath) {
RegexMatchResult result;
String* replaceString = nullptr;
if (isSearchValueRegExp) {
RegExpObject* regexp = searchValue.asPointerValue()->asRegExpObject();
bool isGlobal = regexp->option() & RegExpObject::Option::Global;
if (isGlobal) {
regexp->setLastIndex(state, Value(0));
}
bool testResult = regexp->matchNonGlobally(state, string, result, false, 0);
if (testResult) {
if (isGlobal) {
regexp->createRegexMatchResult(state, string, result);
}
}
RETURN_VALUE_IF_PENDING_EXCEPTION
} else {
size_t idx = string->find(searchString);
if (idx != (size_t)-1) {
std::vector<RegexMatchResult::RegexMatchResultPiece> piece;
RegexMatchResult::RegexMatchResultPiece p;
p.m_start = idx;
p.m_end = idx + searchString->length();
piece.push_back(std::move(p));
result.m_matchResults.push_back(std::move(piece));
}
}
// NOTE: replaceValue.toString should be called after searchValue.toString
if (!functionalReplace) {
replaceString = replaceValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
}
// If no occurrences of searchString were found, return string.
if (result.m_matchResults.size() == 0) {
return string;
}
if (functionalReplace) {
uint32_t matchCount = result.m_matchResults.size();
Value callee = replaceValue;
StringBuilder builer;
builer.appendSubString(string, 0, result.m_matchResults[0][0].m_start);
for (uint32_t i = 0; i < matchCount; i++) {
size_t subLen = result.m_matchResults[i].size();
Value* arguments;
arguments = ALLOCA(sizeof(Value) * (subLen + 2), Value);
for (unsigned j = 0; j < (unsigned)subLen; j++) {
if (result.m_matchResults[i][j].m_start == std::numeric_limits<unsigned>::max())
arguments[j] = Value();
else {
StringBuilder argStrBuilder;
argStrBuilder.appendSubString(string, result.m_matchResults[i][j].m_start, result.m_matchResults[i][j].m_end);
arguments[j] = argStrBuilder.finalize(&state);
}
}
arguments[subLen] = Value((int)result.m_matchResults[i][0].m_start);
arguments[subLen + 1] = string;
// 21.1.3.14 (11) it should be called with this as undefined
Value val = Object::call(state, callee, Value(), subLen + 2, arguments);
RETURN_VALUE_IF_PENDING_EXCEPTION
String* res = val.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
builer.appendSubString(res, 0, res->length());
if (i < matchCount - 1) {
builer.appendSubString(string, result.m_matchResults[i][0].m_end, result.m_matchResults[i + 1][0].m_start);
}
}
builer.appendSubString(string, result.m_matchResults[matchCount - 1][0].m_end, string->length());
return builer.finalize(&state);
} else {
return stringReplaceFastPathHelper(state, string, replaceString, result);
}
} else {
if (!functionalReplace) {
replaceValue = replaceValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
}
size_t pos = string->find(searchString, 0);
String* matched = searchString;
// If no occurrences of searchString were found, return string.
if (pos == SIZE_MAX) {
return Value(string);
}
String* replStr = String::emptyString;
if (functionalReplace) {
Value parameters[3] = { Value(matched), Value(pos), Value(string) };
Value replValue = Object::call(state, replaceValue, Value(), 3, parameters);
RETURN_VALUE_IF_PENDING_EXCEPTION
replStr = replValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
} else {
StringVector captures;
replStr = String::getSubstitution(state, matched, string, pos, captures, Value(), replaceValue.toString(state));
RETURN_VALUE_IF_PENDING_EXCEPTION
}
size_t tailpos = pos + matched->length();
StringBuilder builder;
builder.appendSubString(string, 0, pos);
builder.appendSubString(replStr, 0, replStr->length());
builder.appendSubString(string, tailpos, string->length());
String* newString = builder.finalize(&state);
return Value(newString);
}
}
static Value builtinStringReplaceAll(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (thisValue.isUndefinedOrNull()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().object.string(), true, state.context()->staticStrings().replaceAll.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
}
Value searchValue = argv[0];
Value replaceValue = argv[1];
// If searchValue is neither undefined nor null, then
if (!searchValue.isUndefinedOrNull()) {
// If isRegExp is true, then
if (searchValue.isObject() && searchValue.asObject()->isRegExp(state)) {
Value flags = searchValue.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().flags)).value(state, searchValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
if (flags.isUndefinedOrNull()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().replaceAll.string(), true, state.context()->staticStrings().replaceAll.string(), ErrorObject::Messages::GlobalObject_IllegalFirstArgument);
} else {
String* flagsStr = flags.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
if (!flagsStr->contains("g")) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().replaceAll.string(), true, state.context()->staticStrings().replaceAll.string(), ErrorObject::Messages::GlobalObject_IllegalFirstArgument);
}
}
}
// Let replacer be ? GetMethod(searchValue, @@replace).
Value replacer = Object::getMethod(state, searchValue, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().replace));
RETURN_VALUE_IF_PENDING_EXCEPTION
if (!replacer.isUndefined()) {
Value args[2] = { thisValue, replaceValue };
// Return ? Call(replacer, searchValue, « O, replaceValue »).
return Object::call(state, replacer, searchValue, 2, args);
}
}
String* string = thisValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
String* searchString = searchValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
bool functionalReplace = replaceValue.isCallable();
// If functionalReplace is false, then
if (!functionalReplace) {
replaceValue = replaceValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
}
size_t searchLength = searchString->length();
size_t advanceBy = (1 < searchLength) ? searchLength : 1;
std::vector<size_t> matchPositions;
// Let position be ! StringIndexOf(string, searchString, 0).
size_t position = string->find(searchString, 0);
// Repeat, while position is not -1
while (position != SIZE_MAX) {
matchPositions.push_back(position);
position = string->find(searchString, position + advanceBy);
}
size_t endOfLastMatch = 0;
StringBuilder builder;
String* replacement = String::emptyString;
// For each element p of matchPositions, do
for (size_t i = 0; i < matchPositions.size(); i++) {
size_t p = matchPositions[i];
builder.appendSubString(string, endOfLastMatch, p);
// If functionalReplace is true, then
if (functionalReplace) {
Value args[3] = { searchString, Value(p), string };
Value rep = Object::call(state, replaceValue, Value(), 3, args);
RETURN_VALUE_IF_PENDING_EXCEPTION
replacement = rep.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
} else {
StringVector captures;
replacement = String::getSubstitution(state, searchString, string, p, captures, Value(), replaceValue.asString());
RETURN_VALUE_IF_PENDING_EXCEPTION
}
builder.appendString(replacement);
endOfLastMatch = p + searchLength;
}
// If endOfLastMatch < the length of string, then
if (endOfLastMatch < string->length()) {
builder.appendSubString(string, endOfLastMatch, string->length());
}
return builder.finalize(&state);
}
static Value builtinStringSearch(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (thisValue.isUndefinedOrNull()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().String.string(), true, state.context()->staticStrings().search.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
}
Value regexp = argv[0];
if (!regexp.isUndefinedOrNull()) {
Value searcher = Object::getMethod(state, regexp, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().search));
RETURN_VALUE_IF_PENDING_EXCEPTION
if (!searcher.isUndefined()) {
Value args[1] = { thisValue };
return Object::call(state, searcher, regexp, 1, args);
}
}
String* string = thisValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString : regexp.toString(state), String::emptyString);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value func = rx->get(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().search)).value(state, rx);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value args[1] = { Value(string) };
return Object::call(state, func, rx, 1, args);
}
static Value builtinStringSplit(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (thisValue.isUndefinedOrNull()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().String.string(), true, state.context()->staticStrings().split.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
}
Value separator = argv[0];
Value limit = argv[1];
bool isSeparatorRegExp = separator.isPointerValue() && separator.asPointerValue()->isRegExpObject();
// If separator is neither undefined nor null, then
if (!separator.isUndefinedOrNull()) {
// Let splitter be GetMethod(separator, @@split).
Value splitter = Object::getMethod(state, separator, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().split));
RETURN_VALUE_IF_PENDING_EXCEPTION
// --- Optmize path
// if splitter is builtin RegExp.prototype.split and separator is RegExpObject
// we can use old method(ES5) below
if (isSeparatorRegExp && splitter.isPointerValue() && splitter.asPointerValue() == state.context()->globalObject()->regexpSplitMethod()) {
} else if (!splitter.isUndefined()) {
// If splitter is not undefined, then
// Return Call(splitter, separator, <<O, limit>>).
Value params[2] = { thisValue, limit };
return Object::call(state, splitter, separator, 2, params);
}
}
// Let S be ? ToString(O).
String* S = thisValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let A be ! ArrayCreate(0).
ArrayObject* A = new ArrayObject(state);
// Let lengthA = 0.
size_t lengthA = 0;
// If limit is undefined, let lim be 2^32 - 1; else let lim be ? ToUint32(limit).
uint64_t lim = limit.isUndefined() ? (1ULL << 32) - 1 : limit.toUint32(state);
// Let s be the length of S.
// Let p = 0.
size_t s = S->length(), p = 0;
PointerValue* P;
if (isSeparatorRegExp) {
P = separator.asPointerValue()->asRegExpObject();
} else {
P = separator.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
}
// If lim = 0, return A.
if (lim == 0) {
return A;
}
if (separator.isUndefined()) {
A->defineOwnProperty(state, ObjectPropertyName(state, Value(0)), ObjectPropertyDescriptor(S, ObjectPropertyDescriptor::AllPresent));
return A;
}
std::function<Value(String*, int, String*)> splitMatchUsingStr;
splitMatchUsingStr = [](String* S, int q, String* R) -> Value {
int s = S->length();
int r = R->length();
if (q + r > s)
return Value(false);
for (int i = 0; i < r; i++)
if (S->charAt(q + i) != R->charAt(i))
return Value(false);
return Value(q + r);
};
if (s == 0) {
bool ret = true;
if (P->isRegExpObject()) {
RegexMatchResult result;
ret = P->asRegExpObject()->matchNonGlobally(state, S, result, false, 0);
RETURN_VALUE_IF_PENDING_EXCEPTION
} else {
Value z = splitMatchUsingStr(S, 0, P->asString());
if (z.isBoolean()) {
ret = z.asBoolean();
}
}
if (ret)
return A;
A->defineOwnProperty(state, ObjectPropertyName(state, Value(0)), ObjectPropertyDescriptor(S, ObjectPropertyDescriptor::AllPresent));
return A;
}
size_t q = p;
// 13
if (P->isRegExpObject()) {
RegExpObject* R = P->asRegExpObject();
while (q != s) {
RegexMatchResult result;
bool ret = R->matchNonGlobally(state, S, result, false, (size_t)q);
if (!ret) {
break;
}
if ((size_t)result.m_matchResults[0][0].m_end == p) {
q++;
} else {
if (result.m_matchResults[0][0].m_start >= S->length())
break;
String* T = S->substring(p, result.m_matchResults[0][0].m_start);
A->defineOwnProperty(state, ObjectPropertyName(state, Value(lengthA++)), ObjectPropertyDescriptor(T, ObjectPropertyDescriptor::AllPresent));
if (lengthA == lim)
return A;
p = result.m_matchResults[0][0].m_end;
R->pushBackToRegExpMatchedArray(state, A, lengthA, lim, result, S);
if (lengthA == lim)
return A;
q = p;
}
RETURN_VALUE_IF_PENDING_EXCEPTION
}
} else {
String* R = P->asString();
while (q != s) {
Value e = splitMatchUsingStr(S, q, R);
if (e == Value(false))
q++;
else {
if ((size_t)e.asInt32() == p)
q++;
else {
if (q >= S->length())
break;
String* T = S->substring(p, q);
A->defineOwnProperty(state, ObjectPropertyName(state, Value(lengthA++)), ObjectPropertyDescriptor(T, ObjectPropertyDescriptor::AllPresent));
if (lengthA == lim)
return A;
p = e.asInt32();
q = p;
}
}
}
}
String* T = S->substring(p, s);
A->defineOwnProperty(state, ObjectPropertyName(state, Value(lengthA)), ObjectPropertyDescriptor(T, ObjectPropertyDescriptor::AllPresent));
return A;
}
static Value builtinStringCharCodeAt(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, charCodeAt);
double position = argv[0].toInteger(state);
Value ret;
size_t length = str->length();
if (position < 0 || position >= (int)length)
ret = Value(Value::NanInit);
else {
ret = Value(str->charAt(position));
}
return ret;
}
// https://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.codepointat
static Value builtinStringCodePointAt(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, codePointAt);
double position = argv[0].toInteger(state);
Value ret;
size_t length = str->length();
const size_t size = (int)length;
if (position < 0 || position >= size)
return Value();
char16_t first = str->charAt(position);
if (first < 0xD800 || first > 0xDBFF || (position + 1) == size) {
return Value(first);
}
char16_t second = str->charAt(position + 1);
if (second < 0xDC00 || second > 0xDFFF) {
return Value(first);
}
int cp = ((first - 0xD800) * 1024) + (second - 0xDC00) + 0x10000;
return Value(cp);
}
static Value builtinStringCharAt(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, charAt);
double position = 0;
if (argc > 0) {
position = argv[0].toInteger(state);
}
const auto length = str->length();
if (LIKELY(0 <= position && position < (int64_t)length)) {
char16_t c = str->charAt(position);
return state.context()->staticStrings().charCodeToString(c);
} else {
return String::emptyString;
}
}
static Value builtinStringFromCharCode(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (argc == 1) {
char16_t c = argv[0].toUint32(state) & 0xFFFF;
return state.context()->staticStrings().charCodeToString(c);
}
StringBuilder builder;
for (size_t i = 0; i < argc; i++) {
builder.appendChar((char16_t)argv[i].toUint32(state));
}
return builder.finalize(&state);
}
static Value builtinStringFromCodePoint(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
StringBuilder builder;
for (size_t nextIndex = 0; nextIndex < argc; nextIndex++) {
Value next = argv[nextIndex];
double nextCP = next.toNumber(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
double toIntegerNexCP = next.toInteger(state);
if (nextCP != toIntegerNexCP || nextCP < 0 || nextCP > 0x10FFFF) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::RangeError, "invalid code point");
}
uint32_t cp = (uint32_t)nextCP;
if (cp <= 65535) {
builder.appendChar((char16_t)cp);
} else {
char16_t cu1 = floor((cp - 65536) / 1024) + 0xD800;
char16_t cu2 = ((cp - 65536) % 1024) + 0xDC00;
builder.appendChar(cu1);
builder.appendChar(cu2);
}
}
return builder.finalize(&state);
}
static Value builtinStringConcat(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, concat);
for (size_t i = 0; i < argc; i++) {
String* appendStr = argv[i].toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
str = RopeString::createRopeString(str, appendStr, &state);
}
return Value(str);
}
static Value builtinStringSlice(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, slice);
size_t len = str->length();
double start = argv[0].toInteger(state);
double end = (argv[1].isUndefined()) ? len : argv[1].toInteger(state);
int from = (start < 0) ? std::max(len + start, 0.0) : std::min(start, (double)len);
int to = (end < 0) ? std::max(len + end, 0.0) : std::min(end, (double)len);
int span = std::max(to - from, 0);
return str->substring(from, from + span);
}
#if defined(ENABLE_ICU)
static String* stringToLocaleConvertCase(ExecutionState& state, String* str, String* locale, bool isUpper)
{
int32_t len = str->length();
char16_t* src = ALLOCA(len * 2, char16_t);
if (str->has8BitContent()) {
const LChar* buf = str->characters8();
for (int32_t i = 0; i < len; i++) {
src[i] = buf[i];
}
} else {
memcpy(src, str->characters16(), len * 2);
}
UErrorCode status = U_ZERO_ERROR;
int32_t dest_length = len * 3;
char16_t* dest = ALLOCA(dest_length * 2, char16_t);
if (isUpper) {
dest_length = u_strToUpper(dest, dest_length, src, len, (const char*)locale->characters8(), &status);
} else {
dest_length = u_strToLower(dest, dest_length, src, len, (const char*)locale->characters8(), &status);
}
ASSERT(status != U_BUFFER_OVERFLOW_ERROR);
ASSERT(U_SUCCESS(status));
return new UTF16String(dest, dest_length);
}
#endif
static Value builtinStringToLowerCase(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, toLowerCase);
if (str->has8BitContent()) {
size_t len = str->length();
const LChar* from = str->characters8();
LChar* dest;
Latin1StringData newStr;
if (len <= LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE) {
dest = ALLOCA(len, LChar);
} else {
newStr.resizeWithUninitializedValues(len);
dest = newStr.data();
}
for (size_t i = 0; i < len; i++) {
#if defined(ENABLE_ICU)
char32_t u2 = u_tolower(from[i]);
#else
char32_t u2 = tolower(from[i]);
#endif
ASSERT(u2 < 256);
dest[i] = u2;
}
if (len <= LATIN1_LARGE_INLINE_BUFFER_MAX_SIZE) {
return String::fromLatin1(dest, len);
} else {
return new Latin1String(std::move(newStr));
}
}
#if defined(ENABLE_ICU)
return stringToLocaleConvertCase(state, str, String::emptyString, false);
#else
size_t len = str->length();
UTF16StringData newStr;
if (str->has8BitContent()) {
const LChar* buf = str->characters8();
newStr.resizeWithUninitializedValues(len);
for (size_t i = 0; i < len; i++) {
newStr[i] = buf[i];
}
} else {
newStr = UTF16StringData(str->characters16(), len);
}
char16_t* buf = newStr.data();
for (size_t i = 0; i < len;) {
char32_t c;
size_t iBefore = i;
U16_NEXT(buf, i, len, c);
c = tolower(c);
if (c <= 0x10000) {
char16_t c2 = (char16_t)c;
buf[iBefore] = c2;
} else {
buf[iBefore] = (char16_t)(0xD800 + ((c - 0x10000) >> 10));
buf[iBefore + 1] = (char16_t)(0xDC00 + ((c - 0x10000) & 1023));
}
}
return new UTF16String(std::move(newStr));
#endif
}
static Value builtinStringToUpperCase(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, toUpperCase);
if (str->has8BitContent()) {
Latin1StringData newStr;
size_t len = str->length();
newStr.resizeWithUninitializedValues(len);
bool fitTo8Bit = true;
size_t sharpSCount = 0;
const LChar* buf = str->characters8();
for (size_t i = 0; i < len; i++) {
LChar ch = buf[i];
// U+00B5 and U+00FF are mapped to a character beyond U+00FF
if (UNLIKELY(ch == 0xB5 || ch == 0xFF)) {
fitTo8Bit = false;
break;
}
// Lower case sharp-S converts to "SS" (two characters)
if (UNLIKELY(ch == 0xDF)) {
sharpSCount++;
continue;
}
#if defined(ENABLE_ICU)
char32_t u2 = u_toupper(ch);
#else
char32_t u2 = toupper(ch);
#endif
ASSERT(u2 < 256);
newStr[i] = u2;
}
if (fitTo8Bit) {
if (UNLIKELY(sharpSCount > 0)) {
Latin1StringData newStr2;
newStr2.resizeWithUninitializedValues(len + sharpSCount);
size_t destIndex = 0;
for (size_t i = 0; i < len; i++) {
LChar ch = buf[i];
if (ch != 0xDF) {
newStr2[destIndex++] = newStr[i];
} else {
newStr2[destIndex++] = 'S';
newStr2[destIndex++] = 'S';
}
}
ASSERT(destIndex == len + sharpSCount);
return new Latin1String(std::move(newStr2));
}
return new Latin1String(std::move(newStr));
}
}
#if defined(ENABLE_ICU)
return stringToLocaleConvertCase(state, str, String::emptyString, true);
#else
size_t len = str->length();
UTF16StringData newStr;
if (str->has8BitContent()) {
const LChar* buf = str->characters8();
newStr.resizeWithUninitializedValues(len);
for (size_t i = 0; i < len; i++) {
newStr[i] = buf[i];
}
} else
newStr = UTF16StringData(str->characters16(), len);
char16_t* buf = newStr.data();
for (size_t i = 0; i < len;) {
char32_t c;
size_t iBefore = i;
U16_NEXT(buf, i, len, c);
c = toupper(c);
if (c <= 0x10000) {
char16_t c2 = (char16_t)c;
buf[iBefore] = c2;
} else {
buf[iBefore] = (char16_t)(0xD800 + ((c - 0x10000) >> 10));
buf[iBefore + 1] = (char16_t)(0xDC00 + ((c - 0x10000) & 1023));
}
}
return new UTF16String(std::move(newStr));
#endif
}
static Value builtinStringToLocaleLowerCase(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
RESOLVE_THIS_BINDING_TO_STRING(str, String, toLocaleLowerCase);
Value locales = argc > 0 ? argv[0] : Value();
String* locale = Intl::getLocaleForStringLocaleConvertCase(state, locales);
if (str->has8BitContent() && locale->length() == 0) {
return builtinStringToLowerCase(state, thisValue, argc, argv, newTarget);
} else {
return stringToLocaleConvertCase(state, str, locale, false);
}
#else
return builtinStringToLowerCase(state, thisValue, argc, argv, newTarget);
#endif
}
static Value builtinStringToLocaleUpperCase(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
RESOLVE_THIS_BINDING_TO_STRING(str, String, toLocaleUpperCase);
Value locales = argc > 0 ? argv[0] : Value();
String* locale = Intl::getLocaleForStringLocaleConvertCase(state, locales);
if (str->has8BitContent() && locale->length() == 0) {
return builtinStringToUpperCase(state, thisValue, argc, argv, newTarget);
} else {
return stringToLocaleConvertCase(state, str, locale, true);
}
#else
return builtinStringToUpperCase(state, thisValue, argc, argv, newTarget);
#endif
}
static Value builtinStringTrim(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let S be this value.
RESOLVE_THIS_BINDING_TO_STRING(str, String, trim);
// Return ? TrimString(S, "start+end").
return str->trim(String::TrimBoth);
}
static Value builtinStringTrimStart(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let S be this value.
RESOLVE_THIS_BINDING_TO_STRING(str, String, trimStart);
// Return ? TrimString(S, "start").
return str->trim(String::TrimStart);
}
static Value builtinStringTrimEnd(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let S be this value.
RESOLVE_THIS_BINDING_TO_STRING(str, String, trimEnd);
// Return ? TrimString(S, "end").
return str->trim(String::TrimEnd);
}
static Value builtinStringValueOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (thisValue.isString()) {
return Value(thisValue);
} else if (thisValue.isObject() && thisValue.asObject()->isStringObject()) {
return Value(thisValue.asPointerValue()->asStringObject()->primitiveValue());
}
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_ThisNotString);
RELEASE_ASSERT_NOT_REACHED();
}
static Value builtinStringStartsWith(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let O be ? RequireObjectCoercible(this value).
// Let S be ? ToString(O).
RESOLVE_THIS_BINDING_TO_STRING(S, String, startsWith);
Value searchString = argv[0];
// Let isRegExp be ? IsRegExp(searchString).
// If isRegExp is true, throw a TypeError exception.
if (searchString.isObject() && searchString.asObject()->isRegExp(state)) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, "can't use RegExp with startsWith");
}
// Let searchStr be ? ToString(searchString).
String* searchStr = searchString.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let pos be ? ToInteger(position). (If position is undefined, this step produces the value 0.)
double pos = 0;
if (argc >= 2) {
pos = argv[1].toInteger(state);
}
// Let len be the number of elements in S.
double len = S->length();
// Let start be min(max(pos, 0), len).
double start = std::min(std::max(pos, 0.0), len);
// Let searchLength be the number of elements in searchStr.
double searchLength = searchStr->length();
// If searchLength+start is greater than len, return false.
if (searchLength + start > len) {
return Value(false);
}
// If the sequence of elements of S starting at start of length searchLength is the same as the full element sequence of searchStr, return true.
// Otherwise, return false.
const auto& srcData = S->bufferAccessData();
const auto& src2Data = searchStr->bufferAccessData();
for (size_t i = 0; i < src2Data.length; i++) {
if (srcData.charAt(i + start) != src2Data.charAt(i)) {
return Value(false);
}
}
return Value(true);
}
static Value builtinStringEndsWith(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let O be ? RequireObjectCoercible(this value).
// Let S be ? ToString(O).
RESOLVE_THIS_BINDING_TO_STRING(S, String, endsWith);
Value searchString = argv[0];
// Let isRegExp be ? IsRegExp(searchString).
// If isRegExp is true, throw a TypeError exception.
if (searchString.isObject() && searchString.asObject()->isRegExp(state)) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, "can't use RegExp with endsWith");
}
// Let len be the number of elements in S.
double len = S->length();
// Let searchStr be ? ToString(searchString).
String* searchStr = searchString.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// If endPosition is undefined, let pos be len, else let pos be ? ToInteger(endPosition).
double pos = 0;
if (argc >= 2 && !argv[1].isUndefined()) {
pos = argv[1].toInteger(state);
} else {
pos = len;
}
// Let end be min(max(pos, 0), len).
double end = std::min(std::max(pos, 0.0), len);
// Let searchLength be the number of elements in searchStr.
double searchLength = searchStr->length();
// Let start be end - searchLength.
double start = end - searchLength;
// If start is less than 0, return false.
if (start < 0) {
return Value(false);
}
// If the sequence of elements of S starting at start of length searchLength is the same as the full element sequence of searchStr, return true.
const auto& srcData = S->bufferAccessData();
const auto& src2Data = searchStr->bufferAccessData();
for (size_t i = 0; i < searchLength; i++) {
if (srcData.charAt(i + start) != src2Data.charAt(i)) {
return Value(false);
}
}
return Value(true);
}
// ( template, ...substitutions )
static Value builtinStringRaw(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
Value argTemplate = argv[0];
// Let substitutions be a List consisting of all of the arguments passed to this function, starting with the second argument. If fewer than two arguments were passed, the List is empty.
// Let numberOfSubstitutions be the number of elements in substitutions.
size_t numberOfSubstitutions;
if (argc < 2) {
numberOfSubstitutions = 0;
} else {
numberOfSubstitutions = argc - 1;
}
// Let cooked be ? ToObject(template).
Object* cooked = argTemplate.toObject(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let raw be ? ToObject(? Get(cooked, "raw")).
Value rawValue = cooked->get(state, ObjectPropertyName(state.context()->staticStrings().raw)).value(state, cooked);
RETURN_VALUE_IF_PENDING_EXCEPTION
Object* raw = rawValue.toObject(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let literalSegments be ? ToLength(? Get(raw, "length")).
double literalSegments = raw->length(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// If literalSegments ≤ 0, return the empty string.
if (literalSegments <= 0) {
return String::emptyString;
}
// Let stringElements be a new empty List.
StringBuilder stringElements;
// Let nextIndex be 0.
size_t nextIndex = 0;
// Repeat
while (true) {
// Let nextKey be ! ToString(nextIndex).
// Let nextSeg be ? ToString(? Get(raw, nextKey)).
Value nextSegValue = raw->get(state, ObjectPropertyName(state, Value(nextIndex))).value(state, raw);
RETURN_VALUE_IF_PENDING_EXCEPTION
String* nextSeg = nextSegValue.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Append in order the code unit elements of nextSeg to the end of stringElements.
for (size_t i = 0; i < nextSeg->length(); i++) {
stringElements.appendChar(nextSeg->charAt(i));
}
// If nextIndex + 1 = literalSegments, then
if (nextIndex + 1 == literalSegments) {
// Return the String value whose code units are, in order, the elements in the List stringElements. If stringElements has no elements, the empty string is returned.
return stringElements.finalize(&state);
}
Value next;
// If nextIndex < numberOfSubstitutions, let next be substitutions[nextIndex].
if (nextIndex < numberOfSubstitutions) {
next = argv[nextIndex + 1];
} else {
// Else, let next be the empty String.
next = String::emptyString;
}
// Let nextSub be ? ToString(next).
String* nextSub = next.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Append in order the code unit elements of nextSub to the end of stringElements.
stringElements.appendString(nextSub);
// Let nextIndex be nextIndex + 1.
nextIndex++;
}
}
static Value stringPad(ExecutionState& state, String* S, size_t argc, Value* argv, bool isPadStart)
{
// Let intMaxLength be ? ToLength(maxLength).
// Let stringLength be the number of elements in S.
uint64_t intMaxLength = 0;
if (argc >= 1) {
intMaxLength = argv[0].toLength(state);
}
uint64_t stringLength = S->length();
// If intMaxLength is not greater than stringLength, return S.
if (intMaxLength <= stringLength) {
return S;
}
// If fillString is undefined, let filler be a String consisting solely of the code unit 0x0020 (SPACE).
// Else, let filler be ? ToString(fillString).
String* filler;
if (argc >= 2 && (!argv[1].isUndefined())) {
filler = argv[1].toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
} else {
filler = state.context()->staticStrings().asciiTable[0x20].string();
}
// If filler is the empty String, return S.
if (filler->length() == 0) {
return S;
}
// Let fillLen be intMaxLength - stringLength.
uint64_t fillLen = intMaxLength - stringLength;
// Let truncatedStringFiller be a new String value consisting of repeated concatenations of filler truncated to length fillLen.
StringBuilder sb;
while (sb.contentLength() < fillLen) {
sb.appendString(filler);
}
// Build the string, than truncate the characters over fillLen
String* truncatedStringFiller = sb.finalize(&state);
truncatedStringFiller = truncatedStringFiller->substring(0, fillLen);
// Return a new String value computed by the concatenation of truncatedStringFiller and S.
if (isPadStart) {
sb.appendString(truncatedStringFiller);
sb.appendString(S);
} else {
sb.appendString(S);
sb.appendString(truncatedStringFiller);
}
return sb.finalize(&state);
}
// https://www.ecma-international.org/ecma-262/8.0/#sec-string.prototype.padstart
// 21.1.3.14String.prototype.padStart( maxLength [ , fillString ] )
static Value builtinStringPadStart(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let O be ? RequireObjectCoercible(this value).
// Let S be ? ToString(O).
RESOLVE_THIS_BINDING_TO_STRING(S, String, padStart);
return stringPad(state, S, argc, argv, true);
}
// https://www.ecma-international.org/ecma-262/8.0/#sec-string.prototype.padend
// 21.1.3.13String.prototype.padEnd( maxLength [ , fillString ] )
static Value builtinStringPadEnd(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let O be ? RequireObjectCoercible(this value).
// Let S be ? ToString(O).
RESOLVE_THIS_BINDING_TO_STRING(S, String, padEnd);
return stringPad(state, S, argc, argv, false);
}
// http://www.ecma-international.org/ecma-262/6.0/#sec-createhtml
// Runtime Semantics: CreateHTML ( string, tag, attribute, value )
static String* createHTML(ExecutionState& state, Value string, String* tag, String* attribute, Value value, AtomicString methodName)
{
// Let str be RequireObjectCoercible(string).
// Let S be ToString(str).
// ReturnIfAbrupt(S).
if (string.isUndefinedOrNull()) {
THROW_BUILTIN_ERROR_RETURN_NULL(state, ErrorCode::TypeError, state.context()->staticStrings().String.string(), true, methodName.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
}
String* S = string.toString(state);
RETURN_NULL_IF_PENDING_EXCEPTION
// Let p1 be the String value that is the concatenation of "<" and tag.
StringBuilder sb;
sb.appendChar('<');
sb.appendString(tag);
String* p1 = sb.finalize(&state);
// If attribute is not the empty String, then
if (attribute->length()) {
// Let V be ToString(value).
String* V = value.toString(state);
RETURN_NULL_IF_PENDING_EXCEPTION
// ReturnIfAbrupt(V).
// Let escapedV be the String value that is the same as V except that each occurrence of the code unit 0x0022 (QUOTATION MARK) in V has been replaced with the six code unit sequence "&quot;".
StringBuilder sb;
for (size_t i = 0; i < V->length(); i++) {
char16_t ch = V->charAt(i);
if (ch == 0x22) {
sb.appendString("&quot;");
} else {
sb.appendChar(ch);
}
}
String* escapedV = sb.finalize(&state);
// Let p1 be the String value that is the concatenation of the following String values:
// The String value of p1
// Code unit 0x0020 (SPACE)
// The String value of attribute
// Code unit 0x003D (EQUALS SIGN)
// Code unit 0x0022 (QUOTATION MARK)
// The String value of escapedV
// Code unit 0x0022 (QUOTATION MARK)
sb.appendString(p1);
sb.appendChar((char)0x20);
sb.appendString(attribute);
sb.appendChar((char)0x3d);
sb.appendChar((char)0x22);
sb.appendString(escapedV);
sb.appendChar((char)0x22);
p1 = sb.finalize(&state);
}
// Let p2 be the String value that is the concatenation of p1 and ">".
// Let p3 be the String value that is the concatenation of p2 and S.
// Let p4 be the String value that is the concatenation of p3, "</", tag, and ">".
// Return p4.
sb.appendString(p1);
sb.appendChar('>');
sb.appendString(S);
sb.appendString("</");
sb.appendString(tag);
sb.appendChar('>');
return sb.finalize(&state);
}
// http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties-of-the-string.prototype-object
static Value builtinStringSubstr(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, substr);
if (argc < 1) {
return str;
}
double intStart = argv[0].toInteger(state);
double end;
if (argc > 1) {
if (argv[1].isUndefined()) {
end = std::numeric_limits<double>::infinity();
} else
end = argv[1].toInteger(state);
} else {
end = std::numeric_limits<double>::infinity();
}
double size = str->length();
if (intStart < 0)
intStart = std::max(size + intStart, 0.0);
double resultLength = std::min(std::max(end, 0.0), size - intStart);
if (resultLength <= 0)
return String::emptyString;
return str->substring(intStart, intStart + resultLength);
}
static Value builtinStringAt(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
RESOLVE_THIS_BINDING_TO_STRING(str, String, substr);
size_t len = str->length();
double relativeStart = argv[0].toInteger(state);
if (relativeStart < 0) {
relativeStart = len + relativeStart;
}
if (relativeStart < 0 || relativeStart >= len) {
return Value();
}
return state.context()->staticStrings().charCodeToString(str->charAt(relativeStart));
}
#define DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fnName, P0, P1, P2) \
static Value builtinString##fnName(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget) \
{ \
Value result = createHTML(state, thisValue, P0, P1, P2, state.context()->staticStrings().fnName); \
RETURN_VALUE_IF_PENDING_EXCEPTION \
RETURN_VALUE_IF_PENDING_EXCEPTION \
return result; \
}
// String.prototype.anchor (name)
// Return CreateHTML(S, "a", "name", name).
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(anchor, state.context()->staticStrings().asciiTable[(size_t)'a'].string(), state.context()->staticStrings().name.string(), argv[0])
// String.prototype.big ()
// Return CreateHTML(S, "big", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(big, state.context()->staticStrings().big.string(), String::emptyString, String::emptyString)
// String.prototype.blink ()
// Return CreateHTML(S, "blink", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(blink, state.context()->staticStrings().blink.string(), String::emptyString, String::emptyString)
// String.prototype.bold ()
// Return CreateHTML(S, "b", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(bold, state.context()->staticStrings().asciiTable[(size_t)'b'].string(), String::emptyString, String::emptyString)
// String.prototype.fixed ()
// Return CreateHTML(S, "tt", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fixed, String::fromASCII("tt"), String::emptyString, String::emptyString)
// String.prototype.fontcolor (color)
// Return CreateHTML(S, "font", "color", color).
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fontcolor, String::fromASCII("font"), String::fromASCII("color"), argv[0])
// String.prototype.fontsize (size)
// Return CreateHTML(S, "font", "size", size).
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fontsize, String::fromASCII("font"), state.context()->staticStrings().size.string(), argv[0])
// String.prototype.italics ()
// Return CreateHTML(S, "i", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(italics, state.context()->staticStrings().asciiTable[(size_t)'i'].string(), String::emptyString, String::emptyString)
// String.prototype.link (url)
// Return CreateHTML(S, "a", "href", url).
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(link, state.context()->staticStrings().asciiTable[(size_t)'a'].string(), String::fromASCII("href"), argv[0])
// String.prototype.small ()
// Return CreateHTML(S, "small", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(small, state.context()->staticStrings().small.string(), String::emptyString, String::emptyString)
// String.prototype.strike ()
// Return CreateHTML(S, "strike", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(strike, state.context()->staticStrings().strike.string(), String::emptyString, String::emptyString)
// String.prototype.sub ()
// Return CreateHTML(S, "sub", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(sub, state.context()->staticStrings().sub.string(), String::emptyString, String::emptyString)
// String.prototype.sup ()
// Return CreateHTML(S, "sup", "", "").
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(sup, state.context()->staticStrings().sup.string(), String::emptyString, String::emptyString)
#undef DEFINE_STRING_ADDITIONAL_HTML_FUNCTION
static Value builtinStringIncludes(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let O be ? RequireObjectCoercible(this value).
// Let S be ? ToString(O).
RESOLVE_THIS_BINDING_TO_STRING(S, String, includes);
// Let isRegExp be ? IsRegExp(searchString).
// If isRegExp is true, throw a TypeError exception.
Value searchString = argv[0];
if (searchString.isObject() && searchString.asObject()->isRegExp(state)) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, "can't use RegExp with includes");
}
// Let searchStr be ? ToString(searchString).
String* searchStr = searchString.toString(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let pos be ? ToInteger(position). (If position is undefined, this step produces the value 0.)
double pos = 0;
if (argc >= 2) {
pos = argv[1].toInteger(state);
}
// Let len be the number of elements in S.
double len = S->length();
// Let start be min(max(pos, 0), len).
double start = std::min(std::max(pos, 0.0), len);
// Let searchLen be the number of elements in searchStr.
// If there exists any integer k not smaller than start such that k + searchLen is not greater than len, and for all nonnegative integers j less than searchLen, the code unit at index k+j of S is the same as the code unit at index j of searchStr, return true; but if there is no such integer k, return false.
auto ret = S->find(searchStr, start);
if (ret == SIZE_MAX) {
return Value(false);
}
return Value(true);
}
static Value builtinStringIteratorNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
if (!thisValue.isObject() || !thisValue.asObject()->isStringIteratorObject()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().StringIterator.string(), true, state.context()->staticStrings().next.string(), ErrorObject::Messages::GlobalObject_CalledOnIncompatibleReceiver);
}
StringIteratorObject* iter = thisValue.asObject()->asIteratorObject()->asStringIteratorObject();
return iter->next(state);
}
static Value builtinStringIterator(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let O be ? RequireObjectCoercible(this value).
// Let S be ? ToString(O).
RESOLVE_THIS_BINDING_TO_STRING(S, String, iterator);
// Return CreateStringIterator(S).
return new StringIteratorObject(state, S);
}
void GlobalObject::initializeString(ExecutionState& state)
{
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
ASSERT(self->isGlobalObject());
return self->asGlobalObject()->string();
},
nullptr);
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().String), nativeData, Value(Value::EmptyValue));
}
void GlobalObject::installString(ExecutionState& state)
{
ASSERT(!!m_iteratorPrototype);
const StaticStrings* strings = &state.context()->staticStrings();
m_string = new NativeFunctionObject(state, NativeFunctionInfo(strings->String, builtinStringConstructor, 1), NativeFunctionObject::__ForBuiltinConstructor__);
m_string->setGlobalIntrinsicObject(state);
m_stringPrototype = new StringObject(state, m_objectPrototype, String::emptyString);
m_stringPrototype->setGlobalIntrinsicObject(state, true);
m_string->setFunctionPrototype(state, m_stringPrototype);
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->constructor), ObjectPropertyDescriptor(m_string, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toString),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toString, builtinStringToString, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $21.1.3.4 String.prototype.concat
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->concat),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->concat, builtinStringConcat, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $21.1.3.8 String.prototype.indexOf
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->indexOf),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->indexOf, builtinStringIndexOf, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->lastIndexOf),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->lastIndexOf, builtinStringLastIndexOf, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->localeCompare),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->localeCompare, builtinStringLocaleCompare, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $21.1.3.16 String.prototype.slice
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->slice),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->slice, builtinStringSlice, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $21.1.3.19 String.prototype.substring
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->substring),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->substring, builtinStringSubstring, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->substr),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->substr, builtinStringSubstr, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->match),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->match, builtinStringMatch, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->matchAll),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->matchAll, builtinStringMatchAll, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
#if defined(ENABLE_ICU)
// The length property of the normalize method is 0.
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->normalize),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->normalize, builtinStringNormalize, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
#endif // ENABLE_ICU
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->repeat),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->repeat, builtinStringRepeat, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->replace),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->replace, builtinStringReplace, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->replaceAll),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->replaceAll, builtinStringReplaceAll, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->search),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->search, builtinStringSearch, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->split),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->split, builtinStringSplit, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->charCodeAt),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->charCodeAt, builtinStringCharCodeAt, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->codePointAt),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->codePointAt, builtinStringCodePointAt, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->charAt),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->charAt, builtinStringCharAt, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toLowerCase),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toLowerCase, builtinStringToLowerCase, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toUpperCase),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toUpperCase, builtinStringToUpperCase, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toLocaleLowerCase),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toLocaleLowerCase, builtinStringToLocaleLowerCase, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toLocaleUpperCase),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toLocaleUpperCase, builtinStringToLocaleUpperCase, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->trim),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->trim, builtinStringTrim, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->padStart),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->padStart, builtinStringPadStart, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->padEnd),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->padEnd, builtinStringPadEnd, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
FunctionObject* trimStart = new NativeFunctionObject(state, NativeFunctionInfo(strings->trimStart, builtinStringTrimStart, 0, NativeFunctionInfo::Strict));
FunctionObject* trimEnd = new NativeFunctionObject(state, NativeFunctionInfo(strings->trimEnd, builtinStringTrimEnd, 0, NativeFunctionInfo::Strict));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->trimStart),
ObjectPropertyDescriptor(trimStart, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->trimEnd),
ObjectPropertyDescriptor(trimEnd, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->trimRight),
ObjectPropertyDescriptor(trimEnd, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->trimLeft),
ObjectPropertyDescriptor(trimStart, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $21.1.3.26 String.prototype.valueOf
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->valueOf),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->valueOf, builtinStringValueOf, 0, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// ES6 builtins
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->startsWith),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->startsWith, builtinStringStartsWith, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->endsWith),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->endsWith, builtinStringEndsWith, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->includes),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->includes, builtinStringIncludes, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().iterator),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(AtomicString(state, String::fromASCII("[Symbol.iterator]")), builtinStringIterator, 0, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->at),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->at, builtinStringAt, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
#define DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fnName, argLength) \
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->fnName), \
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->fnName, builtinString##fnName, argLength, NativeFunctionInfo::Strict)), \
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// String.prototype.anchor (name)
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(anchor, 1)
// String.prototype.big ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(big, 0)
// String.prototype.blink ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(blink, 0)
// String.prototype.bold ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(bold, 0)
// String.prototype.fixed ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fixed, 0)
// String.prototype.fontcolor (color)
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fontcolor, 1)
// String.prototype.fontsize (size)
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fontsize, 1)
// String.prototype.italics ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(italics, 0)
// String.prototype.link (url)
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(link, 1)
// String.prototype.small ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(small, 0)
// String.prototype.strike ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(strike, 0)
// String.prototype.sub ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(sub, 0)
// String.prototype.sup ()
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(sup, 0)
#undef DEFINE_STRING_ADDITIONAL_HTML_FUNCTION
m_string->directDefineOwnProperty(state, ObjectPropertyName(strings->fromCharCode),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->fromCharCode, builtinStringFromCharCode, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_string->directDefineOwnProperty(state, ObjectPropertyName(strings->fromCodePoint),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->fromCodePoint, builtinStringFromCodePoint, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_string->directDefineOwnProperty(state, ObjectPropertyName(strings->raw),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->raw, builtinStringRaw, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_string->setFunctionPrototype(state, m_stringPrototype);
m_stringIteratorPrototype = new PrototypeObject(state, m_iteratorPrototype);
m_stringIteratorPrototype->setGlobalIntrinsicObject(state, true);
m_stringIteratorPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().next),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().next, builtinStringIteratorNext, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringIteratorPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag),
ObjectPropertyDescriptor(Value(String::fromASCII("String Iterator")), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
m_stringProxyObject = new StringObject(state);
redefineOwnProperty(state, ObjectPropertyName(strings->String),
ObjectPropertyDescriptor(m_string, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
}
} // namespace Escargot