Mercurial > hg > graal-compiler
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 } |