| Index: test/cctest/test-api.cc
 | 
| ===================================================================
 | 
| --- test/cctest/test-api.cc	(revision 10944)
 | 
| +++ test/cctest/test-api.cc	(working copy)
 | 
| @@ -5529,6 +5529,17 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +int GetUtf8Length(Handle<String> str) {
 | 
| +  int len = str->Utf8Length();
 | 
| +  if (len < 0) {
 | 
| +    i::Handle<i::String> istr(v8::Utils::OpenHandle(*str));
 | 
| +    i::FlattenString(istr);
 | 
| +    len = str->Utf8Length();
 | 
| +  }
 | 
| +  return len;
 | 
| +}
 | 
| +
 | 
| +
 | 
|  THREADED_TEST(StringWrite) {
 | 
|    LocalContext context;
 | 
|    v8::HandleScope scope;
 | 
| @@ -5609,7 +5620,7 @@
 | 
|    CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3));
 | 
|  
 | 
|    memset(utf8buf, 0x1, sizeof(utf8buf));
 | 
| -  len = left_tree->Utf8Length();
 | 
| +  len = GetUtf8Length(left_tree);
 | 
|    int utf8_expected =
 | 
|        (0x80 + (0x800 - 0x80) * 2 + (0xd800 - 0x800) * 3) / kStride;
 | 
|    CHECK_EQ(utf8_expected, len);
 | 
| @@ -5623,7 +5634,7 @@
 | 
|    CHECK_EQ(1, utf8buf[utf8_expected]);
 | 
|  
 | 
|    memset(utf8buf, 0x1, sizeof(utf8buf));
 | 
| -  len = right_tree->Utf8Length();
 | 
| +  len = GetUtf8Length(right_tree);
 | 
|    CHECK_EQ(utf8_expected, len);
 | 
|    len = right_tree->WriteUtf8(utf8buf, utf8_expected, &charlen);
 | 
|    CHECK_EQ(utf8_expected, len);
 | 
