Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 1 | // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 | // Redistribution and use in source and binary forms, with or without |
| 3 | // modification, are permitted provided that the following conditions are |
| 4 | // met: |
| 5 | // |
| 6 | // * Redistributions of source code must retain the above copyright |
| 7 | // notice, this list of conditions and the following disclaimer. |
| 8 | // * Redistributions in binary form must reproduce the above |
| 9 | // copyright notice, this list of conditions and the following |
| 10 | // disclaimer in the documentation and/or other materials provided |
| 11 | // with the distribution. |
| 12 | // * Neither the name of Google Inc. nor the names of its |
| 13 | // contributors may be used to endorse or promote products derived |
| 14 | // from this software without specific prior written permission. |
| 15 | // |
| 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | |
| 28 | // Flags: --track-fields --track-double-fields --allow-natives-syntax |
| 29 | |
| 30 | // Test transitions caused by changes to field representations. |
| 31 | |
| 32 | function create_smi_object() { |
| 33 | var o = {}; |
| 34 | o.x = 1; |
| 35 | o.y = 2; |
| 36 | o.z = 3; |
| 37 | return o; |
| 38 | } |
| 39 | |
| 40 | var o1 = create_smi_object(); |
| 41 | var o2 = create_smi_object(); |
| 42 | |
| 43 | // o1,o2 are smi, smi, smi |
| 44 | assertTrue(%HaveSameMap(o1, o2)); |
| 45 | o1.y = 1.3; |
| 46 | // o1 is smi, double, smi |
| 47 | assertFalse(%HaveSameMap(o1, o2)); |
| 48 | o2.y = 1.5; |
| 49 | // o2 is smi, double, smi |
| 50 | assertTrue(%HaveSameMap(o1, o2)); |
| 51 | |
| 52 | // o3 is initialized as smi, double, smi |
| 53 | var o3 = create_smi_object(); |
| 54 | assertTrue(%HaveSameMap(o1, o3)); |
| 55 | |
| 56 | function set_large(o, v) { |
| 57 | o.x01 = v; o.x02 = v; o.x03 = v; o.x04 = v; o.x05 = v; o.x06 = v; o.x07 = v; |
| 58 | o.x08 = v; o.x09 = v; o.x10 = v; o.x11 = v; o.x12 = v; o.x13 = v; o.x14 = v; |
| 59 | o.x15 = v; o.x16 = v; o.x17 = v; o.x18 = v; o.x19 = v; o.x20 = v; o.x21 = v; |
| 60 | o.x22 = v; o.x23 = v; o.x24 = v; o.x25 = v; o.x26 = v; o.x27 = v; o.x28 = v; |
| 61 | o.x29 = v; o.x30 = v; o.x31 = v; o.x32 = v; o.x33 = v; o.x34 = v; o.x35 = v; |
| 62 | o.x36 = v; o.x37 = v; o.x38 = v; o.x39 = v; o.x40 = v; o.x41 = v; o.x42 = v; |
| 63 | o.y01 = v; o.y02 = v; o.y03 = v; o.y04 = v; o.y05 = v; o.y06 = v; o.y07 = v; |
| 64 | o.y08 = v; o.y09 = v; o.y10 = v; o.y11 = v; o.y12 = v; o.y13 = v; o.y14 = v; |
| 65 | o.y15 = v; o.y16 = v; o.y17 = v; o.y18 = v; o.y19 = v; o.y20 = v; o.y21 = v; |
| 66 | } |
| 67 | |
| 68 | // Check that large object migrations work. |
| 69 | var o4 = {}; |
| 70 | // All smi. |
| 71 | set_large(o4, 0); |
| 72 | assertTrue(%HasFastProperties(o4)); |
| 73 | // All double. |
| 74 | set_large(o4, 1.5); |
| 75 | // o5 is immediately allocated with doubles. |
| 76 | var o5 = {}; |
| 77 | set_large(o5, 0); |
| 78 | assertTrue(%HaveSameMap(o4, o5)); |
| 79 | |
| 80 | function create_smi_object2() { |
| 81 | var o = {}; |
| 82 | o.a = 1; |
| 83 | o.b = 2; |
| 84 | o.c = 3; |
| 85 | return o; |
| 86 | } |
| 87 | |
| 88 | // All smi |
| 89 | var o6 = create_smi_object2(); |
| 90 | var o7 = create_smi_object2(); |
| 91 | |
| 92 | assertTrue(%HaveSameMap(o6, o7)); |
| 93 | // Smi, double, smi. |
| 94 | o6.b = 1.5; |
| 95 | assertFalse(%HaveSameMap(o6, o7)); |
| 96 | // Smi, double, object. |
| 97 | o7.c = {}; |
| 98 | assertFalse(%HaveSameMap(o6, o7)); |
| 99 | // Smi, double, object. |
| 100 | o6.c = {}; |
| 101 | assertTrue(%HaveSameMap(o6, o7)); |
| 102 | |
| 103 | function poly_load(o, b) { |
| 104 | var v = o.field; |
| 105 | if (b) { |
| 106 | return v + 10; |
| 107 | } |
| 108 | return o; |
| 109 | } |
| 110 | |
| 111 | var of1 = {a:0}; |
| 112 | of1.field = {}; |
| 113 | var of2 = {b:0}; |
| 114 | of2.field = 10; |
| 115 | |
| 116 | poly_load(of1, false); |
| 117 | poly_load(of1, false); |
| 118 | poly_load(of2, true); |
| 119 | %OptimizeFunctionOnNextCall(poly_load); |
| 120 | assertEquals("[object Object]10", poly_load(of1, true)); |
| 121 | |
| 122 | // Ensure small object literals with doubles do not share double storage. |
| 123 | function object_literal() { return {"a":1.5}; } |
| 124 | var o8 = object_literal(); |
| 125 | var o9 = object_literal(); |
| 126 | o8.a = 4.6 |
| 127 | assertEquals(1.5, o9.a); |
| 128 | |
| 129 | // Ensure double storage is not leaked in the case of polymorphic loads. |
| 130 | function load_poly(o) { |
| 131 | return o.a; |
| 132 | } |
| 133 | |
| 134 | var o10 = { "a": 1.6 }; |
| 135 | var o11 = { "b": 1, "a": 1.7 }; |
| 136 | load_poly(o10); |
| 137 | load_poly(o10); |
| 138 | load_poly(o11); |
| 139 | %OptimizeFunctionOnNextCall(load_poly); |
| 140 | var val = load_poly(o10); |
| 141 | o10.a = 19.5; |
| 142 | assertFalse(o10.a == val); |
| 143 | |
| 144 | // Ensure polymorphic loads only go monomorphic when the representations are |
| 145 | // compatible. |
| 146 | |
| 147 | // Check polymorphic load from double + object fields. |
| 148 | function load_mono(o) { |
| 149 | return o.a1; |
| 150 | } |
| 151 | |
| 152 | var object = {"x": 1}; |
| 153 | var o10 = { "a1": 1.6 }; |
| 154 | var o11 = { "a1": object, "b": 1 }; |
| 155 | load_mono(o10); |
| 156 | load_mono(o10); |
| 157 | load_mono(o11); |
| 158 | %OptimizeFunctionOnNextCall(load_mono); |
| 159 | assertEquals(object, load_mono(o11)); |
| 160 | |
| 161 | // Check polymorphic load from smi + object fields. |
| 162 | function load_mono2(o) { |
| 163 | return o.a2; |
| 164 | } |
| 165 | |
| 166 | var o12 = { "a2": 5 }; |
| 167 | var o13 = { "a2": object, "b": 1 }; |
| 168 | load_mono2(o12); |
| 169 | load_mono2(o12); |
| 170 | load_mono2(o13); |
| 171 | %OptimizeFunctionOnNextCall(load_mono2); |
| 172 | assertEquals(object, load_mono2(o13)); |
| 173 | |
| 174 | // Check polymorphic load from double + double fields. |
| 175 | function load_mono3(o) { |
| 176 | return o.a3; |
| 177 | } |
| 178 | |
| 179 | var o14 = { "a3": 1.6 }; |
| 180 | var o15 = { "a3": 1.8, "b": 1 }; |
| 181 | load_mono3(o14); |
| 182 | load_mono3(o14); |
| 183 | load_mono3(o15); |
| 184 | %OptimizeFunctionOnNextCall(load_mono3); |
| 185 | assertEquals(1.6, load_mono3(o14)); |
| 186 | assertEquals(1.8, load_mono3(o15)); |
| 187 | |
| 188 | // Check that JSON parsing respects existing representations. |
| 189 | var o16 = JSON.parse('{"a":1.5}'); |
| 190 | var o17 = JSON.parse('{"a":100}'); |
| 191 | assertTrue(%HaveSameMap(o16, o17)); |
| 192 | var o17_a = o17.a; |
| 193 | assertEquals(100, o17_a); |
| 194 | o17.a = 200; |
| 195 | assertEquals(100, o17_a); |
| 196 | assertEquals(200, o17.a); |
| 197 | |
| 198 | // Ensure normalizing results in ignored representations. |
| 199 | var o18 = {}; |
| 200 | o18.field1 = 100; |
| 201 | o18.field2 = 1; |
| 202 | o18.to_delete = 100; |
| 203 | |
| 204 | var o19 = {}; |
| 205 | o19.field1 = 100; |
| 206 | o19.field2 = 1.6; |
| 207 | o19.to_delete = 100; |
| 208 | |
| 209 | assertFalse(%HaveSameMap(o18, o19)); |
| 210 | |
| 211 | delete o18.to_delete; |
| 212 | delete o19.to_delete; |
| 213 | |
| 214 | assertEquals(1, o18.field2); |
| 215 | assertEquals(1.6, o19.field2); |
| 216 | |
| 217 | // Test megamorphic keyed stub behaviour in combination with representations. |
| 218 | var some_object20 = {"a":1}; |
| 219 | var o20 = {}; |
| 220 | o20.smi = 1; |
| 221 | o20.dbl = 1.5; |
| 222 | o20.obj = some_object20; |
| 223 | |
| 224 | function keyed_load(o, k) { |
| 225 | return o[k]; |
| 226 | } |
| 227 | |
| 228 | function keyed_store(o, k, v) { |
| 229 | return o[k] = v; |
| 230 | } |
| 231 | |
| 232 | var smi20 = keyed_load(o20, "smi"); |
| 233 | var dbl20 = keyed_load(o20, "dbl"); |
| 234 | var obj20 = keyed_load(o20, "obj"); |
| 235 | keyed_load(o20, "smi"); |
| 236 | keyed_load(o20, "dbl"); |
| 237 | keyed_load(o20, "obj"); |
| 238 | keyed_load(o20, "smi"); |
| 239 | keyed_load(o20, "dbl"); |
| 240 | keyed_load(o20, "obj"); |
| 241 | |
| 242 | assertEquals(1, smi20); |
| 243 | assertEquals(1.5, dbl20); |
| 244 | assertEquals(some_object20, obj20); |
| 245 | |
| 246 | keyed_store(o20, "smi", 100); |
| 247 | keyed_store(o20, "dbl", 100); |
| 248 | keyed_store(o20, "obj", 100); |
| 249 | keyed_store(o20, "smi", 100); |
| 250 | keyed_store(o20, "dbl", 100); |
| 251 | keyed_store(o20, "obj", 100); |
| 252 | keyed_store(o20, "smi", 100); |
| 253 | keyed_store(o20, "dbl", 100); |
| 254 | keyed_store(o20, "obj", 100); |
| 255 | |
| 256 | assertEquals(1, smi20); |
| 257 | assertEquals(1.5, dbl20); |
| 258 | assertEquals(some_object20, obj20); |
| 259 | |
| 260 | assertEquals(100, o20.smi); |
| 261 | assertEquals(100, o20.dbl); |
| 262 | assertEquals(100, o20.dbl); |
| 263 | |
| 264 | function attr_mismatch_obj(v, writable) { |
| 265 | var o = {}; |
| 266 | o.some_value = v; |
| 267 | Object.defineProperty(o, "second_value", {value:10, writable:writable}); |
| 268 | return o; |
| 269 | } |
| 270 | |
| 271 | function is_writable(o, p) { |
| 272 | return Object.getOwnPropertyDescriptor(o,p).writable; |
| 273 | } |
| 274 | |
| 275 | var writable = attr_mismatch_obj(10, true); |
| 276 | var non_writable1 = attr_mismatch_obj(10.5, false); |
| 277 | assertTrue(is_writable(writable,"second_value")); |
| 278 | assertFalse(is_writable(non_writable1,"second_value")); |
| 279 | writable.some_value = 20.5; |
| 280 | assertTrue(is_writable(writable,"second_value")); |
| 281 | var non_writable2 = attr_mismatch_obj(10.5, false); |
| 282 | assertTrue(%HaveSameMap(non_writable1, non_writable2)); |
| 283 | |
| 284 | function test_f(v) { |
| 285 | var o = {}; |
| 286 | o.vbf = v; |
| 287 | o.func = test_f; |
| 288 | return o; |
| 289 | } |
| 290 | |
| 291 | function test_fic(o) { |
| 292 | return o.vbf; |
| 293 | } |
| 294 | |
| 295 | var ftest1 = test_f(10); |
| 296 | var ftest2 = test_f(10); |
| 297 | var ftest3 = test_f(10.5); |
| 298 | var ftest4 = test_f(10); |
| 299 | assertFalse(%HaveSameMap(ftest1, ftest3)); |
| 300 | assertTrue(%HaveSameMap(ftest3, ftest4)); |
| 301 | ftest2.func = is_writable; |
| 302 | test_fic(ftest1); |
| 303 | test_fic(ftest2); |
| 304 | test_fic(ftest3); |
| 305 | test_fic(ftest4); |
| 306 | assertTrue(%HaveSameMap(ftest1, ftest3)); |
| 307 | assertTrue(%HaveSameMap(ftest3, ftest4)); |
| 308 | |
| 309 | // Test representations and transition conversions. |
| 310 | function read_first_double(o) { |
| 311 | return o.first_double; |
| 312 | } |
| 313 | var df1 = {}; |
| 314 | df1.first_double=1.6; |
| 315 | read_first_double(df1); |
| 316 | read_first_double(df1); |
| 317 | function some_function1() { return 10; } |
| 318 | var df2 = {}; |
| 319 | df2.first_double = 1.7; |
| 320 | df2.second_function = some_function1; |
| 321 | function some_function2() { return 20; } |
| 322 | var df3 = {}; |
| 323 | df3.first_double = 1.7; |
| 324 | df3.second_function = some_function2; |
| 325 | df1.first_double = 10; |
| 326 | read_first_double(df1); |
| 327 | |
| 328 | // Test boilerplates with computed values. |
| 329 | function none_boilerplate(a) { |
| 330 | return {"a_none":a}; |
| 331 | } |
| 332 | %OptimizeFunctionOnNextCall(none_boilerplate); |
| 333 | var none_double1 = none_boilerplate(1.7); |
| 334 | var none_double2 = none_boilerplate(1.9); |
| 335 | assertTrue(%HaveSameMap(none_double1, none_double2)); |
| 336 | assertEquals(1.7, none_double1.a_none); |
| 337 | assertEquals(1.9, none_double2.a_none); |
| 338 | none_double2.a_none = 3.5; |
| 339 | var none_double1 = none_boilerplate(1.7); |
| 340 | var none_double2 = none_boilerplate(3.5); |
| 341 | |
| 342 | function none_to_smi(a) { |
| 343 | return {"a_smi":a}; |
| 344 | } |
| 345 | |
| 346 | var none_smi1 = none_to_smi(1); |
| 347 | var none_smi2 = none_to_smi(2); |
| 348 | %OptimizeFunctionOnNextCall(none_to_smi); |
| 349 | var none_smi3 = none_to_smi(3); |
| 350 | assertTrue(%HaveSameMap(none_smi1, none_smi2)); |
| 351 | assertTrue(%HaveSameMap(none_smi1, none_smi3)); |
| 352 | assertEquals(1, none_smi1.a_smi); |
| 353 | assertEquals(2, none_smi2.a_smi); |
| 354 | assertEquals(3, none_smi3.a_smi); |
| 355 | |
| 356 | function none_to_double(a) { |
| 357 | return {"a_double":a}; |
| 358 | } |
| 359 | |
| 360 | var none_to_double1 = none_to_double(1.5); |
| 361 | var none_to_double2 = none_to_double(2.8); |
| 362 | %OptimizeFunctionOnNextCall(none_to_double); |
| 363 | var none_to_double3 = none_to_double(3.7); |
| 364 | assertTrue(%HaveSameMap(none_to_double1, none_to_double2)); |
| 365 | assertTrue(%HaveSameMap(none_to_double1, none_to_double3)); |
| 366 | assertEquals(1.5, none_to_double1.a_double); |
| 367 | assertEquals(2.8, none_to_double2.a_double); |
| 368 | assertEquals(3.7, none_to_double3.a_double); |
| 369 | |
| 370 | function none_to_object(a) { |
| 371 | return {"an_object":a}; |
| 372 | } |
| 373 | |
| 374 | var none_to_object1 = none_to_object(true); |
| 375 | var none_to_object2 = none_to_object(false); |
| 376 | %OptimizeFunctionOnNextCall(none_to_object); |
| 377 | var none_to_object3 = none_to_object(3.7); |
| 378 | assertTrue(%HaveSameMap(none_to_object1, none_to_object2)); |
| 379 | assertTrue(%HaveSameMap(none_to_object1, none_to_object3)); |
| 380 | assertEquals(true, none_to_object1.an_object); |
| 381 | assertEquals(false, none_to_object2.an_object); |
| 382 | assertEquals(3.7, none_to_object3.an_object); |
| 383 | |
| 384 | function double_to_object(a) { |
| 385 | var o = {"d_to_h":1.8}; |
| 386 | o.d_to_h = a; |
| 387 | return o; |
| 388 | } |
| 389 | |
| 390 | var dh1 = double_to_object(true); |
| 391 | var dh2 = double_to_object(false); |
| 392 | assertTrue(%HaveSameMap(dh1, dh2)); |
| 393 | assertEquals(true, dh1.d_to_h); |
| 394 | assertEquals(false, dh2.d_to_h); |
| 395 | |
| 396 | function smi_to_object(a) { |
| 397 | var o = {"s_to_t":18}; |
| 398 | o.s_to_t = a; |
| 399 | return o; |
| 400 | } |
| 401 | |
| 402 | var st1 = smi_to_object(true); |
| 403 | var st2 = smi_to_object(false); |
| 404 | assertTrue(%HaveSameMap(st1, st2)); |
| 405 | assertEquals(true, st1.s_to_t); |
| 406 | assertEquals(false, st2.s_to_t); |