package org.jruby;
import com.headius.invokebinder.Binder;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.jcodings.Encoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jruby.anno.AnnotationBinder;
import org.jruby.anno.AnnotationHelper;
import org.jruby.anno.FrameField;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyConstant;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JavaMethodDescriptor;
import org.jruby.anno.TypePopulator;
import org.jruby.common.IRubyWarnings.ID;
import org.jruby.embed.Extension;
import org.jruby.exceptions.RaiseException;
import org.jruby.internal.runtime.methods.AliasMethod;
import org.jruby.internal.runtime.methods.AttrReaderMethod;
import org.jruby.internal.runtime.methods.AttrWriterMethod;
import org.jruby.internal.runtime.methods.DefineMethodMethod;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.internal.runtime.methods.JavaMethod;
import org.jruby.internal.runtime.methods.NativeCallMethod;
import org.jruby.internal.runtime.methods.PartialDelegatingMethod;
import org.jruby.internal.runtime.methods.ProcMethod;
import org.jruby.internal.runtime.methods.RefinedMarker;
import org.jruby.internal.runtime.methods.RefinedWrapper;
import org.jruby.internal.runtime.methods.SynchronizedDynamicMethod;
import org.jruby.internal.runtime.methods.UndefinedMethod;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRMethod;
import org.jruby.ir.targets.Bootstrap;
import org.jruby.javasupport.JavaClass;
import org.jruby.javasupport.binding.MethodGatherer;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
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;
import org.jruby.runtime.MethodIndex;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.builtin.Variable;
import org.jruby.runtime.callsite.CacheEntry;
import org.jruby.runtime.ivars.MethodData;
import org.jruby.runtime.marshal.MarshalStream;
import org.jruby.runtime.marshal.UnmarshalStream;
import org.jruby.runtime.opto.Invalidator;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.runtime.profile.MethodEnhancer;
import org.jruby.util.ByteListHelper;
import org.jruby.util.ClassProvider;
import org.jruby.util.CommonByteLists;
import org.jruby.util.IdUtil;
import org.jruby.util.TypeConverter;
import org.jruby.util.cli.Options;
import org.jruby.util.collections.WeakHashSet;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import static org.jruby.anno.FrameField.BACKREF;
import static org.jruby.anno.FrameField.BLOCK;
import static org.jruby.anno.FrameField.CLASS;
import static org.jruby.anno.FrameField.FILENAME;
import static org.jruby.anno.FrameField.LASTLINE;
import static org.jruby.anno.FrameField.LINE;
import static org.jruby.anno.FrameField.METHODNAME;
import static org.jruby.anno.FrameField.SCOPE;
import static org.jruby.anno.FrameField.SELF;
import static org.jruby.anno.FrameField.VISIBILITY;
import static org.jruby.runtime.Visibility.MODULE_FUNCTION;
import static org.jruby.runtime.Visibility.PRIVATE;
import static org.jruby.runtime.Visibility.PROTECTED;
import static org.jruby.runtime.Visibility.PUBLIC;
import static org.jruby.util.RubyStringBuilder.str;
import static org.jruby.util.RubyStringBuilder.ids;
import static org.jruby.util.RubyStringBuilder.types;
@JRubyClass(name="Module")
public class RubyModule extends RubyObject {
private static final Logger LOG = LoggerFactory.getLogger(RubyModule.class);
public static final int CACHEPROXY_F = ObjectFlags.CACHEPROXY_F;
public static final int NEEDSIMPL_F = ObjectFlags.NEEDSIMPL_F;
public static final int REFINED_MODULE_F = ObjectFlags.REFINED_MODULE_F;
public static final int IS_OVERLAID_F = ObjectFlags.IS_OVERLAID_F;
public static final int OMOD_SHARED = ObjectFlags.OMOD_SHARED;
public static final int INCLUDED_INTO_REFINEMENT = ObjectFlags.INCLUDED_INTO_REFINEMENT;
public static final ObjectAllocator MODULE_ALLOCATOR = new ObjectAllocator() {
@Override
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
return new RubyModule(runtime, klass);
}
};
public static RubyClass createModuleClass(Ruby runtime, RubyClass moduleClass) {
moduleClass.setClassIndex(ClassIndex.MODULE);
moduleClass.setReifiedClass(RubyModule.class);
moduleClass.kindOf = new RubyModule.JavaClassKindOf(RubyModule.class);
moduleClass.defineAnnotatedMethods(RubyModule.class);
moduleClass.defineAnnotatedMethods(ModuleKernelMethods.class);
return moduleClass;
}
public void checkValidBindTargetFrom(ThreadContext context, RubyModule originModule) throws RaiseException {
if (!originModule.isModule() && !hasModuleInHierarchy(originModule)) {
if (originModule instanceof MetaClass) {
throw context.runtime.newTypeError("can't bind singleton method to a different class");
} else {
throw context.runtime.newTypeError("bind argument must be an instance of " + originModule.getName());
}
}
}
public ClassIndex getClassIndex() {
return classIndex;
}
@SuppressWarnings("deprecated")
void setClassIndex(ClassIndex classIndex) {
this.classIndex = classIndex;
this.index = classIndex.ordinal();
}
public static class ModuleKernelMethods {
@JRubyMethod
public static IRubyObject autoload(ThreadContext context, IRubyObject self, IRubyObject symbol, IRubyObject file) {
return RubyKernel.autoload(context, self, symbol, file);
}
@JRubyMethod(name = "autoload?")
public static IRubyObject autoload_p(ThreadContext context, IRubyObject self, IRubyObject symbol) {
final Ruby runtime = context.runtime;
final String name = TypeConverter.checkID(symbol).idString();
RubyModule mod = RubyKernel.getModuleForAutoload(runtime, self);
for (; mod != null; mod = mod.getSuperClass()) {
final IRubyObject loadedValue = mod.fetchConstant(name);
if ( loadedValue != null && loadedValue != UNDEF ) return context.nil;
final RubyString file;
if ( mod.isIncluded() ) {
file = mod.getNonIncludedClass().getAutoloadFile(name);
}
else {
file = mod.getAutoloadFile(name);
}
if ( file != null ) {
if ( runtime.getLoadService().featureAlreadyLoaded(file.asJavaString()) ) {
return context.nil;
}
return file;
}
}
return context.nil;
}
}
@Override
public ClassIndex getNativeClassIndex() {
return ClassIndex.MODULE;
}
@Override
public boolean isModule() {
return true;
}
@Override
public boolean isClass() {
return false;
}
public boolean isSingleton() {
return false;
}
public static class KindOf {
public static final KindOf DEFAULT_KIND_OF = new KindOf();
public boolean isKindOf(IRubyObject obj, RubyModule type) {
RubyModule cl = obj.getMetaClass();
return cl.searchAncestor(type.getDelegate().getNonIncludedClass()) != null;
}
}
public static final class JavaClassKindOf extends RubyModule.KindOf {
private final Class klass;
public JavaClassKindOf(Class klass) {
this.klass = klass;
}
@Override
public boolean isKindOf(IRubyObject obj, RubyModule type) {
return klass.isInstance(obj);
}
}
public boolean isInstance(IRubyObject object) {
return kindOf.isKindOf(object, this);
}
public Map<String, ConstantEntry> getConstantMap() {
return constants;
}
public synchronized Map<String, ConstantEntry> getConstantMapForWrite() {
return constants == Collections.EMPTY_MAP ? constants = new ConcurrentHashMap<>(4, 0.9f, 1) : constants;
}
private Map<String, Autoload> getAutoloadMap() {
return autoloads;
}
private synchronized Map<String, Autoload> getAutoloadMapForWrite() {
return autoloads == Collections.EMPTY_MAP ? autoloads = new ConcurrentHashMap<String, Autoload>(4, 0.9f, 1) : autoloads;
}
@SuppressWarnings("unchecked")
public void addIncludingHierarchy(IncludedModule hierarchy) {
synchronized (getRuntime().getHierarchyLock()) {
Set<RubyClass> including = this.includingHierarchies;
if (including == Collections.EMPTY_SET) {
including = this.includingHierarchies = new WeakHashSet(4);
}
including.add(hierarchy);
}
}
public final MethodHandle getIdTest() {
MethodHandle idTest = this.idTest;
if (idTest != null) return idTest;
return this.idTest = newIdTest();
}
protected final MethodHandle newIdTest() {
return Binder.from(boolean.class, ThreadContext.class, IRubyObject.class)
.insert(2, id)
.invoke(testModuleMatch);
}
protected RubyModule(Ruby runtime, RubyClass metaClass, boolean objectSpace) {
super(runtime, metaClass, objectSpace);
id = runtime.allocModuleId();
runtime.addModule(this);
setFlag(NEEDSIMPL_F, !isClass());
updateGeneration(runtime);
if (runtime.getInstanceConfig().isProfiling()) {
cacheEntryFactory = new ProfilingCacheEntryFactory(runtime, NormalCacheEntryFactory);
} else {
cacheEntryFactory = NormalCacheEntryFactory;
}
methodInvalidator = OptoFactory.newMethodInvalidator(this);
}
protected RubyModule(Ruby runtime, RubyClass metaClass) {
this(runtime, metaClass, runtime.isObjectSpaceEnabled());
}
protected RubyModule(Ruby runtime) {
this(runtime, runtime.getModule());
}
public boolean needsImplementer() {
return getFlag(NEEDSIMPL_F);
}
public static RubyModule newModule(Ruby runtime) {
return new RubyModule(runtime);
}
public static RubyModule newModule(Ruby runtime, String name, RubyModule parent, boolean setParent) {
RubyModule module = newModule(runtime);
module.setBaseName(name);
if (setParent) module.setParent(parent);
parent.setConstant(name, module);
return module;
}
public final synchronized void addClassProvider(ClassProvider provider) {
Set<ClassProvider> classProviders = this.classProviders;
if (!classProviders.contains(provider)) {
Set<ClassProvider> cp = new HashSet<>(classProviders.size() + 1);
cp.addAll(classProviders);
cp.add(provider);
this.classProviders = cp;
}
}
public final synchronized void removeClassProvider(ClassProvider provider) {
Set<ClassProvider> cp = new HashSet<>(classProviders);
cp.remove(provider);
this.classProviders = cp;
}
private void checkForCyclicInclude(RubyModule m) throws RaiseException {
if (getNonIncludedClass() == m.getNonIncludedClass()) {
throw getRuntime().newArgumentError("cyclic include detected");
}
}
protected void checkForCyclicPrepend(RubyModule m) throws RaiseException {
if (getNonIncludedClass() == m.getNonIncludedClass()) {
throw getRuntime().newArgumentError(getName() + " cyclic prepend detected " + m.getName());
}
}
private RubyClass searchProvidersForClass(String name, RubyClass superClazz) {
Set<ClassProvider> classProviders = this.classProviders;
if (classProviders == Collections.EMPTY_SET) return null;
RubyClass clazz;
for (ClassProvider classProvider: classProviders) {
if ((clazz = classProvider.defineClassUnder(this, name, superClazz)) != null) {
return clazz;
}
}
return null;
}
private RubyModule searchProvidersForModule(String name) {
Set<ClassProvider> classProviders = this.classProviders;
if (classProviders == Collections.EMPTY_SET) return null;
RubyModule module;
for (ClassProvider classProvider: classProviders) {
if ((module = classProvider.defineModuleUnder(this, name)) != null) {
return module;
}
}
return null;
}
public RubyClass getSuperClass() {
return superClass;
}
public void setSuperClass(RubyClass superClass) {
this.superClass = superClass;
if (superClass != null && superClass.isSynchronized()) becomeSynchronized();
}
public RubyModule getParent() {
return parent;
}
public void setParent(RubyModule parent) {
this.parent = parent;
}
public RubyModule getMethodLocation() {
return methodLocation;
}
public void setMethodLocation(RubyModule module){
methodLocation = module;
}
public Map<String, DynamicMethod> getMethods() {
return this.methods;
}
public Map<String, DynamicMethod> getMethodsForWrite() {
Map<String, DynamicMethod> methods = this.methods;
if (methods != Collections.EMPTY_MAP) return methods;
synchronized (this) {
methods = this.methods;
return methods == Collections.EMPTY_MAP ?
this.methods = new ConcurrentHashMap<>(0, 0.9f, 1) :
methods;
}
}
public DynamicMethod putMethod(Ruby runtime, String id, DynamicMethod method) {
if (hasPrepends()) {
method = method.dup();
method.setImplementationClass(methodLocation);
}
DynamicMethod oldMethod = methodLocation.getMethodsForWrite().put(id, method);
if (oldMethod != null && oldMethod.isRefined()) {
methodLocation.getMethodsForWrite().put(id, new RefinedWrapper(method.getImplementationClass(), method.getVisibility(), id, method));
}
runtime.addProfiledMethod(id, method);
return method;
}
public boolean isIncluded() {
return false;
}
public boolean isPrepended() {
return false;
}
public RubyModule getNonIncludedClass() {
return this;
}
public RubyModule getDelegate() {
return this;
}
public String getBaseName() {
return baseName;
}
public void setBaseName(String name) {
baseName = name;
cachedName = null;
}
public String getName() {
if (cachedName != null) return cachedName;
return calculateName();
}
public RubyString rubyName() {
return cachedRubyName != null ? cachedRubyName : calculateRubyName();
}
public RubyString rubyBaseName() {
String baseName = getBaseName();
return baseName == null ? null : (RubyString) getRuntime().newSymbol(baseName).to_s();
}
private RubyString calculateAnonymousRubyName() {
Ruby runtime = getRuntime();
RubyString anonBase = runtime.newString("#<");
anonBase.append(metaClass.getRealClass().rubyName()).append(runtime.newString(":0x"));
anonBase.append(runtime.newString(Integer.toHexString(System.identityHashCode(this)))).append(runtime.newString(">"));
return anonBase;
}
private RubyString calculateRubyName() {
boolean cache = true;
if (getBaseName() == null) return calculateAnonymousRubyName();
Ruby runtime = getRuntime();
RubyClass objectClass = runtime.getObject();
List<RubyString> parents = new ArrayList<>();
for (RubyModule p = getParent(); p != null && p != objectClass; p = p.getParent()) {
if (p == this) break;
RubyString name = p.rubyBaseName();
if (name == null) {
cache = false;
name = p.rubyName();
}
parents.add(name);
}
Collections.reverse(parents);
RubyString colons = runtime.newString("::");
RubyString fullName = runtime.newString();
fullName.setEncoding(USASCIIEncoding.INSTANCE);
for (RubyString parent: parents) {
fullName.cat19(parent).cat19(colons);
}
fullName.cat19(rubyBaseName());
fullName.setFrozen(true);
if (cache) cachedRubyName = fullName;
return fullName;
}
public String getSimpleName() {
if (baseName != null) return baseName;
return calculateAnonymousName();
}
private String calculateName() {
boolean cache = true;
if (getBaseName() == null) {
return calculateAnonymousName();
}
Ruby runtime = getRuntime();
String name = getBaseName();
RubyClass objectClass = runtime.getObject();
int parentCount = 0;
for (RubyModule p = getParent() ; p != null && p != objectClass ; p = p.getParent()) {
if (p == this) break;
parentCount++;
}
String[] parentNames = new String[parentCount];
int i = parentCount - 1;
int totalLength = name.length() + parentCount * 2;
for (RubyModule p = getParent() ; p != null && p != objectClass ; p = p.getParent(), i--) {
if (p == this) break;
String pName = p.getBaseName();
if(pName == null) {
cache = false;
pName = p.getName();
}
parentNames[i] = pName;
totalLength += pName.length();
}
StringBuilder builder = new StringBuilder(totalLength);
for (String parentName : parentNames) {
builder.append(parentName).append("::");
}
builder.append(name);
String fullName = builder.toString();
if (cache) cachedName = fullName;
return fullName;
}
private String calculateAnonymousName() {
String cachedName = this.cachedName;
if (cachedName == null) {
StringBuilder anonBase = new StringBuilder(24);
anonBase.append("#<").append(metaClass.getRealClass().getName()).append(":0x");
anonBase.append(Integer.toHexString(System.identityHashCode(this))).append('>');
cachedName = this.cachedName = anonBase.toString();
}
return cachedName;
}
@JRubyMethod(name = "refine", required = 1, reads = SCOPE)
public IRubyObject refine(ThreadContext context, IRubyObject klass, Block block) {
if (!block.isGiven()) throw context.runtime.newArgumentError("no block given");
if (block.isEscaped()) throw context.runtime.newArgumentError("can't pass a Proc as a block to Module#refine");
if (!(klass instanceof RubyModule)) throw context.runtime.newTypeError("wrong argument type " + klass.getType() + "(expected Class or Module)");
if (refinements == Collections.EMPTY_MAP) refinements = newRefinementsMap();
if (activatedRefinements == Collections.EMPTY_MAP) activatedRefinements = newActivatedRefinementsMap();
RubyModule moduleToRefine = (RubyModule) klass;
RubyModule refinement = refinements.get(moduleToRefine);
if (refinement == null) {
refinement = createNewRefinedModule(context, moduleToRefine);
addActivatedRefinement(context, moduleToRefine, refinement);
}
yieldRefineBlock(context, refinement, block);
return refinement;
}
private RubyModule createNewRefinedModule(ThreadContext context, RubyModule klass) {
Ruby runtime = context.runtime;
RubyModule newRefinement = new RubyModule(runtime);
RubyClass superClass = refinementSuperclass(runtime, klass);
newRefinement.setSuperClass(superClass);
newRefinement.setFlag(REFINED_MODULE_F, true);
newRefinement.setFlag(NEEDSIMPL_F, false);
newRefinement.refinedClass = klass;
newRefinement.definedAt = this;
refinements.put(klass, newRefinement);
return newRefinement;
}
private static RubyClass refinementSuperclass(Ruby runtime, RubyModule superClass) {
if (superClass.isModule()) {
return new IncludedModuleWrapper(runtime, runtime.getBasicObject(), superClass);
} else {
return (RubyClass) superClass;
}
}
private void yieldRefineBlock(ThreadContext context, RubyModule refinement, Block block) {
block = block.cloneBlockAndFrame();
block.setEvalType(EvalType.MODULE_EVAL);
block.getBinding().setSelf(refinement);
RubyModule overlayModule = block.getBody().getStaticScope().getOverlayModuleForWrite(context);
overlayModule.refinements = refinements;
block.yieldSpecific(context);
}
private RubyClass getAlreadyActivatedRefinementWrapper(RubyClass classWeAreRefining, RubyModule refinement) {
RubyClass moduleWrapperForRefinement = activatedRefinements.get(classWeAreRefining);
if (moduleWrapperForRefinement == null) return classWeAreRefining;
for (RubyModule c = moduleWrapperForRefinement; c != null && c.isIncluded(); c = c.getSuperClass()) {
if (c.getNonIncludedClass() == refinement) return null;
}
return moduleWrapperForRefinement;
}
private void addActivatedRefinement(ThreadContext context, RubyModule moduleToRefine, RubyModule refinement) {
RubyClass superClass = null;
RubyClass c = activatedRefinements.get(moduleToRefine);
if (c != null) {
superClass = c;
while (c != null && c.isIncluded()) {
if (((IncludedModuleWrapper)c).getNonIncludedClass() == refinement) {
return;
}
c = c.getSuperClass();
}
}
refinement.setFlag(IS_OVERLAID_F, true);
IncludedModuleWrapper iclass = new IncludedModuleWrapper(context.runtime, superClass, refinement);
c = iclass;
c.refinedClass = moduleToRefine;
for (refinement = refinement.getSuperClass(); refinement != null; refinement = refinement.getSuperClass()) {
refinement.setFlag(IS_OVERLAID_F, true);
c.setSuperClass(new IncludedModuleWrapper(context.runtime, c.getSuperClass(), refinement));
c = c.getSuperClass();
c.refinedClass = moduleToRefine;
}
activatedRefinements.put(moduleToRefine, iclass);
}
@JRubyMethod(name = "using", required = 1, visibility = PRIVATE, reads = {SELF, SCOPE})
public IRubyObject using(ThreadContext context, IRubyObject refinedModule) {
if (context.getFrameSelf() != this) throw context.runtime.newRuntimeError("Module#using is not called on self");
if (context.getCurrentStaticScope().getIRScope() instanceof IRMethod) {
throw context.runtime.newRuntimeError("Module#using is not permitted in methods");
}
StaticScope staticScope = context.getCurrentStaticScope();
RubyModule overlayModule = staticScope.getOverlayModuleForWrite(context);
usingModule(context, overlayModule, refinedModule);
return this;
}
public static void usingModule(ThreadContext context, RubyModule cref, IRubyObject refinedModule) {
if (!(refinedModule instanceof RubyModule)) throw context.runtime.newTypeError(refinedModule, context.runtime.getModule());
usingModuleRecursive(cref, (RubyModule) refinedModule);
}
private static void usingModuleRecursive(RubyModule cref, RubyModule module) {
Ruby runtime = cref.getRuntime();
RubyClass superClass = module.getSuperClass();
if (superClass != null) usingModuleRecursive(cref, superClass);
if (module instanceof IncludedModule) {
module = module.getDelegate();
} else if (module.isModule()) {
} else {
throw runtime.newTypeError("wrong argument type " + module.getName() + " (expected Module)");
}
Map<RubyModule, RubyModule> refinements = module.refinements;
if (refinements == null) return;
for (Map.Entry<RubyModule, RubyModule> entry: refinements.entrySet()) {
usingRefinement(runtime, cref, entry.getKey(), entry.getValue());
}
}
private static void usingRefinement(Ruby runtime, RubyModule cref, RubyModule klass, RubyModule module) {
RubyModule iclass, c, superclass = klass;
if (cref.refinements == Collections.EMPTY_MAP) {
cref.refinements = newRefinementsMap();
} else {
if (cref.getFlag(OMOD_SHARED)) {
cref.refinements = newRefinementsMap(cref.refinements);
cref.setFlag(OMOD_SHARED, false);
}
if ((c = cref.refinements.get(klass)) != null) {
superclass = c;
while (c != null && c instanceof IncludedModule) {
if (c.getNonIncludedClass() == module) {
return;
}
c = c.getSuperClass();
}
}
}
module.setFlag(IS_OVERLAID_F, true);
superclass = refinementSuperclass(runtime, superclass);
c = iclass = new IncludedModuleWrapper(runtime, (RubyClass) superclass, module);
c.refinedClass = klass;
module = module.getSuperClass();
while (module != null && module != klass) {
module.setFlag(IS_OVERLAID_F, true);
c.setSuperClass(new IncludedModuleWrapper(cref.getRuntime(), c.getSuperClass(), module));
c = c.getSuperClass();
c.refinedClass = klass;
module = module.getSuperClass();
}
cref.refinements.put(klass, iclass);
}
public static Map<RubyModule, RubyModule> newRefinementsMap(Map<RubyModule, RubyModule> refinements) {
return Collections.synchronizedMap(new IdentityHashMap<>(refinements));
}
public static Map<RubyModule, RubyModule> newRefinementsMap() {
return Collections.synchronizedMap(new IdentityHashMap<>());
}
public static Map<RubyModule, IncludedModule> newActivatedRefinementsMap() {
return Collections.synchronizedMap(new IdentityHashMap<>());
}
@JRubyMethod(name = "used_modules", reads = SCOPE)
public IRubyObject used_modules(ThreadContext context) {
StaticScope cref = context.getCurrentStaticScope();
RubyArray ary = context.runtime.newArray();
while (cref != null) {
RubyModule overlay;
if ((overlay = cref.getOverlayModuleForRead()) != null &&
!overlay.refinements.isEmpty()) {
overlay.refinements.entrySet().stream().forEach(entry -> {
RubyModule mod = entry.getValue();
while (mod != null && mod.getNonIncludedClass().isRefinement()) {
ary.push(mod.getNonIncludedClass().definedAt);
mod = mod.getSuperClass();
}
});
}
cref = cref.getPreviousCRefScope();
}
return ary;
}
@Deprecated
public IncludedModuleWrapper newIncludeClass(RubyClass superClazz) {
IncludedModuleWrapper includedModule = new IncludedModuleWrapper(getRuntime(), superClazz, this);
if (getSuperClass() != null) {
includedModule.includeModule(getSuperClass());
}
return includedModule;
}
public RubyModule getModule(String name) {
return (RubyModule) getConstantAt(name);
}
public RubyClass getClass(String name) {
return (RubyClass) getConstantAt(name);
}
@Deprecated
public RubyClass fastGetClass(String internedName) {
return getClass(internedName);
}
public void prependModule(RubyModule module) {
testFrozen("module");
if (module.refinedClass != null) {
throw getRuntime().newArgumentError("refinement module is not allowed");
}
checkForCyclicInclude(module);
synchronized (this) {
if (hasModuleInPrepends(module)) {
invalidateCacheDescendants();
return;
}
infectBy(module);
doPrependModule(module);
invalidateCoreClasses();
invalidateCacheDescendants();
invalidateConstantCacheForModuleInclusion(module);
}
}
@Deprecated
public void prependModule(IRubyObject arg) {
assert arg != null;
if (!(arg instanceof RubyModule)) {
throw getRuntime().newTypeError("Wrong argument type " + arg.getMetaClass().getName() +
" (expected Module).");
}
prependModule((RubyModule) arg);
}
public synchronized void includeModule(IRubyObject arg) {
assert arg != null;
testFrozen("module");
if (!(arg instanceof RubyModule)) {
throw getRuntime().newTypeError("Wrong argument type " + arg.getMetaClass().getName() +
" (expected Module).");
}
RubyModule module = (RubyModule) arg;
if (module.refinedClass != null) {
throw getRuntime().newArgumentError("refinement module is not allowed");
}
checkForCyclicInclude(module);
infectBy(module);
doIncludeModule(module);
invalidateCoreClasses();
invalidateCacheDescendants();
invalidateConstantCacheForModuleInclusion(module);
}
public void defineAnnotatedMethod(Class clazz, String name) {
boolean foundMethod = false;
for (Method method : clazz.getDeclaredMethods()) {
if (method.getName().equals(name) && defineAnnotatedMethod(method, MethodFactory.createFactory(getRuntime().getJRubyClassLoader()))) {
foundMethod = true;
}
}
if (!foundMethod) {
throw new RuntimeException("No JRubyMethod present for method " + name + "on class " + clazz.getName());
}
}
public final void defineAnnotatedConstants(Class clazz) {
for (Field field : clazz.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers())) {
defineAnnotatedConstant(field);
}
}
}
public final boolean defineAnnotatedConstant(Field field) {
JRubyConstant jrubyConstant = field.getAnnotation(JRubyConstant.class);
if (jrubyConstant == null) return false;
Class tp = field.getType();
IRubyObject realVal;
try {
if(tp == Integer.class || tp == Integer.TYPE || tp == Short.class || tp == Short.TYPE || tp == Byte.class || tp == Byte.TYPE) {
realVal = RubyNumeric.int2fix(getRuntime(), field.getInt(null));
} else if(tp == Boolean.class || tp == Boolean.TYPE) {
realVal = field.getBoolean(null) ? getRuntime().getTrue() : getRuntime().getFalse();
} else {
realVal = getRuntime().getNil();
}
} catch(Exception e) {
realVal = getRuntime().getNil();
}
String[] names = jrubyConstant.value();
if (names.length == 0) {
setConstant(field.getName(), realVal);
}
else {
for (String name : names) setConstant(name, realVal);
}
return true;
}
@Extension
public void defineAnnotatedMethods(Class clazz) {
defineAnnotatedMethodsIndividually(clazz);
}
public static final class MethodClumper {
private HashMap<String, List<JavaMethodDescriptor>> annotatedMethods;
private HashMap<String, List<JavaMethodDescriptor>> staticAnnotatedMethods;
public Map<Set<FrameField>, List<String>> readGroups = Collections.EMPTY_MAP;
public Map<Set<FrameField>, List<String>> writeGroups = Collections.EMPTY_MAP;
@SuppressWarnings("deprecation")
public void clump(final Class klass) {
Method[] declaredMethods = MethodGatherer.DECLARED_METHODS.get(klass);
for (Method method: declaredMethods) {
JRubyMethod anno = method.getAnnotation(JRubyMethod.class);
if (anno == null) continue;
if (anno.compat() == org.jruby.CompatVersion.RUBY1_8) continue;
if (method.isBridge()) continue;
JavaMethodDescriptor desc = new JavaMethodDescriptor(method);
final String[] names = anno.name();
String name = names.length == 0 ? method.getName() : names[0];
Map<String, List<JavaMethodDescriptor>> methodsHash;
if (desc.isStatic) {
if ( (methodsHash = staticAnnotatedMethods) == null ) {
methodsHash = staticAnnotatedMethods = new HashMap<>();
}
} else {
if ( (methodsHash = annotatedMethods) == null ) {
methodsHash = annotatedMethods = new HashMap<>();
}
}
List<JavaMethodDescriptor> methodDescs = methodsHash.get(name);
if (methodDescs == null) {
methodsHash.put(name, methodDescs = new ArrayList<>(4));
}
methodDescs.add(desc);
if (anno.reads().length > 0 && readGroups == Collections.EMPTY_MAP) readGroups = new HashMap<>();
if (anno.writes().length > 0 && writeGroups == Collections.EMPTY_MAP) writeGroups = new HashMap<>();
AnnotationHelper.groupFrameFields(readGroups, writeGroups, anno, method.getName());
}
}
@Deprecated
public Map<String, List<JavaMethodDescriptor>> getAllAnnotatedMethods() {
return null;
}
public final Map<String, List<JavaMethodDescriptor>> getAnnotatedMethods() {
return annotatedMethods == null ? Collections.EMPTY_MAP : annotatedMethods;
}
public final Map<String, List<JavaMethodDescriptor>> getStaticAnnotatedMethods() {
return staticAnnotatedMethods == null ? Collections.EMPTY_MAP : staticAnnotatedMethods;
}
}
public static TypePopulator loadPopulatorFor(Class<?> type) {
if (Options.DEBUG_FULLTRACE.load() || Options.REFLECTED_HANDLES.load()) {
LOG.debug("trace mode, using default populator");
} else {
try {
String qualifiedName = Constants.GENERATED_PACKAGE + type.getCanonicalName().replace('.', '$');
String fullName = qualifiedName + AnnotationBinder.POPULATOR_SUFFIX;
String fullPath = fullName.replace('.', '/') + ".class";
if (LOG.isDebugEnabled()) LOG.debug("looking for populator " + fullName);
if (Ruby.getClassLoader().getResource(fullPath) == null) {
LOG.debug("could not find it, using default populator");
} else {
return (TypePopulator) Class.forName(fullName).newInstance();
}
} catch (Throwable ex) {
if (LOG.isDebugEnabled()) LOG.debug("could not find populator, using default (" + ex + ')');
}
}
return new TypePopulator.ReflectiveTypePopulator(type);
}
public final void defineAnnotatedMethodsIndividually(Class clazz) {
getRuntime().POPULATORS.get(clazz).populate(this, clazz);
}
public final boolean defineAnnotatedMethod(String name, List<JavaMethodDescriptor> methods, MethodFactory methodFactory) {
JavaMethodDescriptor desc = methods.get(0);
if (methods.size() == 1) {
return defineAnnotatedMethod(name, desc, methodFactory);
}
DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, methods, name);
define(this, desc, name, dynamicMethod);
return true;
}
public final boolean defineAnnotatedMethod(Method method, MethodFactory methodFactory) {
JRubyMethod jrubyMethod = method.getAnnotation(JRubyMethod.class);
if (jrubyMethod == null) return false;
JavaMethodDescriptor desc = new JavaMethodDescriptor(method);
DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, desc, method.getName());
define(this, desc, method.getName(), dynamicMethod);
return true;
}
public final boolean defineAnnotatedMethod(String name, JavaMethodDescriptor desc, MethodFactory methodFactory) {
JRubyMethod jrubyMethod = desc.anno;
if (jrubyMethod == null) return false;
DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, desc, name);
define(this, desc, name, dynamicMethod);
return true;
}
public void undefineMethod(String name) {
methodLocation.addMethod(name, UndefinedMethod.getInstance());
}
public void undef(ThreadContext context, String name) {
Ruby runtime = context.runtime;
testFrozen("module");
if (name.equals("__send__") || name.equals("object_id") || name.equals("initialize")) {
runtime.getWarnings().warn(ID.UNDEFINING_BAD, "undefining `"+ name +"' may cause serious problems");
}
if (name.equals("method_missing")) {
IRubyObject oldExc = runtime.getGlobalVariables().get("$!");
try {
removeMethod(context, name);
} catch (RaiseException t) {
if (!(t.getException() instanceof RubyNameError)) {
throw t;
} else {
runtime.getGlobalVariables().set("$!", oldExc);
}
}
return;
}
DynamicMethod method = searchMethod(name);
if (method.isUndefined()) {
String s0 = " class";
RubyModule c = this;
if (c.isSingleton()) {
IRubyObject obj = ((MetaClass) c).getAttached();
if (obj instanceof RubyModule) {
c = (RubyModule) obj;
s0 = "";
}
} else if (c.isModule()) {
s0 = " module";
}
throw runtime.newNameError("Undefined method " + name + " for" + s0 + " '" + c.getName() + "'", name);
}
methodLocation.addMethod(name, UndefinedMethod.getInstance());
if (isSingleton()) {
((MetaClass) this).getAttached().callMethod(context, "singleton_method_undefined", runtime.newSymbol(name));
} else {
callMethod(context, "method_undefined", runtime.newSymbol(name));
}
}
@JRubyMethod(name = "include?", required = 1)
public IRubyObject include_p(ThreadContext context, IRubyObject arg) {
if (!arg.isModule()) {
throw context.runtime.newTypeError(arg, context.runtime.getModule());
}
RubyModule moduleToCompare = (RubyModule) arg;
for (RubyModule p = getSuperClass(); p != null; p = p.getSuperClass()) {
if (p.isSame(moduleToCompare)) {
return context.tru;
}
}
return context.fals;
}
@JRubyMethod(name = "singleton_class?")
public IRubyObject singleton_class_p(ThreadContext context) {
return context.runtime.newBoolean(isSingleton());
}
public void addMethod(String id, DynamicMethod method) {
testFrozen("class/module");
RubyModule location = this;
if (methodLocation != this) {
methodLocation.addMethod(id, method);
return;
}
if (this instanceof MetaClass) {
((MetaClass) this).getAttached().testFrozen();
}
if (isRefinement()) {
addRefinedMethodEntry(id, method);
}
addMethodInternal(id, method);
}
private void addRefinedMethodEntry(String id, DynamicMethod method) {
RubyModule methodLocation = refinedClass.getMethodLocation();
DynamicMethod orig = methodLocation.searchMethodCommon(id);
if (orig == null) {
refinedClass.addMethod(id, new RefinedMarker(methodLocation, method.getVisibility(), id));
} else {
if (orig.isRefined()) {
return;
}
refinedClass.addMethod(id, new RefinedWrapper(methodLocation, method.getVisibility(), id, orig));
}
}
public final void addMethodInternal(String name, DynamicMethod method) {
synchronized (methodLocation.getMethodsForWrite()) {
putMethod(getRuntime(), name, method);
invalidateCoreClasses();
invalidateCacheDescendants();
}
}
public final void addMethodAtBootTimeOnly(String id, DynamicMethod method) {
putMethod(getRuntime(), id, method);
}
public void removeMethod(ThreadContext context, String id) {
testFrozen("class/module");
switch (id) {
case "object_id" : warnMethodRemoval(context, id); break;
case "__send__" : warnMethodRemoval(context, id); break;
case "initialize" : warnMethodRemoval(context, id); break;
}
RubySymbol name = context.runtime.newSymbol(id);
Map<String, DynamicMethod> methodsForWrite = methodLocation.getMethodsForWrite();
synchronized (methodsForWrite) {
DynamicMethod method = methodsForWrite.get(id);
if (method == null ||
method.isUndefined() ||
method instanceof RefinedMarker) {
throw context.runtime.newNameError(str(context.runtime, "method '", name, "' not defined in ", rubyName()), id);
}
method = methodsForWrite.remove(id);
if (method.isRefined()) {
methodsForWrite.put(id, new RefinedMarker(method.getImplementationClass(), method.getVisibility(), id));
}
invalidateCoreClasses();
invalidateCacheDescendants();
}
if (isSingleton()) {
((MetaClass) this).getAttached().callMethod(context, "singleton_method_removed", name);
} else {
callMethod(context, "method_removed", name);
}
}
private static void warnMethodRemoval(final ThreadContext context, final String id) {
context.runtime.getWarnings().warn(ID.UNDEFINING_BAD,
str(context.runtime, "removing `", ids(context.runtime, id), "' may cause serious problems"));
}
public final DynamicMethod searchMethod(String name) {
return searchWithCache(name).method;
}
public CacheEntry searchWithCache(String name) {
return searchWithCacheAndRefinements(name, true, null);
}
public CacheEntry searchWithRefinements(String name, StaticScope refinedScope) {
return searchWithCacheAndRefinements(name, true, refinedScope);
}
public final CacheEntry searchWithCache(String id, boolean cacheUndef) {
final CacheEntry entry = cacheHit(id);
return entry != null ? entry : searchWithCacheMiss(getRuntime(), id, cacheUndef);
}
private final CacheEntry searchWithCacheAndRefinements(String id, boolean cacheUndef, StaticScope refinedScope) {
CacheEntry entry = searchWithCache(id, cacheUndef);
if (entry.method.isRefined()) {
for (; refinedScope != null; refinedScope = refinedScope.getEnclosingScope()) {
RubyModule overlay = refinedScope.getOverlayModuleForRead();
if (overlay == null) continue;
CacheEntry maybeEntry = resolveRefinedMethod(overlay.refinements, entry, id, cacheUndef);
if (maybeEntry.method.isUndefined()) continue;
return maybeEntry;
}
return resolveRefinedMethod(null, entry, id, cacheUndef);
}
return entry;
}
private CacheEntry refinedMethodOriginalMethodEntry(Map<RubyModule, RubyModule> refinements, String id, boolean cacheUndef, CacheEntry entry) {
RubyModule superClass;
DynamicMethod method = entry.method;
if (method instanceof RefinedWrapper){
return cacheEntryFactory.newCacheEntry(id, ((RefinedWrapper) method).getWrapped(), entry.sourceModule, entry.token);
} else if ((superClass = entry.sourceModule.getSuperClass()) == null) {
return CacheEntry.NULL_CACHE;
} else {
return resolveRefinedMethod(refinements, superClass.searchWithCache(id, cacheUndef), id, cacheUndef);
}
}
private CacheEntry searchWithCacheMiss(Ruby runtime, final String id, final boolean cacheUndef) {
final int token = generation;
CacheEntry methodEntry = searchMethodEntryInner(id);
if (methodEntry == null) {
if (cacheUndef) {
return addToCache(id, UndefinedMethod.getInstance(), this, token);
}
return cacheEntryFactory.newCacheEntry(id, UndefinedMethod.getInstance(), methodEntry.sourceModule, token);
} else if (!runtime.isBooting()) {
addToCache(id, methodEntry);
}
return methodEntry;
}
@Deprecated
public final int getCacheToken() {
return generation;
}
public final int getGeneration() {
return generation;
}
public final Integer getGenerationObject() {
return generationObject;
}
private final Map<String, CacheEntry> getCachedMethods() {
return this.cachedMethods;
}
private final Map<String, CacheEntry> getCachedMethodsForWrite() {
Map<String, CacheEntry> myCachedMethods = this.cachedMethods;
return myCachedMethods == Collections.EMPTY_MAP ?
this.cachedMethods = new ConcurrentHashMap<>(0, 0.75f, 1) :
myCachedMethods;
}
private CacheEntry cacheHit(String name) {
CacheEntry cacheEntry = methodLocation.getCachedMethods().get(name);
if (cacheEntry != null) {
if (cacheEntry.token == getGeneration()) {
return cacheEntry;
}
}
return null;
}
private void invalidateConstantCacheForModuleInclusion(RubyModule module) {
Map<String, Invalidator> invalidators = null;
for (RubyModule mod : gatherModules(module)) {
for (String name : mod.getConstantMap().keySet()) {
if (invalidators == null) invalidators = new HashMap<>();
invalidators.put(name, getRuntime().getConstantInvalidator(name));
}
}
if (invalidators != null) {
List<Invalidator> values = new ArrayList(invalidators.values());
values.get(0).invalidateAll(values);
}
}
protected static abstract class CacheEntryFactory {
public abstract CacheEntry newCacheEntry(String id, DynamicMethod method, RubyModule sourceModule, int token);
public boolean hasCacheEntryFactory(Class cacheEntryFactoryClass) {
CacheEntryFactory current = this;
while (current instanceof WrapperCacheEntryFactory) {
if (cacheEntryFactoryClass.isAssignableFrom(current.getClass())) {
return true;
}
current = ((WrapperCacheEntryFactory)current).getPrevious();
}
return cacheEntryFactoryClass.isAssignableFrom(current.getClass());
}
}
protected static abstract class WrapperCacheEntryFactory extends CacheEntryFactory {
protected final CacheEntryFactory previous;
public WrapperCacheEntryFactory(CacheEntryFactory previous) {
this.previous = previous;
}
public CacheEntryFactory getPrevious() {
return previous;
}
}
protected static final CacheEntryFactory NormalCacheEntryFactory = new CacheEntryFactory() {
@Override
public CacheEntry newCacheEntry(String id, DynamicMethod method, RubyModule sourceModule, int token) {
return new CacheEntry(method, sourceModule, token);
}
};
protected static class SynchronizedCacheEntryFactory extends WrapperCacheEntryFactory {
public SynchronizedCacheEntryFactory(CacheEntryFactory previous) {
super(previous);
}
@Override
public CacheEntry newCacheEntry(String id, DynamicMethod method, RubyModule sourceModule, int token) {
if (method.isUndefined()) {
return new CacheEntry(method, token);
}
CacheEntry delegated = previous.newCacheEntry(id, method, sourceModule, token);
return new CacheEntry(new SynchronizedDynamicMethod(delegated.method), delegated.sourceModule, delegated.token);
}
}
protected static class ProfilingCacheEntryFactory extends WrapperCacheEntryFactory {
private final MethodEnhancer enhancer;
public ProfilingCacheEntryFactory( Ruby runtime, CacheEntryFactory previous) {
super(previous);
this.enhancer = runtime.getProfilingService().newMethodEnhancer( runtime );
}
private MethodEnhancer getMethodEnhancer() {
return enhancer;
}
@Override
public CacheEntry newCacheEntry(String id, DynamicMethod method, RubyModule sourceModule, int token) {
if (method.isUndefined()) return new CacheEntry(method, token);
CacheEntry delegated = previous.newCacheEntry(id, method, sourceModule, token);
DynamicMethod enhancedMethod = getMethodEnhancer().enhance(id, delegated.method);
return new CacheEntry(enhancedMethod, delegated.sourceModule, delegated.token);
}
}
private volatile CacheEntryFactory cacheEntryFactory;
public void becomeSynchronized() {
cacheEntryFactory = new SynchronizedCacheEntryFactory(cacheEntryFactory);
}
public boolean isSynchronized() {
return cacheEntryFactory.hasCacheEntryFactory(SynchronizedCacheEntryFactory.class);
}
protected CacheEntry addToCache(String id, DynamicMethod method, RubyModule sourceModule, int token) {
CacheEntry entry = cacheEntryFactory.newCacheEntry(id, method, sourceModule, token);
methodLocation.getCachedMethodsForWrite().put(id, entry);
return entry;
}
protected CacheEntry addToCache(String id, CacheEntry entry) {
methodLocation.getCachedMethodsForWrite().put(id, entry);
return entry;
}
public DynamicMethod searchMethodInner(String id) {
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
DynamicMethod method = module.searchMethodCommon(id);
if (method != null) return method.isNull() ? null : method;
}
return null;
}
public CacheEntry searchMethodEntryInner(String id) {
int token = generation;
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
DynamicMethod method = module.searchMethodCommon(id);
if (method != null) return method.isNull() ? null : cacheEntryFactory.newCacheEntry(id, method, module, token);
}
return null;
}
public CacheEntry resolveRefinedMethod(Map<RubyModule, RubyModule> refinements, CacheEntry entry, String id, boolean cacheUndef) {
if (entry != null && entry.method.isRefined()) {
RubyModule refinement = findRefinement(refinements, entry.method.getDefinedClass());
if (refinement == null) {
return refinedMethodOriginalMethodEntry(refinements, id, cacheUndef, entry);
} else {
CacheEntry tmpEntry = refinement.searchWithCache(id);
if (!tmpEntry.method.isRefined()) {
return tmpEntry;
} else {
return refinedMethodOriginalMethodEntry(refinements, id, cacheUndef, entry);
}
}
}
return entry;
}
private static RubyModule findRefinement(Map<RubyModule, RubyModule> refinements, RubyModule target) {
if (refinements == null) {
return null;
}
return refinements.get(target);
}
protected DynamicMethod searchMethodCommon(String id) {
return getMethods().get(id);
}
public void invalidateCacheDescendants() {
LOG.debug("{} invalidating descendants", baseName);
getRuntime().getCaches().incrementMethodInvalidations();
if (includingHierarchies.isEmpty()) {
methodInvalidator.invalidate();
return;
}
List<Invalidator> invalidators = new ArrayList<Invalidator>();
invalidators.add(methodInvalidator);
synchronized (getRuntime().getHierarchyLock()) {
for (RubyClass includingHierarchy : includingHierarchies) {
includingHierarchy.addInvalidatorsAndFlush(invalidators);
}
}
methodInvalidator.invalidateAll(invalidators);
}
@SuppressWarnings("deprecation")
protected void invalidateCoreClasses() {
if (!getRuntime().isBootingCore()) {
if (this == getRuntime().getFixnum()) {
getRuntime().reopenFixnum();
} else if (this == getRuntime().getFloat()) {
getRuntime().reopenFloat();
}
}
}
public Invalidator getInvalidator() {
return methodInvalidator;
}
public void updateGeneration() {
updateGeneration(getRuntime());
}
private void updateGeneration(final Ruby runtime) {
generationObject = generation = runtime.getNextModuleGeneration();
}
@Deprecated
protected void invalidateCacheDescendantsInner() {
methodInvalidator.invalidate();
}
protected void invalidateConstantCache(String constantName) {
getRuntime().getConstantInvalidator(constantName).invalidate();
}
protected void invalidateConstantCaches(Set<String> constantNames) {
if (constantNames.size() > 0) {
Ruby runtime = getRuntime();
List<Invalidator> constantInvalidators = new ArrayList<>(constantNames.size());
for (String name : constantNames) {
constantInvalidators.add(runtime.getConstantInvalidator(name));
}
constantInvalidators.get(0).invalidateAll(constantInvalidators);
}
}
public DynamicMethod retrieveMethod(String name) {
return getMethods().get(name);
}
public RubyModule findImplementer(RubyModule clazz) {
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
if (module.isSame(clazz)) return module;
}
return null;
}
public void addModuleFunction(String name, DynamicMethod method) {
addMethod(name, method);
getSingletonClass().addMethod(name, method);
}
public synchronized void defineAlias(String name, String oldName) {
testFrozen("module");
putAlias(name, searchForAliasMethod(getRuntime(), oldName), oldName);
methodLocation.invalidateCoreClasses();
methodLocation.invalidateCacheDescendants();
}
public void putAlias(String id, DynamicMethod method, String oldName) {
if (id.equals(oldName)) return;
putMethod(getRuntime(), id, new AliasMethod(this, new CacheEntry(method, method.getImplementationClass(), generation), oldName));
if (isRefinement()) {
addRefinedMethodEntry(id, method);
}
}
public void putAlias(String id, CacheEntry entry, String oldName) {
if (id.equals(oldName)) return;
putMethod(getRuntime(), id, new AliasMethod(this, entry, oldName));
if (isRefinement()) {
addRefinedMethodEntry(id, entry.method);
}
}
public synchronized void defineAliases(List<String> aliases, String oldId) {
testFrozen("module");
Ruby runtime = getRuntime();
CacheEntry entry = searchForAliasMethod(runtime, oldId);
for (String name: aliases) {
putAlias(name, entry, oldId);
}
methodLocation.invalidateCoreClasses();
methodLocation.invalidateCacheDescendants();
}
private CacheEntry searchForAliasMethod(Ruby runtime, String id) {
CacheEntry entry = deepMethodSearch(id, runtime);
final DynamicMethod method = entry.method;
if (method instanceof NativeCallMethod) {
DynamicMethod.NativeCall nativeCall = ((NativeCallMethod) method).getNativeCall();
if (nativeCall == null) return entry;
Method javaMethod = nativeCall.getMethod();
JRubyMethod anno = javaMethod.getAnnotation(JRubyMethod.class);
if (anno == null) return entry;
if (anno.reads().length > 0 || anno.writes().length > 0) {
MethodIndex.addMethodReadFields(id, anno.reads());
MethodIndex.addMethodWriteFields(id, anno.writes());
if (runtime.isVerbose()) {
String baseName = getBaseName();
char refChar = '#';
String simpleName = getSimpleName();
if (baseName == null && this instanceof MetaClass) {
IRubyObject attached = ((MetaClass) this).getAttached();
if (attached instanceof RubyModule) {
simpleName = ((RubyModule) attached).getSimpleName();
refChar = '.';
}
}
runtime.getWarnings().warning(simpleName + refChar + id + " accesses caller method's state and should not be aliased");
}
}
}
return entry;
}
public RubyClass defineOrGetClassUnder(String name, RubyClass superClazz) {
return defineOrGetClassUnder(name, superClazz, null);
}
public RubyClass defineOrGetClassUnder(String name, RubyClass superClazz, ObjectAllocator allocator) {
Ruby runtime = getRuntime();
IRubyObject classObj = getConstantAtSpecial(name);
RubyClass clazz;
if (classObj != null) {
if (!(classObj instanceof RubyClass)) throw runtime.newTypeError(name + " is not a class");
clazz = (RubyClass)classObj;
if (superClazz != null) {
RubyClass tmp = clazz.getSuperClass();
while (tmp != null && tmp.isIncluded()) tmp = tmp.getSuperClass();
if (tmp != null) tmp = tmp.getRealClass();
if (tmp != superClazz) throw runtime.newTypeError(str(runtime, "superclass mismatch for class ", ids(runtime, name)));
}
} else if ((clazz = searchProvidersForClass(name, superClazz)) != null) {
} else {
if (superClazz == null) superClazz = runtime.getObject();
if (allocator == null) {
if (isReifiable(runtime, superClazz)) {
if (Options.REIFY_CLASSES.load()) {
allocator = REIFYING_OBJECT_ALLOCATOR;
} else if (Options.REIFY_VARIABLES.load()) {
allocator = IVAR_INSPECTING_OBJECT_ALLOCATOR;
} else {
allocator = OBJECT_ALLOCATOR;
}
} else {
allocator = superClazz.getAllocator();
}
}
clazz = RubyClass.newClass(runtime, superClazz, name, allocator, this, true);
}
return clazz;
}
private boolean isReifiable(Ruby runtime, RubyClass superClass) {
if (superClass == runtime.getObject()) return true;
if (superClass.getAllocator() == IVAR_INSPECTING_OBJECT_ALLOCATOR) return true;
return false;
}
public RubyModule defineOrGetModuleUnder(String name) {
Ruby runtime = getRuntime();
IRubyObject moduleObj = getConstantAtSpecial(name);
RubyModule module;
if (moduleObj != null) {
if (!moduleObj.isModule()) throw runtime.newTypeError(str(runtime, ids(runtime, name), " is not a module"));
module = (RubyModule)moduleObj;
} else if ((module = searchProvidersForModule(name)) != null) {
} else {
module = RubyModule.newModule(runtime, name, this, true);
}
return module;
}
public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator) {
return getRuntime().defineClassUnder(name, superClass, allocator, this);
}
public RubyModule defineModuleUnder(String name) {
return getRuntime().defineModuleUnder(name, this);
}
private void addAccessor(ThreadContext context, RubySymbol identifier, Visibility visibility, boolean readable, boolean writeable) {
String internedIdentifier = identifier.idString();
final Ruby runtime = context.runtime;
if (visibility == MODULE_FUNCTION) {
runtime.getWarnings().warn(ID.ACCESSOR_MODULE_FUNCTION, "attribute accessor as module_function");
visibility = PRIVATE;
}
if (!(IdUtil.isLocal(internedIdentifier) || IdUtil.isConstant(internedIdentifier))) {
throw runtime.newNameError("invalid attribute name", internedIdentifier);
}
final String variableName = identifier.asInstanceVariable().idString();
if (readable) {
addMethod(internedIdentifier, new AttrReaderMethod(methodLocation, visibility, variableName));
callMethod(context, "method_added", identifier);
}
if (writeable) {
identifier = identifier.asWriter();
addMethod(identifier.idString(), new AttrWriterMethod(methodLocation, visibility, variableName));
callMethod(context, "method_added", identifier);
}
}
public void setMethodVisibility(IRubyObject[] methods, Visibility visibility) {
for (int i = 0; i < methods.length; i++) {
exportMethod(TypeConverter.checkID(methods[i]).idString(), visibility);
}
}
public void exportMethod(String name, Visibility visibility) {
Ruby runtime = getRuntime();
CacheEntry entry = deepMethodSearch(name, runtime);
DynamicMethod method = entry.method;
if (method.getVisibility() != visibility) {
if (this == method.getImplementationClass()) {
method.setVisibility(visibility);
} else {
DynamicMethod newMethod = new PartialDelegatingMethod(this, method, visibility);
methodLocation.addMethod(name, newMethod);
}
invalidateCoreClasses();
invalidateCacheDescendants();
}
}
private CacheEntry deepMethodSearch(String id, Ruby runtime) {
CacheEntry orig = searchWithCache(id);
if (orig.method.isRefined()) {
orig = resolveRefinedMethod(null, orig, id, true);
}
if (orig.method.isUndefined() || orig.method.isRefined()) {
if (!isModule()
|| (orig = runtime.getObject().searchWithCache(id)).method.isUndefined()) {
RubySymbol name = runtime.newSymbol(id);
throw runtime.newNameError(undefinedMethodMessage(runtime, name, rubyName(), isModule()), id);
}
}
return orig;
}
public static String undefinedMethodMessage(Ruby runtime, IRubyObject name, IRubyObject modName, boolean isModule) {
return str(runtime, "undefined method `", name, "' for " + (isModule ? "module" : "class") + " `", modName, "'");
}
public boolean isMethodBound(String name, boolean checkVisibility) {
DynamicMethod method = searchMethod(name);
return !method.isUndefined() && !(checkVisibility && method.getVisibility() == PRIVATE);
}
public boolean respondsToMethod(String name, boolean checkVisibility) {
return Helpers.respondsToMethod(searchMethod(name), checkVisibility);
}
@Deprecated
public boolean isMethodBound(String name, boolean checkVisibility, boolean checkRespondTo) {
return checkRespondTo ? respondsToMethod(name, checkVisibility): isMethodBound(name, checkVisibility);
}
public final IRubyObject newMethod(IRubyObject receiver, String methodName, boolean bound, Visibility visibility) {
return newMethod(receiver, methodName, bound, visibility, false, true);
}
public final IRubyObject newMethod(IRubyObject receiver, final String methodName, boolean bound, Visibility visibility, boolean respondToMissing) {
return newMethod(receiver, methodName, bound, visibility, respondToMissing, true);
}
public static class RespondToMissingMethod extends JavaMethod.JavaMethodNBlock {
final String methodName;
public RespondToMissingMethod(RubyModule implClass, Visibility visibility, String methodName) {
super(implClass, visibility, methodName);
setParameterList(REST);
this.methodName = methodName;
}
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
return Helpers.callMethodMissing(context, self, getVisibility(), name, CallType.UNKNOWN, args, block);
}
@Override
public boolean equals(Object other) {
return this == other ||
(other instanceof RespondToMissingMethod &&
((RespondToMissingMethod) other).methodName.equals(methodName));
}
@Override
public int hashCode() {
return methodName.hashCode();
}
}
public IRubyObject newMethod(IRubyObject receiver, final String methodName, boolean bound, Visibility visibility, boolean respondToMissing, boolean priv) {
CacheEntry entry = searchWithCache(methodName);
if (entry.method.isUndefined() || (visibility != null && entry.method.getVisibility() != visibility)) {
if (respondToMissing) {
if (receiver.respondsToMissing(methodName, priv)) {
entry = new CacheEntry(
new RespondToMissingMethod(this, PUBLIC, methodName),
entry.sourceModule,
entry.token);
} else {
throw getRuntime().newNameError("undefined method `" + methodName + "' for class `" + getName() + '\'', methodName);
}
} else {
throw getRuntime().newNameError("undefined method `" + methodName + "' for class `" + getName() + '\'', methodName);
}
}
RubyModule implementationModule = entry.method.getDefinedClass();
RubyModule originModule = this;
while (originModule != implementationModule && (originModule.isSingleton() || originModule.isIncluded())) {
originModule = originModule.getSuperClass();
}
AbstractRubyMethod newMethod;
if (bound) {
newMethod = RubyMethod.newMethod(implementationModule, methodName, originModule, methodName, entry, receiver);
} else {
newMethod = RubyUnboundMethod.newUnboundMethod(implementationModule, methodName, originModule, methodName, entry);
}
newMethod.infectBy(this);
return newMethod;
}
@JRubyMethod(name = "define_method", reads = VISIBILITY)
public IRubyObject define_method(ThreadContext context, IRubyObject arg0, Block block) {
Visibility visibility = getCurrentVisibilityForDefineMethod(context);
return defineMethodFromBlock(context, arg0, block, visibility);
}
private Visibility getCurrentVisibilityForDefineMethod(ThreadContext context) {
return context.getCurrentFrame().getSelf() == this ? context.getCurrentVisibility() : PUBLIC;
}
public IRubyObject defineMethodFromBlock(ThreadContext context, IRubyObject arg0, Block block, Visibility visibility) {
final Ruby runtime = context.runtime;
RubySymbol name = TypeConverter.checkID(arg0);
DynamicMethod newMethod;
if (!block.isGiven()) {
throw runtime.newArgumentError("tried to create Proc object without a block");
}
if (block.getBody() instanceof IRBlockBody &&
runtime.getInstanceConfig().getCompileMode().shouldJIT()) {
IRBlockBody body = (IRBlockBody) block.getBody();
IRClosure closure = body.getScope();
IRMethod method = closure.convertToMethod(name.getBytes());
if (method != null) {
newMethod = new DefineMethodMethod(method, visibility, this, context.getFrameBlock());
Helpers.addInstanceMethod(this, name, newMethod, visibility, context, runtime);
return name;
}
}
newMethod = createProcMethod(runtime, name.idString(), visibility, block);
Helpers.addInstanceMethod(this, name, newMethod, visibility, context, runtime);
return name;
}
@JRubyMethod(name = "define_method", reads = VISIBILITY)
public IRubyObject define_method(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
Visibility visibility = getCurrentVisibilityForDefineMethod(context);
return defineMethodFromCallable(context, arg0, arg1, visibility);
}
public IRubyObject defineMethodFromCallable(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Visibility visibility) {
final Ruby runtime = context.runtime;
RubySymbol name = TypeConverter.checkID(arg0);
DynamicMethod newMethod;
if (runtime.getProc().isInstance(arg1)) {
RubyProc proc = (RubyProc)arg1;
newMethod = createProcMethod(runtime, name.idString(), visibility, proc.getBlock());
} else if (arg1 instanceof AbstractRubyMethod) {
AbstractRubyMethod method = (AbstractRubyMethod)arg1;
checkValidBindTargetFrom(context, (RubyModule) method.owner(context));
newMethod = method.getMethod().dup();
newMethod.setImplementationClass(this);
newMethod.setVisibility(visibility);
} else {
throw runtime.newTypeError("wrong argument type " + arg1.getType().getName() + " (expected Proc/Method)");
}
Helpers.addInstanceMethod(this, name, newMethod, visibility, context, runtime);
return name;
}
@Deprecated
public IRubyObject define_method(ThreadContext context, IRubyObject[] args, Block block) {
switch (args.length) {
case 1:
return define_method(context, args[0], block);
case 2:
return define_method(context, args[0], args[1], block);
default:
throw context.runtime.newArgumentError("wrong number of arguments (" + args.length + " for 2)");
}
}
private DynamicMethod createProcMethod(Ruby runtime, String name, Visibility visibility, Block block) {
block = block.cloneBlockAndFrame();
block.getBinding().getFrame().setKlazz(this);
block.getBinding().getFrame().setName(name);
block.getBinding().setMethod(name);
RubyProc proc = runtime.newProc(Block.Type.LAMBDA, block);
block.getBody().getStaticScope().makeArgumentScope();
return new ProcMethod(this, proc, visibility, name);
}
public IRubyObject name() {
return name19();
}
@JRubyMethod(name = "name")
public IRubyObject name19() {
return getBaseName() == null ? getRuntime().getNil() : rubyName().strDup(getRuntime());
}
protected final IRubyObject cloneMethods(RubyModule clone) {
Ruby runtime = getRuntime();
RubyModule realType = this.getNonIncludedClass();
for (Map.Entry<String, DynamicMethod> entry : getMethods().entrySet()) {
DynamicMethod method = entry.getValue();
if (method.isImplementedBy(realType) || method.isUndefined()) {
DynamicMethod clonedMethod = method.dup();
clonedMethod.setImplementationClass(clone);
clone.putMethod(runtime, entry.getKey(), clonedMethod);
}
}
return clone;
}
@JRubyMethod(name = "initialize_copy", required = 1, visibility = Visibility.PRIVATE)
@Override
public IRubyObject initialize_copy(IRubyObject original) {
if (this instanceof RubyClass) {
checkSafeTypeToCopy((RubyClass) original);
}
super.initialize_copy(original);
RubyModule originalModule = (RubyModule)original;
if (!getMetaClass().isSingleton()) {
setMetaClass(originalModule.getSingletonClassCloneAndAttach(this));
}
setSuperClass(originalModule.getSuperClass());
if (originalModule.hasVariables()) syncVariables(originalModule);
syncConstants(originalModule);
originalModule.cloneMethods(this);
return this;
}
private void checkSafeTypeToCopy(RubyClass original) {
Ruby runtime = getRuntime();
if (original == runtime.getBasicObject()) throw runtime.newTypeError("can't copy the root class");
if (getSuperClass() == runtime.getBasicObject()) throw runtime.newTypeError("already initialized class");
if (original.isSingleton()) throw runtime.newTypeError("can't copy singleton class");
}
public void syncConstants(RubyModule other) {
if (other.getConstantMap() != Collections.EMPTY_MAP) {
getConstantMapForWrite().putAll(other.getConstantMap());
}
}
public void syncClassVariables(RubyModule other) {
if (other.getClassVariablesForRead() != Collections.EMPTY_MAP) {
getClassVariables().putAll(other.getClassVariablesForRead());
}
}
@JRubyMethod(name = "included_modules")
public RubyArray included_modules(ThreadContext context) {
RubyArray ary = context.runtime.newArray();
for (RubyModule p = getSuperClass(); p != null; p = p.getSuperClass()) {
if (p.isIncluded()) {
ary.append(p.getNonIncludedClass());
}
}
return ary;
}
public boolean hasPrepends() {
return methodLocation != this;
}
@JRubyMethod(name = "ancestors")
public RubyArray ancestors(ThreadContext context) {
return context.runtime.newArray(getAncestorList());
}
@Deprecated
public RubyArray ancestors() {
return getRuntime().newArray(getAncestorList());
}
public List<IRubyObject> getAncestorList() {
ArrayList<IRubyObject> list = new ArrayList<IRubyObject>();
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
if (module.methodLocation == module) list.add(module.getDelegate().getNonIncludedClass());
}
return list;
}
public boolean hasModuleInPrepends(RubyModule type) {
RubyModule stopClass = getPrependCeiling();
for (RubyModule module = this; module != stopClass; module = module.getSuperClass()) {
if (type == module.getNonIncludedClass()) return true;
}
return false;
}
private RubyModule getPrependCeiling() {
RubyClass mlSuper = methodLocation.getSuperClass();
return mlSuper == null ? methodLocation : mlSuper.getRealClass();
}
public boolean hasModuleInHierarchy(RubyModule type) {
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
if (module.getNonIncludedClass() == type) return true;
}
return false;
}
@Override
public int hashCode() {
return id;
}
@JRubyMethod(name = "hash")
@Override
public RubyFixnum hash() {
return getRuntime().newFixnum(id);
}
@JRubyMethod(name = "to_s", alias = "inspect")
@Override
public RubyString to_s() {
Ruby runtime = getRuntime();
if (isSingleton()) {
IRubyObject attached = ((MetaClass) this).getAttached();
RubyString buffer = runtime.newString("#<Class:");
if (attached instanceof RubyModule) {
buffer.cat19(attached.inspect().convertToString());
} else if (attached != null) {
buffer.cat19((RubyString) attached.anyToString());
}
buffer.cat('>', buffer.getEncoding());
return buffer;
}
RubyModule refinedClass = this.refinedClass;
if (refinedClass != null) {
RubyString buffer = runtime.newString("#<refinement:");
buffer.cat19(refinedClass.inspect().convertToString());
buffer.cat('@', buffer.getEncoding());
buffer.cat19((definedAt.inspect().convertToString()));
buffer.cat('>', buffer.getEncoding());
return buffer;
}
return rubyName().strDup(runtime);
}
@JRubyMethod(name = "===", required = 1)
@Override
public RubyBoolean op_eqq(ThreadContext context, IRubyObject obj) {
return context.runtime.newBoolean(isInstance(obj));
}
@Override
public boolean equals(Object other) {
return this == other;
}
@JRubyMethod(name = "==", required = 1)
@Override
public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
if(!(other instanceof RubyModule)) return context.fals;
RubyModule otherModule = (RubyModule) other;
if(otherModule.isIncluded()) {
return context.runtime.newBoolean(otherModule.isSame(this));
} else {
return context.runtime.newBoolean(isSame(otherModule));
}
}
@JRubyMethod(name = "freeze")
@Override
public final IRubyObject freeze(ThreadContext context) {
to_s();
return super.freeze(context);
}
@JRubyMethod(name = "<=", required = 1)
public IRubyObject op_le(IRubyObject arg) {
Ruby runtime = getRuntime();
if (!(arg instanceof RubyModule)) {
throw runtime.newTypeError("compared with non class/module");
}
RubyModule argMod = (RubyModule) arg;
if (searchAncestor(argMod.getMethodLocation()) != null) {
return runtime.getTrue();
}
if (argMod.searchAncestor(this) != null) {
return runtime.getFalse();
}
return runtime.getNil();
}
protected RubyModule searchAncestor(RubyModule c) {
RubyModule cl = this;
while (cl != null) {
if (cl == c || cl.isSame(c) || cl.getDelegate().getNonIncludedClass() == c) {
return cl;
}
cl = cl.getSuperClass();
}
return null;
}
@JRubyMethod(name = "<", required = 1)
public IRubyObject op_lt(IRubyObject obj) {
return obj == this ? getRuntime().getFalse() : op_le(obj);
}
@JRubyMethod(name = ">=", required = 1)
public IRubyObject op_ge(IRubyObject obj) {
if (!(obj instanceof RubyModule)) {
throw getRuntime().newTypeError("compared with non class/module");
}
return ((RubyModule) obj).op_le(this);
}
@JRubyMethod(name = ">", required = 1)
public IRubyObject op_gt(IRubyObject obj) {
return this == obj ? getRuntime().getFalse() : op_ge(obj);
}
@JRubyMethod(name = "<=>", required = 1)
public IRubyObject op_cmp(IRubyObject obj) {
if (this == obj) return getRuntime().newFixnum(0);
if (!(obj instanceof RubyModule)) return getRuntime().getNil();
RubyModule module = (RubyModule) obj;
if (module.isKindOfModule(this)) return getRuntime().newFixnum(1);
if (this.isKindOfModule(module)) return getRuntime().newFixnum(-1);
return getRuntime().getNil();
}
public boolean isKindOfModule(RubyModule type) {
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
if (module.isSame(type)) return true;
}
return false;
}
protected boolean isSame(RubyModule module) {
return this == module;
}
@JRubyMethod(name = "initialize", visibility = PRIVATE)
public IRubyObject initialize(ThreadContext context, Block block) {
if (block.isGiven()) {
module_exec(context, new IRubyObject[] {this}, block);
}
return context.nil;
}
public void addReadWriteAttribute(ThreadContext context, String name) {
addAccessor(context, TypeConverter.checkID(context.runtime, name), PUBLIC, true, true);
}
public void addReadAttribute(ThreadContext context, String name) {
addAccessor(context, TypeConverter.checkID(context.runtime, name), PUBLIC, true, false);
}
public void addWriteAttribute(ThreadContext context, String name) {
addAccessor(context, TypeConverter.checkID(context.runtime, name), PUBLIC, false, true);
}
@JRubyMethod(name = "attr", rest = true, reads = VISIBILITY)
public IRubyObject attr(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
if (args.length == 2 && (args[1] == runtime.getTrue() || args[1] == runtime.getFalse())) {
runtime.getWarnings().warn(ID.OBSOLETE_ARGUMENT, "optional boolean argument is obsoleted");
addAccessor(context, TypeConverter.checkID(args[0]), context.getCurrentVisibility(), args[0].isTrue(), args[1].isTrue());
return runtime.getNil();
}
return attr_reader(context, args);
}
@Deprecated
public IRubyObject attr19(ThreadContext context, IRubyObject[] args) {
return attr(context, args);
}
@Deprecated
public IRubyObject attr_reader(IRubyObject[] args) {
return attr_reader(getRuntime().getCurrentContext(), args);
}
@JRubyMethod(name = "attr_reader", rest = true, reads = VISIBILITY)
public IRubyObject attr_reader(ThreadContext context, IRubyObject[] args) {
Visibility visibility = context.getCurrentVisibility();
for (int i = 0; i < args.length; i++) {
addAccessor(context, TypeConverter.checkID(args[i]), visibility, true, false);
}
return context.nil;
}
@JRubyMethod(name = "attr_writer", rest = true, reads = VISIBILITY)
public IRubyObject attr_writer(ThreadContext context, IRubyObject[] args) {
Visibility visibility = context.getCurrentVisibility();
for (int i = 0; i < args.length; i++) {
addAccessor(context, TypeConverter.checkID(args[i]), visibility, false, true);
}
return context.nil;
}
@Deprecated
public IRubyObject attr_accessor(IRubyObject[] args) {
return attr_accessor(getRuntime().getCurrentContext(), args);
}
@JRubyMethod(name = "attr_accessor", rest = true, reads = VISIBILITY)
public IRubyObject attr_accessor(ThreadContext context, IRubyObject[] args) {
Visibility visibility = context.getCurrentVisibility();
for (int i = 0; i < args.length; i++) {
addAccessor(context, TypeConverter.checkID(args[i]), visibility, true, true);
}
return context.nil;
}
private RubyArray instance_methods(IRubyObject[] args, Visibility visibility, boolean not) {
boolean includeSuper = args.length > 0 ? args[0].isTrue() : true;
return instanceMethods(visibility, includeSuper, true, not);
}
public RubyArray instanceMethods(IRubyObject[] args, Visibility visibility, boolean obj, boolean not) {
boolean includeSuper = args.length > 0 ? args[0].isTrue() : true;
return instanceMethods(visibility, includeSuper, obj, not);
}
public RubyArray instanceMethods(Visibility visibility, boolean includeSuper, boolean obj, boolean not) {
RubyArray ary = getRuntime().newArray();
populateInstanceMethodNames(new HashSet<>(), ary, visibility, obj, not, includeSuper);
return ary;
}
final void populateInstanceMethodNames(final Set<String> seen, final RubyArray ary, Visibility visibility,
boolean obj, boolean not, boolean recur) {
Ruby runtime = getRuntime();
RubyModule mod = this;
boolean prepended = false;
if (!recur && methodLocation != this) {
mod = methodLocation;
prepended = true;
}
for (; mod != null; mod = mod.getSuperClass()) {
mod.addMethodSymbols(runtime, seen, ary, not, visibility);
if (!prepended && mod.isIncluded()) continue;
if (obj && mod.isSingleton()) continue;
if (!recur) break;
}
}
protected void addMethodSymbols(Ruby runtime, Set<String> seen, RubyArray ary, boolean not, Visibility visibility) {
getMethods().forEach((id, method) -> {
if (method instanceof RefinedMarker) return;
if (seen.add(id)) {
if ((!not && method.getVisibility() == visibility || (not && method.getVisibility() != visibility))
&& !method.isUndefined()) {
ary.append(runtime.newSymbol(id));
}
}
});
}
public RubyArray instance_methods(IRubyObject[] args) {
return instance_methods19(args);
}
@JRubyMethod(name = "instance_methods", optional = 1)
public RubyArray instance_methods19(IRubyObject[] args) {
return instanceMethods(args, PRIVATE, false, true);
}
public RubyArray public_instance_methods(IRubyObject[] args) {
return public_instance_methods19(args);
}
@JRubyMethod(name = "public_instance_methods", optional = 1)
public RubyArray public_instance_methods19(IRubyObject[] args) {
return instanceMethods(args, PUBLIC, false, false);
}
@JRubyMethod(name = "instance_method", required = 1)
public IRubyObject instance_method(IRubyObject symbol) {
return newMethod(null, TypeConverter.checkID(symbol).idString(), false, null);
}
@JRubyMethod(name = "public_instance_method", required = 1)
public IRubyObject public_instance_method(IRubyObject symbol) {
return newMethod(null, TypeConverter.checkID(symbol).idString(), false, PUBLIC);
}
@JRubyMethod(name = "protected_instance_methods", optional = 1)
public RubyArray protected_instance_methods(IRubyObject[] args) {
return instanceMethods(args, PROTECTED, false, false);
}
@Deprecated
public RubyArray protected_instance_methods19(IRubyObject[] args) {
return protected_instance_methods(args);
}
@JRubyMethod(name = "private_instance_methods", optional = 1)
public RubyArray private_instance_methods(IRubyObject[] args) {
return instanceMethods(args, PRIVATE, false, false);
}
@Deprecated
public RubyArray private_instance_methods19(IRubyObject[] args) {
return private_instance_methods(args);
}
@JRubyMethod(name = "prepend_features", required = 1, visibility = PRIVATE)
public RubyModule prepend_features(IRubyObject include) {
if (!isModule()) {
throw getRuntime().newTypeError(this, getRuntime().getModule());
}
if (!(include instanceof RubyModule)) {
throw getRuntime().newTypeError(include, getRuntime().getModule());
}
if (!(include.isModule() || include.isClass())) {
throw getRuntime().newTypeError(include, getRuntime().getModule());
}
((RubyModule) include).prependModule(this);
return this;
}
@JRubyMethod(name = "append_features", required = 1, visibility = PRIVATE)
public RubyModule append_features(IRubyObject include) {
if (!isModule()) {
throw getRuntime().newTypeError(this, getRuntime().getModule());
}
if (!(include instanceof RubyModule)) {
throw getRuntime().newTypeError(include, getRuntime().getModule());
}
if (!(include.isModule() || include.isClass())) {
throw getRuntime().newTypeError(include, getRuntime().getModule());
}
((RubyModule) include).includeModule(this);
return this;
}
@JRubyMethod(name = "extend_object", required = 1, visibility = PRIVATE)
public IRubyObject extend_object(IRubyObject obj) {
if (!isModule()) {
throw getRuntime().newTypeError(this, getRuntime().getModule());
}
obj.getSingletonClass().includeModule(this);
return obj;
}
@JRubyMethod(name = "include", required = 1, rest = true)
public RubyModule include(IRubyObject[] modules) {
ThreadContext context = metaClass.runtime.getCurrentContext();
for (int i = modules.length; --i >= 0; ) {
IRubyObject module = modules[i];
if ( ! module.isModule() ) {
throw context.runtime.newTypeError(module, context.runtime.getModule());
}
}
for (int i = modules.length - 1; i >= 0; i--) {
IRubyObject module = modules[i];
module.callMethod(context, "append_features", this);
module.callMethod(context, "included", this);
}
return this;
}
@JRubyMethod(name = "include", required = 1)
public RubyModule include(ThreadContext context, IRubyObject module) {
if (!module.isModule()) {
throw context.runtime.newTypeError(module, context.runtime.getModule());
}
module.callMethod(context, "append_features", this);
module.callMethod(context, "included", this);
return this;
}
@JRubyMethod(name = "included", required = 1, visibility = PRIVATE)
public IRubyObject included(ThreadContext context, IRubyObject other) {
return context.nil;
}
@JRubyMethod(name = "extended", required = 1, visibility = PRIVATE)
public IRubyObject extended(ThreadContext context, IRubyObject other, Block block) {
return context.nil;
}
@JRubyMethod(name = "mix", visibility = PRIVATE)
public IRubyObject mix(ThreadContext context, IRubyObject mod) {
Ruby runtime = context.runtime;
if (!mod.isModule()) {
throw runtime.newTypeError(mod, runtime.getModule());
}
for (Map.Entry<String, DynamicMethod> entry : ((RubyModule)mod).methods.entrySet()) {
if (methodLocation.getMethods().containsKey(entry.getKey())) {
throw runtime.newArgumentError("method would conflict - " + entry.getKey());
}
}
for (Map.Entry<String, DynamicMethod> entry : ((RubyModule)mod).methods.entrySet()) {
methodLocation.getMethodsForWrite().put(entry.getKey(), entry.getValue().dup());
}
return mod;
}
@JRubyMethod(name = "mix", visibility = PRIVATE)
public IRubyObject mix(ThreadContext context, IRubyObject mod, IRubyObject hash0) {
Ruby runtime = context.runtime;
RubyHash methodNames;
if (!mod.isModule()) {
throw runtime.newTypeError(mod, runtime.getModule());
}
if (hash0 instanceof RubyHash) {
methodNames = (RubyHash)hash0;
} else {
throw runtime.newTypeError(hash0, runtime.getHash());
}
for (Map.Entry<IRubyObject, IRubyObject> entry : (Set<Map.Entry<IRubyObject, IRubyObject>>)methodNames.directEntrySet()) {
String name = entry.getValue().toString();
if (methods.containsKey(entry.getValue().toString())) {
throw runtime.newArgumentError("constant would conflict - " + name);
}
}
for (Map.Entry<String, DynamicMethod> entry : ((RubyModule)mod).methods.entrySet()) {
if (methods.containsKey(entry.getKey())) {
throw runtime.newArgumentError("method would conflict - " + entry.getKey());
}
}
for (Map.Entry<String, DynamicMethod> entry : ((RubyModule)mod).methods.entrySet()) {
String id = entry.getKey();
IRubyObject mapped = methodNames.fastARef(runtime.newSymbol(id));
if (mapped == NEVER) {
} else if (mapped == context.nil) {
continue;
} else {
id = TypeConverter.checkID(mapped).idString();
}
methodLocation.getMethodsForWrite().put(id, entry.getValue().dup());
}
return mod;
}
private void setVisibility(ThreadContext context, IRubyObject[] args, Visibility visibility) {
if (args.length == 0) {
context.setCurrentVisibility(visibility);
} else {
setMethodVisibility(args, visibility);
}
}
@JRubyMethod(name = "public", rest = true, visibility = PRIVATE, writes = VISIBILITY)
public RubyModule rbPublic(ThreadContext context, IRubyObject[] args) {
checkFrozen();
setVisibility(context, args, PUBLIC);
return this;
}
@JRubyMethod(name = "protected", rest = true, visibility = PRIVATE, writes = VISIBILITY)
public RubyModule rbProtected(ThreadContext context, IRubyObject[] args) {
checkFrozen();
setVisibility(context, args, PROTECTED);
return this;
}
@JRubyMethod(name = "private", rest = true, visibility = PRIVATE, writes = VISIBILITY)
public RubyModule rbPrivate(ThreadContext context, IRubyObject[] args) {
checkFrozen();
setVisibility(context, args, PRIVATE);
return this;
}
@JRubyMethod(name = "module_function", rest = true, visibility = PRIVATE, writes = VISIBILITY)
public RubyModule module_function(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
if (!isModule()) {
throw context.runtime.newTypeError("module_function must be called for modules");
}
if (args.length == 0) {
context.setCurrentVisibility(MODULE_FUNCTION);
} else {
setMethodVisibility(args, PRIVATE);
for (int i = 0; i < args.length; i++) {
RubySymbol name = TypeConverter.checkID(args[i]);
DynamicMethod newMethod = deepMethodSearch(name.idString(), runtime).method.dup();
newMethod.setImplementationClass(getSingletonClass());
newMethod.setVisibility(PUBLIC);
getSingletonClass().addMethod(name.idString(), newMethod);
callMethod(context, "singleton_method_added", name);
}
}
return this;
}
@JRubyMethod(name = "method_added", required = 1, visibility = PRIVATE)
public IRubyObject method_added(ThreadContext context, IRubyObject nothing) {
return context.nil;
}
@JRubyMethod(name = "method_removed", required = 1, visibility = PRIVATE)
public IRubyObject method_removed(ThreadContext context, IRubyObject nothing) {
return context.nil;
}
@JRubyMethod(name = "method_undefined", required = 1, visibility = PRIVATE)
public IRubyObject method_undefined(ThreadContext context, IRubyObject nothing) {
return context.nil;
}
@JRubyMethod(name = "method_defined?", required = 1)
public RubyBoolean method_defined_p(ThreadContext context, IRubyObject symbol) {
return isMethodBound(TypeConverter.checkID(symbol).idString(), true) ? context.tru : context.fals;
}
@JRubyMethod(name = "public_method_defined?", required = 1)
public IRubyObject public_method_defined(ThreadContext context, IRubyObject symbol) {
DynamicMethod method = searchMethod(TypeConverter.checkID(symbol).idString());
return context.runtime.newBoolean(!method.isUndefined() && method.getVisibility() == PUBLIC);
}
@JRubyMethod(name = "protected_method_defined?", required = 1)
public IRubyObject protected_method_defined(ThreadContext context, IRubyObject symbol) {
DynamicMethod method = searchMethod(TypeConverter.checkID(symbol).idString());
return context.runtime.newBoolean(!method.isUndefined() && method.getVisibility() == PROTECTED);
}
@JRubyMethod(name = "private_method_defined?", required = 1)
public IRubyObject private_method_defined(ThreadContext context, IRubyObject symbol) {
DynamicMethod method = searchMethod(TypeConverter.checkID(symbol).idString());
return context.runtime.newBoolean(!method.isUndefined() && method.getVisibility() == PRIVATE);
}
@JRubyMethod(name = "public_class_method", rest = true)
public RubyModule public_class_method(IRubyObject[] args) {
checkFrozen();
getSingletonClass().setMethodVisibility(args, PUBLIC);
return this;
}
@JRubyMethod(name = "private_class_method", rest = true)
public RubyModule private_class_method(IRubyObject[] args) {
checkFrozen();
getSingletonClass().setMethodVisibility(args, PRIVATE);
return this;
}
@JRubyMethod(name = "alias_method", required = 2)
public RubyModule alias_method(ThreadContext context, IRubyObject newId, IRubyObject oldId) {
RubySymbol newSym = TypeConverter.checkID(newId);
RubySymbol oldSym = TypeConverter.checkID(oldId);
defineAlias(newSym.idString(), oldSym.idString());
if (isSingleton()) {
((MetaClass) this).getAttached().callMethod(context, "singleton_method_added", newSym);
} else {
callMethod(context, "method_added", newSym);
}
return this;
}
@JRubyMethod(name = "undef_method", rest = true)
public RubyModule undef_method(ThreadContext context, IRubyObject[] args) {
for (int i=0; i<args.length; i++) {
RubySymbol name = TypeConverter.checkID(args[i]);
undef(context, name.idString());
}
return this;
}
@JRubyMethod(name = {"module_eval", "class_eval"},
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
public IRubyObject module_eval(ThreadContext context, Block block) {
return specificEval(context, this, block, EvalType.MODULE_EVAL);
}
@JRubyMethod(name = {"module_eval", "class_eval"},
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, Block block) {
return specificEval(context, this, arg0, block, EvalType.MODULE_EVAL);
}
@JRubyMethod(name = {"module_eval", "class_eval"},
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
return specificEval(context, this, arg0, arg1, block, EvalType.MODULE_EVAL);
}
@JRubyMethod(name = {"module_eval", "class_eval"},
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
return specificEval(context, this, arg0, arg1, arg2, block, EvalType.MODULE_EVAL);
}
@Deprecated
public IRubyObject module_eval(ThreadContext context, IRubyObject[] args, Block block) {
return specificEval(context, this, args, block, EvalType.MODULE_EVAL);
}
@JRubyMethod(name = {"module_exec", "class_exec"},
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
public IRubyObject module_exec(ThreadContext context, Block block) {
if (block.isGiven()) {
return yieldUnder(context, this, IRubyObject.NULL_ARRAY, block.cloneBlockAndFrame(), EvalType.MODULE_EVAL);
} else {
throw context.runtime.newLocalJumpErrorNoBlock();
}
}
@JRubyMethod(name = {"module_exec", "class_exec"}, rest = true,
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
public IRubyObject module_exec(ThreadContext context, IRubyObject[] args, Block block) {
if (block.isGiven()) {
return yieldUnder(context, this, args, block.cloneBlockAndFrame(), EvalType.MODULE_EVAL);
} else {
throw context.runtime.newLocalJumpErrorNoBlock();
}
}
@JRubyMethod(name = "remove_method", rest = true)
public RubyModule remove_method(ThreadContext context, IRubyObject[] args) {
for(int i=0;i<args.length;i++) {
removeMethod(context, TypeConverter.checkID(args[i]).idString());
}
return this;
}
public static void marshalTo(RubyModule module, MarshalStream output) throws java.io.IOException {
output.registerLinkTarget(module);
output.writeString(MarshalStream.getPathFromClass(module));
}
public static RubyModule unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
String name = RubyString.byteListToString(input.unmarshalString());
RubyModule result = UnmarshalStream.getModuleFromPath(input.getRuntime(), name);
input.registerLinkTarget(result);
return result;
}
@JRubyMethod(name = "nesting", reads = SCOPE, meta = true)
public static RubyArray nesting(ThreadContext context, IRubyObject recv, Block block) {
Ruby runtime = context.runtime;
RubyModule object = runtime.getObject();
StaticScope scope = context.getCurrentStaticScope();
RubyArray result = runtime.newArray();
for (StaticScope current = scope; current.getModule() != object; current = current.getPreviousCRefScope()) {
result.append(current.getModule());
}
return result;
}
private void doIncludeModule(RubyModule baseModule) {
List<RubyModule> modulesToInclude = gatherModules(baseModule);
RubyModule currentInclusionPoint = methodLocation;
ModuleLoop: for (RubyModule nextModule : modulesToInclude) {
checkForCyclicInclude(nextModule);
boolean superclassSeen = false;
for (RubyClass nextClass = getSuperClass(); nextClass != null; nextClass = nextClass.getSuperClass()) {
if (nextClass.isIncluded()) {
if (nextClass.getDelegate() == nextModule.getDelegate()) {
if (!superclassSeen) currentInclusionPoint = nextClass;
continue ModuleLoop;
}
} else {
superclassSeen = true;
}
}
currentInclusionPoint = proceedWithInclude(currentInclusionPoint, nextModule.getDelegate());
}
}
private void doPrependModule(RubyModule baseModule) {
List<RubyModule> modulesToInclude = gatherModules(baseModule);
if (methodLocation == this) {
RubyClass origin = new PrependedModule(getRuntime(), getSuperClass(), this);
if (this instanceof RubyClass) {
RubyClass insertBelowClass = (RubyClass)this;
if (insertBelowClass.getSuperClass() != null) {
insertBelowClass.getSuperClass().replaceSubclass(insertBelowClass, origin);
}
origin.addSubclass(insertBelowClass);
}
setSuperClass(origin);
}
RubyModule currentInclusionPoint = this;
ModuleLoop: for (RubyModule nextModule : modulesToInclude) {
checkForCyclicInclude(nextModule);
boolean superclassSeen = false;
RubyModule stopClass = getPrependCeiling();
for (RubyClass nextClass = getSuperClass(); nextClass != stopClass; nextClass = nextClass.getSuperClass()) {
if (nextClass.isIncluded()) {
if (nextClass.getDelegate() == nextModule.getDelegate()) {
if (!superclassSeen) currentInclusionPoint = nextClass;
continue ModuleLoop;
}
} else {
superclassSeen = true;
}
}
currentInclusionPoint = proceedWithPrepend(currentInclusionPoint, nextModule.getDelegate());
}
}
private List<RubyModule> gatherModules(RubyModule baseModule) {
List<RubyModule> modulesToInclude = new ArrayList<RubyModule>();
for (; baseModule != null; baseModule = baseModule.superClass) {
if (baseModule != baseModule.getMethodLocation()) continue;
modulesToInclude.add(baseModule.getDelegate());
}
return modulesToInclude;
}
private RubyModule proceedWithInclude(RubyModule insertAbove, RubyModule moduleToInclude) {
RubyClass wrapper = new IncludedModuleWrapper(getRuntime(), insertAbove.getSuperClass(), moduleToInclude);
if (insertAbove instanceof RubyClass) {
RubyClass insertAboveClass = (RubyClass)insertAbove;
if (insertAboveClass.getSuperClass() != null) {
insertAboveClass.getSuperClass().replaceSubclass(insertAboveClass, wrapper);
}
wrapper.addSubclass(insertAboveClass);
}
insertAbove.setSuperClass(wrapper);
insertAbove = insertAbove.getSuperClass();
if (isRefinement()) {
moduleToInclude.getMethods().forEach((name, method) -> addRefinedMethodEntry(name, method));
wrapper.setFlag(INCLUDED_INTO_REFINEMENT, true);
}
return insertAbove;
}
private RubyModule proceedWithPrepend(RubyModule insertBelow, RubyModule moduleToPrepend) {
if (!moduleToPrepend.isPrepended()) moduleToPrepend = moduleToPrepend.getNonIncludedClass();
return proceedWithInclude(insertBelow, moduleToPrepend);
}
@JRubyMethod(name = "class_variable_defined?", required = 1)
public IRubyObject class_variable_defined_p(ThreadContext context, IRubyObject var) {
String id = validateClassVariable(context.runtime, var);
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
if (module.hasClassVariable(id)) return context.tru;
}
return context.fals;
}
public IRubyObject class_variable_get(IRubyObject name) {
return getClassVar(name, validateClassVariable(getRuntime(), name));
}
@JRubyMethod(name = "class_variable_get")
public IRubyObject class_variable_get19(IRubyObject name) {
return class_variable_get(name);
}
public IRubyObject class_variable_set(IRubyObject name, IRubyObject value) {
return setClassVar(validateClassVariable(getRuntime(), name), value);
}
@JRubyMethod(name = "class_variable_set")
public IRubyObject class_variable_set19(IRubyObject name, IRubyObject value) {
return class_variable_set(name, value);
}
public IRubyObject remove_class_variable(ThreadContext context, IRubyObject name) {
return removeClassVariable(validateClassVariable(context.runtime, name));
}
@JRubyMethod(name = "remove_class_variable")
public IRubyObject remove_class_variable19(ThreadContext context, IRubyObject name) {
return remove_class_variable(context, name);
}
@Deprecated
public RubyArray class_variables19(ThreadContext context) {
return class_variables(context);
}
@JRubyMethod(name = "class_variables")
public RubyArray class_variables(ThreadContext context) {
Ruby runtime = context.runtime;
RubyArray ary = runtime.newArray();
Collection<String> names = classVariablesCommon(true);
for (String name : names) {
ary.add(runtime.newSymbol(name));
}
return ary;
}
@JRubyMethod(name = "class_variables")
public RubyArray class_variables(ThreadContext context, IRubyObject inherit) {
Ruby runtime = context.runtime;
RubyArray ary = runtime.newArray();
Collection<String> names = classVariablesCommon(inherit.isTrue());
for (String name : names) {
ary.add(runtime.newSymbol(name));
}
return ary;
}
private Collection<String> classVariablesCommon(boolean inherit) {
Set<String> names = new HashSet<String>();
for (RubyModule p = this; p != null; p = p.getSuperClass()) {
names.addAll(p.getClassVariableNameList());
if (!inherit) break;
}
return names;
}
public RubyBoolean const_defined_p(ThreadContext context, IRubyObject symbol) {
return const_defined_p19(context, new IRubyObject[]{symbol});
}
private RubyBoolean constantDefined(Ruby runtime, RubySymbol symbol, boolean inherit) {
if (symbol.validConstantName()) {
return runtime.newBoolean(getConstantSkipAutoload(symbol.idString(), inherit, inherit) != null);
}
throw runtime.newNameError(str(runtime, "wrong constant name", ids(runtime, symbol)), symbol.idString());
}
@JRubyMethod(name = "const_defined?", required = 1, optional = 1)
public RubyBoolean const_defined_p19(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
boolean inherit = args.length == 1 || (!args[1].isNil() && args[1].isTrue());
if (args[0] instanceof RubySymbol) return constantDefined(runtime, ((RubySymbol) args[0]), inherit);
RubyString fullName = args[0].convertToString();
IRubyObject value = ByteListHelper.split(fullName.getByteList(), CommonByteLists.COLON_COLON, (index, segment, module) -> {
if (index == 0) {
if (segment.realSize() == 0) return runtime.getObject();
module = this;
}
String id = RubySymbol.newConstantSymbol(runtime, fullName, segment).idString();
IRubyObject obj = ((RubyModule) module).getConstantNoConstMissing(id, inherit, inherit);
if (obj == null) return null;
if (!(obj instanceof RubyModule)) throw runtime.newTypeError(segment + " does not refer to class/module");
return obj;
}, (index, segment, module) -> {
if (module == null) module = this;
String id = RubySymbol.newConstantSymbol(runtime, fullName, segment).idString();
return ((RubyModule) module).getConstantSkipAutoload(id, inherit, inherit);
});
return runtime.newBoolean(value != null);
}
public IRubyObject const_get(IRubyObject symbol) {
return const_get_2_0(getRuntime().getCurrentContext(), new IRubyObject[]{symbol});
}
public IRubyObject const_get_1_9(ThreadContext context, IRubyObject[] args) {
return const_get_2_0(context, args);
}
@JRubyMethod(name = "const_get", required = 1, optional = 1)
public IRubyObject const_get_2_0(ThreadContext context, IRubyObject[] args) {
final Ruby runtime = context.runtime;
boolean inherit = args.length == 1 || ( ! args[1].isNil() && args[1].isTrue() );
final IRubyObject symbol = args[0];
RubySymbol fullName = TypeConverter.checkID(symbol);
String name = fullName.idString();
int sep = name.indexOf("::");
if (symbol instanceof RubySymbol && sep != -1) {
throw runtime.newNameError("wrong constant name", name);
}
RubyModule mod = this;
if (sep == 0) {
mod = runtime.getObject();
name = name.substring(2);
}
if (name.length() == 0) {
throw runtime.newNameError("wrong constant name ", fullName.idString());
}
while ( ( sep = name.indexOf("::") ) != -1 ) {
final String segment = name.substring(0, sep);
IRubyObject obj = mod.getConstant(validateConstant(segment, symbol), inherit, inherit);
if (obj instanceof RubyModule) {
mod = (RubyModule) obj;
} else {
throw runtime.newTypeError(segment + " does not refer to class/module");
}
name = name.substring(sep + 2);
}
return mod.getConstant(validateConstant(name, symbol), inherit, inherit);
}
@JRubyMethod(name = "const_set", required = 2)
public IRubyObject const_set(IRubyObject name, IRubyObject value) {
return setConstant(validateConstant(name), value);
}
@JRubyMethod(name = "remove_const", required = 1, visibility = PRIVATE)
public IRubyObject remove_const(ThreadContext context, IRubyObject rubyName) {
String id = validateConstant(rubyName);
IRubyObject value = deleteConstant(id);
if (value != null) {
invalidateConstantCache(id);
if (value != UNDEF) return value;
removeAutoload(id);
return context.nil;
}
if (hasConstantInHierarchy(id)) throw cannotRemoveError(id);
throw context.runtime.newNameError("constant " + id + " not defined for " + getName(), id);
}
private boolean hasConstantInHierarchy(final String name) {
for (RubyModule p = this; p != null; p = p.getSuperClass()) {
if (p.hasConstant(name)) {
return true;
}
}
return false;
}
@JRubyMethod(name = "const_missing", required = 1)
public IRubyObject const_missing(ThreadContext context, IRubyObject rubyName, Block block) {
Ruby runtime = context.runtime;
if (this != runtime.getObject()) {
throw runtime.newNameError("uninitialized constant %2$s::%1$s", this, rubyName);
} else {
throw runtime.newNameError("uninitialized constant %1$s", this, rubyName);
}
}
@JRubyMethod(name = "constants")
public RubyArray constants(ThreadContext context) {
return constantsCommon(context, true, true);
}
@JRubyMethod(name = "constants")
public RubyArray constants(ThreadContext context, IRubyObject allConstants) {
return constantsCommon(context, false, allConstants.isTrue());
}
@Deprecated
public RubyArray constants19(ThreadContext context) {
return constants(context);
}
@Deprecated
public RubyArray constants19(ThreadContext context, IRubyObject allConstants) {
return constants(context, allConstants);
}
@Deprecated
public RubyArray constantsCommon19(ThreadContext context, boolean replaceModule, boolean allConstants) {
return constantsCommon(context, replaceModule, allConstants);
}
private RubyArray constantsCommon(ThreadContext context, boolean replaceModule, boolean allConstants) {
Ruby runtime = context.runtime;
Collection<String> constantNames = constantsCommon(runtime, replaceModule, allConstants, false);
RubyArray array = runtime.newArray(constantNames.size());
for (String name : constantNames) {
array.append(runtime.newSymbol(name));
}
return array;
}
public Collection<String> constantsCommon(Ruby runtime, boolean replaceModule, boolean allConstants) {
return constantsCommon(runtime, replaceModule, allConstants, true);
}
public Collection<String> constantsCommon(Ruby runtime, boolean replaceModule, boolean allConstants, boolean includePrivate) {
final RubyModule objectClass = runtime.getObject();
final Collection<String> constantNames;
if (allConstants) {
if ((replaceModule && runtime.getModule() == this) || objectClass == this) {
constantNames = objectClass.getConstantNames(includePrivate);
} else {
Set<String> names = new HashSet<>();
for (RubyModule module = this; module != null && module != objectClass; module = module.getSuperClass()) {
names.addAll(module.getConstantNames(includePrivate));
}
constantNames = names;
}
} else {
if ((replaceModule && runtime.getModule() == this) || objectClass == this) {
constantNames = objectClass.getConstantNames(includePrivate);
} else {
constantNames = getConstantNames(includePrivate);
}
}
return constantNames;
}
public void deprecateConstant(Ruby runtime, String name) {
ConstantEntry entry = getConstantMap().get(name);
if (entry == null) {
throw runtime.newNameError(str(runtime, "constant ", types(runtime, this), "::", ids(runtime, name), " not defined"), name);
}
storeConstant(name, entry.value, entry.hidden, true);
invalidateConstantCache(name);
}
@JRubyMethod
public IRubyObject deprecate_constant(ThreadContext context, IRubyObject name) {
checkFrozen();
deprecateConstant(context.runtime, validateConstant(name));
return this;
}
@JRubyMethod(rest = true)
public IRubyObject deprecate_constant(ThreadContext context, IRubyObject[] names) {
for (IRubyObject name: names) {
deprecate_constant(context, name);
}
return this;
}
@JRubyMethod
public IRubyObject private_constant(ThreadContext context, IRubyObject name) {
checkFrozen();
String id = validateConstant(name);
setConstantVisibility(context.runtime, id, true);
invalidateConstantCache(id);
return this;
}
@JRubyMethod(required = 1, rest = true)
public IRubyObject private_constant(ThreadContext context, IRubyObject[] rubyNames) {
for (IRubyObject rubyName : rubyNames) {
private_constant(context, rubyName);
}
return this;
}
@JRubyMethod
public IRubyObject public_constant(ThreadContext context, IRubyObject name) {
checkFrozen();
String id = validateConstant(name);
setConstantVisibility(context.runtime, id, false);
invalidateConstantCache(id);
return this;
}
@JRubyMethod(required = 1, rest = true)
public IRubyObject public_constant(ThreadContext context, IRubyObject[] rubyNames) {
for (IRubyObject rubyName : rubyNames) {
public_constant(context, rubyName);
}
return this;
}
@JRubyMethod(name = "prepend", required = 1, rest = true)
public IRubyObject prepend(ThreadContext context, IRubyObject[] modules) {
for (int i = modules.length; --i >= 0; ) {
IRubyObject obj = modules[i];
if (!obj.isModule()) {
throw context.runtime.newTypeError(obj, context.runtime.getModule());
}
}
for (int i = modules.length - 1; i >= 0; i--) {
modules[i].callMethod(context, "prepend_features", this);
modules[i].callMethod(context, "prepended", this);
}
return this;
}
@JRubyMethod(name = "prepended", required = 1, visibility = PRIVATE)
public IRubyObject prepended(ThreadContext context, IRubyObject other) {
return context.nil;
}
public final void setConstantVisibility(Ruby runtime, String name, boolean hidden) {
ConstantEntry entry = getConstantMap().get(name);
if (entry == null) {
throw runtime.newNameError("constant " + getName() + "::" + name + " not defined", name);
}
storeConstant(name, entry.value, hidden);
}
public IRubyObject setClassVar(String name, IRubyObject value) {
RubyModule module = this;
RubyModule highest = this;
do {
if (module.hasClassVariable(name)) {
highest = module;
}
} while ((module = module.getSuperClass()) != null);
return highest.storeClassVariable(name, value);
}
@Deprecated
public IRubyObject fastSetClassVar(final String internedName, final IRubyObject value) {
return setClassVar(internedName, value);
}
public IRubyObject getClassVar(String name) {
IRubyObject value = getClassVarQuiet(name);
if (value == null) {
throw getRuntime().newNameError("uninitialized class variable %1$s in %2$s", this, name);
}
return value;
}
public IRubyObject getClassVar(IRubyObject nameObject, String name) {
IRubyObject value = getClassVarQuiet(name);
if (value == null) {
throw getRuntime().newNameError("uninitialized class variable %1$s in %2$s", this, nameObject);
}
return value;
}
public IRubyObject getClassVarQuiet(String name) {
assert IdUtil.isClassVariable(name);
Object value;
RubyModule module = this;
RubyModule highest = null;
do {
if (module.hasClassVariable(name)) {
highest = module;
}
} while ((module = module.getSuperClass()) != null);
if (highest != null) return highest.fetchClassVariable(name);
return null;
}
@Deprecated
public IRubyObject fastGetClassVar(String internedName) {
return getClassVar(internedName);
}
public boolean isClassVarDefined(String name) {
RubyModule module = this;
do {
if (module.hasClassVariable(name)) return true;
} while ((module = module.getSuperClass()) != null);
return false;
}
@Deprecated
public boolean fastIsClassVarDefined(String internedName) {
return isClassVarDefined(internedName);
}
public IRubyObject removeClassVariable(String name) {
String javaName = validateClassVariable(name);
IRubyObject value;
if ((value = deleteClassVariable(javaName)) != null) {
return value;
}
if (isClassVarDefined(javaName)) {
throw cannotRemoveError(javaName);
}
throw getRuntime().newNameError("class variable " + javaName + " not defined for " + getName(), javaName);
}
public IRubyObject getConstantAtSpecial(String name) {
IRubyObject value;
if (this == getRuntime().getObject()) {
value = getConstantNoConstMissing(name);
} else {
value = fetchConstant(name);
}
return value == UNDEF ? resolveUndefConstant(name) : value;
}
public IRubyObject getConstantAt(String name) {
return getConstantAt(name, true);
}
public IRubyObject getConstantAt(String name, boolean includePrivate) {
IRubyObject value = fetchConstant(name, includePrivate);
return value == UNDEF ? resolveUndefConstant(name) : value;
}
@Deprecated
public IRubyObject fastGetConstantAt(String internedName) {
return getConstantAt(internedName);
}
public IRubyObject getConstant(String name) {
return getConstant(name, true);
}
public IRubyObject getConstant(String name, boolean inherit) {
return getConstant(name, inherit, true);
}
public IRubyObject getConstant(String name, boolean inherit, boolean includeObject) {
assert name != null : "null name";
IRubyObject value = getConstantNoConstMissing(name, inherit, includeObject);
Ruby runtime = metaClass.runtime;
return value != null ? value :
callMethod(runtime.getCurrentContext(), "const_missing", runtime.newSymbol(name));
}
@Deprecated
public IRubyObject fastGetConstant(String internedName) {
return getConstant(internedName);
}
@Deprecated
public IRubyObject fastGetConstant(String internedName, boolean inherit) {
return getConstant(internedName, inherit);
}
public IRubyObject getConstantNoConstMissing(String name) {
return getConstantNoConstMissing(name, true);
}
public IRubyObject getConstantNoConstMissing(String name, boolean inherit) {
return getConstantNoConstMissing(name, inherit, true);
}
public IRubyObject getConstantNoConstMissing(String name, boolean inherit, boolean includeObject) {
IRubyObject constant = iterateConstantNoConstMissing(name, this, inherit, true);
if (constant == null && !isClass() && includeObject) {
constant = iterateConstantNoConstMissing(name, getRuntime().getObject(), inherit, true);
}
return constant;
}
public final IRubyObject getConstantNoConstMissingSkipAutoload(String name) {
return getConstantSkipAutoload(name, true, true);
}
@Deprecated
public IRubyObject getConstantNoConstMissingSKipAutoload(String name) {
return getConstantSkipAutoload(name, true, true);
}
private IRubyObject getConstantSkipAutoload(String name, boolean inherit, boolean includeObject) {
IRubyObject constant = iterateConstantNoConstMissing(name, this, inherit, false);
if (constant == null && !isClass() && includeObject) {
constant = iterateConstantNoConstMissing(name, getRuntime().getObject(), inherit, false);
}
return constant;
}
private static IRubyObject iterateConstantNoConstMissing(String name,
RubyModule init, boolean inherit, boolean loadConstant) {
for (RubyModule mod = init; mod != null; mod = mod.getSuperClass()) {
final IRubyObject value = mod.fetchConstant(name, true);
if ( value == UNDEF ) return mod.getAutoloadConstant(name, loadConstant);
if ( value != null ) return value;
if ( ! inherit ) break;
}
return null;
}
public IRubyObject getConstantFrom(String name) {
IRubyObject value = getConstantFromNoConstMissing(name);
return value != null ? value : getConstantFromConstMissing(name);
}
@Deprecated
public IRubyObject fastGetConstantFrom(String internedName) {
return getConstantFrom(internedName);
}
public IRubyObject getConstantFromNoConstMissing(String name) {
return getConstantFromNoConstMissing(name, true);
}
public IRubyObject getConstantFromNoConstMissing(String name, boolean includePrivate) {
final Ruby runtime = getRuntime();
final RubyClass objectClass = runtime.getObject();
RubyModule mod = this; IRubyObject value;
while ( mod != null ) {
if ( ( value = mod.fetchConstant(name, includePrivate) ) != null ) {
if ( value == UNDEF ) return mod.resolveUndefConstant(name);
if ( mod == objectClass && this != objectClass ) {
return null;
}
return value;
}
mod = mod.getSuperClass();
}
return null;
}
@Deprecated
public IRubyObject fastGetConstantFromNoConstMissing(String internedName) {
return getConstantFromNoConstMissing(internedName);
}
public IRubyObject getConstantFromConstMissing(String name) {
final Ruby runtime = metaClass.runtime;
return callMethod(runtime.getCurrentContext(), "const_missing", runtime.fastNewSymbol(name));
}
@Deprecated
public IRubyObject fastGetConstantFromConstMissing(String internedName) {
return getConstantFromConstMissing(internedName);
}
public final IRubyObject resolveUndefConstant(String name) {
return getAutoloadConstant(name);
}
public IRubyObject setConstantQuiet(String name, IRubyObject value) {
return setConstantCommon(name, value, false, false);
}
public IRubyObject setConstant(String name, IRubyObject value) {
return setConstantCommon(name, value, false, true);
}
public IRubyObject setConstant(String name, IRubyObject value, boolean hidden) {
return setConstantCommon(name, value, hidden, true);
}
private IRubyObject setConstantCommon(String name, IRubyObject value, boolean hidden, boolean warn) {
IRubyObject oldValue = fetchConstant(name);
setParentForModule(name, value);
if (oldValue != null) {
boolean notAutoload = oldValue != UNDEF;
if (notAutoload || !setAutoloadConstant(name, value)) {
if (warn && notAutoload) {
getRuntime().getWarnings().warn(ID.CONSTANT_ALREADY_INITIALIZED, "already initialized constant " + name);
}
if (hidden) storeConstant(name, value, true);
else storeConstant(name, value);
}
} else {
if (hidden) storeConstant(name, value, true);
else storeConstant(name, value);
}
invalidateConstantCache(name);
return value;
}
private void setParentForModule(final String name, final IRubyObject value) {
if ( value instanceof RubyModule ) {
RubyModule module = (RubyModule) value;
if (module != this && module.getBaseName() == null) {
module.setBaseName(name);
module.setParent(this);
}
module.calculateName();
}
}
@Deprecated
public IRubyObject fastSetConstant(String internedName, IRubyObject value) {
return setConstant(internedName, value);
}
@Extension
public void defineConstant(String name, IRubyObject value) {
assert value != null;
if (!IdUtil.isValidConstantName(name)) {
throw getRuntime().newNameError("bad constant name " + name, name);
}
setConstant(name, value);
}
public boolean isConstantDefined(String name) {
assert IdUtil.isConstant(name);
boolean isObject = this == getRuntime().getObject();
RubyModule module = this;
do {
Object value;
if ((value = module.constantTableFetch(name)) != null) {
if (value != UNDEF) return true;
return getAutoloadMap().get(name) != null;
}
} while (isObject && (module = module.getSuperClass()) != null );
return false;
}
public boolean fastIsConstantDefined(String internedName) {
assert internedName.equals(internedName.intern()) : internedName + " is not interned";
assert IdUtil.isConstant(internedName);
boolean isObject = this == getRuntime().getObject();
RubyModule module = this;
do {
Object value;
if ((value = module.constantTableFetch(internedName)) != null) {
if (value != UNDEF) return true;
return getAutoloadMap().get(internedName) != null;
}
} while (isObject && (module = module.getSuperClass()) != null );
return false;
}
public boolean fastIsConstantDefined19(String internedName) {
return fastIsConstantDefined19(internedName, true);
}
public boolean fastIsConstantDefined19(String internedName, boolean inherit) {
assert internedName.equals(internedName.intern()) : internedName + " is not interned";
assert IdUtil.isConstant(internedName);
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
Object value;
if ((value = module.constantTableFetch(internedName)) != null) {
if (value != UNDEF) return true;
return getAutoloadMap().get(internedName) != null;
}
if (!inherit) {
break;
}
}
return false;
}
private RaiseException cannotRemoveError(String id) {
Ruby runtime = getRuntime();
return getRuntime().newNameError(str(runtime, "cannot remove ", ids(runtime, id), " for ", types(runtime, this)), id);
}
public boolean hasInternalModuleVariable(final String name) {
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
if (module.hasInternalVariable(name)) return true;
}
return false;
}
public IRubyObject searchInternalModuleVariable(final String name) {
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
IRubyObject value = (IRubyObject)module.getInternalVariable(name);
if (value != null) return value;
}
return null;
}
public void setInternalModuleVariable(final String name, final IRubyObject value) {
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
if (module.hasInternalVariable(name)) {
module.setInternalVariable(name, value);
return;
}
}
setInternalVariable(name, value);
}
protected Map<String, IRubyObject> getClassVariables() {
if (CLASSVARS_UPDATER == null) {
return getClassVariablesForWriteSynchronized();
}
return getClassVariablesForWriteAtomic();
}
private Map<String,IRubyObject> getClassVariablesForWriteSynchronized() {
Map<String, IRubyObject> myClassVars = classVariables;
if ( myClassVars == Collections.EMPTY_MAP ) {
synchronized (this) {
myClassVars = classVariables;
if ( myClassVars == Collections.EMPTY_MAP ) {
return classVariables = new ConcurrentHashMap<String, IRubyObject>(4, 0.75f, 2);
}
return myClassVars;
}
}
return myClassVars;
}
private Map<String,IRubyObject> getClassVariablesForWriteAtomic() {
while (true) {
Map<String, IRubyObject> myClassVars = classVariables;
if ( myClassVars != Collections.EMPTY_MAP ) return myClassVars;
Map<String, IRubyObject> newClassVars;
newClassVars = new ConcurrentHashMap<String, IRubyObject>(4, 0.75f, 2);
if (CLASSVARS_UPDATER.compareAndSet(this, myClassVars, newClassVars)) {
return newClassVars;
}
}
}
protected Map<String, IRubyObject> getClassVariablesForRead() {
return classVariables;
}
public boolean hasClassVariable(String name) {
assert IdUtil.isClassVariable(name);
return getClassVariablesForRead().containsKey(name);
}
@Deprecated
public boolean fastHasClassVariable(String internedName) {
return hasClassVariable(internedName);
}
public IRubyObject fetchClassVariable(String name) {
assert IdUtil.isClassVariable(name);
return getClassVariablesForRead().get(name);
}
@Deprecated
public IRubyObject fastFetchClassVariable(String internedName) {
return fetchClassVariable(internedName);
}
public IRubyObject storeClassVariable(String name, IRubyObject value) {
assert IdUtil.isClassVariable(name) && value != null;
ensureClassVariablesSettable();
getClassVariables().put(name, value);
return value;
}
@Deprecated
public IRubyObject fastStoreClassVariable(String internedName, IRubyObject value) {
return storeClassVariable(internedName, value);
}
public IRubyObject deleteClassVariable(String name) {
assert IdUtil.isClassVariable(name);
ensureClassVariablesSettable();
return getClassVariablesForRead().remove(name);
}
public List<String> getClassVariableNameList() {
return new ArrayList<String>(getClassVariablesForRead().keySet());
}
protected final String validateClassVariable(String name) {
if (IdUtil.isValidClassVariableName(name)) {
return name;
}
throw getRuntime().newNameError("`%1$s' is not allowed as a class variable name", this, name);
}
protected final String validateClassVariable(IRubyObject nameObj, String name) {
if (IdUtil.isValidClassVariableName(name)) {
return name;
}
throw getRuntime().newNameError("`%1$s' is not allowed as a class variable name", this, nameObj);
}
protected String validateClassVariable(Ruby runtime, IRubyObject object) {
RubySymbol name = TypeConverter.checkID(object);
if (!name.validClassVariableName()) {
throw getRuntime().newNameError(str(runtime, "`", ids(runtime, name), "' is not allowed as a class variable name"), this, object);
}
return name.idString();
}
protected final void ensureClassVariablesSettable() {
checkAndRaiseIfFrozen();
}
public boolean hasConstant(String name) {
assert IdUtil.isConstant(name);
return constantTableContains(name);
}
@Deprecated
public boolean fastHasConstant(String internedName) {
return hasConstant(internedName);
}
public IRubyObject fetchConstant(String name) {
return fetchConstant(name, true);
}
public IRubyObject fetchConstant(String name, boolean includePrivate) {
ConstantEntry entry = constantEntryFetch(name);
if (entry == null) return null;
if (entry.hidden && !includePrivate) {
RubyModule recv = this;
if (recv.isIncluded()) recv = recv.getNonIncludedClass();
throw getRuntime().newNameError("private constant " + getName() + "::" + name + " referenced", recv, name);
}
if (entry.deprecated) {
final Ruby runtime = getRuntime();
if ( "Object".equals( getName() ) ) {
runtime.getWarnings().warn(ID.CONSTANT_DEPRECATED, "constant ::"+ name +" is deprecated");
}
else {
runtime.getWarnings().warn(ID.CONSTANT_DEPRECATED, "constant "+ getName() +"::"+ name +" is deprecated");
}
}
return entry.value;
}
@Deprecated
public IRubyObject fastFetchConstant(String internedName) {
return fetchConstant(internedName);
}
public IRubyObject storeConstant(String name, IRubyObject value) {
assert value != null : "value is null";
assert IdUtil.isConstant(name) : "invalid constant name: " + name;
ensureConstantsSettable();
return constantTableStore(name, value);
}
public IRubyObject storeConstant(String name, IRubyObject value, boolean hidden) {
assert value != null : "value is null";
ensureConstantsSettable();
return constantTableStore(name, value, hidden);
}
private IRubyObject storeConstant(String name, IRubyObject value, boolean hidden, boolean deprecated) {
assert value != null : "value is null";
assert IdUtil.isConstant(name) : "invalid constant name: " + name;
ensureConstantsSettable();
return constantTableStore(name, value, hidden, deprecated);
}
@Deprecated
public IRubyObject fastStoreConstant(String internedName, IRubyObject value) {
return storeConstant(internedName, value);
}
public IRubyObject deleteConstant(String name) {
assert IdUtil.isConstant(name);
ensureConstantsSettable();
return constantTableRemove(name);
}
@Deprecated
public List<Variable<IRubyObject>> getStoredConstantList() {
return null;
}
@Deprecated
public List<String> getStoredConstantNameList() {
return new ArrayList<String>(getConstantMap().keySet());
}
public Collection<String> getConstantNames() {
return getConstantMap().keySet();
}
public Collection<String> getConstantNames(boolean includePrivate) {
if (includePrivate) return getConstantNames();
if (getConstantMap().size() == 0) {
return Collections.EMPTY_SET;
}
HashSet<String> publicNames = new HashSet<String>(getConstantMap().size());
for (Map.Entry<String, ConstantEntry> entry : getConstantMap().entrySet()) {
if (entry.getValue().hidden) continue;
publicNames.add(entry.getKey());
}
return publicNames;
}
protected final String validateConstant(IRubyObject name) {
return RubySymbol.retrieveIDSymbol(name, (sym, newSym) -> {
if (!sym.validConstantName()) {
throw getRuntime().newNameError(str(getRuntime(), "wrong constant name ", name), sym.idString());
}
}).idString();
}
protected final String validateConstant(String name, IRubyObject errorName) {
if (IdUtil.isValidConstantName(name)) return name;
Ruby runtime = getRuntime();
Encoding resultEncoding = runtime.getDefaultInternalEncoding();
if (resultEncoding == null) resultEncoding = runtime.getDefaultExternalEncoding();
RubyString nameString = errorName.asString();
if ((nameString.getEncoding() != resultEncoding && !nameString.isAsciiOnly()) ||
nameString.toString().indexOf('\0') > -1 ) {
nameString = (RubyString) nameString.inspect();
}
throw getRuntime().newNameError("wrong constant name " + nameString, name);
}
protected final void ensureConstantsSettable() {
checkAndRaiseIfFrozen();
}
private void checkAndRaiseIfFrozen() throws RaiseException {
if ( isFrozen() ) {
if (this instanceof RubyClass) {
if (getBaseName() == null) {
throw getRuntime().newFrozenError(getName());
}
throw getRuntime().newFrozenError("#<Class:" + getName() + '>');
}
throw getRuntime().newFrozenError("Module");
}
}
@Override
public final void checkFrozen() {
if ( isFrozen() ) {
throw getRuntime().newFrozenError(isClass() ? "class" : "module");
}
}
protected boolean constantTableContains(String name) {
return getConstantMap().containsKey(name);
}
protected IRubyObject constantTableFetch(String name) {
ConstantEntry entry = getConstantMap().get(name);
if (entry == null) return null;
return entry.value;
}
protected ConstantEntry constantEntryFetch(String name) {
return getConstantMap().get(name);
}
protected IRubyObject constantTableStore(String name, IRubyObject value) {
Map<String, ConstantEntry> constMap = getConstantMapForWrite();
boolean hidden = false;
ConstantEntry entry = constMap.get(name);
if (entry != null) hidden = entry.hidden;
constMap.put(name, new ConstantEntry(value, hidden));
return value;
}
protected IRubyObject constantTableStore(String name, IRubyObject value, boolean hidden) {
return constantTableStore(name, value, hidden, false);
}
protected IRubyObject constantTableStore(String name, IRubyObject value, boolean hidden, boolean deprecated) {
Map<String, ConstantEntry> constMap = getConstantMapForWrite();
constMap.put(name, new ConstantEntry(value, hidden, deprecated));
return value;
}
protected IRubyObject constantTableRemove(String name) {
ConstantEntry entry = getConstantMapForWrite().remove(name);
if (entry == null) return null;
return entry.value;
}
protected final void defineAutoload(String name, AutoloadMethod loadMethod) {
final Autoload existingAutoload = getAutoloadMap().get(name);
if (existingAutoload == null || existingAutoload.getValue() == null) {
storeConstant(name, RubyObject.UNDEF);
getAutoloadMapForWrite().put(name, new Autoload(loadMethod));
}
}
protected final IRubyObject finishAutoload(String name) {
final Autoload autoload = getAutoloadMap().get(name);
if ( autoload == null ) return null;
final IRubyObject value = autoload.getValue();
if ( value != null ) {
storeConstant(name, value);
}
removeAutoload(name);
invalidateConstantCache(name);
return value;
}
public final IRubyObject getAutoloadConstant(String name) {
return getAutoloadConstant(name, true);
}
protected IRubyObject getAutoloadConstant(String name, boolean loadConstant) {
final Autoload autoload = getAutoloadMap().get(name);
if ( autoload == null ) return null;
if ( ! loadConstant ) return RubyObject.UNDEF;
return autoload.getConstant( getRuntime().getCurrentContext() );
}
private boolean setAutoloadConstant(String name, IRubyObject value) {
final Autoload autoload = getAutoloadMap().get(name);
if ( autoload != null ) {
boolean set = autoload.setConstant(getRuntime().getCurrentContext(), value);
if ( ! set ) removeAutoload(name);
return set;
}
return false;
}
private void removeAutoload(String name) {
getAutoloadMapForWrite().remove(name);
}
protected RubyString getAutoloadFile(String name) {
final Autoload autoload = getAutoloadMap().get(name);
return autoload == null ? null : autoload.getFile();
}
private static void define(RubyModule module, JavaMethodDescriptor desc, final String simpleName, DynamicMethod dynamicMethod) {
JRubyMethod jrubyMethod = desc.anno;
final String[] names = jrubyMethod.name();
final String[] aliases = jrubyMethod.alias();
RubyModule singletonClass;
if (jrubyMethod.meta()) {
singletonClass = module.getSingletonClass();
dynamicMethod.setImplementationClass(singletonClass);
final String baseName;
if (names.length == 0) {
baseName = desc.name;
singletonClass.addMethod(baseName, dynamicMethod);
} else {
baseName = names[0];
for (String name : names) singletonClass.addMethod(name, dynamicMethod);
}
if (aliases.length > 0) {
for (String alias : aliases) singletonClass.defineAlias(alias, baseName);
}
} else {
String baseName;
if (names.length == 0) {
baseName = desc.name;
module.getMethodLocation().addMethod(baseName, dynamicMethod);
} else {
baseName = names[0];
for (String name : names) module.getMethodLocation().addMethod(name, dynamicMethod);
}
if (aliases.length > 0) {
for (String alias : aliases) module.defineAlias(alias, baseName);
}
if (jrubyMethod.module()) {
singletonClass = module.getSingletonClass();
DynamicMethod moduleMethod = dynamicMethod.dup();
moduleMethod.setImplementationClass(singletonClass);
moduleMethod.setVisibility(PUBLIC);
if (names.length == 0) {
baseName = desc.name;
singletonClass.addMethod(desc.name, moduleMethod);
} else {
baseName = names[0];
for (String name : names) singletonClass.addMethod(name, moduleMethod);
}
if (aliases.length > 0) {
for (String alias : aliases) singletonClass.defineAlias(alias, baseName);
}
}
}
}
@Deprecated
public IRubyObject initialize(Block block) {
return initialize(getRuntime().getCurrentContext());
}
public KindOf kindOf = KindOf.DEFAULT_KIND_OF;
public final int id;
private MethodHandle idTest;
public RubyModule parent;
protected String baseName;
private transient String cachedName;
private transient RubyString cachedRubyName;
@SuppressWarnings("unchecked")
private volatile Map<String, ConstantEntry> constants = Collections.EMPTY_MAP;
public static class ConstantEntry {
public final IRubyObject value;
public final boolean hidden;
final boolean deprecated;
public ConstantEntry(IRubyObject value, boolean hidden) {
this.value = value;
this.hidden = hidden;
this.deprecated = false;
}
ConstantEntry(IRubyObject value, boolean hidden, boolean deprecated) {
this.value = value;
this.hidden = hidden;
this.deprecated = deprecated;
}
public ConstantEntry dup() {
return new ConstantEntry(value, hidden, deprecated);
}
}
public interface AutoloadMethod {
void load(Ruby runtime);
RubyString getFile();
}
private static final class Autoload {
private volatile ThreadContext ctx;
private final Object ctxLock = new Object();
private volatile IRubyObject value;
private final AutoloadMethod loadMethod;
Autoload(AutoloadMethod loadMethod) {
this.ctx = null;
this.value = null;
this.loadMethod = loadMethod;
}
IRubyObject getConstant(ThreadContext ctx) {
synchronized (ctxLock) {
if (this.ctx == null) {
this.ctx = ctx;
} else if (isSelf(ctx)) {
return getValue();
}
loadMethod.load(ctx.runtime);
}
return getValue();
}
boolean setConstant(ThreadContext ctx, IRubyObject newValue) {
synchronized(ctxLock) {
boolean isSelf = isSelf(ctx);
if (isSelf) value = newValue;
return isSelf;
}
}
IRubyObject getValue() {
return value;
}
RubyString getFile() { return loadMethod.getFile(); }
private boolean isSelf(ThreadContext rhs) {
return ctx != null && ctx.getThread() == rhs.getThread();
}
}
public void setJavaProxy(boolean javaProxy) {
this.javaProxy = javaProxy;
}
public boolean getJavaProxy() {
return javaProxy;
}
public boolean getCacheProxy() {
return getFlag(CACHEPROXY_F);
}
public void setCacheProxy(boolean cacheProxy) {
setFlag(CACHEPROXY_F, cacheProxy);
}
@Override
public <T> T toJava(Class<T> target) {
if (target == Class.class) {
final ThreadContext context = metaClass.runtime.getCurrentContext();
IRubyObject javaClass = JavaClass.java_class(context, this);
if ( ! javaClass.isNil() ) return javaClass.toJava(target);
}
return super.toJava(target);
}
public Set<String> discoverInstanceVariables() {
HashSet<String> set = new HashSet();
RubyModule cls = this;
while (cls != null) {
Map<String, DynamicMethod> methods = cls.getNonIncludedClass().getMethodLocation().getMethods();
methods.forEach((name, method) -> set.addAll(method.getMethodData().getIvarNames()));
cls = cls.getSuperClass();
}
return set;
}
public boolean isRefinement() {
return getFlag(REFINED_MODULE_F);
}
public boolean isIncludedIntoRefinement() {
return getFlag(INCLUDED_INTO_REFINEMENT);
}
public boolean isMethodBuiltin(String methodName) {
DynamicMethod method = searchMethod(methodName);
return method != null && method.isBuiltin();
}
public Map<RubyModule, RubyModule> getRefinements() {
return refinements;
}
public Map<RubyModule, RubyModule> getRefinementsForWrite() {
Map<RubyModule, RubyModule> refinements = this.refinements;
return !refinements.isEmpty() ? refinements : (this.refinements = newRefinementsMap());
}
public void setRefinements(Map<RubyModule, RubyModule> refinements) {
this.refinements = refinements;
}
private volatile Map<String, Autoload> autoloads = Collections.EMPTY_MAP;
protected volatile Map<String, DynamicMethod> methods = Collections.EMPTY_MAP;
protected Map<String, CacheEntry> cachedMethods = Collections.EMPTY_MAP;
protected int generation;
protected Integer generationObject;
protected volatile Set<RubyClass> includingHierarchies = Collections.EMPTY_SET;
protected volatile RubyModule methodLocation = this;
private transient volatile Set<ClassProvider> classProviders = Collections.EMPTY_SET;
protected RubyClass superClass;
@Deprecated
public int index;
@Deprecated
public static final Set<String> SCOPE_CAPTURING_METHODS = new HashSet<String>(Arrays.asList(
"eval",
"module_eval",
"class_eval",
"instance_eval",
"module_exec",
"class_exec",
"instance_exec",
"binding",
"local_variables"
));
protected ClassIndex classIndex = ClassIndex.NO_INDEX;
private volatile Map<String, IRubyObject> classVariables = Collections.EMPTY_MAP;
private volatile Map<RubyModule, RubyModule> refinements = Collections.EMPTY_MAP;
private volatile Map<RubyModule, IncludedModule> activatedRefinements = Collections.EMPTY_MAP;
volatile RubyModule refinedClass = null;
private volatile RubyModule definedAt = null;
private static final AtomicReferenceFieldUpdater<RubyModule, Map> CLASSVARS_UPDATER;
static {
AtomicReferenceFieldUpdater<RubyModule, Map> updater = null;
try {
updater = AtomicReferenceFieldUpdater.newUpdater(RubyModule.class, Map.class, "classVariables");
}
catch (final RuntimeException ex) {
if (ex.getCause() instanceof AccessControlException) {
}
else {
throw ex;
}
}
CLASSVARS_UPDATER = updater;
}
protected final Invalidator methodInvalidator;
private boolean javaProxy = false;
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final MethodHandle testModuleMatch = Binder
.from(boolean.class, ThreadContext.class, IRubyObject.class, int.class)
.invokeStaticQuiet(LOOKUP, Bootstrap.class, "testModuleMatch");
}