Bug 1052839 - Selfhost substr/slice/substring, r=waldo,till

This commit is contained in:
Hannes Verschore 2014-11-04 09:43:00 +01:00
parent 6ce2bfc5f9
commit bd7bdb5ff6
18 changed files with 417 additions and 193 deletions

View File

@ -4,6 +4,126 @@
/*global intl_Collator: false, */
/* ES6 Draft Oct 14, 2014 21.1.3.19 */
function String_substring(start, end) {
// Steps 1-3.
CheckObjectCoercible(this);
var str = ToString(this);
// Step 4.
var len = str.length;
// Step 5.
var intStart = ToInteger(start);
// Step 6.
var intEnd = (end === undefined) ? len : ToInteger(end);
// Step 7.
var finalStart = std_Math_min(std_Math_max(intStart, 0), len);
// Step 8.
var finalEnd = std_Math_min(std_Math_max(intEnd, 0), len);
// Steps 9-10.
var from, to;
if (finalStart < finalEnd) {
from = finalStart;
to = finalEnd;
} else {
from = finalEnd;
to = finalStart;
}
// Step 11.
// While |from| and |to - from| are bounded to the length of |str| and this
// and thus definitely in the int32 range, they can still be typed as
// double. Eagerly truncate since SubstringKernel only accepts int32.
return SubstringKernel(str, from | 0, (to - from) | 0);
}
function String_static_substring(string, start, end) {
if (arguments.length < 1)
ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.substring');
return callFunction(String_substring, string, start, end);
}
/* ES6 Draft Oct 14, 2014 B.2.3.1 */
function String_substr(start, length) {
// Steps 1-2.
CheckObjectCoercible(this);
var str = ToString(this);
// Steps 3-4.
var intStart = ToInteger(start);
// Steps 5-7.
var size = str.length;
// Use |size| instead of +Infinity to avoid performing calculations with
// doubles. (The result is the same either way.)
var end = (length === undefined) ? size : ToInteger(length);
// Step 8.
if (intStart < 0)
intStart = std_Math_max(intStart + size, 0);
// Step 9.
var resultLength = std_Math_min(std_Math_max(end, 0), size - intStart)
// Step 10.
if (resultLength <= 0)
return "";
// Step 11.
// While |intStart| and |resultLength| are bounded to the length of |str|
// and thus definitely in the int32 range, they can still be typed as
// double. Eagerly truncate since SubstringKernel only accepts int32.
return SubstringKernel(str, intStart | 0, resultLength | 0);
}
function String_static_substr(string, start, length) {
if (arguments.length < 1)
ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.substr');
return callFunction(String_substr, string, start, length);
}
/* ES6 Draft Oct 14, 2014 21.1.3.16 */
function String_slice(start, end) {
// Steps 1-3.
CheckObjectCoercible(this);
var str = ToString(this);
// Step 4.
var len = str.length;
// Step 5.
var intStart = ToInteger(start);
// Step 6.
var intEnd = (end === undefined) ? len : ToInteger(end);
// Step 7.
var from = (intStart < 0) ? std_Math_max(len + intStart, 0) : std_Math_min(intStart, len);
// Step 8.
var to = (intEnd < 0) ? std_Math_max(len + intEnd, 0) : std_Math_min(intEnd, len);
// Step 9.
var span = std_Math_max(to - from, 0);
// Step 10.
// While |from| and |span| are bounded to the length of |str|
// and thus definitely in the int32 range, they can still be typed as
// double. Eagerly truncate since SubstringKernel only accepts int32.
return SubstringKernel(str, from | 0, span | 0);
}
function String_static_slice(string, start, end) {
if (arguments.length < 1)
ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'String.slice');
return callFunction(String_slice, string, start, end);
}
/* ES6 Draft September 5, 2013 21.1.3.3 */
function String_codePointAt(pos) {
// Steps 1-3.

View File

@ -32,6 +32,7 @@
// The few items below here are either self-hosted or installing them under a
// std_Foo name would require ugly contortions, so they just get aliased here.
var std_Array_indexOf = ArrayIndexOf;
var std_String_substring = String_substring;
// WeakMap is a bare constructor without properties or methods.
var std_WeakMap = WeakMap;
// StopIteration is a bare constructor without properties or methods.

View File

@ -5944,6 +5944,111 @@ ConcatFatInlineString(MacroAssembler &masm, Register lhs, Register rhs, Register
masm.ret();
}
typedef bool (*SubstringKernelFn)(JSContext *cx, HandleString str, int32_t begin, int32_t len,
MutableHandleString rstring);
static const VMFunction SubstringKernelInfo =
FunctionInfo<SubstringKernelFn>(SubstringKernel);
bool CodeGenerator::visitSubstr(LSubstr *lir)
{
Register string = ToRegister(lir->string());
Register begin = ToRegister(lir->begin());
Register length = ToRegister(lir->length());
Register output = ToRegister(lir->output());
Register temp = ToRegister(lir->temp());
Address stringFlags(string, JSString::offsetOfFlags());
Label isLatin1, notInline, nonZero, isInlinedLatin1;
// For every edge case use the C++ variant.
// Note: we also use this upon allocation failure in newGCString and
// newGCFatInlineString. To squeeze out even more performance those failures
// can be handled by allocate in ool code and returning to jit code to fill
// in all data.
OutOfLineCode *ool = oolCallVM(SubstringKernelInfo, lir,
(ArgList(), string, begin, length),
StoreRegisterTo(output));
if (!ool)
return false;
Label *slowPath = ool->entry();
Label *done = ool->rejoin();
// Zero length, return emptystring.
masm.branchTest32(Assembler::NonZero, length, length, &nonZero);
const JSAtomState &names = GetIonContext()->runtime->names();
masm.movePtr(ImmGCPtr(names.empty), output);
masm.jump(done);
// Use slow path for ropes.
masm.bind(&nonZero);
static_assert(JSString::ROPE_FLAGS == 0,
"rope flags must be zero for (flags & TYPE_FLAGS_MASK) == 0 "
"to be a valid is-rope check");
masm.branchTest32(Assembler::Zero, stringFlags, Imm32(JSString::TYPE_FLAGS_MASK), slowPath);
// Handle inlined strings by creating a FatInlineString.
masm.branchTest32(Assembler::Zero, stringFlags, Imm32(JSString::INLINE_CHARS_BIT), &notInline);
masm.newGCFatInlineString(output, temp, slowPath);
masm.store32(length, Address(output, JSString::offsetOfLength()));
Address stringStorage(string, JSInlineString::offsetOfInlineStorage());
Address outputStorage(output, JSInlineString::offsetOfInlineStorage());
masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT),
&isInlinedLatin1);
{
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
Address(output, JSString::offsetOfFlags()));
masm.computeEffectiveAddress(stringStorage, temp);
BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
masm.computeEffectiveAddress(chars, begin);
masm.computeEffectiveAddress(outputStorage, temp);
CopyStringChars(masm, temp, begin, length, string, sizeof(char16_t), sizeof(char16_t));
masm.store16(Imm32(0), Address(temp, 0));
masm.jump(done);
}
masm.bind(&isInlinedLatin1);
{
masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
Address(output, JSString::offsetOfFlags()));
masm.computeEffectiveAddress(stringStorage, temp);
static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
masm.addPtr(temp, begin);
masm.computeEffectiveAddress(outputStorage, temp);
CopyStringChars(masm, temp, begin, length, string, sizeof(char), sizeof(char));
masm.store8(Imm32(0), Address(temp, 0));
masm.jump(done);
}
// Handle other cases with a DependentString.
masm.bind(&notInline);
masm.newGCString(output, temp, slowPath);
masm.store32(length, Address(output, JSString::offsetOfLength()));
masm.storePtr(string, Address(output, JSDependentString::offsetOfBase()));
masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
{
masm.store32(Imm32(JSString::DEPENDENT_FLAGS), Address(output, JSString::offsetOfFlags()));
masm.loadPtr(Address(string, JSString::offsetOfNonInlineChars()), temp);
BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
masm.computeEffectiveAddress(chars, temp);
masm.storePtr(temp, Address(output, JSString::offsetOfNonInlineChars()));
masm.jump(done);
}
masm.bind(&isLatin1);
{
masm.store32(Imm32(JSString::DEPENDENT_FLAGS | JSString::LATIN1_CHARS_BIT),
Address(output, JSString::offsetOfFlags()));
masm.loadPtr(Address(string, JSString::offsetOfNonInlineChars()), temp);
static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
masm.addPtr(begin, temp);
masm.storePtr(temp, Address(output, JSString::offsetOfNonInlineChars()));
masm.jump(done);
}
masm.bind(done);
return true;
}
JitCode *
JitCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
{

View File

@ -186,6 +186,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitTypedObjectProto(LTypedObjectProto *ins);
bool visitTypedObjectUnsizedLength(LTypedObjectUnsizedLength *ins);
bool visitStringLength(LStringLength *lir);
bool visitSubstr(LSubstr *lir);
bool visitInitializedLength(LInitializedLength *lir);
bool visitSetInitializedLength(LSetInitializedLength *lir);
bool visitNotO(LNotO *ins);

View File

@ -765,6 +765,7 @@ class IonBuilder
const Class *clasp3 = nullptr,
const Class *clasp4 = nullptr);
InliningStatus inlineIsConstructing(CallInfo &callInfo);
InliningStatus inlineSubstringKernel(CallInfo &callInfo);
// Testing functions.
InliningStatus inlineForceSequentialOrInParallelSection(CallInfo &callInfo);

