Mercurial > hg > graal-compiler
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/RubyBasicObject.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +import java.util.*; +import java.util.Map.Entry; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.lookup.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * Represents the Ruby {@code BasicObject} class - the root of the Ruby class hierarchy. + */ +public class RubyBasicObject { + + @CompilationFinal protected RubyClass rubyClass; + protected RubyClass rubySingletonClass; + + protected LookupNode lookupNode; + + protected long objectID = -1; + + public boolean hasPrivateLayout = false; + private ObjectLayout objectLayout; + + public static final int PRIMITIVE_STORAGE_LOCATIONS_COUNT = 14; + protected int primitiveStorageLocation01; + protected int primitiveStorageLocation02; + protected int primitiveStorageLocation03; + protected int primitiveStorageLocation04; + protected int primitiveStorageLocation05; + protected int primitiveStorageLocation06; + protected int primitiveStorageLocation07; + protected int primitiveStorageLocation08; + protected int primitiveStorageLocation09; + protected int primitiveStorageLocation10; + protected int primitiveStorageLocation11; + protected int primitiveStorageLocation12; + protected int primitiveStorageLocation13; + protected int primitiveStorageLocation14; + + // A bit map to indicate which primitives are set, so that they can be Nil + protected int primitiveSetMap; + + protected Object[] objectStorageLocations; + + public RubyBasicObject(RubyClass rubyClass) { + if (rubyClass != null) { + unsafeSetRubyClass(rubyClass); + + if (rubyClass.getContext().getConfiguration().getFullObjectSpace()) { + rubyClass.getContext().getObjectSpaceManager().add(this); + } + } + } + + public void initialize() { + } + + public LookupNode getLookupNode() { + return lookupNode; + } + + public RubyClass getRubyClass() { + assert rubyClass != null; + return rubyClass; + } + + public boolean hasPrivateLayout() { + return hasPrivateLayout; + } + + public ObjectLayout getObjectLayout() { + return objectLayout; + } + + public ObjectLayout getUpdatedObjectLayout() { + updateLayout(); + return objectLayout; + } + + /** + * Does this object have an instance variable defined? + */ + public boolean isInstanceVariableDefined(String name) { + if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) { + updateLayout(); + } + + return objectLayout.findStorageLocation(name) != null; + } + + /** + * Set an instance variable to be a value. Slow path. + */ + public void setInstanceVariable(String name, Object value) { + CompilerAsserts.neverPartOfCompilation(); + + // If the object's layout doesn't match the class, update + + if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) { + updateLayout(); + } + + // Find the storage location + + StorageLocation storageLocation = objectLayout.findStorageLocation(name); + + if (storageLocation == null) { + /* + * It doesn't exist, so create a new layout for the class that includes it and update + * the layout of this object. + */ + + rubyClass.setObjectLayoutForInstances(rubyClass.getObjectLayoutForInstances().withNewVariable(rubyClass.getContext(), name, value.getClass())); + updateLayout(); + + storageLocation = objectLayout.findStorageLocation(name); + } + + // Try to write to that storage location + + try { + storageLocation.write(this, value); + } catch (GeneralizeStorageLocationException e) { + /* + * It might not be able to store the type that we passed, if not generalize the class's + * layout and update the layout of this object. + */ + + rubyClass.setObjectLayoutForInstances(rubyClass.getObjectLayoutForInstances().withGeneralisedVariable(rubyClass.getContext(), name)); + updateLayout(); + + storageLocation = objectLayout.findStorageLocation(name); + + // Try to write to the generalized storage location + + try { + storageLocation.write(this, value); + } catch (GeneralizeStorageLocationException e1) { + // We know that we just generalized it, so this should not happen + throw new RuntimeException("Generalised an instance variable, but it still rejected the value"); + } + } + } + + /** + * Get the value of an instance variable, or Nil if it isn't defined. Slow path. + */ + public Object getInstanceVariable(String name) { + CompilerAsserts.neverPartOfCompilation(); + + // If the object's layout doesn't match the class, update + + if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) { + updateLayout(); + } + + // Find the storage location + + final StorageLocation storageLocation = objectLayout.findStorageLocation(name); + + // Get the value + + if (storageLocation == null) { + return NilPlaceholder.INSTANCE; + } + + return storageLocation.read(this, true); + } + + public String[] getInstanceVariableNames() { + final Set<String> instanceVariableNames = getInstanceVariables().keySet(); + return instanceVariableNames.toArray(new String[instanceVariableNames.size()]); + } + + public RubyClass getSingletonClass() { + if (rubySingletonClass == null) { + /* + * The object a of class A has a singleton class a' of class Class, with name + * #<Class:#<A:objectid>>, and with superclass that is A. + * + * irb(main):001:0> class A; end + * + * => nil + * + * irb(main):002:0> a = A.new + * + * => #<A:0x007ff612a631e0> + * + * irb(main):003:0> a.singleton_class + * + * => #<Class:#<A:0x007ff612a631e0>> + * + * irb(main):004:0> a.singleton_class.class + * + * => Class + * + * irb(main):005:0> a.singleton_class.superclass + * + * => A + */ + + rubySingletonClass = new RubyClass(rubyClass.getParentModule(), rubyClass, String.format("#<Class:#<%s:%d>>", rubyClass.getName(), getObjectID()), true); + + lookupNode = new LookupFork(rubySingletonClass, rubyClass); + } + + return rubySingletonClass; + } + + public long getObjectID() { + if (objectID == -1) { + objectID = rubyClass.getContext().getNextObjectID(); + } + + return objectID; + } + + public String inspect() { + return toString(); + } + + /** + * Get a map of all instance variables. + */ + protected Map<String, Object> getInstanceVariables() { + if (objectLayout == null) { + return Collections.emptyMap(); + } + + final Map<String, Object> instanceVariableMap = new HashMap<>(); + + for (Entry<String, StorageLocation> entry : objectLayout.getAllStorageLocations().entrySet()) { + final String name = entry.getKey(); + final StorageLocation storageLocation = entry.getValue(); + + if (storageLocation.isSet(this)) { + instanceVariableMap.put(name, storageLocation.read(this, true)); + } + } + + return instanceVariableMap; + } + + /** + * Set instance variables from a map. + */ + protected void setInstanceVariables(Map<String, Object> instanceVariables) { + assert instanceVariables != null; + + if (objectLayout == null) { + updateLayout(); + } + + for (Entry<String, Object> entry : instanceVariables.entrySet()) { + final StorageLocation storageLocation = objectLayout.findStorageLocation(entry.getKey()); + assert storageLocation != null; + + try { + storageLocation.write(this, entry.getValue()); + } catch (GeneralizeStorageLocationException e) { + throw new RuntimeException("Should not have to be generalising when setting instance variables - " + entry.getValue().getClass().getName() + ", " + + storageLocation.getStoredClass().getName()); + } + } + } + + /** + * Update the layout of this object to match that of its class. + */ + @CompilerDirectives.SlowPath + public void updateLayout() { + // Get the current values of instance variables + + final Map<String, Object> instanceVariableMap = getInstanceVariables(); + + // Use the layout of the class + + objectLayout = rubyClass.getObjectLayoutForInstances(); + + // Make all primitives as unset + + primitiveSetMap = 0; + + // Create a new array for objects + + allocateObjectStorageLocations(); + + // Restore values + + setInstanceVariables(instanceVariableMap); + } + + private void allocateObjectStorageLocations() { + final int objectStorageLocationsUsed = objectLayout.getObjectStorageLocationsUsed(); + + if (objectStorageLocationsUsed == 0) { + objectStorageLocations = null; + } else { + objectStorageLocations = new Object[objectStorageLocationsUsed]; + } + } + + public void switchToPrivateLayout() { + final RubyContext context = getRubyClass().getContext(); + + final Map<String, Object> instanceVariables = getInstanceVariables(); + + hasPrivateLayout = true; + objectLayout = ObjectLayout.EMPTY; + + for (Entry<String, Object> entry : instanceVariables.entrySet()) { + objectLayout = objectLayout.withNewVariable(context, entry.getKey(), entry.getValue().getClass()); + } + + setInstanceVariables(instanceVariables); + } + + public void extend(RubyModule module) { + getSingletonClass().include(module); + } + + @Override + public String toString() { + return "#<" + rubyClass.getName() + ":0x" + Long.toHexString(getObjectID()) + ">"; + } + + public boolean hasSingletonClass() { + return rubySingletonClass != null; + } + + public Object send(String name, RubyProc block, Object... args) { + final RubyMethod method = getLookupNode().lookupMethod(name); + + if (method == null || method.isUndefined()) { + throw new RaiseException(getRubyClass().getContext().getCoreLibrary().noMethodError(name, toString())); + } + + return method.call(null, this, block, args); + } + + public void unsafeSetRubyClass(RubyClass newRubyClass) { + assert rubyClass == null; + + rubyClass = newRubyClass; + lookupNode = rubyClass; + } + +}