mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
- Add every builtin function templates in es5 - Modification on Makefile target "check" should not trigger rebuild - Add JSON object - Implement some toString functions - Initialize m_value properly in ObjectPropertyDescriptor constructor
714 lines
34 KiB
C++
714 lines
34 KiB
C++
#include "Escargot.h"
|
|
#include "GlobalObject.h"
|
|
#include "Context.h"
|
|
#include "StringObject.h"
|
|
#include "ErrorObject.h"
|
|
#include "RegExpObject.h"
|
|
#include "ArrayObject.h"
|
|
|
|
namespace Escargot {
|
|
|
|
static Value builtinStringConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
if (isNewExpression) {
|
|
Object* thisObject = thisValue.toObject(state);
|
|
StringObject* stringObject = thisObject->asStringObject();
|
|
if (argc == 0) {
|
|
stringObject->setPrimitiveValue(state, String::emptyString);
|
|
} else {
|
|
stringObject->setPrimitiveValue(state, argv[0].toString(state));
|
|
}
|
|
return stringObject;
|
|
} else {
|
|
// called as function
|
|
if (argc == 0)
|
|
return String::emptyString;
|
|
Value value = argv[0];
|
|
return value.toString(state);
|
|
}
|
|
}
|
|
|
|
static Value builtinStringToString(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
if (thisValue.isObject() && thisValue.asObject()->isStringObject()) {
|
|
return thisValue.asObject()->asStringObject()->primitiveValue();
|
|
}
|
|
|
|
if (thisValue.isString())
|
|
return thisValue.toString(state);
|
|
|
|
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, state.context()->staticStrings().String.string(), true, state.context()->staticStrings().toString.string(), errorMessage_GlobalObject_ThisNotString);
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
static Value builtinStringIndexOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(str, String, indexOf);
|
|
String* searchStr = argv[0].toString(state);
|
|
|
|
Value val;
|
|
if (argc > 1) {
|
|
val = argv[1];
|
|
}
|
|
size_t pos;
|
|
if (val.isUndefined()) {
|
|
pos = 0;
|
|
} else {
|
|
pos = val.toInteger(state);
|
|
}
|
|
|
|
size_t len = str->length();
|
|
size_t start = std::min(std::max(pos, (size_t)0), 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, bool isNewExpression)
|
|
{
|
|
// Let S be ToString(O).
|
|
RESOLVE_THIS_BINDING_TO_STRING(S, String, indexOf);
|
|
String* searchStr = argv[0].toString(state);
|
|
|
|
double numPos;
|
|
if (argc > 0) {
|
|
numPos = argv[1].toNumber(state);
|
|
} 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, bool isNewExpression)
|
|
{
|
|
state.throwException(new ASCIIString(errorMessage_NotImplemented));
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
static Value builtinStringSubstring(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(str, String, indexOf);
|
|
if (argc == 0) {
|
|
return str;
|
|
} else {
|
|
int len = str->length();
|
|
double doubleStart = argv[0].toNumber(state);
|
|
Value end = argv[1];
|
|
double doubleEnd = (argc < 2 || end.isUndefined()) ? len : end.toNumber(state);
|
|
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);
|
|
if (c < ESCARGOT_ASCII_TABLE_MAX) {
|
|
return state.context()->staticStrings().asciiTable[c].string();
|
|
}
|
|
}
|
|
return str->subString(from, to);
|
|
}
|
|
}
|
|
|
|
static Value builtinStringMatch(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(str, String, match);
|
|
Value argument = argv[0];
|
|
RegExpObject* regexp;
|
|
if (argument.isPointerValue() && argument.asPointerValue()->isRegExpObject()) {
|
|
regexp = argument.asPointerValue()->asRegExpObject();
|
|
} else {
|
|
regexp = new RegExpObject(state, argument.toString(state), String::emptyString);
|
|
}
|
|
|
|
(void)regexp->lastIndex().toInteger(state);
|
|
bool isGlobal = regexp->option() & RegExpObject::Option::Global;
|
|
if (isGlobal) {
|
|
regexp->setLastIndex(Value(0));
|
|
}
|
|
|
|
RegexMatchResult result;
|
|
bool testResult = regexp->matchNonGlobally(state, str, result, false, 0);
|
|
if (!testResult) {
|
|
regexp->setLastIndex(Value(0));
|
|
return Value(Value::Null);
|
|
}
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
|
|
// if global flag is on, match method returns an Array containing all matched substrings
|
|
if (isGlobal) {
|
|
return regexp->createMatchedArray(state, str, result);
|
|
} else {
|
|
return regexp->createRegExpMatchedArray(state, result, str);
|
|
}
|
|
}
|
|
|
|
static Value builtinStringReplace(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(string, String, replace);
|
|
Value searchValue = argv[0];
|
|
Value replaceValue = argv[1];
|
|
String* replaceString = nullptr;
|
|
bool replaceValueIsFunction = replaceValue.isFunction();
|
|
if (!replaceValueIsFunction)
|
|
replaceString = replaceValue.toString(state);
|
|
RegexMatchResult result;
|
|
|
|
if (searchValue.isPointerValue() && searchValue.asPointerValue()->isRegExpObject()) {
|
|
RegExpObject* regexp = searchValue.asPointerValue()->asRegExpObject();
|
|
bool isGlobal = regexp->option() & RegExpObject::Option::Global;
|
|
|
|
if (isGlobal) {
|
|
regexp->setLastIndex(Value(0));
|
|
}
|
|
bool testResult = regexp->matchNonGlobally(state, string, result, false, 0);
|
|
if (testResult) {
|
|
if (isGlobal) {
|
|
regexp->createRegexMatchResult(state, string, result);
|
|
}
|
|
} else {
|
|
regexp->setLastIndex(Value(0));
|
|
}
|
|
} else {
|
|
String* searchString = searchValue.toString(state);
|
|
size_t idx = string->find(searchString);
|
|
if (idx != (size_t)-1) {
|
|
Vector<RegexMatchResult::RegexMatchResultPiece, gc_malloc_pointer_free_allocator<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));
|
|
}
|
|
}
|
|
|
|
if (result.m_matchResults.size() == 0) {
|
|
return string;
|
|
}
|
|
|
|
if (replaceValueIsFunction) {
|
|
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++) {
|
|
int subLen = result.m_matchResults[i].size();
|
|
Value* arguments;
|
|
// #define ALLOCA(bytes, typenameWithoutPointer, ec) (typenameWithoutPointer*)alloca(bytes)
|
|
arguments = ALLOCA(sizeof(Value) * (subLen + 2), Value, state);
|
|
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();
|
|
}
|
|
}
|
|
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
|
|
String* res = FunctionObject::call(callee, state, Value(), subLen + 2, arguments).toString(state);
|
|
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();
|
|
} else {
|
|
ASSERT(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';
|
|
int peek = replaceString->charAt(j + 2) - '0';
|
|
bool usePeek = false;
|
|
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();
|
|
}
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
static Value builtinStringSearch(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
state.throwException(new ASCIIString(errorMessage_NotImplemented));
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
static Value builtinStringSplit(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
// 1, 2, 3
|
|
RESOLVE_THIS_BINDING_TO_STRING(S, String, split);
|
|
ArrayObject* A = new ArrayObject(state);
|
|
|
|
// 4, 5
|
|
size_t lengthA = 0;
|
|
size_t lim;
|
|
if (argv[1].isUndefined()) {
|
|
lim = Value::InvalidIndexValue - 1;
|
|
} else {
|
|
lim = argv[1].toUint32(state);
|
|
}
|
|
|
|
// 6, 7
|
|
size_t s = S->length(), p = 0;
|
|
|
|
// 8
|
|
Value separator = argv[0];
|
|
PointerValue* P;
|
|
if (separator.isPointerValue() && separator.asPointerValue()->isRegExpObject()) {
|
|
P = separator.asPointerValue()->asRegExpObject();
|
|
} else {
|
|
P = separator.toString(state);
|
|
}
|
|
|
|
// 9
|
|
if (lim == 0)
|
|
return A;
|
|
|
|
// 10
|
|
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);
|
|
};
|
|
// 11
|
|
if (s == 0) {
|
|
bool ret = true;
|
|
if (P->isRegExpObject()) {
|
|
RegexMatchResult result;
|
|
ret = P->asRegExpObject()->matchNonGlobally(state, S, result, false, 0);
|
|
} 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;
|
|
}
|
|
|
|
// 12
|
|
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;
|
|
}
|
|
}
|
|
} 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 14, 15, 16
|
|
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, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(str, String, indexOf);
|
|
int position = argv[0].toInteger(state);
|
|
Value ret;
|
|
if (position < 0 || position >= (int)str->length())
|
|
ret = Value(std::numeric_limits<double>::quiet_NaN());
|
|
else
|
|
ret = Value(str->charAt(position));
|
|
return ret;
|
|
}
|
|
|
|
static Value builtinStringCharAt(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(str, String, charAt);
|
|
|
|
int64_t position;
|
|
if (argc == 0) {
|
|
position = 0;
|
|
} else if (argc > 0) {
|
|
position = argv[0].toInteger(state);
|
|
} else {
|
|
return Value(String::emptyString);
|
|
}
|
|
|
|
if (LIKELY(0 <= position && position < (int64_t)str->length())) {
|
|
char16_t c = str->charAt(position);
|
|
if (LIKELY(c < ESCARGOT_ASCII_TABLE_MAX)) {
|
|
return state.context()->staticStrings().asciiTable[c].string();
|
|
} else {
|
|
return String::fromCharCode(c);
|
|
}
|
|
} else {
|
|
return String::emptyString;
|
|
}
|
|
}
|
|
|
|
static Value builtinStringFromCharCode(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
if (argc == 1) {
|
|
char16_t c = argv[0].toUint32(state) & 0xFFFF;
|
|
if (c < ESCARGOT_ASCII_TABLE_MAX)
|
|
return state.context()->staticStrings().asciiTable[c].string();
|
|
return String::fromCharCode(c);
|
|
} else {
|
|
StringBuilder builder;
|
|
for (size_t i = 0; i < argc; i++) {
|
|
builder.appendChar((char16_t)argv[i].toInteger(state));
|
|
}
|
|
return builder.finalize();
|
|
}
|
|
return Value();
|
|
}
|
|
|
|
static Value builtinStringConcat(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(str, String, concat);
|
|
for (size_t i = 0; i < argc; i++) {
|
|
String* appendStr = argv[i].toString(state);
|
|
str = RopeString::createRopeString(str, appendStr);
|
|
}
|
|
return Value(str);
|
|
}
|
|
|
|
static Value builtinStringSlice(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
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);
|
|
}
|
|
|
|
static Value builtinStringToLowerCase(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(str, String, toLowerCase);
|
|
if (str->hasASCIIContent()) {
|
|
ASCIIStringData newStr;
|
|
size_t len = str->length();
|
|
newStr.resizeWithUninitializedValues(len);
|
|
const char* buf = str->characters8();
|
|
for (size_t i = 0; i < str->length(); i++) {
|
|
newStr[i] = u_tolower(buf[i]);
|
|
}
|
|
return new ASCIIString(std::move(newStr));
|
|
} else {
|
|
size_t len = str->length();
|
|
UTF16StringData newStr(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 = u_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));
|
|
}
|
|
}
|
|
|
|
static Value builtinStringToUpperCase(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
RESOLVE_THIS_BINDING_TO_STRING(str, String, toUpperCase);
|
|
if (str->hasASCIIContent()) {
|
|
ASCIIStringData newStr;
|
|
size_t len = str->length();
|
|
newStr.resizeWithUninitializedValues(len);
|
|
const char* buf = str->characters8();
|
|
for (size_t i = 0; i < str->length(); i++) {
|
|
newStr[i] = u_toupper(buf[i]);
|
|
}
|
|
return new ASCIIString(std::move(newStr));
|
|
} else {
|
|
size_t len = str->length();
|
|
UTF16StringData newStr(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 = u_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));
|
|
}
|
|
}
|
|
|
|
static Value builtinStringToLocaleLowerCase(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
state.throwException(new ASCIIString(errorMessage_NotImplemented));
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
static Value builtinStringToLocaleUpperCase(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
state.throwException(new ASCIIString(errorMessage_NotImplemented));
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
static Value builtinStringTrim(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
state.throwException(new ASCIIString(errorMessage_NotImplemented));
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
static Value builtinStringValueOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
|
|
{
|
|
if (thisValue.isString()) {
|
|
return Value(thisValue);
|
|
} else if (thisValue.isObject() && thisValue.asObject()->isStringObject()) {
|
|
return Value(thisValue.asPointerValue()->asStringObject()->primitiveValue());
|
|
}
|
|
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, errorMessage_GlobalObject_ThisNotString);
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
|
|
void GlobalObject::installString(ExecutionState& state)
|
|
{
|
|
const StaticStrings* strings = &state.context()->staticStrings();
|
|
m_string = new FunctionObject(state, NativeFunctionInfo(strings->String, builtinStringConstructor, 1, [](ExecutionState& state, size_t argc, Value* argv) -> Object* {
|
|
return new StringObject(state);
|
|
}),
|
|
FunctionObject::__ForBuiltin__);
|
|
m_string->markThisObjectDontNeedStructureTransitionTable(state);
|
|
m_string->setPrototype(state, m_functionPrototype);
|
|
m_stringPrototype = m_objectPrototype;
|
|
m_stringPrototype = new StringObject(state, String::emptyString);
|
|
m_stringPrototype->setPrototype(state, m_objectPrototype);
|
|
m_string->setFunctionPrototype(state, m_stringPrototype);
|
|
m_stringPrototype->defineOwnProperty(state, ObjectPropertyName(strings->constructor), ObjectPropertyDescriptor(m_string, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->toString),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->toString, builtinStringToString, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
// $21.1.3.4 String.prototype.concat
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->concat),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->concat, builtinStringConcat, 1, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
// $21.1.3.8 String.prototype.indexOf
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->indexOf),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->indexOf, builtinStringIndexOf, 1, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->lastIndexOf),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->lastIndexOf, builtinStringLastIndexOf, 1, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->localeCompare),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->localeCompare, builtinStringLocaleCompare, 1, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
// $21.1.3.16 String.prototype.slice
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->slice),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->slice, builtinStringSlice, 2, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
// $21.1.3.19 String.prototype.substring
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->substring),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->indexOf, builtinStringSubstring, 2, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->match),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->match, builtinStringMatch, 1, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->replace),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->replace, builtinStringReplace, 2, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->search),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->search, builtinStringSearch, 2, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->split),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->split, builtinStringSplit, 2, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->charCodeAt),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->charCodeAt, builtinStringCharCodeAt, 1, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->charAt),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->charAt, builtinStringCharAt, 1, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->toLowerCase),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->toLowerCase, builtinStringToLowerCase, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->toUpperCase),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->toUpperCase, builtinStringToUpperCase, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->toLocaleLowerCase),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->toLocaleLowerCase, builtinStringToLocaleLowerCase, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->toLocaleUpperCase),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->toLocaleUpperCase, builtinStringToLocaleUpperCase, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->trim),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->trim, builtinStringTrim, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
// $21.1.3.26 String.prototype.valueOf
|
|
m_stringPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->valueOf),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->valueOf, builtinStringValueOf, 0, nullptr, NativeFunctionInfo::Strict)),
|
|
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_string->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->fromCharCode),
|
|
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(strings->fromCharCode, builtinStringFromCharCode, 1, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
|
|
m_string->setFunctionPrototype(state, m_stringPrototype);
|
|
|
|
defineOwnProperty(state, ObjectPropertyName(strings->String),
|
|
ObjectPropertyDescriptor(m_string, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
|
}
|
|
}
|