Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   ***** BEGIN LICENSE BLOCK *****
   * Version: EPL 1.0/GPL 2.0/LGPL 2.1
   *
   * The contents of this file are subject to the Eclipse Public
   * License Version 1.0 (the "License"); you may not use this file
   * except in compliance with the License. You may obtain a copy of
   * the License at http://www.eclipse.org/legal/epl-v10.html
   *
  * Software distributed under the License is distributed on an "AS
  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  * implied. See the License for the specific language governing
  * rights and limitations under the License.
  * 
  * Alternatively, the contents of this file may be used under the terms of
  * either of the GNU General Public License Version 2 or later (the "GPL"),
  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the EPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the EPL, the GPL or the LGPL.
  ***** END LICENSE BLOCK *****/
 package org.jruby.runtime.ivars;
 
 import java.util.Map;
 import org.jruby.Ruby;
This class encapculates all logic relating to the management of instance variable tables in RubyBasicObject instances. The logic originally lived in both RubyBasicObject and RubyClass, tightly coupled to each and difficult to follow as it bounced back and forth. We moved the logic here for a couple reasons:
  • To have a single place from which we could follow ivar logic.
  • To make it easier to swap in new implementations of instance variable logic as we work toward reifying ivars into fields.
  • To remove rather noisy logic from RubyBasicObject and RubyClass.
  •  
     public class VariableTableManager {
        
    the "real" class associated with this table
     
         private final RubyClass realClass;
        
        
    an empty array of String
     
         private static String[] EMPTY_STRING_ARRAY = new String[0];
        
        
    a map from strings to accessors for this table
     
    an array of all registered variable names
     
         private volatile String[] variableNames = ;

        
    whether a slot has been allocated to object_id
     
         private volatile boolean hasObjectID = false;
        
        
    whether objects associated with this table use fields
     
         private volatile int fieldVariables = 0;
        
        
    a lazy accessor for object_id
     
         private final VariableAccessorField objectIdVariableAccessorField = new VariableAccessorField("object_id");
        
    a lazy accessor for C ext handle
     
         private final VariableAccessorField cextHandleVariableAccessorField = new VariableAccessorField("cext");
        
    a lazy accessor for FFI handle
     
         private final VariableAccessorField ffiHandleVariableAccessorField = new VariableAccessorField("ffi");
        
    a lazy accessor for object group
     
         private final VariableAccessorField objectGroupVariableAccessorField = new VariableAccessorField("objectspace_group");
        
        
    Construct a new VariableTable Manager for the given "real" class.

    Parameters:
    realClass the "real" class associated with this table
     
         public VariableTableManager(RubyClass realClass) {
             this. = realClass;
         }

        
    Get the map of all current variable accessors with intent to read from it.

    Returns:
    a map of current variable accessors
     
             return ;
         }
        
        
    Whether this table has been used to allocate space for an object_id.

    Returns:
    true if object_id has been allocated; false otherwise
        public boolean hasObjectID() {
            return ;
        }
        
        
    Get the object_id from a given RubyBasicObject, possibly allocating space for it.

    Parameters:
    self the object from which to get object_id
    Returns:
    the object's object_id (possibly new)
        public long getObjectId(RubyBasicObject self) {
            Long id = (Long)objectIdAccessor.get(self);
            if (id != nullreturn id;
            
            synchronized (self) {
                objectIdAccessor = getObjectIdAccessorField().getVariableAccessorForRead();
                id = (Long)objectIdAccessor.get(self);
                if (id != nullreturn id;
                return initObjectId(selfgetObjectIdAccessorField().getVariableAccessorForWrite(this));
            }
        }
        
        
    Virtual entry point for setting a variable into an object.

    Parameters:
    self the object into which to set the value
    index the index allocated for the value
    value the value
        public void setVariableInternal(RubyBasicObject selfint indexObject value) {
            if(. == null) {
                SynchronizedVariableAccessor.setVariable(self,,index,value);
            } else {
                StampedVariableAccessor.setVariable(self,,index,value);
            }
        }
        
        
    Static entry point for setting a variable in an object.

    Parameters:
    realClass the "real" class of the object
    self the object into which to set the variable
    index the index allocated for the variable
    value the value of the variable
        public static void setVariableInternal(RubyClass realClassRubyBasicObject selfint indexObject value) {
            if(. == null) {
                SynchronizedVariableAccessor.setVariable(self,realClass,index,value);
            } else {
                StampedVariableAccessor.setVariable(self,realClass,index,value);
            }
        }

        
    Get the variable accessor for the given name with intent to use it for writing.

    Parameters:
    name the name of the variable
    Returns:
    an accessor appropriate for writing
            VariableAccessor ivarAccessor = .get(name);
            if (ivarAccessor == null) {
                synchronized () {
                    Map<StringVariableAccessormyVariableAccessors = ;
                    ivarAccessor = myVariableAccessors.get(name);
                    if (ivarAccessor == null) {
                        // allocate a new accessor and populate a new table
                        ivarAccessor = allocateVariableAccessor(name);
                        Map<StringVariableAccessornewVariableAccessors = new HashMap<StringVariableAccessor>(myVariableAccessors.size() + 1);
                        newVariableAccessors.putAll(myVariableAccessors);
                        newVariableAccessors.put(nameivarAccessor);
                         = newVariableAccessors;
                    }
                }
            }
            return ivarAccessor;
        }
        
        public VariableAccessor getVariableAccessorForVar(String nameint index) {
            VariableAccessor ivarAccessor = .get(name);
            if (ivarAccessor == null) {
                synchronized () {
                    Map<StringVariableAccessormyVariableAccessors = ;
                    ivarAccessor = myVariableAccessors.get(name);
                    if (ivarAccessor == null) {
                        // allocate a new accessor and populate a new table
                        ivarAccessor = allocateVariableAccessorForVar(nameindex);
                        Map<StringVariableAccessornewVariableAccessors = new HashMap<StringVariableAccessor>(myVariableAccessors.size() + 1);
                        newVariableAccessors.putAll(myVariableAccessors);
                        newVariableAccessors.put(nameivarAccessor);
                         = newVariableAccessors;
                    }
                }
            }
            return ivarAccessor;
        }

        
    Get the variable accessor for the given name with intent to use it for reading.

    Parameters:
    name the name of the variable
    Returns:
    an accessor appropriate for reading
            VariableAccessor accessor = getVariableAccessorsForRead().get(name);
            if (accessor == nullaccessor = .;
            return accessor;
        }

        
    Retrieve the lazy accessor (VariableAccessorField) for object_id.

    Returns:
    the lazy accessor for object_id
            return ;
        }

        
    Retrieve the lazy accessor (VariableAccessorField) for C ext handle.

    Returns:
    the lazy accessor for C ext handle
            return ;
        }

        
    Retrieve the read accessor for C ext handle.

    Returns:
    the read accessor for C ext handle
        }

        
    Retrieve the write accessor for C ext handle.

    Returns:
    the write accessor for C ext handle
        }

        
    Retrieve the lazy accessor (VariableAccessorField) for FFI handle.

    Returns:
    the lazy accessor for FFI handle
            return ;
        }

        
    Retrieve the read accessor for FFI handle.

    Returns:
    the read accessor for FFI handle
        }

        
    Retrieve the write accessor for FFI handle.

    Returns:
    the write accessor for FFI handle
        }

        
    Retrieve the lazy accessor (VariableAccessorField) for object group.

    Returns:
    the lazy accessor for object group
            return ;
        }

        
    Retrieve the read accessor for object group.

    Returns:
    the read accessor for object group
        }

        
    Retrieve the write accessor for object group.

    Returns:
    the write accessor for object group
        }
        
        
    Retrieve the C ext handle for the given object.

    Parameters:
    self the object
    Returns:
    the object's C ext handle
        public final Object getNativeHandle(RubyBasicObject self) {
            return getNativeHandleAccessorForRead().get(self);
        }

        
    Set the C ext handle for the given object.

    Parameters:
    self the object
    value the object's C ext handle
        public final void setNativeHandle(RubyBasicObject selfObject value) {
            int index = getNativeHandleAccessorForRead().getIndex();
            if(index == -1) {
                return;
            }
            setVariableInternal(selfindexvalue);
        }

        
    Retrieve the FFI ext handle for the given object.

    Parameters:
    self the object
    Returns:
    the object's FFI handle
        public final Object getFFIHandle(RubyBasicObject self) {
            return getFFIHandleAccessorForRead().get(self);
        }

        
    Set the FFI handle for the given object.

    Parameters:
    self the object
    self the object's FFI handle
        public final void setFFIHandle(RubyBasicObject selfObject value) {
            int index = getFFIHandleAccessorForWrite().getIndex();
            setVariableInternal(selfindexvalue);
        }

        
    Get the size of the variable table, excluding extra vars (object_id, etc).

    Returns:
    the variable table's size, excluding extras
        public int getVariableTableSize() {
            return .size();
        }

        
    Get the size of the variable table, including extra vars (object_etc, etc).

    Returns:
        public int getVariableTableSizeWithExtras() {
            return .;
        }

        
    Get a Map representing all variables registered in the variable table.

    Returns:
    a map of names to accessors for all variables
        }

        
    Get an array of all the known instance variable names. The offset into the array indicates the offset of the variable's value in the per-object variable array.

    Returns:
    a copy of the array of known instance variable names
        public String[] getVariableNames() {
            String[] original = ;
            String[] copy = new String[original.length];
            System.arraycopy(original, 0, copy, 0, original.length);
            return copy;
        }
        
        
    Sync one this object's variables with other's - this is used to make rbClone work correctly.

    Parameters:
    self the object into which to sync variables
    other the object from which to sync variables
        public void syncVariables(RubyBasicObject selfIRubyObject other) {
            RubyClass otherRealClass = other.getMetaClass().getRealClass();
            boolean sameTable = otherRealClass == ;
            if (sameTable &&  == 0) {
                int idIndex = otherRealClass.getObjectIdAccessorField().getVariableAccessorForRead().getIndex();
                
                Object[] otherVars = ((RubyBasicObjectother).;
                
                if(. == null)
                {
                    synchronized (self) {
                        self.varTable = makeSyncedTable(self.varTableotherVarsidIndex);
                    }
                } else {
                    for(;;) {
                        int oldStamp = self.varTableStamp;
                        // wait for read mode
                        if((oldStamp & 0x01) == 1)
                            continue;
                        // acquire exclusive write mode
                        if(!..compareAndSwapInt(self.oldStamp, ++oldStamp))
                            continue;
                        
                        Object[] currentTable = (Object[]) ..getObjectVolatile(self.);
                        Object[] newTable = makeSyncedTable(currentTable,otherVarsidIndex);
                        
                        ..putOrderedObject(self.newTable);
                        
                        // release write mode
                        self.varTableStamp = oldStamp+1;
                        break;
                    }
                    
                }
            } else {
                for (Map.Entry<StringVariableAccessorentry : otherRealClass.getVariableAccessorsForRead().entrySet()) {
                    VariableAccessor accessor = entry.getValue();
                    Object value = accessor.get(other);
                    if (value != null) {
                        if (sameTable) {
                            accessor.set(selfvalue);
                        } else {
                            .getVariableAccessorForWrite(accessor.getName()).set(selfvalue);
                        }
                    }
                }
            }
        }
        
        
    Returns true if object has any variables, defined as:
    • instance variables
    • class variables
    • constants
    • internal variables, such as those used when marshaling Ranges and Exceptions

    Returns:
    true if object has any variables, else false
        public boolean hasVariables(RubyBasicObject object) {
            // we check both to exclude object_id
            Object[] myVarTable;
            return  > 0 || getVariableTableSize() > 0 && (myVarTable = object.varTable) != null && myVarTable.length > 0;
        }
        public void serializeVariables(RubyBasicObject objectObjectOutputStream oosthrows IOException {
            if (object.varTable != null) {
                Map<StringVariableAccessoraccessors = getVariableAccessorsForRead();
                oos.writeInt(accessors.size());
                for (VariableAccessor accessor : accessors.values()) {
                    oos.writeUTF(.);
                    oos.writeObject(accessor.get(object));
                }
            } else {
                oos.writeInt(0);
            }
        }
            int varCount = ois.readInt();
            for (int i = 0; i < varCounti++) {
                String name = ois.readUTF();
                Object value = ois.readObject();
                getVariableAccessorForWrite(name).set(objectvalue);
            }
        }
        
        public Object clearVariable(RubyBasicObject objectString name) {
            synchronized(object) {
                Object value = getVariableAccessorForRead(name).get(object);
                getVariableAccessorForWrite(name).set(objectnull);
                
                return value;
            }
        }

        
    We lazily stand up the object ID since it forces us to stand up per-object state for a given object. We also check for ObjectSpace here, and normally we do not register a given object ID into ObjectSpace due to the high cost associated with constructing the related weakref. Most uses of id/object_id will only ever need it to be a unique identifier, and the id2ref behavior provided by ObjectSpace is considered internal and not generally supported.

    Parameters:
    objectIdAccessor The variable accessor to use for storing the generated object ID
    Returns:
    The generated object ID
        protected synchronized long initObjectId(RubyBasicObject selfVariableAccessor objectIdAccessor) {
            Ruby runtime = self.getRuntime();
            long id;
            
            if (runtime.isObjectSpaceEnabled()) {
                id = runtime.getObjectSpace().createAndRegisterObjectId(self);
            } else {
                id = ObjectSpace.calculateObjectId(self);
            }
            
            // we use a direct path here to avoid frozen checks
            setObjectId(selfobjectIdAccessor.getIndex(), id);
            return id;
        }

        
    Update object_id with the given value.

    Parameters:
    self the object into which to set object_id
    index the index allocated to store object_id
    value the value of object_id
        private static void setObjectId(RubyClass realClassRubyBasicObject selfint indexlong value) {
            if (index < 0) return;
            setVariableInternal(realClassselfindexvalue);
        }

        
    Make a new variable table based on the values in a current and other (incoming) table, excluding object_id at specified index.

    Parameters:
    currentTable the current table
    otherTable the other (incoming) table
    objectIdIdx the index of object_id to exclude
    Returns:
    a new table formed by combining the given tables
        private static Object[] makeSyncedTable(Object[] currentTableObject[] otherTableint objectIdIdx) {
            if(currentTable == null || currentTable.length < otherTable.length) {
                currentTable = otherTable.clone();
            } else {
                System.arraycopy(otherTable, 0, currentTable, 0, otherTable.length);
            }
        
            // null out object ID so we don't share it
            if (objectIdIdx >= 0 && objectIdIdx < currentTable.length) {
                currentTable[objectIdIdx] = null;
            }
            
            return currentTable;
        }

        
    Allocate a new VariableAccessor for the named variable.

    Parameters:
    name the name of the variable
    Returns:
    the new VariableAccessor
        synchronized final VariableAccessor allocateVariableAccessor(String name) {
            int id = .;
            String[] myVariableNames = ;
            int newIndex = myVariableNames.length;
            String[] newVariableNames = new String[newIndex + 1];
            VariableAccessor newVariableAccessor;
            if (. == null) {
                newVariableAccessor = new SynchronizedVariableAccessor(namenewIndexid);
            } else {
                newVariableAccessor = new StampedVariableAccessor(namenewIndexid);
            }
            System.arraycopy(myVariableNames, 0, newVariableNames, 0, newIndex);
            newVariableNames[newIndex] = name;
             = newVariableNames;
            return newVariableAccessor;
        }
        
        synchronized final VariableAccessor allocateVariableAccessorForVar(String nameint index) {
            int id = .;
            String[] myVariableNames = ;
            int newIndex = myVariableNames.length;
            String[] newVariableNames = new String[newIndex + 1];
            
             += 1;
            VariableAccessor newVariableAccessor;
            switch (index) {
                case 0:
                    newVariableAccessor = new VariableAccessorVar0(namenewIndexid);
                    break;
                case 1:
                    newVariableAccessor = new VariableAccessorVar1(namenewIndexid);
                    break;
                case 2:
                    newVariableAccessor = new VariableAccessorVar2(namenewIndexid);
                    break;
                case 3:
                    newVariableAccessor = new VariableAccessorVar3(namenewIndexid);
                    break;
                case 4:
                    newVariableAccessor = new VariableAccessorVar4(namenewIndexid);
                    break;
                case 5:
                    newVariableAccessor = new VariableAccessorVar5(namenewIndexid);
                    break;
                case 6:
                    newVariableAccessor = new VariableAccessorVar6(namenewIndexid);
                    break;
                case 7:
                    newVariableAccessor = new VariableAccessorVar7(namenewIndexid);
                    break;
                case 8:
                    newVariableAccessor = new VariableAccessorVar8(namenewIndexid);
                    break;
                case 9:
                    newVariableAccessor = new VariableAccessorVar9(namenewIndexid);
                    break;
                default:
                    throw new RuntimeException("unsupported var index in " +  + ": " + index);
            }
            System.arraycopy(myVariableNames, 0, newVariableNames, 0, newIndex);
            newVariableNames[newIndex] = name;
             = newVariableNames;
            return newVariableAccessor;
        }
    New to GrepCode? Check out our FAQ X