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
32function create_smi_object() {
33  var o = {};
34  o.x = 1;
35  o.y = 2;
36  o.z = 3;
37  return o;
38}
39
40var o1 = create_smi_object();
41var o2 = create_smi_object();
42
43// o1,o2 are smi, smi, smi
44assertTrue(%HaveSameMap(o1, o2));
45o1.y = 1.3;
46// o1 is smi, double, smi
47assertFalse(%HaveSameMap(o1, o2));
48o2.y = 1.5;
49// o2 is smi, double, smi
50assertTrue(%HaveSameMap(o1, o2));
51
52// o3 is initialized as smi, double, smi
53var o3 = create_smi_object();
54assertTrue(%HaveSameMap(o1, o3));
55
56function 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.
69var o4 = {};
70// All smi.
71set_large(o4, 0);
72assertTrue(%HasFastProperties(o4));
73// All double.
74set_large(o4, 1.5);
75// o5 is immediately allocated with doubles.
76var o5 = {};
77set_large(o5, 0);
78assertTrue(%HaveSameMap(o4, o5));
79
80function 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
89var o6 = create_smi_object2();
90var o7 = create_smi_object2();
91
92assertTrue(%HaveSameMap(o6, o7));
93// Smi, double, smi.
94o6.b = 1.5;
95assertFalse(%HaveSameMap(o6, o7));
96// Smi, double, object.
97o7.c = {};
98assertFalse(%HaveSameMap(o6, o7));
99// Smi, double, object.
100o6.c = {};
101assertTrue(%HaveSameMap(o6, o7));
102
103function poly_load(o, b) {
104  var v = o.field;
105  if (b) {
106    return v + 10;
107  }
108  return o;
109}
110
111var of1 = {a:0};
112of1.field = {};
113var of2 = {b:0};
114of2.field = 10;
115
116poly_load(of1, false);
117poly_load(of1, false);
118poly_load(of2, true);
119%OptimizeFunctionOnNextCall(poly_load);
120assertEquals("[object Object]10", poly_load(of1, true));
121
122// Ensure small object literals with doubles do not share double storage.
123function object_literal() { return {"a":1.5}; }
124var o8 = object_literal();
125var o9 = object_literal();
126o8.a = 4.6
127assertEquals(1.5, o9.a);
128
129// Ensure double storage is not leaked in the case of polymorphic loads.
130function load_poly(o) {
131  return o.a;
132}
133
134var o10 = { "a": 1.6 };
135var o11 = { "b": 1, "a": 1.7 };
136load_poly(o10);
137load_poly(o10);
138load_poly(o11);
139%OptimizeFunctionOnNextCall(load_poly);
140var val = load_poly(o10);
141o10.a = 19.5;
142assertFalse(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.
148function load_mono(o) {
149  return o.a1;
150}
151
152var object = {"x": 1};
153var o10 = { "a1": 1.6 };
154var o11 = { "a1": object, "b": 1 };
155load_mono(o10);
156load_mono(o10);
157load_mono(o11);
158%OptimizeFunctionOnNextCall(load_mono);
159assertEquals(object, load_mono(o11));
160
161// Check polymorphic load from smi + object fields.
162function load_mono2(o) {
163  return o.a2;
164}
165
166var o12 = { "a2": 5 };
167var o13 = { "a2": object, "b": 1 };
168load_mono2(o12);
169load_mono2(o12);
170load_mono2(o13);
171%OptimizeFunctionOnNextCall(load_mono2);
172assertEquals(object, load_mono2(o13));
173
174// Check polymorphic load from double + double fields.
175function load_mono3(o) {
176  return o.a3;
177}
178
179var o14 = { "a3": 1.6 };
180var o15 = { "a3": 1.8, "b": 1 };
181load_mono3(o14);
182load_mono3(o14);
183load_mono3(o15);
184%OptimizeFunctionOnNextCall(load_mono3);
185assertEquals(1.6, load_mono3(o14));
186assertEquals(1.8, load_mono3(o15));
187
188// Check that JSON parsing respects existing representations.
189var o16 = JSON.parse('{"a":1.5}');
190var o17 = JSON.parse('{"a":100}');
191assertTrue(%HaveSameMap(o16, o17));
192var o17_a = o17.a;
193assertEquals(100, o17_a);
194o17.a = 200;
195assertEquals(100, o17_a);
196assertEquals(200, o17.a);
197
198// Ensure normalizing results in ignored representations.
199var o18 = {};
200o18.field1 = 100;
201o18.field2 = 1;
202o18.to_delete = 100;
203
204var o19 = {};
205o19.field1 = 100;
206o19.field2 = 1.6;
207o19.to_delete = 100;
208
209assertFalse(%HaveSameMap(o18, o19));
210
211delete o18.to_delete;
212delete o19.to_delete;
213
214assertEquals(1, o18.field2);
215assertEquals(1.6, o19.field2);
216
217// Test megamorphic keyed stub behaviour in combination with representations.
218var some_object20 = {"a":1};
219var o20 = {};
220o20.smi = 1;
221o20.dbl = 1.5;
222o20.obj = some_object20;
223
224function keyed_load(o, k) {
225  return o[k];
226}
227
228function keyed_store(o, k, v) {
229  return o[k] = v;
230}
231
232var smi20 = keyed_load(o20, "smi");
233var dbl20 = keyed_load(o20, "dbl");
234var obj20 = keyed_load(o20, "obj");
235keyed_load(o20, "smi");
236keyed_load(o20, "dbl");
237keyed_load(o20, "obj");
238keyed_load(o20, "smi");
239keyed_load(o20, "dbl");
240keyed_load(o20, "obj");
241
242assertEquals(1, smi20);
243assertEquals(1.5, dbl20);
244assertEquals(some_object20, obj20);
245
246keyed_store(o20, "smi", 100);
247keyed_store(o20, "dbl", 100);
248keyed_store(o20, "obj", 100);
249keyed_store(o20, "smi", 100);
250keyed_store(o20, "dbl", 100);
251keyed_store(o20, "obj", 100);
252keyed_store(o20, "smi", 100);
253keyed_store(o20, "dbl", 100);
254keyed_store(o20, "obj", 100);
255
256assertEquals(1, smi20);
257assertEquals(1.5, dbl20);
258assertEquals(some_object20, obj20);
259
260assertEquals(100, o20.smi);
261assertEquals(100, o20.dbl);
262assertEquals(100, o20.dbl);
263
264function 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
271function is_writable(o, p) {
272  return Object.getOwnPropertyDescriptor(o,p).writable;
273}
274
275var writable = attr_mismatch_obj(10, true);
276var non_writable1 = attr_mismatch_obj(10.5, false);
277assertTrue(is_writable(writable,"second_value"));
278assertFalse(is_writable(non_writable1,"second_value"));
279writable.some_value = 20.5;
280assertTrue(is_writable(writable,"second_value"));
281var non_writable2 = attr_mismatch_obj(10.5, false);
282assertTrue(%HaveSameMap(non_writable1, non_writable2));
283
284function test_f(v) {
285  var o = {};
286  o.vbf = v;
287  o.func = test_f;
288  return o;
289}
290
291function test_fic(o) {
292  return o.vbf;
293}
294
295var ftest1 = test_f(10);
296var ftest2 = test_f(10);
297var ftest3 = test_f(10.5);
298var ftest4 = test_f(10);
299assertFalse(%HaveSameMap(ftest1, ftest3));
300assertTrue(%HaveSameMap(ftest3, ftest4));
301ftest2.func = is_writable;
302test_fic(ftest1);
303test_fic(ftest2);
304test_fic(ftest3);
305test_fic(ftest4);
306assertTrue(%HaveSameMap(ftest1, ftest3));
307assertTrue(%HaveSameMap(ftest3, ftest4));
308
309// Test representations and transition conversions.
310function read_first_double(o) {
311  return o.first_double;
312}
313var df1 = {};
314df1.first_double=1.6;
315read_first_double(df1);
316read_first_double(df1);
317function some_function1() { return 10; }
318var df2 = {};
319df2.first_double = 1.7;
320df2.second_function = some_function1;
321function some_function2() { return 20; }
322var df3 = {};
323df3.first_double = 1.7;
324df3.second_function = some_function2;
325df1.first_double = 10;
326read_first_double(df1);
327
328// Test boilerplates with computed values.
329function none_boilerplate(a) {
330  return {"a_none":a};
331}
332%OptimizeFunctionOnNextCall(none_boilerplate);
333var none_double1 = none_boilerplate(1.7);
334var none_double2 = none_boilerplate(1.9);
335assertTrue(%HaveSameMap(none_double1, none_double2));
336assertEquals(1.7, none_double1.a_none);
337assertEquals(1.9, none_double2.a_none);
338none_double2.a_none = 3.5;
339var none_double1 = none_boilerplate(1.7);
340var none_double2 = none_boilerplate(3.5);
341
342function none_to_smi(a) {
343  return {"a_smi":a};
344}
345
346var none_smi1 = none_to_smi(1);
347var none_smi2 = none_to_smi(2);
348%OptimizeFunctionOnNextCall(none_to_smi);
349var none_smi3 = none_to_smi(3);
350assertTrue(%HaveSameMap(none_smi1, none_smi2));
351assertTrue(%HaveSameMap(none_smi1, none_smi3));
352assertEquals(1, none_smi1.a_smi);
353assertEquals(2, none_smi2.a_smi);
354assertEquals(3, none_smi3.a_smi);
355
356function none_to_double(a) {
357  return {"a_double":a};
358}
359
360var none_to_double1 = none_to_double(1.5);
361var none_to_double2 = none_to_double(2.8);
362%OptimizeFunctionOnNextCall(none_to_double);
363var none_to_double3 = none_to_double(3.7);
364assertTrue(%HaveSameMap(none_to_double1, none_to_double2));
365assertTrue(%HaveSameMap(none_to_double1, none_to_double3));
366assertEquals(1.5, none_to_double1.a_double);
367assertEquals(2.8, none_to_double2.a_double);
368assertEquals(3.7, none_to_double3.a_double);
369
370function none_to_object(a) {
371  return {"an_object":a};
372}
373
374var none_to_object1 = none_to_object(true);
375var none_to_object2 = none_to_object(false);
376%OptimizeFunctionOnNextCall(none_to_object);
377var none_to_object3 = none_to_object(3.7);
378assertTrue(%HaveSameMap(none_to_object1, none_to_object2));
379assertTrue(%HaveSameMap(none_to_object1, none_to_object3));
380assertEquals(true, none_to_object1.an_object);
381assertEquals(false, none_to_object2.an_object);
382assertEquals(3.7, none_to_object3.an_object);
383
384function double_to_object(a) {
385  var o = {"d_to_h":1.8};
386  o.d_to_h = a;
387  return o;
388}
389
390var dh1 = double_to_object(true);
391var dh2 = double_to_object(false);
392assertTrue(%HaveSameMap(dh1, dh2));
393assertEquals(true, dh1.d_to_h);
394assertEquals(false, dh2.d_to_h);
395
396function smi_to_object(a) {
397  var o = {"s_to_t":18};
398  o.s_to_t = a;
399  return o;
400}
401
402var st1 = smi_to_object(true);
403var st2 = smi_to_object(false);
404assertTrue(%HaveSameMap(st1, st2));
405assertEquals(true, st1.s_to_t);
406assertEquals(false, st2.s_to_t);
407