Fixed crashes exposed though fuzzing.
The %_OneByteSeqStringSetChar intrinsic expects its arguments to be checked before being called for efficiency reasons, but the fuzzer provided no such checks. Now the intrinsic is robust to bad input if FLAG_debug_code is set. R=yangguo@chromium.org TEST=test/mjsunit/regress/regress-320948.js BUG=chromium:320948 LOG=Y Review URL: https://codereview.chromium.org/72813004 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17886 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
1cfcf25efe
commit
06c7620302
@ -3485,31 +3485,6 @@ void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask) {
|
||||
__ SmiTst(index);
|
||||
__ Check(eq, kNonSmiIndex);
|
||||
__ SmiTst(value);
|
||||
__ Check(eq, kNonSmiValue);
|
||||
|
||||
__ ldr(ip, FieldMemOperand(string, String::kLengthOffset));
|
||||
__ cmp(index, ip);
|
||||
__ Check(lt, kIndexIsTooLarge);
|
||||
|
||||
__ cmp(index, Operand(Smi::FromInt(0)));
|
||||
__ Check(ge, kIndexIsNegative);
|
||||
|
||||
__ ldr(ip, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||
__ ldrb(ip, FieldMemOperand(ip, Map::kInstanceTypeOffset));
|
||||
|
||||
__ and_(ip, ip, Operand(kStringRepresentationMask | kStringEncodingMask));
|
||||
__ cmp(ip, Operand(encoding_mask));
|
||||
__ Check(eq, kUnexpectedStringType);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
ASSERT_EQ(3, args->length());
|
||||
@ -3524,8 +3499,14 @@ void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
|
||||
__ Pop(index, value);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ SmiTst(value);
|
||||
__ ThrowIf(ne, kNonSmiValue);
|
||||
__ SmiTst(index);
|
||||
__ ThrowIf(ne, kNonSmiIndex);
|
||||
__ SmiUntag(index, index);
|
||||
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
|
||||
EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
|
||||
__ SmiTag(index, index);
|
||||
}
|
||||
|
||||
__ SmiUntag(value, value);
|
||||
@ -3551,8 +3532,14 @@ void FullCodeGenerator::EmitTwoByteSeqStringSetChar(CallRuntime* expr) {
|
||||
__ Pop(index, value);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ SmiTst(value);
|
||||
__ ThrowIf(ne, kNonSmiValue);
|
||||
__ SmiTst(index);
|
||||
__ ThrowIf(ne, kNonSmiIndex);
|
||||
__ SmiUntag(index, index);
|
||||
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
|
||||
EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
|
||||
__ SmiTag(index, index);
|
||||
}
|
||||
|
||||
__ SmiUntag(value, value);
|
||||
|
@ -1906,10 +1906,13 @@ LInstruction* LChunkBuilder::DoSeqStringGetChar(HSeqStringGetChar* instr) {
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) {
|
||||
LOperand* string = UseRegister(instr->string());
|
||||
LOperand* index = UseRegisterOrConstant(instr->index());
|
||||
LOperand* value = UseRegister(instr->value());
|
||||
return new(zone()) LSeqStringSetChar(string, index, value);
|
||||
LOperand* string = UseRegisterAtStart(instr->string());
|
||||
LOperand* index = FLAG_debug_code
|
||||
? UseRegisterAtStart(instr->index())
|
||||
: UseRegisterOrConstantAtStart(instr->index());
|
||||
LOperand* value = UseRegisterAtStart(instr->value());
|
||||
LOperand* context = FLAG_debug_code ? UseFixed(instr->context(), cp) : NULL;
|
||||
return new(zone()) LSeqStringSetChar(context, string, index, value);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1401,19 +1401,21 @@ class LSeqStringGetChar V8_FINAL : public LTemplateInstruction<1, 2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LSeqStringSetChar V8_FINAL : public LTemplateInstruction<1, 3, 0> {
|
||||
class LSeqStringSetChar V8_FINAL : public LTemplateInstruction<1, 4, 0> {
|
||||
public:
|
||||
LSeqStringSetChar(LOperand* string,
|
||||
LSeqStringSetChar(LOperand* context,
|
||||
LOperand* string,
|
||||
LOperand* index,
|
||||
LOperand* value) {
|
||||
inputs_[0] = string;
|
||||
inputs_[1] = index;
|
||||
inputs_[2] = value;
|
||||
inputs_[0] = context;
|
||||
inputs_[1] = string;
|
||||
inputs_[2] = index;
|
||||
inputs_[3] = value;
|
||||
}
|
||||
|
||||
LOperand* string() { return inputs_[0]; }
|
||||
LOperand* index() { return inputs_[1]; }
|
||||
LOperand* value() { return inputs_[2]; }
|
||||
LOperand* string() { return inputs_[1]; }
|
||||
LOperand* index() { return inputs_[2]; }
|
||||
LOperand* value() { return inputs_[3]; }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar, "seq-string-set-char")
|
||||
DECLARE_HYDROGEN_ACCESSOR(SeqStringSetChar)
|
||||
|
@ -2027,17 +2027,13 @@ void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) {
|
||||
Register value = ToRegister(instr->value());
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
Register scratch = scratch0();
|
||||
__ ldr(scratch, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||
__ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
|
||||
|
||||
__ and_(scratch, scratch,
|
||||
Operand(kStringRepresentationMask | kStringEncodingMask));
|
||||
Register index = ToRegister(instr->index());
|
||||
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
|
||||
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
|
||||
__ cmp(scratch, Operand(encoding == String::ONE_BYTE_ENCODING
|
||||
? one_byte_seq_type : two_byte_seq_type));
|
||||
__ Check(eq, kUnexpectedStringType);
|
||||
int encoding_mask =
|
||||
instr->hydrogen()->encoding() == String::ONE_BYTE_ENCODING
|
||||
? one_byte_seq_type : two_byte_seq_type;
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, encoding_mask);
|
||||
}
|
||||
|
||||
MemOperand operand = BuildSeqStringOperand(string, instr->index(), encoding);
|
||||
|
@ -3391,6 +3391,42 @@ int MacroAssembler::CalculateStackPassedWords(int num_reg_arguments,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask) {
|
||||
Label is_object;
|
||||
SmiTst(string);
|
||||
ThrowIf(eq, kNonObject);
|
||||
|
||||
ldr(ip, FieldMemOperand(string, HeapObject::kMapOffset));
|
||||
ldrb(ip, FieldMemOperand(ip, Map::kInstanceTypeOffset));
|
||||
|
||||
and_(ip, ip, Operand(kStringRepresentationMask | kStringEncodingMask));
|
||||
cmp(ip, Operand(encoding_mask));
|
||||
ThrowIf(ne, kUnexpectedStringType);
|
||||
|
||||
// The index is assumed to be untagged coming in, tag it to compare with the
|
||||
// string length without using a temp register, it is restored at the end of
|
||||
// this function.
|
||||
Label index_tag_ok, index_tag_bad;
|
||||
TrySmiTag(index, index, &index_tag_bad);
|
||||
b(&index_tag_ok);
|
||||
bind(&index_tag_bad);
|
||||
Throw(kIndexIsTooLarge);
|
||||
bind(&index_tag_ok);
|
||||
|
||||
ldr(ip, FieldMemOperand(string, String::kLengthOffset));
|
||||
cmp(index, ip);
|
||||
ThrowIf(ge, kIndexIsTooLarge);
|
||||
|
||||
cmp(index, Operand(Smi::FromInt(0)));
|
||||
ThrowIf(lt, kIndexIsNegative);
|
||||
|
||||
SmiUntag(index, index);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
|
||||
int num_double_arguments,
|
||||
Register scratch) {
|
||||
@ -3774,6 +3810,52 @@ void MacroAssembler::ClampDoubleToUint8(Register result_reg,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Throw(BailoutReason reason) {
|
||||
Label throw_start;
|
||||
bind(&throw_start);
|
||||
#ifdef DEBUG
|
||||
const char* msg = GetBailoutReason(reason);
|
||||
if (msg != NULL) {
|
||||
RecordComment("Throw message: ");
|
||||
RecordComment(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
mov(r0, Operand(Smi::FromInt(reason)));
|
||||
push(r0);
|
||||
// Disable stub call restrictions to always allow calls to throw.
|
||||
if (!has_frame_) {
|
||||
// We don't actually want to generate a pile of code for this, so just
|
||||
// claim there is a stack frame, without generating one.
|
||||
FrameScope scope(this, StackFrame::NONE);
|
||||
CallRuntime(Runtime::kThrowMessage, 1);
|
||||
} else {
|
||||
CallRuntime(Runtime::kThrowMessage, 1);
|
||||
}
|
||||
// will not return here
|
||||
if (is_const_pool_blocked()) {
|
||||
// If the calling code cares throw the exact number of
|
||||
// instructions generated, we insert padding here to keep the size
|
||||
// of the ThrowMessage macro constant.
|
||||
static const int kExpectedThrowMessageInstructions = 10;
|
||||
int throw_instructions = InstructionsGeneratedSince(&throw_start);
|
||||
ASSERT(throw_instructions <= kExpectedThrowMessageInstructions);
|
||||
while (throw_instructions++ < kExpectedThrowMessageInstructions) {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::ThrowIf(Condition cc, BailoutReason reason) {
|
||||
Label L;
|
||||
b(NegateCondition(cc), &L);
|
||||
Throw(reason);
|
||||
// will not return here
|
||||
bind(&L);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::LoadInstanceDescriptors(Register map,
|
||||
Register descriptors) {
|
||||
ldr(descriptors, FieldMemOperand(map, Map::kDescriptorsOffset));
|
||||
|
@ -661,6 +661,12 @@ class MacroAssembler: public Assembler {
|
||||
// handler chain.
|
||||
void ThrowUncatchable(Register value);
|
||||
|
||||
// Throw a message string as an exception.
|
||||
void Throw(BailoutReason reason);
|
||||
|
||||
// Throw a message string as an exception if a condition is not true.
|
||||
void ThrowIf(Condition cc, BailoutReason reason);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inline caching support
|
||||
|
||||
@ -1341,6 +1347,11 @@ class MacroAssembler: public Assembler {
|
||||
|
||||
void JumpIfNotUniqueName(Register reg, Label* not_unique_name);
|
||||
|
||||
void EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Patching helpers.
|
||||
|
||||
|
@ -497,11 +497,6 @@ class FullCodeGenerator: public AstVisitor {
|
||||
INLINE_RUNTIME_FUNCTION_LIST(EMIT_INLINE_RUNTIME_CALL)
|
||||
#undef EMIT_INLINE_RUNTIME_CALL
|
||||
|
||||
void EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask);
|
||||
|
||||
// Platform-specific code for resuming generators.
|
||||
void EmitGeneratorResume(Expression *generator,
|
||||
Expression *value,
|
||||
|
@ -7282,31 +7282,35 @@ class HSeqStringGetChar V8_FINAL : public HTemplateInstruction<2> {
|
||||
};
|
||||
|
||||
|
||||
class HSeqStringSetChar V8_FINAL : public HTemplateInstruction<3> {
|
||||
class HSeqStringSetChar V8_FINAL : public HTemplateInstruction<4> {
|
||||
public:
|
||||
DECLARE_INSTRUCTION_FACTORY_P4(HSeqStringSetChar, String::Encoding,
|
||||
HValue*, HValue*, HValue*);
|
||||
DECLARE_INSTRUCTION_WITH_CONTEXT_FACTORY_P4(
|
||||
HSeqStringSetChar, String::Encoding,
|
||||
HValue*, HValue*, HValue*);
|
||||
|
||||
String::Encoding encoding() { return encoding_; }
|
||||
HValue* string() { return OperandAt(0); }
|
||||
HValue* index() { return OperandAt(1); }
|
||||
HValue* value() { return OperandAt(2); }
|
||||
HValue* context() { return OperandAt(0); }
|
||||
HValue* string() { return OperandAt(1); }
|
||||
HValue* index() { return OperandAt(2); }
|
||||
HValue* value() { return OperandAt(3); }
|
||||
|
||||
virtual Representation RequiredInputRepresentation(int index) V8_OVERRIDE {
|
||||
return (index == 0) ? Representation::Tagged()
|
||||
return (index <= 1) ? Representation::Tagged()
|
||||
: Representation::Integer32();
|
||||
}
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar)
|
||||
|
||||
private:
|
||||
HSeqStringSetChar(String::Encoding encoding,
|
||||
HSeqStringSetChar(HValue* context,
|
||||
String::Encoding encoding,
|
||||
HValue* string,
|
||||
HValue* index,
|
||||
HValue* value) : encoding_(encoding) {
|
||||
SetOperandAt(0, string);
|
||||
SetOperandAt(1, index);
|
||||
SetOperandAt(2, value);
|
||||
SetOperandAt(0, context);
|
||||
SetOperandAt(1, string);
|
||||
SetOperandAt(2, index);
|
||||
SetOperandAt(3, value);
|
||||
set_representation(Representation::Tagged());
|
||||
SetGVNFlag(kChangesStringChars);
|
||||
}
|
||||
|
@ -1869,6 +1869,11 @@ HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left,
|
||||
// We can safely skip the write barrier for storing map here.
|
||||
AddStoreMapConstantNoWriteBarrier(string, map);
|
||||
|
||||
// Length must be stored into the string before we copy characters to
|
||||
// make debug verification code happy.
|
||||
Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
||||
length);
|
||||
|
||||
// Copy bytes from the left string.
|
||||
BuildCopySeqStringChars(
|
||||
left, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
|
||||
@ -1900,6 +1905,11 @@ HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left,
|
||||
// We can safely skip the write barrier for storing map here.
|
||||
AddStoreMapConstantNoWriteBarrier(string, map);
|
||||
|
||||
// Length must be stored into the string before we copy characters to
|
||||
// make debug verification code happy.
|
||||
Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
||||
length);
|
||||
|
||||
// Copy bytes from the left string.
|
||||
BuildCopySeqStringChars(
|
||||
left, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
|
||||
@ -1921,8 +1931,6 @@ HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left,
|
||||
HValue* string = Pop();
|
||||
Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
|
||||
Add<HConstant>(String::kEmptyHashField));
|
||||
Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
||||
length);
|
||||
Push(string);
|
||||
}
|
||||
if_sameencodingandsequential.JoinContinuation(&handled);
|
||||
@ -9715,9 +9723,10 @@ void HOptimizedGraphBuilder::GenerateOneByteSeqStringSetChar(
|
||||
HValue* value = Pop();
|
||||
HValue* index = Pop();
|
||||
HValue* string = Pop();
|
||||
HSeqStringSetChar* result = New<HSeqStringSetChar>(
|
||||
String::ONE_BYTE_ENCODING, string, index, value);
|
||||
return ast_context()->ReturnInstruction(result, call->id());
|
||||
Add<HSeqStringSetChar>(String::ONE_BYTE_ENCODING, string,
|
||||
index, value);
|
||||
Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
||||
return ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
||||
}
|
||||
|
||||
|
||||
@ -9730,9 +9739,10 @@ void HOptimizedGraphBuilder::GenerateTwoByteSeqStringSetChar(
|
||||
HValue* value = Pop();
|
||||
HValue* index = Pop();
|
||||
HValue* string = Pop();
|
||||
HSeqStringSetChar* result = New<HSeqStringSetChar>(
|
||||
String::TWO_BYTE_ENCODING, string, index, value);
|
||||
return ast_context()->ReturnInstruction(result, call->id());
|
||||
Add<HSeqStringSetChar>(String::TWO_BYTE_ENCODING, string,
|
||||
index, value);
|
||||
Add<HSimulate>(call->id(), FIXED_SIMULATE);
|
||||
return ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
||||
}
|
||||
|
||||
|
||||
|
@ -3440,32 +3440,6 @@ void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask) {
|
||||
__ test(index, Immediate(kSmiTagMask));
|
||||
__ Check(zero, kNonSmiIndex);
|
||||
__ test(value, Immediate(kSmiTagMask));
|
||||
__ Check(zero, kNonSmiValue);
|
||||
|
||||
__ cmp(index, FieldOperand(string, String::kLengthOffset));
|
||||
__ Check(less, kIndexIsTooLarge);
|
||||
|
||||
__ cmp(index, Immediate(Smi::FromInt(0)));
|
||||
__ Check(greater_equal, kIndexIsNegative);
|
||||
|
||||
__ push(value);
|
||||
__ mov(value, FieldOperand(string, HeapObject::kMapOffset));
|
||||
__ movzx_b(value, FieldOperand(value, Map::kInstanceTypeOffset));
|
||||
|
||||
__ and_(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
|
||||
__ cmp(value, Immediate(encoding_mask));
|
||||
__ Check(equal, kUnexpectedStringType);
|
||||
__ pop(value);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
ASSERT_EQ(3, args->length());
|
||||
@ -3482,12 +3456,20 @@ void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
|
||||
__ pop(index);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
|
||||
EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
|
||||
__ test(value, Immediate(kSmiTagMask));
|
||||
__ ThrowIf(not_zero, kNonSmiValue);
|
||||
__ test(index, Immediate(kSmiTagMask));
|
||||
__ ThrowIf(not_zero, kNonSmiValue);
|
||||
}
|
||||
|
||||
__ SmiUntag(value);
|
||||
__ SmiUntag(index);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
|
||||
}
|
||||
|
||||
__ mov_b(FieldOperand(string, index, times_1, SeqOneByteString::kHeaderSize),
|
||||
value);
|
||||
context()->Plug(string);
|
||||
@ -3509,8 +3491,14 @@ void FullCodeGenerator::EmitTwoByteSeqStringSetChar(CallRuntime* expr) {
|
||||
__ pop(index);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ test(value, Immediate(kSmiTagMask));
|
||||
__ ThrowIf(not_zero, kNonSmiValue);
|
||||
__ test(index, Immediate(kSmiTagMask));
|
||||
__ ThrowIf(not_zero, kNonSmiValue);
|
||||
__ SmiUntag(index);
|
||||
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
|
||||
EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
|
||||
__ SmiTag(index);
|
||||
}
|
||||
|
||||
__ SmiUntag(value);
|
||||
|
@ -2130,17 +2130,14 @@ void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) {
|
||||
Register string = ToRegister(instr->string());
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ push(string);
|
||||
__ mov(string, FieldOperand(string, HeapObject::kMapOffset));
|
||||
__ movzx_b(string, FieldOperand(string, Map::kInstanceTypeOffset));
|
||||
|
||||
__ and_(string, Immediate(kStringRepresentationMask | kStringEncodingMask));
|
||||
Register value = ToRegister(instr->value());
|
||||
Register index = ToRegister(instr->index());
|
||||
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
|
||||
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
|
||||
__ cmp(string, Immediate(encoding == String::ONE_BYTE_ENCODING
|
||||
? one_byte_seq_type : two_byte_seq_type));
|
||||
__ Check(equal, kUnexpectedStringType);
|
||||
__ pop(string);
|
||||
int encoding_mask =
|
||||
instr->hydrogen()->encoding() == String::ONE_BYTE_ENCODING
|
||||
? one_byte_seq_type : two_byte_seq_type;
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, encoding_mask);
|
||||
}
|
||||
|
||||
Operand operand = BuildSeqStringOperand(string, instr->index(), encoding);
|
||||
|
@ -1892,13 +1892,36 @@ LInstruction* LChunkBuilder::DoSeqStringGetChar(HSeqStringGetChar* instr) {
|
||||
}
|
||||
|
||||
|
||||
LOperand* LChunkBuilder::GetSeqStringSetCharOperand(HSeqStringSetChar* instr) {
|
||||
if (instr->encoding() == String::ONE_BYTE_ENCODING) {
|
||||
if (FLAG_debug_code) {
|
||||
return UseFixed(instr->value(), eax);
|
||||
} else {
|
||||
return UseFixedOrConstant(instr->value(), eax);
|
||||
}
|
||||
} else {
|
||||
if (FLAG_debug_code) {
|
||||
return UseRegisterAtStart(instr->value());
|
||||
} else {
|
||||
return UseRegisterOrConstantAtStart(instr->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) {
|
||||
LOperand* string = UseRegisterAtStart(instr->string());
|
||||
LOperand* index = UseRegisterOrConstantAtStart(instr->index());
|
||||
LOperand* value = (instr->encoding() == String::ONE_BYTE_ENCODING)
|
||||
? UseFixedOrConstant(instr->value(), eax)
|
||||
: UseRegisterOrConstantAtStart(instr->value());
|
||||
return new(zone()) LSeqStringSetChar(string, index, value);
|
||||
LOperand* index = FLAG_debug_code
|
||||
? UseRegisterAtStart(instr->index())
|
||||
: UseRegisterOrConstantAtStart(instr->index());
|
||||
LOperand* value = GetSeqStringSetCharOperand(instr);
|
||||
LOperand* context = FLAG_debug_code ? UseFixed(instr->context(), esi) : NULL;
|
||||
LInstruction* result = new(zone()) LSeqStringSetChar(context, string,
|
||||
index, value);
|
||||
if (FLAG_debug_code) {
|
||||
result = MarkAsCall(result, instr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1377,19 +1377,21 @@ class LSeqStringGetChar V8_FINAL : public LTemplateInstruction<1, 2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LSeqStringSetChar V8_FINAL : public LTemplateInstruction<1, 3, 0> {
|
||||
class LSeqStringSetChar V8_FINAL : public LTemplateInstruction<1, 4, 0> {
|
||||
public:
|
||||
LSeqStringSetChar(LOperand* string,
|
||||
LSeqStringSetChar(LOperand* context,
|
||||
LOperand* string,
|
||||
LOperand* index,
|
||||
LOperand* value) {
|
||||
inputs_[0] = string;
|
||||
inputs_[1] = index;
|
||||
inputs_[2] = value;
|
||||
inputs_[0] = context;
|
||||
inputs_[1] = string;
|
||||
inputs_[2] = index;
|
||||
inputs_[3] = value;
|
||||
}
|
||||
|
||||
LOperand* string() { return inputs_[0]; }
|
||||
LOperand* index() { return inputs_[1]; }
|
||||
LOperand* value() { return inputs_[2]; }
|
||||
LOperand* string() { return inputs_[1]; }
|
||||
LOperand* index() { return inputs_[2]; }
|
||||
LOperand* value() { return inputs_[3]; }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar, "seq-string-set-char")
|
||||
DECLARE_HYDROGEN_ACCESSOR(SeqStringSetChar)
|
||||
@ -2917,6 +2919,8 @@ class LChunkBuilder V8_FINAL BASE_EMBEDDED {
|
||||
|
||||
enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY };
|
||||
|
||||
LOperand* GetSeqStringSetCharOperand(HSeqStringSetChar* instr);
|
||||
|
||||
// Marks a call for the register allocator. Assigns a pointer map to
|
||||
// support GC and lazy deoptimization. Assigns an environment to support
|
||||
// eager deoptimization if CAN_DEOPTIMIZE_EAGERLY.
|
||||
|
@ -3065,6 +3065,40 @@ void MacroAssembler::Abort(BailoutReason reason) {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Throw(BailoutReason reason) {
|
||||
#ifdef DEBUG
|
||||
const char* msg = GetBailoutReason(reason);
|
||||
if (msg != NULL) {
|
||||
RecordComment("Throw message: ");
|
||||
RecordComment(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
push(eax);
|
||||
push(Immediate(Smi::FromInt(reason)));
|
||||
// Disable stub call restrictions to always allow calls to throw.
|
||||
if (!has_frame_) {
|
||||
// We don't actually want to generate a pile of code for this, so just
|
||||
// claim there is a stack frame, without generating one.
|
||||
FrameScope scope(this, StackFrame::NONE);
|
||||
CallRuntime(Runtime::kThrowMessage, 1);
|
||||
} else {
|
||||
CallRuntime(Runtime::kThrowMessage, 1);
|
||||
}
|
||||
// will not return here
|
||||
int3();
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::ThrowIf(Condition cc, BailoutReason reason) {
|
||||
Label L;
|
||||
j(NegateCondition(cc), &L);
|
||||
Throw(reason);
|
||||
// will not return here
|
||||
bind(&L);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::LoadInstanceDescriptors(Register map,
|
||||
Register descriptors) {
|
||||
mov(descriptors, FieldOperand(map, Map::kDescriptorsOffset));
|
||||
@ -3230,6 +3264,42 @@ void MacroAssembler::JumpIfNotUniqueName(Operand operand,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask) {
|
||||
Label is_object;
|
||||
JumpIfNotSmi(string, &is_object, Label::kNear);
|
||||
Throw(kNonObject);
|
||||
bind(&is_object);
|
||||
|
||||
push(value);
|
||||
mov(value, FieldOperand(string, HeapObject::kMapOffset));
|
||||
movzx_b(value, FieldOperand(value, Map::kInstanceTypeOffset));
|
||||
|
||||
and_(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
|
||||
cmp(value, Immediate(encoding_mask));
|
||||
pop(value);
|
||||
ThrowIf(not_equal, kUnexpectedStringType);
|
||||
|
||||
// The index is assumed to be untagged coming in, tag it to compare with the
|
||||
// string length without using a temp register, it is restored at the end of
|
||||
// this function.
|
||||
SmiTag(index);
|
||||
// Can't use overflow here directly, compiler can't seem to disambiguate.
|
||||
ThrowIf(NegateCondition(no_overflow), kIndexIsTooLarge);
|
||||
|
||||
cmp(index, FieldOperand(string, String::kLengthOffset));
|
||||
ThrowIf(greater_equal, kIndexIsTooLarge);
|
||||
|
||||
cmp(index, Immediate(Smi::FromInt(0)));
|
||||
ThrowIf(less, kIndexIsNegative);
|
||||
|
||||
// Restore the index
|
||||
SmiUntag(index);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
|
||||
int frame_alignment = OS::ActivationFrameAlignment();
|
||||
if (frame_alignment != 0) {
|
||||
|
@ -583,6 +583,12 @@ class MacroAssembler: public Assembler {
|
||||
// Throw past all JS frames to the top JS entry frame.
|
||||
void ThrowUncatchable(Register value);
|
||||
|
||||
// Throw a message string as an exception.
|
||||
void Throw(BailoutReason reason);
|
||||
|
||||
// Throw a message string as an exception if a condition is not true.
|
||||
void ThrowIf(Condition cc, BailoutReason reason);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inline caching support
|
||||
|
||||
@ -941,6 +947,11 @@ class MacroAssembler: public Assembler {
|
||||
void JumpIfNotUniqueName(Operand operand, Label* not_unique_name,
|
||||
Label::Distance distance = Label::kFar);
|
||||
|
||||
void EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask);
|
||||
|
||||
static int SafepointRegisterStackIndex(Register reg) {
|
||||
return SafepointRegisterStackIndex(reg.code());
|
||||
}
|
||||
|
@ -1196,6 +1196,7 @@ class MaybeObject BASE_EMBEDDED {
|
||||
V(kNonSmiIndex, "Non-smi index") \
|
||||
V(kNonSmiKeyInArrayLiteral, "Non-smi key in array literal") \
|
||||
V(kNonSmiValue, "Non-smi value") \
|
||||
V(kNonObject, "Non-object value") \
|
||||
V(kNotEnoughVirtualRegistersForValues, \
|
||||
"not enough virtual registers for values") \
|
||||
V(kNotEnoughSpillSlotsForOsr, \
|
||||
|
@ -9449,6 +9449,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ThrowNotDateError) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_ThrowMessage) {
|
||||
HandleScope scope(isolate);
|
||||
ASSERT(args.length() == 1);
|
||||
CONVERT_SMI_ARG_CHECKED(message_id, 0);
|
||||
const char* message = GetBailoutReason(
|
||||
static_cast<BailoutReason>(message_id));
|
||||
Handle<Name> message_handle =
|
||||
isolate->factory()->NewStringFromAscii(CStrVector(message));
|
||||
return isolate->Throw(*message_handle);
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(MaybeObject*, Runtime_StackGuard) {
|
||||
SealHandleScope shs(isolate);
|
||||
|
@ -408,6 +408,7 @@ namespace internal {
|
||||
F(ReThrow, 1, 1) \
|
||||
F(ThrowReferenceError, 1, 1) \
|
||||
F(ThrowNotDateError, 0, 1) \
|
||||
F(ThrowMessage, 1, 1) \
|
||||
F(StackGuard, 0, 1) \
|
||||
F(Interrupt, 0, 1) \
|
||||
F(PromoteScheduledException, 0, 1) \
|
||||
|
@ -3407,30 +3407,6 @@ void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask) {
|
||||
__ Check(masm()->CheckSmi(index), kNonSmiIndex);
|
||||
__ Check(masm()->CheckSmi(value), kNonSmiValue);
|
||||
|
||||
__ SmiCompare(index, FieldOperand(string, String::kLengthOffset));
|
||||
__ Check(less, kIndexIsTooLarge);
|
||||
|
||||
__ SmiCompare(index, Smi::FromInt(0));
|
||||
__ Check(greater_equal, kIndexIsNegative);
|
||||
|
||||
__ push(value);
|
||||
__ movq(value, FieldOperand(string, HeapObject::kMapOffset));
|
||||
__ movzxbq(value, FieldOperand(value, Map::kInstanceTypeOffset));
|
||||
|
||||
__ andb(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
|
||||
__ cmpq(value, Immediate(encoding_mask));
|
||||
__ Check(equal, kUnexpectedStringType);
|
||||
__ pop(value);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
ASSERT_EQ(3, args->length());
|
||||
@ -3446,12 +3422,18 @@ void FullCodeGenerator::EmitOneByteSeqStringSetChar(CallRuntime* expr) {
|
||||
__ pop(index);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
|
||||
EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
|
||||
__ ThrowIf(NegateCondition(__ CheckSmi(value)), kNonSmiValue);
|
||||
__ ThrowIf(NegateCondition(__ CheckSmi(index)), kNonSmiValue);
|
||||
}
|
||||
|
||||
__ SmiToInteger32(value, value);
|
||||
__ SmiToInteger32(index, index);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, one_byte_seq_type);
|
||||
}
|
||||
|
||||
__ movb(FieldOperand(string, index, times_1, SeqOneByteString::kHeaderSize),
|
||||
value);
|
||||
context()->Plug(string);
|
||||
@ -3473,12 +3455,18 @@ void FullCodeGenerator::EmitTwoByteSeqStringSetChar(CallRuntime* expr) {
|
||||
__ pop(index);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
|
||||
EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
|
||||
__ ThrowIf(NegateCondition(__ CheckSmi(value)), kNonSmiValue);
|
||||
__ ThrowIf(NegateCondition(__ CheckSmi(index)), kNonSmiValue);
|
||||
}
|
||||
|
||||
__ SmiToInteger32(value, value);
|
||||
__ SmiToInteger32(index, index);
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, two_byte_seq_type);
|
||||
}
|
||||
|
||||
__ movw(FieldOperand(string, index, times_2, SeqTwoByteString::kHeaderSize),
|
||||
value);
|
||||
context()->Plug(rax);
|
||||
|
@ -1732,17 +1732,14 @@ void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) {
|
||||
Register string = ToRegister(instr->string());
|
||||
|
||||
if (FLAG_debug_code) {
|
||||
__ push(string);
|
||||
__ movq(string, FieldOperand(string, HeapObject::kMapOffset));
|
||||
__ movzxbq(string, FieldOperand(string, Map::kInstanceTypeOffset));
|
||||
|
||||
__ andb(string, Immediate(kStringRepresentationMask | kStringEncodingMask));
|
||||
Register value = ToRegister(instr->value());
|
||||
Register index = ToRegister(instr->index());
|
||||
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
|
||||
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
|
||||
__ cmpq(string, Immediate(encoding == String::ONE_BYTE_ENCODING
|
||||
? one_byte_seq_type : two_byte_seq_type));
|
||||
__ Check(equal, kUnexpectedStringType);
|
||||
__ pop(string);
|
||||
int encoding_mask =
|
||||
instr->hydrogen()->encoding() == String::ONE_BYTE_ENCODING
|
||||
? one_byte_seq_type : two_byte_seq_type;
|
||||
__ EmitSeqStringSetCharCheck(string, index, value, encoding_mask);
|
||||
}
|
||||
|
||||
Operand operand = BuildSeqStringOperand(string, instr->index(), encoding);
|
||||
|
@ -1796,9 +1796,19 @@ LInstruction* LChunkBuilder::DoSeqStringGetChar(HSeqStringGetChar* instr) {
|
||||
|
||||
LInstruction* LChunkBuilder::DoSeqStringSetChar(HSeqStringSetChar* instr) {
|
||||
LOperand* string = UseRegisterAtStart(instr->string());
|
||||
LOperand* index = UseRegisterOrConstantAtStart(instr->index());
|
||||
LOperand* value = UseRegisterOrConstantAtStart(instr->value());
|
||||
return new(zone()) LSeqStringSetChar(string, index, value);
|
||||
LOperand* index = FLAG_debug_code
|
||||
? UseRegisterAtStart(instr->index())
|
||||
: UseRegisterOrConstantAtStart(instr->index());
|
||||
LOperand* value = FLAG_debug_code
|
||||
? UseRegisterAtStart(instr->value())
|
||||
: UseRegisterOrConstantAtStart(instr->value());
|
||||
LOperand* context = FLAG_debug_code ? UseFixed(instr->context(), rsi) : NULL;
|
||||
LInstruction* result = new(zone()) LSeqStringSetChar(context, string,
|
||||
index, value);
|
||||
if (FLAG_debug_code) {
|
||||
result = MarkAsCall(result, instr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1339,19 +1339,21 @@ class LSeqStringGetChar V8_FINAL : public LTemplateInstruction<1, 2, 0> {
|
||||
};
|
||||
|
||||
|
||||
class LSeqStringSetChar V8_FINAL : public LTemplateInstruction<1, 3, 0> {
|
||||
class LSeqStringSetChar V8_FINAL : public LTemplateInstruction<1, 4, 0> {
|
||||
public:
|
||||
LSeqStringSetChar(LOperand* string,
|
||||
LSeqStringSetChar(LOperand* context,
|
||||
LOperand* string,
|
||||
LOperand* index,
|
||||
LOperand* value) {
|
||||
inputs_[0] = string;
|
||||
inputs_[1] = index;
|
||||
inputs_[2] = value;
|
||||
inputs_[0] = context;
|
||||
inputs_[1] = string;
|
||||
inputs_[2] = index;
|
||||
inputs_[3] = value;
|
||||
}
|
||||
|
||||
LOperand* string() { return inputs_[0]; }
|
||||
LOperand* index() { return inputs_[1]; }
|
||||
LOperand* value() { return inputs_[2]; }
|
||||
LOperand* string() { return inputs_[1]; }
|
||||
LOperand* index() { return inputs_[2]; }
|
||||
LOperand* value() { return inputs_[3]; }
|
||||
|
||||
DECLARE_CONCRETE_INSTRUCTION(SeqStringSetChar, "seq-string-set-char")
|
||||
DECLARE_HYDROGEN_ACCESSOR(SeqStringSetChar)
|
||||
|
@ -3206,6 +3206,39 @@ void MacroAssembler::TaggedToI(Register result_reg,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Throw(BailoutReason reason) {
|
||||
#ifdef DEBUG
|
||||
const char* msg = GetBailoutReason(reason);
|
||||
if (msg != NULL) {
|
||||
RecordComment("Throw message: ");
|
||||
RecordComment(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
push(rax);
|
||||
Push(Smi::FromInt(reason));
|
||||
if (!has_frame_) {
|
||||
// We don't actually want to generate a pile of code for this, so just
|
||||
// claim there is a stack frame, without generating one.
|
||||
FrameScope scope(this, StackFrame::NONE);
|
||||
CallRuntime(Runtime::kThrowMessage, 1);
|
||||
} else {
|
||||
CallRuntime(Runtime::kThrowMessage, 1);
|
||||
}
|
||||
// Control will not return here.
|
||||
int3();
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::ThrowIf(Condition cc, BailoutReason reason) {
|
||||
Label L;
|
||||
j(NegateCondition(cc), &L);
|
||||
Throw(reason);
|
||||
// will not return here
|
||||
bind(&L);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::LoadInstanceDescriptors(Register map,
|
||||
Register descriptors) {
|
||||
movq(descriptors, FieldOperand(map, Map::kDescriptorsOffset));
|
||||
@ -4631,6 +4664,39 @@ int MacroAssembler::ArgumentStackSlotsForCFunctionCall(int num_arguments) {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask) {
|
||||
Label is_object;
|
||||
JumpIfNotSmi(string, &is_object);
|
||||
Throw(kNonObject);
|
||||
bind(&is_object);
|
||||
|
||||
push(value);
|
||||
movq(value, FieldOperand(string, HeapObject::kMapOffset));
|
||||
movzxbq(value, FieldOperand(value, Map::kInstanceTypeOffset));
|
||||
|
||||
andb(value, Immediate(kStringRepresentationMask | kStringEncodingMask));
|
||||
cmpq(value, Immediate(encoding_mask));
|
||||
pop(value);
|
||||
ThrowIf(not_equal, kUnexpectedStringType);
|
||||
|
||||
// The index is assumed to be untagged coming in, tag it to compare with the
|
||||
// string length without using a temp register, it is restored at the end of
|
||||
// this function.
|
||||
Integer32ToSmi(index, index);
|
||||
SmiCompare(index, FieldOperand(string, String::kLengthOffset));
|
||||
ThrowIf(greater_equal, kIndexIsTooLarge);
|
||||
|
||||
SmiCompare(index, Smi::FromInt(0));
|
||||
ThrowIf(less, kIndexIsNegative);
|
||||
|
||||
// Restore the index
|
||||
SmiToInteger32(index, index);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PrepareCallCFunction(int num_arguments) {
|
||||
int frame_alignment = OS::ActivationFrameAlignment();
|
||||
ASSERT(frame_alignment != 0);
|
||||
|
@ -792,6 +792,11 @@ class MacroAssembler: public Assembler {
|
||||
Label* on_fail,
|
||||
Label::Distance near_jump = Label::kFar);
|
||||
|
||||
void EmitSeqStringSetCharCheck(Register string,
|
||||
Register index,
|
||||
Register value,
|
||||
uint32_t encoding_mask);
|
||||
|
||||
// Checks if the given register or operand is a unique name
|
||||
void JumpIfNotUniqueName(Register reg, Label* not_unique_name,
|
||||
Label::Distance distance = Label::kFar);
|
||||
@ -1058,6 +1063,12 @@ class MacroAssembler: public Assembler {
|
||||
// Propagate an uncatchable exception out of the current JS stack.
|
||||
void ThrowUncatchable(Register value);
|
||||
|
||||
// Throw a message string as an exception.
|
||||
void Throw(BailoutReason reason);
|
||||
|
||||
// Throw a message string as an exception if a condition is not true.
|
||||
void ThrowIf(Condition cc, BailoutReason reason);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Inline caching support
|
||||
|
||||
|
@ -116,6 +116,7 @@ function testArgumentTypes(name, argc) {
|
||||
|
||||
var knownProblems = {
|
||||
"Abort": true,
|
||||
"ThrowMessage": true,
|
||||
|
||||
// Avoid calling the concat operation, because weird lengths
|
||||
// may lead to out-of-memory. Ditto for StringBuilderJoin.
|
||||
|
@ -116,6 +116,7 @@ function testArgumentTypes(name, argc) {
|
||||
|
||||
var knownProblems = {
|
||||
"Abort": true,
|
||||
"ThrowMessage": true,
|
||||
|
||||
// Avoid calling the concat operation, because weird lengths
|
||||
// may lead to out-of-memory. Ditto for StringBuilderJoin.
|
||||
|
@ -116,6 +116,7 @@ function testArgumentTypes(name, argc) {
|
||||
|
||||
var knownProblems = {
|
||||
"Abort": true,
|
||||
"ThrowMessage": true,
|
||||
|
||||
// Avoid calling the concat operation, because weird lengths
|
||||
// may lead to out-of-memory. Ditto for StringBuilderJoin.
|
||||
|
@ -116,6 +116,7 @@ function testArgumentTypes(name, argc) {
|
||||
|
||||
var knownProblems = {
|
||||
"Abort": true,
|
||||
"ThrowMessage": true,
|
||||
|
||||
// Avoid calling the concat operation, because weird lengths
|
||||
// may lead to out-of-memory. Ditto for StringBuilderJoin.
|
||||
|
81
test/mjsunit/regress/regress-320948.js
Normal file
81
test/mjsunit/regress/regress-320948.js
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2013 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax --debug-code
|
||||
|
||||
var one_byte = %NewString(10, true);
|
||||
var two_byte = %NewString(10, false);
|
||||
|
||||
function foo1(s, arg1, arg2) {
|
||||
return %_OneByteSeqStringSetChar(s, arg1, arg2)
|
||||
}
|
||||
foo1(one_byte, 0, 0);
|
||||
assertThrows("{ foo1(4, 0, 0); }");
|
||||
assertThrows("{ foo1(one_byte, new Object(), 0); }");
|
||||
assertThrows("{ foo1(one_byte, 0, new Object()); }");
|
||||
assertThrows("{ foo1(one_byte, 100000, 100; }");
|
||||
assertThrows("{ foo1(one_byte, -1, 100; }");
|
||||
|
||||
function bar1(s, arg1, arg2) {
|
||||
return %_OneByteSeqStringSetChar(s, arg1, arg2)
|
||||
}
|
||||
|
||||
bar1(one_byte, 0, 0);
|
||||
bar1(one_byte, 0, 0);
|
||||
bar1(one_byte, 0, 0);
|
||||
%OptimizeFunctionOnNextCall(bar1);
|
||||
bar1(one_byte, 0, 0);
|
||||
assertThrows("{ bar1(4, 0, 0); }");
|
||||
assertThrows("{ bar1(one_byte, new Object(), 0); }");
|
||||
assertThrows("{ bar1(one_byte, 0, new Object()); }");
|
||||
assertThrows("{ bar1(one_byte, 100000, 100; }");
|
||||
assertThrows("{ bar1(one_byte, -1, 100; }");
|
||||
|
||||
function foo2(s, arg1, arg2) {
|
||||
return %_TwoByteSeqStringSetChar(s, arg1, arg2)
|
||||
}
|
||||
foo2(two_byte, 0, 0);
|
||||
assertThrows("{ foo2(4, 0, 0); }");
|
||||
assertThrows("{ foo2(two_byte, new Object(), 0); }");
|
||||
assertThrows("{ foo2(two_byte, 0, new Object()); }");
|
||||
assertThrows("{ foo2(two_byte, 100000, 100; }");
|
||||
assertThrows("{ foo2(two_byte, -1, 100; }");
|
||||
|
||||
function bar2(s, arg1, arg2) {
|
||||
return %_TwoByteSeqStringSetChar(s, arg1, arg2)
|
||||
}
|
||||
|
||||
bar2(two_byte, 0, 0);
|
||||
bar2(two_byte, 0, 0);
|
||||
bar2(two_byte, 0, 0);
|
||||
%OptimizeFunctionOnNextCall(bar2);
|
||||
bar2(two_byte, 0, 0);
|
||||
assertThrows("{ bar2(4, 0, 0); }");
|
||||
assertThrows("{ bar2(two_byte, new Object(), 0); }");
|
||||
assertThrows("{ bar2(two_byte, 0, new Object()); }");
|
||||
assertThrows("{ bar2(two_byte, 100000, 100; }");
|
||||
assertThrows("{ bar2(two_byte, -1, 100; }");
|
Loading…
Reference in New Issue
Block a user