Skip to content

Commit

Permalink
Showing 9 changed files with 186 additions and 57 deletions.
75 changes: 75 additions & 0 deletions core/src/main/java/org/jruby/FlagRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.jruby;

import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;

/**
* This class serves as a registry of all bit flags we use on JRuby objects.
*
* In order to maximally use our bit flags and prevent overlap between ancestors and dependents,
* this class registers flags on a first-come, first-served basis using previous flags registered
* for ancestor classes as a base line for new flags in a descendant.
*
* Because of the first-come, first-served nature, the most general types will need to register
* their flags first. This guarantees all bit flags from the progenitor on down will be packed
* tightly while avoiding overlaps.
*/
public class FlagRegistry {
private final Map<Class, Integer> currentShift = new HashMap<>();
private final Map<Class, BitSet> registry = new HashMap<>();

/**
* Register a new flag for the given class.
*
* The bit index for the new flag will be calculated at runtime, by walking parent classes
* and looking for previously-registered flags. Ancestors should register all their flags
* before decendants (which means they should not be registered in a static initializer
* unless the parent is known to have fully run its own static initializers).
*
* @param klass the class for which to register a new flag
* @return an integer with the new flag bit set
*/
public synchronized int newFlag(Class klass) {
Class currentKlass = klass;
Integer shift = null;
while (currentKlass != null &&
(shift = currentShift.get(currentKlass)) == null) {
currentKlass = currentKlass.getSuperclass();
}
if (shift == null) shift = 0;

BitSet flags = registry.get(klass);
if (flags == null) {
flags = new BitSet();
registry.put(klass, flags);
}
flags.set(shift);

assert flagsAreValid(klass, shift);

currentShift.put(klass, shift + 1);

return 1 << shift++;
}

public synchronized void printFlags() {
System.out.println(registry);
}

private boolean flagsAreValid(Class klass, int bitIndex) {
BitSet gathered = new BitSet();
Class currentKlass = klass;
while (currentKlass != null) {
BitSet flags = registry.get(klass);
if (flags != null) {
if (flags.intersects(gathered)) {
throw new AssertionError(klass.getName() + " uses flag " + bitIndex + " that overlaps with " + currentKlass);
}
gathered.and(flags);
}
currentKlass = currentKlass.getSuperclass();
}
return true;
}
}
79 changes: 36 additions & 43 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@

import org.jcodings.Encoding;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.runtime.Constants;
import org.jruby.runtime.ivars.VariableAccessor;
import java.io.IOException;
import java.io.ObjectInputStream;
@@ -43,7 +44,6 @@

import org.jruby.anno.JRubyMethod;
import org.jruby.common.IRubyWarnings.ID;
import org.jruby.exceptions.JumpException;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.javasupport.JavaObject;
import org.jruby.javasupport.JavaUtil;
@@ -68,13 +68,9 @@
import org.jruby.runtime.marshal.CoreObjectType;
import org.jruby.util.IdUtil;
import org.jruby.util.TypeConverter;
import org.jruby.util.io.EncodingUtils;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import org.jruby.util.unsafe.UnsafeHolder;

import static org.jruby.runtime.Helpers.invokedynamic;
import static org.jruby.runtime.invokedynamic.MethodNames.HASH;
import static org.jruby.runtime.invokedynamic.MethodNames.OP_EQUAL;
import static org.jruby.runtime.invokedynamic.MethodNames.OP_CMP;
import static org.jruby.runtime.invokedynamic.MethodNames.EQL;
@@ -139,7 +135,7 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
public static final String ERR_INSECURE_SET_INST_VAR = "Insecure: can't modify instance variable";

public static final int ALL_F = -1;
public static final int FALSE_F = 1 << 0;
public static final int FALSE_F = Constants.FALSE_F;
/**
* This flag is a bit funny. It's used to denote that this value
* is nil. It's a bit counterintuitive for a Java programmer to
@@ -150,27 +146,9 @@ public class RubyBasicObject implements Cloneable, IRubyObject, Serializable, Co
* final. It turns out using a flag for this actually gives us
* better performance than having a polymorphic {@link #isNil()} method.
*/
public static final int NIL_F = 1 << 1;
public static final int FROZEN_F = 1 << 2;
public static final int TAINTED_F = 1 << 3;

public static final int FL_USHIFT = 4;