View File

@ -3392,6 +3392,36 @@ class LStringSplit : public LCallInstructionHelper<1, 2, 0>
}
};
class LSubstr : public LInstructionHelper<1, 3, 1>
{
public:
LIR_HEADER(Substr)
LSubstr(const LAllocation &string, const LAllocation &begin, const LAllocation &length,
const LDefinition &temp)
{
setOperand(0, string);
setOperand(1, begin);
setOperand(2, length);
setTemp(0, temp);
}
const LAllocation *string() {
return getOperand(0);
}
const LAllocation *begin() {
return getOperand(1);
}
const LAllocation *length() {
return getOperand(2);
}
const LDefinition *temp() {
return getTemp(0);
}
const MStringSplit *mir() const {
return mir_->toStringSplit();
}
};
// Convert a 32-bit integer to a double.
class LInt32ToDouble : public LInstructionHelper<1, 1, 0>
{

View File

@ -185,6 +185,7 @@
_(RegExpTest) \
_(RegExpReplace) \
_(StringReplace) \
_(Substr) \
_(Lambda) \
_(LambdaArrow) \
_(LambdaForSingleton) \

View File

@ -2153,6 +2153,16 @@ LIRGenerator::visitStringReplace(MStringReplace *ins)
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitSubstr(MSubstr *ins)
{
LSubstr *lir = new (alloc()) LSubstr(useFixed(ins->string(), CallTempReg1),
useRegister(ins->begin()),
useRegister(ins->length()),
temp());
return define(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitLambda(MLambda *ins)
{

View File

@ -144,6 +144,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitCharCodeAt(MCharCodeAt *ins);
bool visitFromCharCode(MFromCharCode *ins);
bool visitStringSplit(MStringSplit *ins);
bool visitSubstr(MSubstr *ins);
bool visitStart(MStart *start);
bool visitOsrEntry(MOsrEntry *entry);
bool visitNop(MNop *nop);

View File

@ -197,6 +197,8 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSFunction *target)
return inlineToString(callInfo);
if (native == intrinsic_IsConstructing)
return inlineIsConstructing(callInfo);
if (native == intrinsic_SubstringKernel)
return inlineSubstringKernel(callInfo);
// TypedObject intrinsics.
if (native == intrinsic_ObjectIsTypedObject)
@ -1538,6 +1540,38 @@ IonBuilder::inlineStrReplace(CallInfo &callInfo)
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineSubstringKernel(CallInfo &callInfo)
{
MOZ_ASSERT(callInfo.argc() == 3);
MOZ_ASSERT(!callInfo.constructing());
// Return: String.
if (getInlineReturnType() != MIRType_String)
return InliningStatus_NotInlined;
// Arg 0: String.
if (callInfo.getArg(0)->type() != MIRType_String)
return InliningStatus_NotInlined;
// Arg 1: Int.
if (callInfo.getArg(1)->type() != MIRType_Int32)
return InliningStatus_NotInlined;
// Arg 2: Int.
if (callInfo.getArg(2)->type() != MIRType_Int32)
return InliningStatus_NotInlined;
callInfo.setImplicitlyUsedUnchecked();
MSubstr *substr = MSubstr::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
callInfo.getArg(2));
current->add(substr);
current->push(substr);
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
{

View File

@ -6854,6 +6854,47 @@ class MStringReplace
}
};
class MSubstr
: public MTernaryInstruction,
public Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>
{
private:
MSubstr(MDefinition *string, MDefinition *begin, MDefinition *length)
: MTernaryInstruction(string, begin, length)
{
setResultType(MIRType_String);
}
public:
INSTRUCTION_HEADER(Substr);
static MSubstr *New(TempAllocator &alloc, MDefinition *string, MDefinition *begin,
MDefinition *length)
{
return new(alloc) MSubstr(string, begin, length);
}
MDefinition *string() {
return getOperand(0);
}
MDefinition *begin() {
return getOperand(1);
}
MDefinition *length() {
return getOperand(2);
}
bool congruentTo(const MDefinition *ins) const {
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
};
struct LambdaFunctionInfo
{
// The functions used in lambdas are the canonical original function in

View File

@ -95,6 +95,7 @@ namespace jit {
_(CharCodeAt) \
_(FromCharCode) \
_(StringSplit) \
_(Substr) \
_(Return) \
_(Throw) \
_(Box) \

View File

@ -320,6 +320,7 @@ class ParallelSafetyVisitor : public MDefinitionVisitor
UNSAFE_OP(CallInstanceOf)
UNSAFE_OP(ProfilerStackOp)
UNSAFE_OP(GuardString)
UNSAFE_OP(Substr)
UNSAFE_OP(NewDeclEnvObject)
UNSAFE_OP(In)
UNSAFE_OP(InArray)

View File

@ -977,6 +977,7 @@ bool intrinsic_IsCallable(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_ThrowError(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_NewDenseArray(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_IsConstructing(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_SubstringKernel(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp);
bool intrinsic_DefineDataProperty(JSContext *cx, unsigned argc, Value *vp);

View File

@ -575,9 +575,18 @@ ValueToIntegerRange(JSContext *cx, HandleValue v, int32_t *out)
return true;
}
static JSString *
DoSubstr(JSContext *cx, JSString *str, size_t begin, size_t len)
bool
js::SubstringKernel(JSContext *cx, HandleString str, int32_t beginInt, int32_t lengthInt,
MutableHandleString substr)
{
MOZ_ASSERT(0 <= beginInt);
MOZ_ASSERT(0 <= lengthInt);
MOZ_ASSERT(beginInt <= str->length());
MOZ_ASSERT(lengthInt <= str->length() - beginInt);
uint32_t begin = beginInt;
uint32_t len = lengthInt;
/*
* Optimization for one level deep ropes.
* This is common for the following pattern:
@ -592,15 +601,21 @@ DoSubstr(JSContext *cx, JSString *str, size_t begin, size_t len)
/* Substring is totally in leftChild of rope. */
if (begin + len <= rope->leftChild()->length()) {
str = rope->leftChild();
return NewDependentString(cx, str, begin, len);
JSLinearString *str = NewDependentString(cx, rope->leftChild(), begin, len);
if (!str)
return false;
substr.set(str);
return true;
}
/* Substring is totally in rightChild of rope. */
if (begin >= rope->leftChild()->length()) {
str = rope->rightChild();
begin -= rope->leftChild()->length();
return NewDependentString(cx, str, begin, len);
JSLinearString *str = NewDependentString(cx, rope->rightChild(), begin, len);
if (!str)
return false;
substr.set(str);
return true;
}
/*
@ -616,74 +631,17 @@ DoSubstr(JSContext *cx, JSString *str, size_t begin, size_t len)
Rooted<JSRope *> ropeRoot(cx, rope);
RootedString lhs(cx, NewDependentString(cx, ropeRoot->leftChild(), begin, lhsLength));
if (!lhs)
return nullptr;
return false;
RootedString rhs(cx, NewDependentString(cx, ropeRoot->rightChild(), 0, rhsLength));
if (!rhs)
return nullptr;
return JSRope::new_<CanGC>(cx, lhs, rhs, len);
}
return NewDependentString(cx, str, begin, len);
}
bool
js::str_substring(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSString *str = ThisToStringForStringProto(cx, args);
if (!str)
return false;
int32_t length, begin, end;
if (args.length() > 0) {
end = length = int32_t(str->length());
if (args[0].isInt32()) {
begin = args[0].toInt32();
} else {
RootedString strRoot(cx, str);
if (!ValueToIntegerRange(cx, args[0], &begin))
return false;
str = strRoot;
}
if (begin < 0)
begin = 0;
else if (begin > length)
begin = length;
if (args.hasDefined(1)) {
if (args[1].isInt32()) {
end = args[1].toInt32();
} else {
RootedString strRoot(cx, str);
if (!ValueToIntegerRange(cx, args[1], &end))
return false;
str = strRoot;
}
if (end > length) {
end = length;
} else {
if (end < 0)
end = 0;
if (end < begin) {
int32_t tmp = begin;
begin = end;
end = tmp;
}
}
}
str = DoSubstr(cx, str, size_t(begin), size_t(end - begin));
if (!str)
return false;
substr.set(JSRope::new_<CanGC>(cx, lhs, rhs, len));
return true;
}
args.rval().setString(str);
substr.set(NewDependentString(cx, str, begin, len));
return true;
}
@ -3992,54 +3950,6 @@ js::str_split_string(JSContext *cx, HandleTypeObject type, HandleString str, Han
return aobj;
}
static bool
str_substr(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedString str(cx, ThisToStringForStringProto(cx, args));
if (!str)
return false;
int32_t length, len, begin;
if (args.length() > 0) {
length = int32_t(str->length());
if (!ValueToIntegerRange(cx, args[0], &begin))
return false;
if (begin >= length) {
args.rval().setString(cx->runtime()->emptyString);
return true;
}
if (begin < 0) {
begin += length; /* length + INT_MIN will always be less than 0 */
if (begin < 0)
begin = 0;
}
if (args.hasDefined(1)) {
if (!ValueToIntegerRange(cx, args[1], &len))
return false;
if (len <= 0) {
args.rval().setString(cx->runtime()->emptyString);
return true;
}
if (uint32_t(length) < uint32_t(begin + len))
len = length - begin;
} else {
len = length - begin;
}
str = DoSubstr(cx, str, size_t(begin), size_t(len));
if (!str)
return false;
}
args.rval().setString(str);
return true;
}
/*
* Python-esque sequence operations.
*/
@ -4076,73 +3986,6 @@ str_concat(JSContext *cx, unsigned argc, Value *vp)
return true;
}
static bool
str_slice(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() == 1 && args.thisv().isString() && args[0].isInt32()) {
JSString *str = args.thisv().toString();
size_t begin = args[0].toInt32();
size_t end = str->length();
if (begin <= end) {
size_t length = end - begin;
if (length == 0) {
str = cx->runtime()->emptyString;
} else {
str = (length == 1)
? cx->staticStrings().getUnitStringForElement(cx, str, begin)
: NewDependentString(cx, str, begin, length);
if (!str)
return false;
}
args.rval().setString(str);
return true;
}
}
RootedString str(cx, ThisToStringForStringProto(cx, args));
if (!str)
return false;
if (args.length() != 0) {
double begin, end, length;
if (!ToInteger(cx, args[0], &begin))
return false;
length = str->length();
if (begin < 0) {
begin += length;
if (begin < 0)
begin = 0;
} else if (begin > length) {
begin = length;
}
if (args.hasDefined(1)) {
if (!ToInteger(cx, args[1], &end))
return false;
if (end < 0) {
end += length;
if (end < 0)
end = 0;
} else if (end > length) {
end = length;
}
if (end < begin)
end = begin;
} else {
end = length;
}
str = NewDependentString(cx, str, size_t(begin), size_t(end - begin));
if (!str)
return false;
}
args.rval().setString(str);
return true;
}
static const JSFunctionSpec string_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN("quote", str_quote, 0,JSFUN_GENERIC_NATIVE),
@ -4152,11 +3995,11 @@ static const JSFunctionSpec string_methods[] = {
/* Java-like methods. */
JS_FN(js_toString_str, js_str_toString, 0,0),
JS_FN(js_valueOf_str, js_str_toString, 0,0),
JS_FN("substring", str_substring, 2,JSFUN_GENERIC_NATIVE),
JS_FN("toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE),
JS_FN("toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE),
JS_FN("charAt", js_str_charAt, 1,JSFUN_GENERIC_NATIVE),
JS_FN("charCodeAt", js_str_charCodeAt, 1,JSFUN_GENERIC_NATIVE),
JS_SELF_HOSTED_FN("substring", "String_substring", 2,0),
JS_SELF_HOSTED_FN("codePointAt", "String_codePointAt", 1,0),
JS_FN("contains", str_contains, 1,JSFUN_GENERIC_NATIVE),
JS_FN("indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE),
@ -4183,11 +4026,11 @@ static const JSFunctionSpec string_methods[] = {
JS_FN("search", str_search, 1,JSFUN_GENERIC_NATIVE),
JS_FN("replace", str_replace, 2,JSFUN_GENERIC_NATIVE),
JS_FN("split", str_split, 2,JSFUN_GENERIC_NATIVE),
JS_FN("substr", str_substr, 2,JSFUN_GENERIC_NATIVE),
JS_SELF_HOSTED_FN("substr", "String_substr", 2,0),
/* Python-esque sequence methods. */
JS_FN("concat", str_concat, 1,JSFUN_GENERIC_NATIVE),
JS_FN("slice", str_slice, 2,JSFUN_GENERIC_NATIVE),
JS_SELF_HOSTED_FN("slice", "String_slice", 2,0),
/* HTML string methods. */
JS_SELF_HOSTED_FN("bold", "String_bold", 0,0),
@ -4293,13 +4136,17 @@ js::str_fromCharCode_one_arg(JSContext *cx, HandleValue code, MutableHandleValue
static const JSFunctionSpec string_static_methods[] = {
JS_FN("fromCharCode", js::str_fromCharCode, 1, 0),
JS_SELF_HOSTED_FN("fromCodePoint", "String_static_fromCodePoint", 1, 0),
JS_SELF_HOSTED_FN("raw", "String_static_raw", 2, 0),
JS_SELF_HOSTED_FN("fromCodePoint", "String_static_fromCodePoint", 1,0),
JS_SELF_HOSTED_FN("raw", "String_static_raw", 2,0),
JS_SELF_HOSTED_FN("substring", "String_static_substring", 3,0),
JS_SELF_HOSTED_FN("substr", "String_static_substr", 3,0),
JS_SELF_HOSTED_FN("slice", "String_static_slice", 3,0),
// This must be at the end because of bug 853075: functions listed after
// self-hosted methods aren't available in self-hosted code.
#if EXPOSE_INTL_API
JS_SELF_HOSTED_FN("localeCompare", "String_static_localeCompare", 2,0),
JS_SELF_HOSTED_FN("localeCompare", "String_static_localeCompare", 2,0),
#endif
JS_FS_END
};

View File

@ -249,6 +249,15 @@ EqualChars(const Char1 *s1, const Char2 *s2, size_t len)
return true;
}
/*
* Computes |str|'s substring for the range [beginInt, beginInt + lengthInt).
* Negative, overlarge, swapped, etc. |beginInt| and |lengthInt| are forbidden
* and constitute API misuse.
*/
bool
SubstringKernel(JSContext *cx, HandleString str, int32_t beginInt, int32_t lengthInt,
MutableHandleString substr);
/*
* Inflate bytes in ASCII encoding to char16_t code units. Return null on error,
* otherwise return the char16_t buffer that was malloc'ed. length is updated to
@ -311,9 +320,6 @@ str_lastIndexOf(JSContext *cx, unsigned argc, Value *vp);
extern bool
str_startsWith(JSContext *cx, unsigned argc, Value *vp);
extern bool
str_substring(JSContext *cx, unsigned argc, Value *vp);
extern bool
str_toLowerCase(JSContext *cx, unsigned argc, Value *vp);

View File

@ -118,6 +118,26 @@ intrinsic_IsConstructor(JSContext *cx, unsigned argc, Value *vp)
return true;
}
bool
js::intrinsic_SubstringKernel(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args[0].isString());
MOZ_ASSERT(args[1].isInt32());
MOZ_ASSERT(args[2].isInt32());
RootedString str(cx, args[0].toString());
int32_t begin = args[1].toInt32();
int32_t length = args[2].toInt32();
RootedString substr(cx);
if (!SubstringKernel(cx, str, begin, length, &substr))
return false;
args.rval().setString(substr);
return true;
}
static bool
intrinsic_OwnPropertyKeys(JSContext *cx, unsigned argc, Value *vp)
{
@ -1015,7 +1035,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("std_String_replace", str_replace, 2,0),
JS_FN("std_String_split", str_split, 2,0),
JS_FN("std_String_startsWith", str_startsWith, 1,0),
JS_FN("std_String_substring", str_substring, 2,0),
JS_FN("std_String_toLowerCase", str_toLowerCase, 0,0),
JS_FN("std_String_toUpperCase", str_toUpperCase, 0,0),
@ -1040,6 +1059,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("_IsConstructing", intrinsic_IsConstructing, 0,0),
JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
JS_FN("SubstringKernel", intrinsic_SubstringKernel, 3,0),
JS_FN("UnsafePutElements", intrinsic_UnsafePutElements, 3,0),
JS_FN("_DefineDataProperty", intrinsic_DefineDataProperty, 4,0),

View File

@ -475,6 +475,9 @@ class JSString : public js::gc::TenuredCell
}
static size_t offsetOfNonInlineChars() {
static_assert(offsetof(JSString, d.s.u2.nonInlineCharsTwoByte) ==
offsetof(JSString, d.s.u2.nonInlineCharsLatin1),
"nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset");
return offsetof(JSString, d.s.u2.nonInlineCharsTwoByte);
}