| @@ -5748,6 +5759,217 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +static void Utf16Helper(
 | 
| +    LocalContext& context,
 | 
| +    const char* name,
 | 
| +    const char* lengths_name,
 | 
| +    int len) {
 | 
| +  Local<v8::Array> a =
 | 
| +      Local<v8::Array>::Cast(context->Global()->Get(v8_str(name)));
 | 
| +  Local<v8::Array> alens =
 | 
| +      Local<v8::Array>::Cast(context->Global()->Get(v8_str(lengths_name)));
 | 
| +  for (int i = 0; i < len; i++) {
 | 
| +    Local<v8::String> string =
 | 
| +      Local<v8::String>::Cast(a->Get(i));
 | 
| +    Local<v8::Number> expected_len =
 | 
| +      Local<v8::Number>::Cast(alens->Get(i));
 | 
| +    int length = GetUtf8Length(string);
 | 
| +    CHECK_EQ(static_cast<int>(expected_len->Value()), length);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static uint16_t StringGet(Handle<String> str, int index) {
 | 
| +  i::Handle<i::String> istring =
 | 
| +      v8::Utils::OpenHandle(String::Cast(*str));
 | 
| +  return istring->Get(index);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void WriteUtf8Helper(
 | 
| +    LocalContext& context,
 | 
| +    const char* name,
 | 
| +    const char* lengths_name,
 | 
| +    int len) {
 | 
| +  Local<v8::Array> b =
 | 
| +      Local<v8::Array>::Cast(context->Global()->Get(v8_str(name)));
 | 
| +  Local<v8::Array> alens =
 | 
| +      Local<v8::Array>::Cast(context->Global()->Get(v8_str(lengths_name)));
 | 
| +  char buffer[1000];
 | 
| +  char buffer2[1000];
 | 
| +  for (int i = 0; i < len; i++) {
 | 
| +    Local<v8::String> string =
 | 
| +      Local<v8::String>::Cast(b->Get(i));
 | 
| +    Local<v8::Number> expected_len =
 | 
| +      Local<v8::Number>::Cast(alens->Get(i));
 | 
| +    int utf8_length = static_cast<int>(expected_len->Value());
 | 
| +    for (int j = utf8_length + 1; j >= 0; j--) {
 | 
| +      memset(reinterpret_cast<void*>(&buffer), 42, sizeof(buffer));
 | 
| +      memset(reinterpret_cast<void*>(&buffer2), 42, sizeof(buffer2));
 | 
| +      int nchars;
 | 
| +      int utf8_written =
 | 
| +          string->WriteUtf8(buffer, j, &nchars, String::NO_OPTIONS);
 | 
| +      int utf8_written2 =
 | 
| +          string->WriteUtf8(buffer2, j, &nchars, String::NO_NULL_TERMINATION);
 | 
| +      CHECK_GE(utf8_length + 1, utf8_written);
 | 
| +      CHECK_GE(utf8_length, utf8_written2);
 | 
| +      for (int k = 0; k < utf8_written2; k++) {
 | 
| +        CHECK_EQ(buffer[k], buffer2[k]);
 | 
| +      }
 | 
| +      CHECK(nchars * 3 >= utf8_written - 1);
 | 
| +      CHECK(nchars <= utf8_written);
 | 
| +      if (j == utf8_length + 1) {
 | 
| +        CHECK_EQ(utf8_written2, utf8_length);
 | 
| +        CHECK_EQ(utf8_written2 + 1, utf8_written);
 | 
| +      }
 | 
| +      CHECK_EQ(buffer[utf8_written], 42);
 | 
| +      if (j > utf8_length) {
 | 
| +        if (utf8_written != 0) CHECK_EQ(buffer[utf8_written - 1], 0);
 | 
| +        if (utf8_written > 1) CHECK_NE(buffer[utf8_written - 2], 42);
 | 
| +        Handle<String> roundtrip = v8_str(buffer);
 | 
| +        CHECK(roundtrip->Equals(string));
 | 
| +      } else {
 | 
| +        if (utf8_written != 0) CHECK_NE(buffer[utf8_written - 1], 42);
 | 
| +      }
 | 
| +      if (utf8_written2 != 0) CHECK_NE(buffer[utf8_written - 1], 42);
 | 
| +      if (nchars >= 2) {
 | 
| +        uint16_t trail = StringGet(string, nchars - 1);
 | 
| +        uint16_t lead = StringGet(string, nchars - 2);
 | 
| +        if (((lead & 0xfc00) == 0xd800) &&
 | 
| +            ((trail & 0xfc00) == 0xdc00)) {
 | 
| +          unsigned char u1 = buffer2[utf8_written2 - 4];
 | 
| +          unsigned char u2 = buffer2[utf8_written2 - 3];
 | 
| +          unsigned char u3 = buffer2[utf8_written2 - 2];
 | 
| +          unsigned char u4 = buffer2[utf8_written2 - 1];
 | 
| +          CHECK_EQ((u1 & 0xf8), 0xf0);
 | 
| +          CHECK_EQ((u2 & 0xc0), 0x80);
 | 
| +          CHECK_EQ((u3 & 0xc0), 0x80);
 | 
| +          CHECK_EQ((u4 & 0xc0), 0x80);
 | 
| +          uint32_t c = 0x10000 + ((lead & 0x3ff) << 10) + (trail & 0x3ff);
 | 
| +          CHECK_EQ((u4 & 0x3f), (c & 0x3f));
 | 
| +          CHECK_EQ((u3 & 0x3f), ((c >> 6) & 0x3f));
 | 
| +          CHECK_EQ((u2 & 0x3f), ((c >> 12) & 0x3f));
 | 
| +          CHECK_EQ((u1 & 0x3), c >> 18);
 | 
| +        }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +THREADED_TEST(Utf16) {
 | 
| +  LocalContext context;
 | 
| +  v8::HandleScope scope;
 | 
| +  CompileRun(
 | 
| +      "var pad = '01234567890123456789';"
 | 
| +      "var p = [];"
 | 
| +      "var plens = [20, 3, 3];"
 | 
| +      "p.push('01234567890123456789');"
 | 
| +      "var lead = 0xd800;"
 | 
| +      "var trail = 0xdc00;"
 | 
| +      "p.push(String.fromCharCode(0xd800));"
 | 
| +      "p.push(String.fromCharCode(0xdc00));"
 | 
| +      "var a = [];"
 | 
| +      "var b = [];"
 | 
| +      "var alens = [];"
 | 
| +      "for (var i = 0; i < 3; i++) {"
 | 
| +      "  p[1] = String.fromCharCode(lead++);"
 | 
| +      "  for (var j = 0; j < 3; j++) {"
 | 
| +      "    p[2] = String.fromCharCode(trail++);"
 | 
| +      "    a.push(p[i] + p[j]);"
 | 
| +      "    b.push(p[i] + p[j]);"
 | 
| +      "    alens.push(plens[i] + plens[j]);"
 | 
| +      "  }"
 | 
| +      "}"
 | 
| +      "alens[5] -= 2;"  // Here the surrogate pairs match up.
 | 
| +      "var a2 = [];"
 | 
| +      "var b2 = [];"
 | 
| +      "var a2lens = [];"
 | 
| +      "for (var m = 0; m < 9; m++) {"
 | 
| +      "  for (var n = 0; n < 9; n++) {"
 | 
| +      "    a2.push(a[m] + a[n]);"
 | 
| +      "    b2.push(b[m] + b[n]);"
 | 
| +      "    var utf = alens[m] + alens[n];"  // And here.
 | 
| +           // The 'n's that start with 0xdc.. are 6-8
 | 
| +           // The 'm's that end with 0xd8.. are 1, 4 and 7
 | 
| +      "    if ((m % 3) == 1 && n >= 6) utf -= 2;"
 | 
| +      "    a2lens.push(utf);"
 | 
| +      "  }"
 | 
| +      "}");
 | 
| +  Utf16Helper(context, "a", "alens", 9);
 | 
| +  Utf16Helper(context, "a2", "a2lens", 81);
 | 
| +  WriteUtf8Helper(context, "b", "alens", 9);
 | 
| +  WriteUtf8Helper(context, "b2", "a2lens", 81);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static bool SameSymbol(Handle<String> s1, Handle<String> s2) {
 | 
| +  i::Handle<i::String> is1(v8::Utils::OpenHandle(*s1));
 | 
| +  i::Handle<i::String> is2(v8::Utils::OpenHandle(*s2));
 | 
| +  return *is1 == *is2;
 | 
| +}
 | 
| +
 | 
| +
 | 
| +static void SameSymbolHelper(const char* a, const char* b) {
 | 
| +  Handle<String> symbol1 = v8::String::NewSymbol(a);
 | 
| +  Handle<String> symbol2 = v8::String::NewSymbol(b);
 | 
| +  CHECK(SameSymbol(symbol1, symbol2));
 | 
| +}
 | 
| +
 | 
| +
 | 
| +THREADED_TEST(Utf16Symbol) {
 | 
| +  LocalContext context;
 | 
| +  v8::HandleScope scope;
 | 
| +
 | 
| +  Handle<String> symbol1 = v8::String::NewSymbol("abc");
 | 
| +  Handle<String> symbol2 = v8::String::NewSymbol("abc");
 | 
| +  CHECK(SameSymbol(symbol1, symbol2));
 | 
| +
 | 
| +  SameSymbolHelper("\360\220\220\205",  // 4 byte encoding.
 | 
| +                   "\355\240\201\355\260\205");  // 2 3-byte surrogates.
 | 
| +  SameSymbolHelper("\355\240\201\355\260\206",  // 2 3-byte surrogates.
 | 
| +                   "\360\220\220\206");  // 4 byte encoding.
 | 
| +  SameSymbolHelper("x\360\220\220\205",  // 4 byte encoding.
 | 
| +                   "x\355\240\201\355\260\205");  // 2 3-byte surrogates.
 | 
| +  SameSymbolHelper("x\355\240\201\355\260\206",  // 2 3-byte surrogates.
 | 
| +                   "x\360\220\220\206");  // 4 byte encoding.
 | 
| +  CompileRun(
 | 
| +      "var sym0 = 'benedictus';"
 | 
| +      "var sym0b = 'S\303\270ren';"
 | 
| +      "var sym1 = '\355\240\201\355\260\207';"
 | 
| +      "var sym2 = '\360\220\220\210';"
 | 
| +      "var sym3 = 'x\355\240\201\355\260\207';"
 | 
| +      "var sym4 = 'x\360\220\220\210';"
 | 
| +      "if (sym1.length != 2) throw sym1;"
 | 
| +      "if (sym1.charCodeAt(1) != 0xdc07) throw sym1.charCodeAt(1);"
 | 
| +      "if (sym2.length != 2) throw sym2;"
 | 
| +      "if (sym2.charCodeAt(1) != 0xdc08) throw sym2.charCodeAt(2);"
 | 
| +      "if (sym3.length != 3) throw sym3;"
 | 
| +      "if (sym3.charCodeAt(2) != 0xdc07) throw sym1.charCodeAt(2);"
 | 
| +      "if (sym4.length != 3) throw sym4;"
 | 
| +      "if (sym4.charCodeAt(2) != 0xdc08) throw sym2.charCodeAt(2);");
 | 
| +  Handle<String> sym0 = v8::String::NewSymbol("benedictus");
 | 
| +  Handle<String> sym0b = v8::String::NewSymbol("S\303\270ren");
 | 
| +  Handle<String> sym1 = v8::String::NewSymbol("\355\240\201\355\260\207");
 | 
| +  Handle<String> sym2 = v8::String::NewSymbol("\360\220\220\210");
 | 
| +  Handle<String> sym3 = v8::String::NewSymbol("x\355\240\201\355\260\207");
 | 
| +  Handle<String> sym4 = v8::String::NewSymbol("x\360\220\220\210");
 | 
| +  v8::Local<v8::Object> global = context->Global();
 | 
| +  Local<Value> s0 = global->Get(v8_str("sym0"));
 | 
| +  Local<Value> s0b = global->Get(v8_str("sym0b"));
 | 
| +  Local<Value> s1 = global->Get(v8_str("sym1"));
 | 
| +  Local<Value> s2 = global->Get(v8_str("sym2"));
 | 
| +  Local<Value> s3 = global->Get(v8_str("sym3"));
 | 
| +  Local<Value> s4 = global->Get(v8_str("sym4"));
 | 
| +  CHECK(SameSymbol(sym0, Handle<String>(String::Cast(*s0))));
 | 
| +  CHECK(SameSymbol(sym0b, Handle<String>(String::Cast(*s0b))));
 | 
| +  CHECK(SameSymbol(sym1, Handle<String>(String::Cast(*s1))));
 | 
| +  CHECK(SameSymbol(sym2, Handle<String>(String::Cast(*s2))));
 | 
| +  CHECK(SameSymbol(sym3, Handle<String>(String::Cast(*s3))));
 | 
| +  CHECK(SameSymbol(sym4, Handle<String>(String::Cast(*s4))));
 | 
| +}
 | 
| +
 | 
| +
 | 
|  THREADED_TEST(ToArrayIndex) {
 | 
|    v8::HandleScope scope;
 | 
|    LocalContext context;
 | 
| 
 |