/***** 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) 2002-2004 Jan Arne Petersen * Copyright (C) 2004-2006 Thomas E Enebo * Copyright (C) 2004 Stefan Matthias Aust * * 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 org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.cli.Options; import org.jruby.util.log.Logger; import org.jruby.util.log.LoggerFactory; import static org.jruby.api.Access.classClass; import static org.jruby.api.Error.typeError; public final class MetaClass extends RubyClass { /** * rb_class_boot for meta classes ({@link #makeMetaClass(ThreadContext, RubyClass)}) */ MetaClass(Ruby runtime, RubyClass superClass, RubyBasicObject attached) { super(runtime, superClass, false); metaClassInit(superClass, attached); if (LOG_SINGLETONS || LOG_SINGLETONS_VERBOSE) { logSingleton(runtime, superClass, attached); } } /** * This is an internal API only used by Ruby constructor before ThreadContext exists. This is for bootstrapping * only. * @param runtime * @param superClass * @param Class * @param attached */ MetaClass(Ruby runtime, RubyClass superClass, RubyClass Class, RubyBasicObject attached) { super(runtime, superClass, Class); metaClassInit(superClass, attached); } private void metaClassInit(RubyClass superClass, RubyBasicObject attached) { this.attached = attached; // use same ClassIndex as metaclass, since we're technically still of that type classIndex(superClass.getClassIndex()); superClass.addSubclass(this); } private static void logSingleton(Ruby runtime, RubyClass superClass, RubyBasicObject attached) { if (runtime.isBooting()) return; // don't log singleton created during boot String attachedString = attached == null ? "null object" : "object of type " + attached.getMetaClass(); LOG.info("singleton class created for type " + superClass + " attached to " + attachedString); if (LOG_SINGLETONS_VERBOSE) { LOG.info(new Exception("singleton creation stack trace")); } } @Override public final IRubyObject allocate(ThreadContext context) { throw typeError(context, "can't create instance of virtual class"); } @Override public boolean isSingleton() { return true; } /** * rb_make_metaclass * @param superClass * @return singleton-class for this (singleton) class */ @Override public RubyClass makeMetaClass(ThreadContext context, RubyClass superClass) { MetaClass klass = new MetaClass(context.runtime, getSuperSingletonMetaClass(context), this); setMetaClass(klass); // Foo.singleton_class.singleton_class: #> // #>'s singleton_class == #> klass.setMetaClass(klass); return klass; } private RubyClass getSuperSingletonMetaClass(ThreadContext context) { if (attached instanceof RubyClass att) { RubyClass superClass = att.superClass(); if (superClass != null) superClass = superClass.getRealClass(); // #'s singleton class == Class.singleton_class // Context should be safe here as we never make a metaclass from a metaclass before first TC is made. return superClass == null ? classClass(context).singletonClass(context) : superClass.getMetaClass().singletonClass(context); } return getSuperClass().getRealClass().getMetaClass(); // NOTE: is this correct? } @Override RubyClass toSingletonClass(ThreadContext context, RubyBasicObject target) { return attached == target ? this : super.toSingletonClass(context, target); } @Override public IRubyObject attached_object(ThreadContext context) { return attached; } public RubyBasicObject getAttached() { return attached; } public void setAttached(RubyBasicObject attached) { this.attached = attached; } private RubyBasicObject attached; private static final Logger LOG = LoggerFactory.getLogger(MetaClass.class); private static final boolean LOG_SINGLETONS = Options.LOG_SINGLETONS.load(); private static final boolean LOG_SINGLETONS_VERBOSE = Options.LOG_SINGLETONS_VERBOSE.load(); }