summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js
blob: 251d30d861ad9fce7c90cc7b2d35ceaa0e2399bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Check JS::ubi::Node::size results for strings.

// We actually hard-code specific sizes into this test, even though they're
// implementation details, because in practice there are only two architecture
// variants to consider (32-bit and 64-bit), and if these sizes change, that's
// something SpiderMonkey hackers really want to know; they're supposed to be
// stable.

// Run this test only if we're using jemalloc. Other malloc implementations
// exhibit surprising behaviors. For example, 32-bit Fedora builds have
// non-deterministic allocation sizes.
var config = getBuildConfiguration();
if (!config['moz-memory'])
  quit(0);

if (config['pointer-byte-size'] == 4)
  var s = (s32, s64) => s32
else
  var s = (s32, s64) => s64

// Return the byte size of |obj|, ensuring that the size is not affected by
// being tenured. (We use 'survives a GC' as an approximation for 'tenuring'.)
function tByteSize(obj) {
  var nurserySize = byteSize(obj);
  minorgc();
  var tenuredSize = byteSize(obj);
  if (nurserySize != tenuredSize) {
    print("nursery size: " + nurserySize + "  tenured size: " + tenuredSize);
    return -1; // make the stack trace point at the real test
  }

  return tenuredSize;
}

// There are four representations of flat strings, with the following capacities
// (excluding a terminating null character):
//
//                      32-bit                  64-bit                test
// representation       Latin-1   char16_t      Latin-1   char16_t    label
// ========================================================================
// JSExternalString            (cannot be tested in shell)            -
// JSThinInlineString   7         3             15        7           T
// JSFatInlineString    23        11            23        11          F
// JSExtensibleString          - limited by available memory -        X
// JSUndependedString          - same as JSExtensibleString -

// Note that atoms are 8 bytes larger than non-atoms, to store the atom's hash code.

// Latin-1
assertEq(tByteSize(""),                                                 s(24, 32)); // T, T
assertEq(tByteSize("1"),                                                s(24, 32)); // T, T
assertEq(tByteSize("1234567"),                                          s(24, 32)); // T, T
assertEq(tByteSize("12345678"),                                         s(40, 32)); // F, T
assertEq(tByteSize("123456789.12345"),                                  s(40, 32)); // F, T
assertEq(tByteSize("123456789.123456"),                                 s(40, 40)); // F, F
assertEq(tByteSize("123456789.123456789.123"),                          s(40, 40)); // F, F
assertEq(tByteSize("123456789.123456789.1234"),                         s(56, 64)); // X, X
assertEq(tByteSize("123456789.123456789.123456789.1"),                  s(56, 64)); // X, X
assertEq(tByteSize("123456789.123456789.123456789.12"),                 s(72, 80)); // X, X

// Inline char16_t atoms.
// "Impassionate gods have never seen the red that is the Tatsuta River."
//   - Ariwara no Narihira
assertEq(tByteSize("千"),						s(24, 32)); // T, T
assertEq(tByteSize("千早"),    						s(24, 32)); // T, T
assertEq(tByteSize("千早ぶ"),    					s(24, 32)); // T, T
assertEq(tByteSize("千早ぶる"),    					s(40, 32)); // F, T
assertEq(tByteSize("千早ぶる神"),    					s(40, 32)); // F, T
assertEq(tByteSize("千早ぶる神代"),					s(40, 32)); // F, T
assertEq(tByteSize("千早ぶる神代も"),					s(40, 32)); // F, T
assertEq(tByteSize("千早ぶる神代もき"),					s(40, 40)); // F, F
assertEq(tByteSize("千早ぶる神代もきかず龍"),				s(40, 40)); // F, F
assertEq(tByteSize("千早ぶる神代もきかず龍田"),    			s(56, 64)); // X, X
assertEq(tByteSize("千早ぶる神代もきかず龍田川 か"),    			s(56, 64)); // X, X
assertEq(tByteSize("千早ぶる神代もきかず龍田川 から"),    			s(72, 80)); // X, X
assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水く"),    	s(72, 80)); // X, X
assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くく"),    	s(88, 96)); // X, X
assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くくるとは"),	s(88, 96)); // X, X

// A Latin-1 rope. This changes size when flattened.
// "In a village of La Mancha, the name of which I have no desire to call to mind"
//   - Miguel de Cervantes, Don Quixote
var fragment8 = "En un lugar de la Mancha, de cuyo nombre no quiero acordarme"; // 60 characters
var rope8 = fragment8;
for (var i = 0; i < 10; i++) // 1024 repetitions
  rope8 = rope8 + rope8;
assertEq(tByteSize(rope8),                                              s(16, 24));
var matches8 = rope8.match(/(de cuyo nombre no quiero acordarme)/);
assertEq(tByteSize(rope8),                                              s(16 + 65536,  24 + 65536));

// Test extensible strings.
//
// Appending another copy of the fragment should yield another rope.
//
// Flatting that should turn the original rope into a dependent string, and
// yield a new linear string, of the some size as the original.
rope8a = rope8 + fragment8;
assertEq(tByteSize(rope8a),                                             s(16, 24));
rope8a.match(/x/, function() { assertEq(true, false); });
assertEq(tByteSize(rope8a),                                             s(16 + 65536,  24 + 65536));
assertEq(tByteSize(rope8),                                              s(16, 24));


// A char16_t rope. This changes size when flattened.
// "From the Heliconian Muses let us begin to sing"
//   --- Hesiod, Theogony
var fragment16 = "μουσάων Ἑλικωνιάδων ἀρχώμεθ᾽ ἀείδειν";
var rope16 = fragment16;
for (var i = 0; i < 10; i++) // 1024 repetitions
  rope16 = rope16 + rope16;
assertEq(tByteSize(rope16),                                     s(16,  24));
let matches16 = rope16.match(/(Ἑλικωνιάδων ἀρχώμεθ᾽)/);
assertEq(tByteSize(rope16),                                     s(16 + 131072,  24 + 131072));

// Latin-1 and char16_t dependent strings.
assertEq(tByteSize(rope8.substr(1000, 2000)),                   s(16,  24));
assertEq(tByteSize(rope16.substr(1000, 2000)),                  s(16,  24));
assertEq(tByteSize(matches8[0]),                                s(16,  24));
assertEq(tByteSize(matches8[1]),                                s(16,  24));
assertEq(tByteSize(matches16[0]),                               s(16,  24));
assertEq(tByteSize(matches16[1]),                               s(16,  24));

// Test extensible strings.
//
// Appending another copy of the fragment should yield another rope.
//
// Flatting that should turn the original rope into a dependent string, and
// yield a new linear string, of the some size as the original.
rope16a = rope16 + fragment16;
assertEq(tByteSize(rope16a),                                    s(16, 24));
rope16a.match(/x/, function() { assertEq(true, false); });
assertEq(tByteSize(rope16a),                                    s(16 + 131072,  24 + 131072));
assertEq(tByteSize(rope16),                                     s(16, 24));