13514
|
1 /*
|
|
2 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This
|
|
3 * code is released under a tri EPL/GPL/LGPL license. You can use it,
|
|
4 * redistribute it and/or modify it under the terms of the:
|
|
5 *
|
|
6 * Eclipse Public License version 1.0
|
|
7 * GNU General Public License version 2
|
|
8 * GNU Lesser General Public License version 2.1
|
|
9 */
|
|
10 package com.oracle.truffle.ruby.test.runtime;
|
|
11
|
|
12 import org.junit.*;
|
|
13
|
|
14 import com.oracle.truffle.ruby.runtime.*;
|
|
15 import com.oracle.truffle.ruby.runtime.core.*;
|
|
16 import com.oracle.truffle.ruby.runtime.objects.*;
|
|
17
|
|
18 import static org.junit.Assert.*;
|
|
19
|
|
20 /**
|
|
21 * Test the object layout classes.
|
|
22 */
|
|
23 public class ObjectLayoutTests {
|
|
24
|
|
25 @Test
|
|
26 public void testNewInstanceVariable() {
|
|
27 final RubyContext context = new RubyContext(null);
|
|
28
|
|
29 // Create a class and an instance
|
|
30
|
|
31 final RubyClass classA = new RubyClass(context, null, null, null, "A");
|
|
32 final ObjectLayout layoutClassA = classA.getObjectLayoutForInstances();
|
|
33
|
|
34 final RubyBasicObject objectA = new RubyBasicObject(classA);
|
|
35 final ObjectLayout layoutObjectA = objectA.getObjectLayout();
|
|
36
|
|
37 // Add an instance variable to the instance
|
|
38
|
|
39 objectA.setInstanceVariable("foo", 14);
|
|
40
|
|
41 // That should have changed the layout of the class
|
|
42
|
|
43 assertNotSame(layoutClassA, classA.getObjectLayoutForInstances());
|
|
44
|
|
45 // If we notify the object, it should also change the layout of that
|
|
46
|
|
47 objectA.updateLayout();
|
|
48 assertNotSame(layoutObjectA, objectA.getObjectLayout());
|
|
49
|
|
50 // We should be able to find that instance variable as a storage location in the class
|
|
51
|
|
52 assertNotNull(classA.getObjectLayoutForInstances().findStorageLocation("foo"));
|
|
53
|
|
54 // We should be able to read that value back out
|
|
55
|
|
56 assertEquals(14, objectA.getInstanceVariable("foo"));
|
|
57 }
|
|
58
|
|
59 @Test
|
|
60 public void testOverflowPrimitives() {
|
|
61 final RubyContext context = new RubyContext(null);
|
|
62
|
|
63 // Create a class and an instance
|
|
64
|
|
65 final RubyClass classA = new RubyClass(context, null, null, null, "A");
|
|
66 final RubyBasicObject objectA = new RubyBasicObject(classA);
|
|
67
|
|
68 // Add many more Fixnums that we have space for primitives
|
|
69
|
|
70 final int count = 100;
|
|
71
|
|
72 for (int n = 0; n < count; n++) {
|
|
73 objectA.setInstanceVariable("foo" + n, n);
|
|
74 }
|
|
75
|
|
76 // We should be able to read them back out
|
|
77
|
|
78 for (int n = 0; n < count; n++) {
|
|
79 assertEquals(n, objectA.getInstanceVariable("foo" + n));
|
|
80 }
|
|
81 }
|
|
82
|
|
83 @Test
|
|
84 public void testGeneralisation() {
|
|
85 final RubyContext context = new RubyContext(null);
|
|
86
|
|
87 // Create a class and two instances
|
|
88
|
|
89 final RubyClass classA = new RubyClass(context, null, null, null, "A");
|
|
90 final RubyBasicObject object1 = new RubyBasicObject(classA);
|
|
91 final RubyBasicObject object2 = new RubyBasicObject(classA);
|
|
92
|
|
93 // Set an instance variable to be a Fixnum in object 1
|
|
94
|
|
95 object1.setInstanceVariable("foo", 14);
|
|
96
|
|
97 // We should be able to read that instance variable back, and it should still be a Fixnum
|
|
98
|
|
99 assertEquals(14, object1.getInstanceVariable("foo"));
|
|
100 assertSame(Integer.class, object1.getInstanceVariable("foo").getClass());
|
|
101
|
|
102 // The underlying instance store should be Fixnum
|
|
103
|
|
104 assertSame(FixnumStorageLocation.class, object1.getObjectLayout().findStorageLocation("foo").getClass());
|
|
105
|
|
106 /*
|
|
107 * The same instance variable in object 2 should be Nil. Note that this requires that we
|
|
108 * realise that even though the instance variable is known about in the layout of object 2,
|
|
109 * and we are using a primitive int to hold it, that it hasn't been set and is actually Nil.
|
|
110 * We don't want it to appear as 0.
|
|
111 */
|
|
112
|
|
113 assertSame(NilPlaceholder.INSTANCE, object2.getInstanceVariable("foo"));
|
|
114
|
|
115 /*
|
|
116 * We should be able to set the same instance variable in object 2 to also be a Fixnum
|
|
117 * without changing the layout.
|
|
118 */
|
|
119
|
|
120 final ObjectLayout objectLayout2 = object2.getObjectLayout();
|
|
121 object2.setInstanceVariable("foo", 2);
|
|
122 assertEquals(2, object2.getInstanceVariable("foo"));
|
|
123 assertSame(Integer.class, object2.getInstanceVariable("foo").getClass());
|
|
124 assertSame(objectLayout2, object2.getObjectLayout());
|
|
125
|
|
126 // Set the instance variable in object 2 to be a Float
|
|
127
|
|
128 object2.setInstanceVariable("foo", 2.25);
|
|
129
|
|
130 // We should be able to read that instance variable back, and it should still be a Fixnum
|
|
131
|
|
132 assertEquals(2.25, object2.getInstanceVariable("foo"));
|
|
133 assertSame(Double.class, object2.getInstanceVariable("foo").getClass());
|
|
134
|
|
135 // Object 1 should give still think the instance variable is a Fixnum
|
|
136
|
|
137 assertEquals(14, object1.getInstanceVariable("foo"));
|
|
138 assertSame(Integer.class, object1.getInstanceVariable("foo").getClass());
|
|
139
|
|
140 // The underlying instance store in both objects should now be Object
|
|
141
|
|
142 assertSame(ObjectStorageLocation.class, object1.getObjectLayout().findStorageLocation("foo").getClass());
|
|
143 assertSame(ObjectStorageLocation.class, object2.getObjectLayout().findStorageLocation("foo").getClass());
|
|
144
|
|
145 }
|
|
146
|
|
147 @Test
|
|
148 public void testSubclasses() {
|
|
149 final RubyContext context = new RubyContext(null);
|
|
150
|
|
151 // Create two classes, A, and a subclass, B, and an instance of each
|
|
152
|
|
153 final RubyClass classA = new RubyClass(context, null, null, null, "A");
|
|
154 final RubyClass classB = new RubyClass(context, null, null, classA, "B");
|
|
155
|
|
156 ObjectLayout layoutClassA = classA.getObjectLayoutForInstances();
|
|
157 ObjectLayout layoutClassB = classA.getObjectLayoutForInstances();
|
|
158
|
|
159 final RubyBasicObject objectA = new RubyBasicObject(classA);
|
|
160 final RubyBasicObject objectB = new RubyBasicObject(classB);
|
|
161
|
|
162 ObjectLayout layoutObjectA = objectA.getObjectLayout();
|
|
163 ObjectLayout layoutObjectB = objectB.getObjectLayout();
|
|
164
|
|
165 // Add an instance variable to the instance of A
|
|
166
|
|
167 objectA.setInstanceVariable("foo", 14);
|
|
168
|
|
169 // That should have changed the layout of both classes
|
|
170
|
|
171 assertNotSame(layoutClassA, classA.getObjectLayoutForInstances());
|
|
172 assertNotSame(layoutClassB, classB.getObjectLayoutForInstances());
|
|
173
|
|
174 layoutClassA = classA.getObjectLayoutForInstances();
|
|
175 layoutClassB = classA.getObjectLayoutForInstances();
|
|
176
|
|
177 // If we notify the objects, both of them should have changed layouts
|
|
178
|
|
179 objectA.updateLayout();
|
|
180 objectB.updateLayout();
|
|
181 assertNotSame(layoutObjectA, objectA.getObjectLayout());
|
|
182 assertNotSame(layoutObjectB, objectB.getObjectLayout());
|
|
183
|
|
184 layoutObjectA = objectA.getObjectLayout();
|
|
185 layoutObjectB = objectB.getObjectLayout();
|
|
186
|
|
187 // We should be able to find that instance variable as a storage location in both classes
|
|
188
|
|
189 assertNotNull(classA.getObjectLayoutForInstances().findStorageLocation("foo"));
|
|
190 assertNotNull(classB.getObjectLayoutForInstances().findStorageLocation("foo"));
|
|
191
|
|
192 // We should be able to read that value back out
|
|
193
|
|
194 assertEquals(14, objectA.getInstanceVariable("foo"));
|
|
195
|
|
196 // Add an instance variable to the instance of B
|
|
197
|
|
198 objectB.setInstanceVariable("bar", 2);
|
|
199
|
|
200 // This should not have changed the layout of A or the instance of A
|
|
201
|
|
202 assertSame(layoutClassA, classA.getObjectLayoutForInstances());
|
|
203 assertSame(layoutObjectA, objectA.getObjectLayout());
|
|
204
|
|
205 // But the layout of B and the instance of B should have changed
|
|
206
|
|
207 assertNotSame(layoutClassB, classB.getObjectLayoutForInstances());
|
|
208
|
|
209 objectB.updateLayout();
|
|
210 assertNotSame(layoutObjectB, objectB.getObjectLayout());
|
|
211
|
|
212 // We should be able to find the new instance variable in the instance of B but not A
|
|
213
|
|
214 assertNull(classA.getObjectLayoutForInstances().findStorageLocation("bar"));
|
|
215 assertNotNull(classB.getObjectLayoutForInstances().findStorageLocation("bar"));
|
|
216
|
|
217 // We should be able to read that value back out
|
|
218
|
|
219 assertEquals(2, objectB.getInstanceVariable("bar"));
|
|
220 }
|
|
221
|
|
222 @Test
|
|
223 public void testPerObjectInstanceVariables() {
|
|
224 final RubyContext context = new RubyContext(null);
|
|
225
|
|
226 // Create a class and an instance
|
|
227
|
|
228 final RubyClass classA = new RubyClass(context, context.getCoreLibrary().getClassClass(), null, null, "A");
|
|
229 final RubyBasicObject objectA = new RubyBasicObject(classA);
|
|
230
|
|
231 ObjectLayout layoutClassA = classA.getObjectLayoutForInstances();
|
|
232 ObjectLayout layoutObjectA = objectA.getObjectLayout();
|
|
233
|
|
234 // Add an instance variable to the instance of A
|
|
235
|
|
236 objectA.setInstanceVariable("foo", 2);
|
|
237
|
|
238 // That should have changed the layout of the class and the object
|
|
239
|
|
240 assertNotSame(layoutClassA, classA.getObjectLayoutForInstances());
|
|
241 assertNotSame(layoutObjectA, objectA.getObjectLayout());
|
|
242 layoutClassA = classA.getObjectLayoutForInstances();
|
|
243 layoutObjectA = classA.getObjectLayout();
|
|
244
|
|
245 // We should be able to read the value back out
|
|
246
|
|
247 assertEquals(2, objectA.getInstanceVariable("foo"));
|
|
248
|
|
249 /*
|
|
250 * Switch object A to having a private object layout, as would be done by calls such as
|
|
251 * instance_variable_set.
|
|
252 */
|
|
253
|
|
254 objectA.switchToPrivateLayout();
|
|
255
|
|
256 // Set an instance variable on object A
|
|
257
|
|
258 objectA.setInstanceVariable("bar", 14);
|
|
259
|
|
260 // The layout of object A, however, should have changed
|
|
261
|
|
262 // CS: it hasn't changed because it's still null
|
|
263 // assertNotSame(layoutObjectA, objectA.getObjectLayout());
|
|
264
|
|
265 // We should be able to read the value back out
|
|
266
|
|
267 assertEquals(14, objectA.getInstanceVariable("bar"));
|
|
268
|
|
269 /*
|
|
270 * We should also be able to read the first variable back out, even though we've switched to
|
|
271 * private layout since then.
|
|
272 */
|
|
273
|
|
274 assertEquals(2, objectA.getInstanceVariable("foo"));
|
|
275 }
|
|
276
|
|
277 }
|