[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/jruby/jruby/master/core/src/main/java/org/jruby/RubyModule.java [Back]  [Original]

/* **** BEGIN LICENSE BLOCK ***** * Version: EPL 2.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse Public * License Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/epl-v20.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2001 Chad Fowler * Copyright (C) 2001 Alan Moore * Copyright (C) 2001-2002 Benoit Cerrina * Copyright (C) 2001-2004 Jan Arne Petersen * Copyright (C) 2002-2004 Anders Bengtsson * Copyright (C) 2004 Thomas E Enebo * Copyright (C) 2004-2005 Charles O Nutter * Copyright (C) 2004 Stefan Matthias Aust * Copyright (C) 2006-2007 Miguel Covarrubias * Copyright (C) 2007 William N Dortch * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the EPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby; import com.headius.invokebinder.Binder; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; 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.CopyOnWriteArraySet; import java.util.function.Consumer; import jnr.constants.Constant; 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.api.Convert; import org.jruby.api.JRubyAPI; import org.jruby.api.Warn; import org.jruby.embed.Extension; import org.jruby.exceptions.LoadError; import org.jruby.exceptions.RaiseException; import org.jruby.exceptions.RuntimeError; import org.jruby.internal.runtime.AbstractIRMethod; 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.DelegatingDynamicMethod; 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.Interp; import org.jruby.ir.JIT; import org.jruby.javasupport.JavaUtil; import org.jruby.javasupport.binding.MethodGatherer; import org.jruby.parser.StaticScope; import org.jruby.runtime.Arity; 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.JavaSites; import org.jruby.runtime.MethodFactory; import org.jruby.runtime.MethodIndex; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.PositionAware; import org.jruby.runtime.Signature; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.backtrace.RubyStackTraceElement; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.callsite.CacheEntry; import org.jruby.runtime.callsite.CachingCallSite; import org.jruby.runtime.load.LoadService; import org.jruby.runtime.marshal.MarshalDumper; import org.jruby.runtime.marshal.MarshalLoader; import org.jruby.runtime.opto.Invalidator; import org.jruby.runtime.opto.OptoFactory; import org.jruby.runtime.profile.MethodEnhancer; import org.jruby.util.RubyStringBuilder; import org.jruby.util.ByteList; import org.jruby.util.ClassProvider; import org.jruby.util.IdUtil; import org.jruby.util.StringSupport; import org.jruby.util.cli.Options; import org.jruby.util.io.RubyInputStream; import org.jruby.util.io.RubyOutputStream; import org.jruby.util.log.Logger; import org.jruby.util.log.LoggerFactory; import static org.jruby.RubySymbol.newHardSymbol; 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.api.Access.basicObjectClass; import static org.jruby.api.Access.instanceConfig; import static org.jruby.api.Access.loadService; import static org.jruby.api.Access.objectClass; import static org.jruby.api.Check.checkID; import static org.jruby.api.Convert.asBoolean; import static org.jruby.api.Convert.asFixnum; import static org.jruby.api.Convert.asSymbol; import static org.jruby.api.Convert.castAsHash; import static org.jruby.api.Convert.castAsModule; import static org.jruby.api.Create.allocArray; import static org.jruby.api.Create.dupString; import static org.jruby.api.Create.newArray; import static org.jruby.api.Create.newEmptyArray; import static org.jruby.api.Create.newString; import static org.jruby.api.Define.defineModule; import static org.jruby.api.Error.argumentError; import static org.jruby.api.Error.frozenError; import static org.jruby.api.Error.nameError; import static org.jruby.api.Error.runtimeError; import static org.jruby.api.Error.typeError; import static org.jruby.api.Warn.warn; import static org.jruby.api.Warn.warnDeprecated; import static org.jruby.api.Warn.warning; import static org.jruby.api.Warn.warningDeprecated; 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.runtime.Visibility.UNDEFINED; import static org.jruby.util.CommonByteLists.COLON_COLON; 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); // static { LOG.setDebugEnable(true); } // enable DEBUG output private byte moduleFlags; static final byte CACHEPROXY = 0b00000001; static final byte NEEDSIMPL = 0b00000010; static final byte REFINED_MODULE = 0b00000100; static final byte IS_OVERLAID = 0b00001000; static final byte SHARED_MODULE = 0b00010000; static final byte INCLUDED_IN_REFINEMENT = 0b00100000; static final byte TEMP_NAME = 0b01000000; /** @deprecated external access to global object flags is going away */ @Deprecated(since = "10.0.3.0") public static final int CACHEPROXY_F = CACHEPROXY; /** @deprecated external access to global object flags is going away */ @Deprecated(since = "10.0.3.0") public static final int NEEDSIMPL_F = NEEDSIMPL; /** @deprecated external access to global object flags is going away */ @Deprecated(since = "10.0.3.0") public static final int REFINED_MODULE_F = REFINED_MODULE; /** @deprecated external access to global object flags is going away */ @Deprecated(since = "10.0.3.0") public static final int IS_OVERLAID_F = IS_OVERLAID; /** @deprecated external access to global object flags is going away */ @Deprecated(since = "10.0.3.0") public static final int OMOD_SHARED = SHARED_MODULE; /** @deprecated external access to global object flags is going away */ @Deprecated(since = "10.0.3.0") public static final int INCLUDED_INTO_REFINEMENT = INCLUDED_IN_REFINEMENT; /** @deprecated external access to global object flags is going away */ @Deprecated(since = "10.0.3.0") public static final int TEMPORARY_NAME = TEMP_NAME; public static final String BUILTIN_CONSTANT = ""; public static final ObjectAllocator MODULE_ALLOCATOR = RubyModule::new; public static void finishModuleClass(RubyClass Module) { Module.reifiedClass(RubyModule.class). kindOf(new RubyModule.JavaClassKindOf(RubyModule.class)). classIndex(ClassIndex.MODULE); } public static void finishCreateModuleClass(ThreadContext context, RubyClass Module) { Module.defineMethods(context, RubyModule.class); } public void checkValidBindTargetFrom(ThreadContext context, RubyModule originModule, boolean fromBind) throws RaiseException { // Module methods can always be transplanted if (!originModule.isModule() && !hasModuleInHierarchy(originModule)) { if (originModule instanceof MetaClass) { throw typeError(context, "can't bind singleton method to a different class"); } else { String thing = fromBind ? "an instance" : "a subclass"; // bind : define_method throw typeError(context, "bind argument must be " + thing + " of ", originModule, ""); } } } /** * Get the ClassIndex for this class. Will be NO_CLASS for non-core types. */ public ClassIndex getClassIndex() { return classIndex; } void set(int flag, boolean set) { if (set) { moduleFlags |= flag; } else { moduleFlags &= ~flag; } } private boolean get(int flag) { return (moduleFlags & flag) != 0; } /** * Set the ClassIndex for this core class. Only used at boot time for core types. * * @param classIndex the ClassIndex for this type * @deprecated Use {@link org.jruby.RubyModule#classIndex(ClassIndex)} instead. */ @Deprecated(since = "10.0.0.0") void setClassIndex(ClassIndex classIndex) { classIndex(classIndex); } @JRubyMethod public IRubyObject autoload(ThreadContext context, IRubyObject symbol, IRubyObject file) { final RubyString fileString = RubyFile.get_path(context, file); if (fileString.isEmpty()) throw argumentError(context, "empty file name"); final String symbolStr = symbol.asJavaString(); if (!IdUtil.isValidConstantName(symbol)) { throw nameError(context, "autoload must be constant name", symbol); } IRubyObject existingValue = fetchConstant(context, symbolStr); if (existingValue != null && existingValue != RubyObject.UNDEF) return context.nil; defineAutoload(context, symbolStr, fileString); return context.nil; } @JRubyMethod(name = "autoload?") public IRubyObject autoload_p(ThreadContext context, IRubyObject symbol) { return hasAutoload(context, checkID(context, symbol).idString(), true); } @JRubyMethod(name = "autoload?") public IRubyObject autoload_p(ThreadContext context, IRubyObject symbol, IRubyObject inherit) { return hasAutoload(context, checkID(context, symbol).idString(), inherit.isTrue()); } public IRubyObject hasAutoload(ThreadContext context, String idString, boolean inherit) { for (RubyModule mod = this; mod != null; mod = mod.getSuperClass()) { final IRubyObject loadedValue = mod.fetchConstant(context, idString); if (loadedValue == UNDEF) { final RubyString file; Autoload autoload = mod.getAutoloadMap().get(idString); // autoload has been evacuated if (autoload == null) return context.nil; // autoload has been completed if (autoload.getValue() != null) return context.nil; file = autoload.getPath(); // autoload is in progress on another thread if (autoload.ctx != null && !autoload.isSelf(context)) return file; // file load is in progress or file is already loaded if (!loadService(context).featureAlreadyLoaded(file.asJavaString())) return file; } if (!inherit) break; } 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(); // Not sure how but this happens in test_objects_are_released_by_cache_map if (cl == null) return false; return cl.hasAncestor(type); } } 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 boolean hasAncestor(RubyModule type) { return searchAncestor(type.getDelegate().getOrigin()) != null; } public Map getConstantMap() { Map constants = getOrigin().constants; return constants == null ? Collections.EMPTY_MAP : constants; } public Map getConstantMapForWrite() { Map constants = this.constants; if (constants == null) { if (!CONSTANTS_HANDLE.compareAndSet(this, null, constants = new ConcurrentHashMap(2, 0.9f, 1))) { constants = this.constants; } } return constants; } /** * AutoloadMap must be accessed after checking ConstantMap. Checking UNDEF value in constantMap works as a guard. * For looking up constant, check constantMap first then try to get an Autoload object from autoloadMap. * For setting constant, update constantMap first and remove an Autoload object from autoloadMap. */ protected Map getAutoloadMap() { Map autoloads = this.autoloads; return autoloads == null ? Collections.EMPTY_MAP : autoloads; } protected Map getAutoloadMapForWrite() { Map autoloads = this.autoloads; if (autoloads == null) { if (!AUTOLOADS_HANDLE.compareAndSet(this, null, autoloads = new ConcurrentHashMap(2, 0.9f, 1))) { autoloads = this.autoloads; } } return autoloads; } private RubyClass.RubyClassSet getIncludingHierarchiesForRead() { RubyClass.RubyClassSet includingHierarchies = this.includingHierarchies; return includingHierarchies == null ? RubyClass.EMPTY_RUBYCLASS_SET : includingHierarchies; } protected RubyClass.RubyClassSet getIncludingHierarchiesForWrite() { RubyClass.RubyClassSet includingHierarchies = this.includingHierarchies; if (includingHierarchies == null) { if (!INCLUDING_HIERARCHIES_HANDLE.compareAndSet(this, null, includingHierarchies = new RubyClass.WeakRubyClassSet(4))) { includingHierarchies = this.includingHierarchies; } } return includingHierarchies; } @SuppressWarnings("unchecked") public void addIncludingHierarchy(IncludedModule hierarchy) { getIncludingHierarchiesForWrite().addClass(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); } /** separate path for MetaClass construction * */ protected RubyModule(Ruby runtime, RubyClass metaClass, boolean objectSpace) { super(runtime, metaClass, objectSpace); id = runtime.allocModuleId(); // track module instances separately, since they don't descend from BasicObject if (metaClass == runtime.getModule()) { runtime.addModule(this); } // if (parent == null) parent = runtime.getObject(); set(NEEDSIMPL, !isClass()); updateGeneration(runtime); if (runtime.getInstanceConfig().isProfiling()) { cacheEntryFactory = new ProfilingCacheEntryFactory(runtime, NormalCacheEntryFactory); } else { cacheEntryFactory = NormalCacheEntryFactory; } // set up an invalidator for use in new optimization strategies methodInvalidator = OptoFactory.newMethodInvalidator(this); } /** used by MODULE_ALLOCATOR and RubyClass constructors * */ protected RubyModule(Ruby runtime, RubyClass metaClass) { this(runtime, metaClass, runtime.isObjectSpaceEnabled()); } /** standard path for Module construction * */ public RubyModule(Ruby runtime) { this(runtime, runtime.getModule()); } public boolean needsImplementer() { return get(NEEDSIMPL); } /** rb_module_new * */ @Deprecated(since = "10.0.0.0") public static RubyModule newModule(Ruby runtime) { return new RubyModule(runtime); } @Deprecated(since = "10.0.0.0") public static RubyModule newModule(Ruby runtime, String name, RubyModule parent, boolean setParent, String file, int line) { return newModule(runtime.getCurrentContext(), name, parent, setParent, file, line); } /** rb_module_new/rb_define_module_id/rb_name_class/rb_set_class_path * * This is used by IR to define a new module. */ public static RubyModule newModule(ThreadContext context, String name, RubyModule parent, boolean setParent, String file, int line) { RubyModule module = defineModule(context).baseName(name); if (setParent) module.setParent(parent); if (file != null) { parent.setConstant(context, name, module, file, line); } else { parent.setConstant(context, name, module); } return module; } @Deprecated(since = "10.0.0.0") public static RubyModule newModule(Ruby runtime, String name, RubyModule parent, boolean setParent) { var context = runtime.getCurrentContext(); RubyModule module = new RubyModule(runtime).baseName(name); if (setParent) module.setParent(parent); parent.setConstant(context, name, module); return module; } public static RubyModule newModuleBootstrap(Ruby runtime, String name, RubyModule parent) { var module = new RubyModule(runtime).baseName(name); parent.defineConstantBootstrap(name, module); return module; } /** * Return the class providers set for read operations. * * @return the class provider set or an empty set if there are no class providers */ private Set getClassProvidersForRead() { Set classProviders = this.classProviders; return classProviders == null ? Collections.EMPTY_SET : classProviders; } /** * Return the class providers set for write operations. * * @return the class provider set suitable for updating */ private Set getClassProvidersForWrite() { Set classProviders = this.classProviders; if (classProviders == null) { if (!CLASS_PROVIDERS_HANDLE.compareAndSet(this, null, classProviders = new CopyOnWriteArraySet())) { classProviders = this.classProviders; } } return classProviders; } // synchronized method per JRUBY-1173 (unsafe Double-Checked Locking) // FIXME: synchronization is still wrong in CP code public final synchronized void addClassProvider(ClassProvider provider) { getClassProvidersForWrite().add(provider); } public final synchronized void removeClassProvider(ClassProvider provider) { getClassProvidersForWrite().remove(provider); } private void checkForCyclicInclude(ThreadContext context, RubyModule m) throws RaiseException { if (isSameOrigin(m)) throw argumentError(context, "cyclic include detected"); } protected void checkForCyclicPrepend(ThreadContext context, RubyModule m) throws RaiseException { if (isSameOrigin(m)) throw argumentError(context, getName(context) + " cyclic prepend detected " + m.getName(context)); } private RubyClass searchProvidersForClass(ThreadContext context, String name, RubyClass superClazz) { Set classProviders = this.classProviders; if (classProviders == null) return null; RubyClass clazz; for (ClassProvider classProvider: classProviders) { if ((clazz = classProvider.defineClassUnder(context, this, name, superClazz)) != null) { return clazz; } } return null; } private RubyModule searchProvidersForModule(ThreadContext context, String name) { Set classProviders = this.classProviders; if (classProviders == null) return null; RubyModule module; for (ClassProvider classProvider: classProviders) { if ((module = classProvider.defineModuleUnder(context, this, name)) != null) { return module; } } return null; } /** Getter for property superClass. * @return Value of property superClass. */ public RubyClass getSuperClass() { return superClass(); } /** * @param superClass * @deprecated Use {@link RubyModule#superClass(RubyClass)} instead. */ @Deprecated(since = "10.0.0.0") public void setSuperClass(RubyClass superClass) { superClass(superClass); } public RubyModule getParent() { return parent; } public void setParent(RubyModule parent) { this.parent = parent; } public RubyModule getMethodLocation() { RubyModule methodLocation = this.methodLocation; return methodLocation == null ? this : methodLocation; } public void setMethodLocation(RubyModule module){ methodLocation = module; } public Map getMethods() { Map methods = this.methods; return methods == null ? Collections.EMPTY_MAP : methods; } public Map getMethodsForWrite() { Map methods = this.methods; if (methods == null) { if (!METHODS_HANDLE.compareAndSet(this, null, methods = new ConcurrentHashMap(2, 0.9f, 1))) { methods = this.methods; } } return methods; } @Deprecated(since = "10.0.0.0") public DynamicMethod putMethod(Ruby runtime, String id, DynamicMethod method) { return putMethod(getCurrentContext(), id, method); } /** *

Note: Internal API - only public as its used by generated code!

* @param context the current thread context * @param id identifier string (8859_1). Matching entry in symbol table. * @param method * @return method */ // NOTE: used by AnnotationBinder public DynamicMethod putMethod(ThreadContext context, String id, DynamicMethod method) { RubyModule methodLocation = getMethodLocation(); 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)); } context.runtime.addProfiledMethod(id, method); context.runtime.invalidateBuiltin(methodLocation, id); return method; } /** * Is this module one that in an included one (e.g. an {@link IncludedModuleWrapper}). * @see IncludedModule */ public boolean isIncluded() { return false; } public boolean isPrepended() { return false; } /** * In an included or prepended module what is the ACTUAL module it represents? * @return the actual module of an included/prepended module. */ public RubyModule getOrigin() { return this; } public RubyModule getDelegate() { return this; } /** * Get the "real" module, either the current one or the nearest ancestor that is not a singleton or include wrapper. * * See {@link RubyClass#getRealClass()}. * * @return the nearest non-singleton non-include module in the hierarchy */ public RubyModule getRealModule() { RubyModule cl = this; while (cl != null && (cl.isSingleton() || cl.isIncluded())) { cl = cl.superClass; } return cl; } /** * Get the base name of this class, or null if it is an anonymous class. * * @return base name of the class */ public String getBaseName() { return baseName; } /** * Set the base name of the class. If null, the class effectively becomes * anonymous (though constants elsewhere may reference it). * @param name the new base name of the class * @deprecated Use {@link org.jruby.RubyModule#baseName(String)} instead. */ @Deprecated(since = "10.0.0.0") public void setBaseName(String name) { baseName(name); } @Deprecated(since = "10.0.0.0") public String getName() { return getName(getCurrentContext()); } /** * Generate a fully-qualified class name or a #-style name for anonymous and singleton classes. * * Ruby C equivalent = "classname" * * @return The generated class name */ public String getName(ThreadContext context) { return cachedName != null ? cachedName : calculateName(context); } @Deprecated(since = "10.0.0.0") public RubyString rubyName() { return rubyName(getCurrentContext()); } /** * Generate a fully-qualified class name or a #-style name for anonymous and singleton classes which * is properly encoded. The returned string is always frozen. * * @return a properly encoded class name. * * Note: getId() is only really valid for ASCII values. This should be favored over using it. */ public RubyString rubyName(ThreadContext context) { return cachedRubyName != null ? cachedRubyName : calculateRubyName(context); } @Deprecated(since = "10.0.0.0") public RubySymbol symbolName() { return symbolName(getCurrentContext()); } /** * Generate a fully-qualified class name or a #-style name as a Symbol. If any element of the path is anonymous, * returns null. * * @return a properly encoded non-anonymous class path as a Symbol, or null. */ public RubySymbol symbolName(ThreadContext context) { IRubyObject symbolName = cachedSymbolName; if (symbolName == null) { cachedSymbolName = symbolName = calculateSymbolName(context); } return symbolName == UNDEF ? null : (RubySymbol) symbolName; } @Deprecated(since = "10.0.0.0") public RubyString rubyBaseName() { return rubyBaseName(getCurrentContext()); } public RubyString rubyBaseName(ThreadContext context) { String baseName = getBaseName(); return baseName == null ? null : (RubyString) asSymbol(context, baseName).to_s(context); } private RubyString calculateAnonymousRubyName(ThreadContext context) { RubyString anonBase = newString(context, "#")); return (RubyString) anonBase.freeze(context); } private RubyString calculateRubyName(ThreadContext context) { boolean cache = true; if (getBaseName() == null) return calculateAnonymousRubyName(context); // no name...anonymous! if (usingTemporaryName()) { // temporary name cachedRubyName = (RubyString) asSymbol(context, baseName).toRubyString(context).freeze(context); return cachedRubyName; } RubyClass Object = objectClass(context); List parents = new ArrayList(5); for (RubyModule p = getParent(); p != null && p != Object && p != this; // Break out of cyclic namespaces like C::A = C2; C2::A = C (jruby/jruby#2314) p = p.getParent()) { RubyString name = p.rubyBaseName(context); // This is needed when the enclosing class or module is a singleton. // In that case, we generated a name such as null::Foo, which broke // Marshalling, among others. The correct thing to do in this situation // is to insert the generate the name of form # if // it's a singleton module/class, which this code accomplishes. if (name == null) { cache = false; name = p.rubyName(context); } parents.add(name); } RubyString fullName = buildPathString(context, parents); if (cache) cachedRubyName = (RubyString) fullName.freeze(context); return fullName; } /** * Calculate the module's name path as a Symbol. If any element in the path is anonymous, this will return null. * * Used primarily by Marshal.dump for class names. * * @return a non-anonymous class path as a Symbol, or null */ private IRubyObject calculateSymbolName(ThreadContext context) { if (getBaseName() == null) return UNDEF; if (usingTemporaryName()) return asSymbol(context, baseName); RubyClass Object = objectClass(context); List parents = new ArrayList(5); for (RubyModule p = getParent(); p != null && p != Object && p != this; // Break out of cyclic namespaces like C::A = C2; C2::A = C (jruby/jruby#2314) p = p.getParent()) { RubyString name = p.rubyBaseName(context); if (name == null) { return UNDEF; } parents.add(name); } RubyString fullName = buildPathString(context, parents); return fullName.intern(context); } private RubyString buildPathString(ThreadContext context, List parents) { RubyString colons = newString(context, "::"); RubyString fullName = context.runtime.newString(); // newString creates empty ByteList which ends up as fullName.setEncoding(USASCIIEncoding.INSTANCE); // ASCII-8BIT. 8BIT is unfriendly to string concats. for (int i = parents.size() - 1; i >= 0; i--) { RubyString rubyString = fullName.catWithCodeRange(parents.get(i)); rubyString.catWithCodeRange(colons); } fullName.catWithCodeRange(rubyBaseName(context)); fullName.setFrozen(true); return fullName; } @Deprecated(since = "10.0.0.0") public String getSimpleName() { return getSimpleName(getCurrentContext()); } /** * Get the "simple" name for the class, which is either the "base" name or * the "anonymous" class name. * * @return the "simple" name of the class */ public String getSimpleName(ThreadContext context) { return baseName != null ? baseName : calculateAnonymousName(context); } /** * Recalculate the fully-qualified name of this class/module. */ private String calculateName(ThreadContext context) { boolean cache = true; // we are anonymous, use anonymous name if (getBaseName() == null) return calculateAnonymousName(context); String name = getBaseName(); var Object = objectClass(context); // First, we count the parents int parentCount = 0; for (RubyModule p = getParent() ; p != null && p != Object ; p = p.getParent()) { // Break out of cyclic namespaces like C::A = C2; C2::A = C (jruby/jruby#2314) if (p == this) break; parentCount++; } // Allocate a String array for all of their names and populate it String[] parentNames = new String[parentCount]; int i = parentCount - 1; int totalLength = name.length() + parentCount * 2; // name length + enough :: for all parents for (RubyModule p = getParent() ; p != null && p != Object ; p = p.getParent(), i--) { // Break out of cyclic namespaces like C::A = C2; C2::A = C (jruby/jruby#2314) if (p == this) break; String pName = p.getBaseName(); // This is needed when the enclosing class or module is a singleton. // In that case, we generated a name such as null::Foo, which broke // Marshalling, among others. The correct thing to do in this situation // is to insert the generate the name of form # if // it's a singleton module/class, which this code accomplishes. if(pName == null) { cache = false; pName = p.getName(context); } parentNames[i] = pName; totalLength += pName.length(); } // Then build from the front using a StringBuilder 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(ThreadContext context) { String cachedName = this.cachedName; if (cachedName == null) { // anonymous classes get the # format cachedName = this.cachedName = "#
Web Proxy Viewer  |  New URL  |  Original Page