Fixed crashes exposed though fuzzing.
authordanno@chromium.org <danno@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 19 Nov 2013 16:41:07 +0000 (16:41 +0000)
committerdanno@chromium.org <danno@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 19 Nov 2013 16:41:07 +0000 (16:41 +0000)
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

29 files changed:
src/arm/full-codegen-arm.cc
src/arm/lithium-arm.cc
src/arm/lithium-arm.h
src/arm/lithium-codegen-arm.cc
src/arm/macro-assembler-arm.cc
src/arm/macro-assembler-arm.h
src/full-codegen.h
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/full-codegen-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.cc
src/ia32/lithium-ia32.h
src/ia32/macro-assembler-ia32.cc
src/ia32/macro-assembler-ia32.h
src/objects.h
src/runtime.cc
src/runtime.h
src/x64/full-codegen-x64.cc
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.cc
src/x64/lithium-x64.h
src/x64/macro-assembler-x64.cc
src/x64/macro-assembler-x64.h
test/mjsunit/fuzz-natives-part1.js
test/mjsunit/fuzz-natives-part2.js
test/mjsunit/fuzz-natives-part3.js
test/mjsunit/fuzz-natives-part4.js
test/mjsunit/regress/regress-320948.js [new file with mode: 0644]

index 136101b..d95055c 100644 (file)
@@ -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);
index 71ce8f1..a6644af 100644 (file)
@@ -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);
 }
 
 
index f67747b..5b9278b 100644 (file)
@@ -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)
index 56717a2..0d4d805 100644 (file)
@@ -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);
index 9060195..acd398a 100644 (file)
@@ -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));
index 97b2575..4897064 100644 (file)
@@ -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.
 
index e27662e..11d5341 100644 (file)
@@ -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,
index c77c2eb..1423c72 100644 (file)
@@ -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);
   }
index 61211ec..22da947 100644 (file)
@@ -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());
 }
 
 
index a1236ed..bacfe83 100644 (file)
@@ -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);
index 2a118a9..654c04d 100644 (file)
@@ -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);
index 5a4a3bc..5fcd0a2 100644 (file)
@@ -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;
 }
 
 
index b0d031a..bc2b71b 100644 (file)
@@ -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.
index 914a4c2..d394c09 100644 (file)
@@ -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) {
index 29bb78d..58e5907 100644 (file)
@@ -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());
   }
index 1839699..352edaa 100644 (file)
@@ -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,                                               \
index 213166a..b5a1081 100644 (file)
@@ -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);
index 8b650e9..4872ab5 100644 (file)
@@ -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) \
index 7d0d00a..8947e2f 100644 (file)
@@ -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);
index cafde51..ba8ccc5 100644 (file)
@@ -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);
index b911c48..0b2bf82 100644 (file)
@@ -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;
 }
 
 
index 6e236c7..a475d8c 100644 (file)
@@ -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)
index 5127ddf..25f4bd3 100644 (file)
@@ -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);
index 4bea461..f7bf1ca 100644 (file)
@@ -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
 
index ffcbd1d..c30069e 100644 (file)
@@ -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.
index f796b80..e73f3bd 100644 (file)
@@ -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.
index 61e0211..6a73498 100644 (file)
@@ -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.
index 831722b..edeaa87 100644 (file)
@@ -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.
diff --git a/test/mjsunit/regress/regress-320948.js b/test/mjsunit/regress/regress-320948.js
new file mode 100644 (file)
index 0000000..734031c
--- /dev/null
@@ -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; }");