public static final int USER0_F = (1<<(FL_USHIFT+0));
public static final int USER1_F = (1<<(FL_USHIFT+1));
public static final int USER2_F = (1<<(FL_USHIFT+2));
public static final int USER3_F = (1<<(FL_USHIFT+3));
public static final int USER4_F = (1<<(FL_USHIFT+4));
public static final int USER5_F = (1<<(FL_USHIFT+5));
public static final int USER6_F = (1<<(FL_USHIFT+6));
public static final int USER7_F = (1<<(FL_USHIFT+7));
public static final int USER8_F = (1<<(FL_USHIFT+8));
public static final int USER9_F = (1<<(FL_USHIFT+9));
public static final int USERA_F = (1<<(FL_USHIFT+10));
public static final int REFINED_MODULE_F = USER9_F;
public static final int IS_OVERLAID_F = USERA_F;

public static final int COMPARE_BY_IDENTITY_F = USER8_F;
public static final int NIL_F = Constants.NIL_F;
public static final int FROZEN_F = Constants.FROZEN_F;
public static final int TAINTED_F = Constants.TAINTED_F;

/**
* A value that is used as a null sentinel in among other places
@@ -312,14 +290,6 @@ protected final void testFrozen() {
* <li>{@link #NIL_F}</li>
* <li>{@link #FROZEN_F}</li>
* <li>{@link #TAINTED_F}</li>
* <li>{@link #USER0_F}</li>
* <li>{@link #USER1_F}</li>
* <li>{@link #USER2_F}</li>
* <li>{@link #USER3_F}</li>
* <li>{@link #USER4_F}</li>
* <li>{@link #USER5_F}</li>
* <li>{@link #USER6_F}</li>
* <li>{@link #USER7_F}</li>
* </ul>
*
* @param flag the actual flag to set or unset.
@@ -342,14 +312,6 @@ public final void setFlag(int flag, boolean set) {
* <li>{@link #NIL_F}</li>
* <li>{@link #FROZEN_F}</li>
* <li>{@link #TAINTED_F}</li>
* <li>{@link #USER0_F}</li>
* <li>{@link #USER1_F}</li>
* <li>{@link #USER2_F}</li>
* <li>{@link #USER3_F}</li>
* <li>{@link #USER4_F}</li>
* <li>{@link #USER5_F}</li>
* <li>{@link #USER6_F}</li>
* <li>{@link #USER7_F}</li>
* </ul>
*
* @param flag the flag to get
@@ -3086,4 +3048,35 @@ public final Object getNativeHandle() {
@Deprecated
public final void setNativeHandle(Object value) {
}

@Deprecated
public static final int FL_USHIFT = 4;
@Deprecated
public static final int USER0_F = (1<<(FL_USHIFT+0));
@Deprecated
public static final int USER1_F = (1<<(FL_USHIFT+1));
@Deprecated
public static final int USER2_F = (1<<(FL_USHIFT+2));
@Deprecated
public static final int USER3_F = (1<<(FL_USHIFT+3));
@Deprecated
public static final int USER4_F = (1<<(FL_USHIFT+4));
@Deprecated
public static final int USER5_F = (1<<(FL_USHIFT+5));
@Deprecated
public static final int USER6_F = (1<<(FL_USHIFT+6));
@Deprecated
public static final int USER7_F = (1<<(FL_USHIFT+7));
@Deprecated
public static final int USER8_F = (1<<(FL_USHIFT+8));
@Deprecated
public static final int USER9_F = (1<<(FL_USHIFT+9));
@Deprecated
public static final int USERA_F = (1<<(FL_USHIFT+10));
@Deprecated
public static final int REFINED_MODULE_F = USER9_F;
@Deprecated
public static final int IS_OVERLAID_F = USERA_F;
@Deprecated
public static final int COMPARE_BY_IDENTITY_F = USER8_F;
}
5 changes: 4 additions & 1 deletion core/src/main/java/org/jruby/RubyHash.java
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.Constants;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.Signature;
@@ -116,6 +117,8 @@
public class RubyHash extends RubyObject implements Map {
public static final int DEFAULT_INSPECT_STR_SIZE = 20;

public static final int COMPARE_BY_IDENTITY_F = Constants.COMPARE_BY_IDENTITY_F;

public static RubyClass createHashClass(Ruby runtime) {
RubyClass hashc = runtime.defineClass("Hash", runtime.getObject(), HASH_ALLOCATOR);
runtime.setHash(hashc);
@@ -227,7 +230,7 @@ public static final RubyHash newHash(Ruby runtime, Map valueMap, IRubyObject def
protected int size = 0;
private int threshold;

private static final int PROCDEFAULT_HASH_F = 1 << 10;
private static final int PROCDEFAULT_HASH_F = Constants.PROCDEFAULT_HASH_F;

private IRubyObject ifNone;

2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
@@ -120,7 +120,7 @@ public static RubyModule createKernelModule(Ruby runtime) {

module.defineAnnotatedMethods(RubyKernel.class);

module.setFlag(RubyObject.USER7_F, false); //Kernel is the only normal Module that doesn't need an implementor
module.setFlag(RubyModule.NEEDSIMPL_F, false); //Kernel is the only normal Module that doesn't need an implementor

runtime.setPrivateMethodMissing(new MethodMissingMethod(module, PRIVATE, CallType.NORMAL));
runtime.setProtectedMethodMissing(new MethodMissingMethod(module, PROTECTED, CallType.NORMAL));
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/RubyMatchData.java
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@
import org.jruby.anno.JRubyClass;
import org.jruby.runtime.Block;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.Constants;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
@@ -275,7 +276,7 @@ private void updateCharOffset() {
charOffsetUpdated = true;
}

private static final int MATCH_BUSY = USER2_F;
private static final int MATCH_BUSY = Constants.MATCH_BUSY;

// rb_match_busy
public final void use() {
16 changes: 11 additions & 5 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -88,6 +88,7 @@
import org.jruby.runtime.Block;
import org.jruby.runtime.CallSite;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.Constants;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.IRBlockBody;
import org.jruby.runtime.MethodFactory;
@@ -128,6 +129,11 @@ public class RubyModule extends RubyObject {
private static final Logger LOG = LoggerFactory.getLogger(RubyModule.class);
// static { LOG.setDebugEnable(true); } // enable DEBUG output

public static final int CACHEPROXY_F = Constants.CACHEPROXY_F;
public static final int NEEDSIMPL_F = Constants.NEEDSIMPL_F;
public static final int REFINED_MODULE_F = Constants.REFINED_MODULE_F;
public static final int IS_OVERLAID_F = Constants.IS_OVERLAID_F;

public static final ObjectAllocator MODULE_ALLOCATOR = new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
@@ -297,7 +303,7 @@ protected RubyModule(Ruby runtime, RubyClass metaClass, boolean objectSpace) {
id = runtime.allocModuleId();
runtime.addModule(this);
// if (parent == null) parent = runtime.getObject();
setFlag(USER7_F, !isClass());
setFlag(NEEDSIMPL_F, !isClass());
generationObject = generation = runtime.getNextModuleGeneration();

if (runtime.getInstanceConfig().isProfiling()) {
@@ -325,7 +331,7 @@ protected RubyModule(Ruby runtime) {
}

public boolean needsImplementer() {
return getFlag(USER7_F);
return getFlag(NEEDSIMPL_F);
}

/** rb_module_new
@@ -607,7 +613,7 @@ private RubyModule createNewRefinedModule(ThreadContext context, RubyClass class
RubyModule newRefinement = new RubyModule(context.runtime);
newRefinement.setSuperClass(classWeAreRefining);
newRefinement.setFlag(REFINED_MODULE_F, true);
newRefinement.setFlag(RubyObject.USER7_F, false); // Refinement modules should not do implementer check
newRefinement.setFlag(NEEDSIMPL_F, false); // Refinement modules should not do implementer check
newRefinement.refinedClass = classWeAreRefining;
newRefinement.definedAt = this;
refinements.put(classWeAreRefining, newRefinement);
@@ -4606,15 +4612,15 @@ public boolean getJavaProxy() {
* and alive using the ObjectProxyCache.
*/
public boolean getCacheProxy() {
return getFlag(USER0_F);
return getFlag(CACHEPROXY_F);
}

