comparison graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/RubyBasicObject.java @ 13514:0fbee3eb71f0

Ruby: import project.
author Chris Seaton <chris.seaton@oracle.com>
date Mon, 06 Jan 2014 17:12:09 +0000
parents
children 50c11b9a7fdf
comparison
equal deleted inserted replaced
13513:64a23ce736a0 13514:0fbee3eb71f0
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.runtime.objects;
11
12 import java.util.*;
13 import java.util.Map.Entry;
14
15 import com.oracle.truffle.api.*;
16 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
17 import com.oracle.truffle.ruby.runtime.*;
18 import com.oracle.truffle.ruby.runtime.control.*;
19 import com.oracle.truffle.ruby.runtime.core.*;
20 import com.oracle.truffle.ruby.runtime.lookup.*;
21 import com.oracle.truffle.ruby.runtime.methods.*;
22
23 /**
24 * Represents the Ruby {@code BasicObject} class - the root of the Ruby class hierarchy.
25 */
26 public class RubyBasicObject {
27
28 @CompilationFinal protected RubyClass rubyClass;
29 protected RubyClass rubySingletonClass;
30
31 protected LookupNode lookupNode;
32
33 protected long objectID = -1;
34
35 public boolean hasPrivateLayout = false;
36 private ObjectLayout objectLayout;
37
38 public static final int PRIMITIVE_STORAGE_LOCATIONS_COUNT = 14;
39 protected int primitiveStorageLocation01;
40 protected int primitiveStorageLocation02;
41 protected int primitiveStorageLocation03;
42 protected int primitiveStorageLocation04;
43 protected int primitiveStorageLocation05;
44 protected int primitiveStorageLocation06;
45 protected int primitiveStorageLocation07;
46 protected int primitiveStorageLocation08;
47 protected int primitiveStorageLocation09;
48 protected int primitiveStorageLocation10;
49 protected int primitiveStorageLocation11;
50 protected int primitiveStorageLocation12;
51 protected int primitiveStorageLocation13;
52 protected int primitiveStorageLocation14;
53
54 // A bit map to indicate which primitives are set, so that they can be Nil
55 protected int primitiveSetMap;
56
57 protected Object[] objectStorageLocations;
58
59 public RubyBasicObject(RubyClass rubyClass) {
60 if (rubyClass != null) {
61 unsafeSetRubyClass(rubyClass);
62
63 if (rubyClass.getContext().getConfiguration().getFullObjectSpace()) {
64 rubyClass.getContext().getObjectSpaceManager().add(this);
65 }
66 }
67 }
68
69 public void initialize() {
70 }
71
72 public LookupNode getLookupNode() {
73 return lookupNode;
74 }
75
76 public RubyClass getRubyClass() {
77 assert rubyClass != null;
78 return rubyClass;
79 }
80
81 public boolean hasPrivateLayout() {
82 return hasPrivateLayout;
83 }
84
85 public ObjectLayout getObjectLayout() {
86 return objectLayout;
87 }
88
89 public ObjectLayout getUpdatedObjectLayout() {
90 updateLayout();
91 return objectLayout;
92 }
93
94 /**
95 * Does this object have an instance variable defined?
96 */
97 public boolean isInstanceVariableDefined(String name) {
98 if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) {
99 updateLayout();
100 }
101
102 return objectLayout.findStorageLocation(name) != null;
103 }
104
105 /**
106 * Set an instance variable to be a value. Slow path.
107 */
108 public void setInstanceVariable(String name, Object value) {
109 CompilerAsserts.neverPartOfCompilation();
110
111 // If the object's layout doesn't match the class, update
112
113 if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) {
114 updateLayout();
115 }
116
117 // Find the storage location
118
119 StorageLocation storageLocation = objectLayout.findStorageLocation(name);
120
121 if (storageLocation == null) {
122 /*
123 * It doesn't exist, so create a new layout for the class that includes it and update
124 * the layout of this object.
125 */
126
127 rubyClass.setObjectLayoutForInstances(rubyClass.getObjectLayoutForInstances().withNewVariable(rubyClass.getContext(), name, value.getClass()));
128 updateLayout();
129
130 storageLocation = objectLayout.findStorageLocation(name);
131 }
132
133 // Try to write to that storage location
134
135 try {
136 storageLocation.write(this, value);
137 } catch (GeneralizeStorageLocationException e) {
138 /*
139 * It might not be able to store the type that we passed, if not generalize the class's
140 * layout and update the layout of this object.
141 */
142
143 rubyClass.setObjectLayoutForInstances(rubyClass.getObjectLayoutForInstances().withGeneralisedVariable(rubyClass.getContext(), name));
144 updateLayout();
145
146 storageLocation = objectLayout.findStorageLocation(name);
147
148 // Try to write to the generalized storage location
149
150 try {
151 storageLocation.write(this, value);
152 } catch (GeneralizeStorageLocationException e1) {
153 // We know that we just generalized it, so this should not happen
154 throw new RuntimeException("Generalised an instance variable, but it still rejected the value");
155 }
156 }
157 }
158
159 /**
160 * Get the value of an instance variable, or Nil if it isn't defined. Slow path.
161 */
162 public Object getInstanceVariable(String name) {
163 CompilerAsserts.neverPartOfCompilation();
164
165 // If the object's layout doesn't match the class, update
166
167 if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) {
168 updateLayout();
169 }
170
171 // Find the storage location
172
173 final StorageLocation storageLocation = objectLayout.findStorageLocation(name);
174
175 // Get the value
176
177 if (storageLocation == null) {
178 return NilPlaceholder.INSTANCE;
179 }
180
181 return storageLocation.read(this, true);
182 }
183
184 public String[] getInstanceVariableNames() {
185 final Set<String> instanceVariableNames = getInstanceVariables().keySet();
186 return instanceVariableNames.toArray(new String[instanceVariableNames.size()]);
187 }
188
189 public RubyClass getSingletonClass() {
190 if (rubySingletonClass == null) {
191 /*
192 * The object a of class A has a singleton class a' of class Class, with name
193 * #<Class:#<A:objectid>>, and with superclass that is A.
194 *
195 * irb(main):001:0> class A; end
196 *
197 * => nil
198 *
199 * irb(main):002:0> a = A.new
200 *
201 * => #<A:0x007ff612a631e0>
202 *
203 * irb(main):003:0> a.singleton_class
204 *
205 * => #<Class:#<A:0x007ff612a631e0>>
206 *
207 * irb(main):004:0> a.singleton_class.class
208 *
209 * => Class
210 *
211 * irb(main):005:0> a.singleton_class.superclass
212 *
213 * => A
214 */
215
216 rubySingletonClass = new RubyClass(rubyClass.getParentModule(), rubyClass, String.format("#<Class:#<%s:%d>>", rubyClass.getName(), getObjectID()), true);
217
218 lookupNode = new LookupFork(rubySingletonClass, rubyClass);
219 }
220
221 return rubySingletonClass;
222 }
223
224 public long getObjectID() {
225 if (objectID == -1) {
226 objectID = rubyClass.getContext().getNextObjectID();
227 }
228
229 return objectID;
230 }
231
232 public String inspect() {
233 return toString();
234 }
235
236 /**
237 * Get a map of all instance variables.
238 */
239 protected Map<String, Object> getInstanceVariables() {
240 if (objectLayout == null) {
241 return Collections.emptyMap();
242 }
243
244 final Map<String, Object> instanceVariableMap = new HashMap<>();
245
246 for (Entry<String, StorageLocation> entry : objectLayout.getAllStorageLocations().entrySet()) {
247 final String name = entry.getKey();
248 final StorageLocation storageLocation = entry.getValue();
249
250 if (storageLocation.isSet(this)) {
251 instanceVariableMap.put(name, storageLocation.read(this, true));
252 }
253 }
254
255 return instanceVariableMap;
256 }
257
258 /**
259 * Set instance variables from a map.
260 */
261 protected void setInstanceVariables(Map<String, Object> instanceVariables) {
262 assert instanceVariables != null;
263
264 if (objectLayout == null) {
265 updateLayout();
266 }
267
268 for (Entry<String, Object> entry : instanceVariables.entrySet()) {
269 final StorageLocation storageLocation = objectLayout.findStorageLocation(entry.getKey());
270 assert storageLocation != null;
271
272 try {
273 storageLocation.write(this, entry.getValue());
274 } catch (GeneralizeStorageLocationException e) {
275 throw new RuntimeException("Should not have to be generalising when setting instance variables - " + entry.getValue().getClass().getName() + ", " +
276 storageLocation.getStoredClass().getName());
277 }
278 }
279 }
280
281 /**
282 * Update the layout of this object to match that of its class.
283 */
284 @CompilerDirectives.SlowPath
285 public void updateLayout() {
286 // Get the current values of instance variables
287
288 final Map<String, Object> instanceVariableMap = getInstanceVariables();
289
290 // Use the layout of the class
291
292 objectLayout = rubyClass.getObjectLayoutForInstances();
293
294 // Make all primitives as unset
295
296 primitiveSetMap = 0;
297
298 // Create a new array for objects
299
300 allocateObjectStorageLocations();
301
302 // Restore values
303
304 setInstanceVariables(instanceVariableMap);
305 }
306
307 private void allocateObjectStorageLocations() {
308 final int objectStorageLocationsUsed = objectLayout.getObjectStorageLocationsUsed();
309
310 if (objectStorageLocationsUsed == 0) {
311 objectStorageLocations = null;
312 } else {
313 objectStorageLocations = new Object[objectStorageLocationsUsed];
314 }
315 }
316
317 public void switchToPrivateLayout() {
318 final RubyContext context = getRubyClass().getContext();
319
320 final Map<String, Object> instanceVariables = getInstanceVariables();
321
322 hasPrivateLayout = true;
323 objectLayout = ObjectLayout.EMPTY;
324
325 for (Entry<String, Object> entry : instanceVariables.entrySet()) {
326 objectLayout = objectLayout.withNewVariable(context, entry.getKey(), entry.getValue().getClass());
327 }
328
329 setInstanceVariables(instanceVariables);
330 }
331
332 public void extend(RubyModule module) {
333 getSingletonClass().include(module);
334 }
335
336 @Override
337 public String toString() {
338 return "#<" + rubyClass.getName() + ":0x" + Long.toHexString(getObjectID()) + ">";
339 }
340
341 public boolean hasSingletonClass() {
342 return rubySingletonClass != null;
343 }
344
345 public Object send(String name, RubyProc block, Object... args) {
346 final RubyMethod method = getLookupNode().lookupMethod(name);
347
348 if (method == null || method.isUndefined()) {
349 throw new RaiseException(getRubyClass().getContext().getCoreLibrary().noMethodError(name, toString()));
350 }
351
352 return method.call(null, this, block, args);
353 }
354
355 public void unsafeSetRubyClass(RubyClass newRubyClass) {
356 assert rubyClass == null;
357
358 rubyClass = newRubyClass;
359 lookupNode = rubyClass;
360 }
361
362 }