/**
* Set whether this Java proxy class should try to keep its instances idempotent
* and alive using the ObjectProxyCache.
*/
public void setCacheProxy(boolean cacheProxy) {
setFlag(USER0_F, cacheProxy);
setFlag(CACHEPROXY_F, cacheProxy);
}

@Override
6 changes: 4 additions & 2 deletions core/src/main/java/org/jruby/ext/stringio/StringIO.java
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@

import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jruby.FlagRegistry;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
@@ -48,6 +49,7 @@
import org.jruby.ast.util.ArgsUtil;
import org.jruby.java.addons.IOJavaAddons;
import org.jruby.runtime.Block;
import org.jruby.runtime.Constants;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
@@ -79,8 +81,8 @@ static class StringIOData {
}
StringIOData ptr;

private static final int STRIO_READABLE = USER4_F;
private static final int STRIO_WRITABLE = USER5_F;
private static final int STRIO_READABLE = Constants.STRIO_READABLE;
private static final int STRIO_WRITABLE = Constants.STRIO_WRITABLE;
private static final int STRIO_READWRITE = (STRIO_READABLE | STRIO_WRITABLE);

private static ObjectAllocator STRINGIO_ALLOCATOR = new ObjectAllocator() {
19 changes: 15 additions & 4 deletions core/src/main/java/org/jruby/util/StringSupport.java
Original file line number Diff line number Diff line change
@@ -35,13 +35,15 @@
import org.jcodings.specific.UTF8Encoding;
import org.jcodings.util.IntHash;
import org.joni.Matcher;
import org.jruby.FlagRegistry;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyEncoding;
import org.jruby.RubyIO;
import org.jruby.RubyObject;
import org.jruby.RubyString;
import org.jruby.runtime.Block;
import org.jruby.runtime.Constants;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.collections.IntHashMap;
@@ -54,11 +56,20 @@
import java.util.List;

public final class StringSupport {
public static final int CR_MASK = RubyObject.USER0_F | RubyObject.USER1_F;
public static final int CR_7BIT_F = Constants.CR_7BIT_F;
public static final int CR_VALID_F = Constants.CR_VALID_F;
public static final int CR_UNKNOWN = 0;
public static final int CR_7BIT = RubyObject.USER0_F;
public static final int CR_VALID = RubyObject.USER1_F;
public static final int CR_BROKEN = RubyObject.USER0_F | RubyObject.USER1_F;

// We hardcode these so they can be used in a switch below. The assert verifies they match FlagRegistry's value.
public static final int CR_7BIT = 16;
public static final int CR_VALID = 32;
static {
assert CR_7BIT == CR_7BIT_F : "CR_7BIT = " + CR_7BIT + " but should be " + CR_7BIT_F;
assert CR_VALID == CR_VALID_F : "CR_VALID = " + CR_VALID + " but should be " + CR_VALID_F;
}

public static final int CR_BROKEN = CR_7BIT | CR_VALID;
public static final int CR_MASK = CR_7BIT | CR_VALID;

static final int ARRAY_BYTE_BASE_OFFSET;
static {
38 changes: 38 additions & 0 deletions core/src/main/resources/org/jruby/runtime/Constants.java
Original file line number Diff line number Diff line change
@@ -30,6 +30,14 @@
***** END LICENSE BLOCK *****/
package org.jruby.runtime;

import org.jruby.FlagRegistry;
import org.jruby.RubyBasicObject;
import org.jruby.RubyHash;
import org.jruby.RubyMatchData;
import org.jruby.RubyModule;
import org.jruby.RubyString;
import org.jruby.ext.stringio.StringIO;

public final class Constants {
public static final String PLATFORM = "java";

@@ -71,6 +79,36 @@ public final class Constants {
* The JIT threshold to the specified method invocation count.
*/
public static final int JIT_THRESHOLD = 50;

private static final FlagRegistry registry = new FlagRegistry();

// These flags must be registered from top of hierarchy down to maintain order.
// TODO: Replace these during the build with their calculated values.
public static final int FALSE_F = registry.newFlag(RubyBasicObject.class);
public static final int NIL_F = registry.newFlag(RubyBasicObject.class);
public static final int FROZEN_F = registry.newFlag(RubyBasicObject.class);
public static final int TAINTED_F = registry.newFlag(RubyBasicObject.class);

public static final int CACHEPROXY_F = registry.newFlag(RubyModule.class);
public static final int NEEDSIMPL_F = registry.newFlag(RubyModule.class);
public static final int REFINED_MODULE_F = registry.newFlag(RubyModule.class);
public static final int IS_OVERLAID_F = registry.newFlag(RubyModule.class);

public static final int CR_7BIT_F = registry.newFlag(RubyString.class);
public static final int CR_VALID_F = registry.newFlag(RubyString.class);

public static final int STRIO_READABLE = registry.newFlag(StringIO.class);
public static final int STRIO_WRITABLE = registry.newFlag(StringIO.class);

public static final int MATCH_BUSY = registry.newFlag(RubyMatchData.class);

public static final int COMPARE_BY_IDENTITY_F = registry.newFlag(RubyHash.class);
public static final int PROCDEFAULT_HASH_F = registry.newFlag(RubyHash.class);

private static final boolean DEBUG = false;
static {
if (DEBUG) registry.printFlags();
}

private static String jruby_revision = "@jruby.revision@";

0 comments on commit d698f96

Please sign in to comment.