* Eliminate visibility checking for almost all fcall and vcall paths.
[jruby.git] / src / org / jruby / RubyClass.java
blob83cfed10671001d08ef17512ae0549692feed9b8
1 /***** BEGIN LICENSE BLOCK *****
2 * Version: CPL 1.0/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Common Public
5 * License Version 1.0 (the "License"); you may not use this file
6 * except in compliance with the License. You may obtain a copy of
7 * the License at http://www.eclipse.org/legal/cpl-v10.html
9 * Software distributed under the License is distributed on an "AS
10 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11 * implied. See the License for the specific language governing
12 * rights and limitations under the License.
14 * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
15 * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
16 * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
17 * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
19 * Alternatively, the contents of this file may be used under the terms of
20 * either of the GNU General Public License Version 2 or later (the "GPL"),
21 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
22 * in which case the provisions of the GPL or the LGPL are applicable instead
23 * of those above. If you wish to allow use of your version of this file only
24 * under the terms of either the GPL or the LGPL, and not to allow others to
25 * use your version of this file under the terms of the CPL, indicate your
26 * decision by deleting the provisions above and replace them with the notice
27 * and other provisions required by the GPL or the LGPL. If you do not delete
28 * the provisions above, a recipient may use your version of this file under
29 * the terms of any one of the CPL, the GPL or the LGPL.
30 ***** END LICENSE BLOCK *****/
31 package org.jruby;
33 import java.io.IOException;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.Set;
38 import org.jruby.anno.JRubyMethod;
39 import org.jruby.anno.JRubyClass;
41 import org.jruby.internal.runtime.methods.DynamicMethod;
42 import org.jruby.internal.runtime.methods.JavaMethod;
43 import org.jruby.javasupport.util.RuntimeHelpers;
44 import org.jruby.runtime.Block;
45 import org.jruby.runtime.CallSite;
46 import org.jruby.runtime.CallSite.InlineCachingCallSite;
47 import org.jruby.runtime.CallType;
48 import org.jruby.runtime.ClassIndex;
49 import org.jruby.runtime.ObjectAllocator;
50 import org.jruby.runtime.ObjectMarshal;
51 import org.jruby.runtime.ThreadContext;
52 import org.jruby.runtime.Visibility;
53 import org.jruby.runtime.builtin.IRubyObject;
54 import org.jruby.runtime.marshal.MarshalStream;
55 import org.jruby.runtime.marshal.UnmarshalStream;
56 import org.jruby.util.collections.WeakHashSet;
58 /**
60 * @author jpetersen
62 @JRubyClass(name="Class", parent="Module")
63 public class RubyClass extends RubyModule {
64 public static final int CS_IDX_INITIALIZE = 0;
65 public static final String[] CS_NAMES = {
66 "initialize"
68 private final CallSite[] baseCallSites = new CallSite[CS_NAMES.length];
70 for(int i = 0; i < CS_NAMES.length; i++) {
71 baseCallSites[i] = new InlineCachingCallSite(CS_NAMES[i], CallType.FUNCTIONAL);
75 private CallSite[] extraCallSites;
77 public static void createClassClass(Ruby runtime, RubyClass classClass) {
78 classClass.index = ClassIndex.CLASS;
79 classClass.kindOf = new RubyModule.KindOf() {
80 @Override
81 public boolean isKindOf(IRubyObject obj, RubyModule type) {
82 return obj instanceof RubyClass;
86 classClass.undefineMethod("module_function");
87 classClass.undefineMethod("append_features");
88 classClass.undefineMethod("extend_object");
90 classClass.defineAnnotatedMethods(RubyClass.class);
92 classClass.addMethod("new", new SpecificArityNew(classClass, Visibility.PUBLIC));
94 // This is a non-standard method; have we decided to start extending Ruby?
95 //classClass.defineFastMethod("subclasses", callbackFactory.getFastOptMethod("subclasses"));
97 // FIXME: for some reason this dispatcher causes a VerifyError...
98 //classClass.dispatcher = callbackFactory.createDispatcher(classClass);
101 public static final ObjectAllocator CLASS_ALLOCATOR = new ObjectAllocator() {
102 public IRubyObject allocate(Ruby runtime, RubyClass klass) {
103 RubyClass clazz = new RubyClass(runtime);
104 clazz.allocator = ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR; // Class.allocate object is not allocatable before it is initialized
105 return clazz;
109 public ObjectAllocator getAllocator() {
110 return allocator;
113 public void setAllocator(ObjectAllocator allocator) {
114 this.allocator = allocator;
117 @JRubyMethod(name = "allocate")
118 public IRubyObject allocate() {
119 if (superClass == null) throw runtime.newTypeError("can't instantiate uninitialized class");
120 IRubyObject obj = allocator.allocate(runtime, this);
121 if (obj.getMetaClass().getRealClass() != getRealClass()) throw runtime.newTypeError("wrong instance allocation");
122 return obj;
125 public CallSite[] getExtraCallSites() {
126 return extraCallSites;
129 @Override
130 public int getNativeTypeIndex() {
131 return ClassIndex.CLASS;
134 @Override
135 public boolean isModule() {
136 return false;
139 @Override
140 public boolean isClass() {
141 return true;
144 @Override
145 public boolean isSingleton() {
146 return false;
149 /** boot_defclass
150 * Create an initial Object meta class before Module and Kernel dependencies have
151 * squirreled themselves together.
153 * @param runtime we need it
154 * @return a half-baked meta class for object
156 public static RubyClass createBootstrapClass(Ruby runtime, String name, RubyClass superClass, ObjectAllocator allocator) {
157 RubyClass obj;
159 if (superClass == null ) { // boot the Object class
160 obj = new RubyClass(runtime);
161 obj.marshal = DEFAULT_OBJECT_MARSHAL;
162 } else { // boot the Module and Class classes
163 obj = new RubyClass(runtime, superClass);
165 obj.setAllocator(allocator);
166 obj.setBaseName(name);
167 return obj;
170 private final Ruby runtime;
171 private ObjectAllocator allocator; // the default allocator
172 protected ObjectMarshal marshal;
173 private Set<RubyClass> subclasses;
175 /** separate path for MetaClass and IncludedModuleWrapper construction
176 * (rb_class_boot version for MetaClasses)
177 * no marshal, allocator initialization and addSubclass(this) here!
179 protected RubyClass(Ruby runtime, RubyClass superClass, boolean objectSpace) {
180 super(runtime, runtime.getClassClass(), objectSpace);
181 this.runtime = runtime;
182 this.superClass = superClass; // this is the only case it might be null here (in MetaClass construction)
185 /** used by CLASS_ALLOCATOR (any Class' class will be a Class!)
186 * also used to bootstrap Object class
188 protected RubyClass(Ruby runtime) {
189 super(runtime, runtime.getClassClass());
190 this.runtime = runtime;
191 index = ClassIndex.CLASS;
194 /** rb_class_boot (for plain Classes)
195 * also used to bootstrap Module and Class classes
197 protected RubyClass(Ruby runtime, RubyClass superClazz) {
198 this(runtime);
199 superClass = superClazz;
200 marshal = superClazz.marshal; // use parent's marshal
201 superClazz.addSubclass(this);
203 infectBy(superClass);
206 /**
207 * A constructor which allows passing in an array of supplementary call sites.
209 protected RubyClass(Ruby runtime, RubyClass superClazz, CallSite[] extraCallSites) {
210 this(runtime);
211 this.superClass = superClazz;
212 this.marshal = superClazz.marshal; // use parent's marshal
213 superClazz.addSubclass(this);
215 this.extraCallSites = extraCallSites;
217 infectBy(superClass);
220 /**
221 * Construct a new class with the given name scoped under Object (global)
222 * and with Object as its immediate superclass.
223 * Corresponds to rb_class_new in MRI.
225 public static RubyClass newClass(Ruby runtime, RubyClass superClass) {
226 if (superClass == runtime.getClassClass()) throw runtime.newTypeError("can't make subclass of Class");
227 if (superClass.isSingleton()) throw runtime.newTypeError("can't make subclass of virtual class");
228 return new RubyClass(runtime, superClass);
231 /**
232 * A variation on newClass that allow passing in an array of supplementary
233 * call sites to improve dynamic invocation.
235 public static RubyClass newClass(Ruby runtime, RubyClass superClass, CallSite[] extraCallSites) {
236 if (superClass == runtime.getClassClass()) throw runtime.newTypeError("can't make subclass of Class");
237 if (superClass.isSingleton()) throw runtime.newTypeError("can't make subclass of virtual class");
238 return new RubyClass(runtime, superClass, extraCallSites);
241 /**
242 * Construct a new class with the given name, allocator, parent class,
243 * and containing class. If setParent is true, the class's parent will be
244 * explicitly set to the provided parent (rather than the new class just
245 * being assigned to a constant in that parent).
246 * Corresponds to rb_class_new/rb_define_class_id/rb_name_class/rb_set_class_path
247 * in MRI.
249 public static RubyClass newClass(Ruby runtime, RubyClass superClass, String name, ObjectAllocator allocator, RubyModule parent, boolean setParent) {
250 RubyClass clazz = newClass(runtime, superClass);
251 clazz.setBaseName(name);
252 clazz.setAllocator(allocator);
253 clazz.makeMetaClass(superClass.getMetaClass());
254 if (setParent) clazz.setParent(parent);
255 parent.setConstant(name, clazz);
256 clazz.inherit(superClass);
257 return clazz;
260 /**
261 * A variation on newClass that allows passing in an array of supplementary
262 * call sites to improve dynamic invocation performance.
264 public static RubyClass newClass(Ruby runtime, RubyClass superClass, String name, ObjectAllocator allocator, RubyModule parent, boolean setParent, CallSite[] extraCallSites) {
265 RubyClass clazz = newClass(runtime, superClass, extraCallSites);
266 clazz.setBaseName(name);
267 clazz.setAllocator(allocator);
268 clazz.makeMetaClass(superClass.getMetaClass());
269 if (setParent) clazz.setParent(parent);
270 parent.setConstant(name, clazz);
271 clazz.inherit(superClass);
272 return clazz;
275 /** rb_make_metaclass
278 @Override
279 public RubyClass makeMetaClass(RubyClass superClass) {
280 if (isSingleton()) { // could be pulled down to RubyClass in future
281 MetaClass klass = new MetaClass(getRuntime(), superClass); // rb_class_boot
282 setMetaClass(klass);
284 klass.setAttached(this);
285 klass.setMetaClass(klass);
286 klass.setSuperClass(getSuperClass().getRealClass().getMetaClass());
288 return klass;
289 } else {
290 return super.makeMetaClass(superClass);
294 @Deprecated
295 public IRubyObject invoke(ThreadContext context, IRubyObject self, int methodIndex, String name, IRubyObject[] args, CallType callType, Block block) {
296 return invoke(context, self, name, args, callType, block);
299 public boolean notVisibleAndNotMethodMissing(DynamicMethod method, String name, IRubyObject caller, CallType callType) {
300 return !method.isCallableFrom(caller, callType) && !name.equals("method_missing");
303 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
304 CallType callType, Block block) {
305 DynamicMethod method = searchMethod(name);
306 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
307 return RuntimeHelpers.callMethodMissing(context, self, method, name, context.getFrameSelf(), callType, block);
309 return method.call(context, self, this, name, block);
312 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name, Block block) {
313 DynamicMethod method = searchMethod(name);
314 if (method.isUndefined()) {
315 return RuntimeHelpers.callMethodMissing(context, self, method, name, context.getFrameSelf(), CallType.FUNCTIONAL, block);
317 return method.call(context, self, this, name, block);
320 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
321 IRubyObject[] args, CallType callType, Block block) {
322 assert args != null;
323 DynamicMethod method = searchMethod(name);
324 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
325 return RuntimeHelpers.callMethodMissing(context, self, method, name, args, context.getFrameSelf(), callType, block);
327 return method.call(context, self, this, name, args, block);
330 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
331 IRubyObject[] args, Block block) {
332 assert args != null;
333 DynamicMethod method = searchMethod(name);
334 if (method.isUndefined()) {
335 return RuntimeHelpers.callMethodMissing(context, self, method, name, args, context.getFrameSelf(), CallType.FUNCTIONAL, block);
337 return method.call(context, self, this, name, args, block);
340 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
341 IRubyObject arg, CallType callType, Block block) {
342 DynamicMethod method = searchMethod(name);
343 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
344 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, context.getFrameSelf(), callType, block);
346 return method.call(context, self, this, name, arg, block);
349 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
350 IRubyObject arg, Block block) {
351 DynamicMethod method = searchMethod(name);
352 if (method.isUndefined()) {
353 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, context.getFrameSelf(), CallType.FUNCTIONAL, block);
355 return method.call(context, self, this, name, arg, block);
358 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
359 IRubyObject arg0, IRubyObject arg1, CallType callType, Block block) {
360 DynamicMethod method = searchMethod(name);
361 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
362 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, context.getFrameSelf(), callType, block);
364 return method.call(context, self, this, name, arg0, arg1, block);
367 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
368 IRubyObject arg0, IRubyObject arg1, Block block) {
369 DynamicMethod method = searchMethod(name);
370 if (method.isUndefined()) {
371 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, context.getFrameSelf(), CallType.FUNCTIONAL, block);
373 return method.call(context, self, this, name, arg0, arg1, block);
376 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
377 IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, CallType callType, Block block) {
378 DynamicMethod method = searchMethod(name);
379 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
380 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, context.getFrameSelf(), callType, block);
382 return method.call(context, self, this, name, arg0, arg1, arg2, block);
385 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
386 IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
387 DynamicMethod method = searchMethod(name);
388 if (method.isUndefined()) {
389 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, context.getFrameSelf(), CallType.FUNCTIONAL, block);
391 return method.call(context, self, this, name, arg0, arg1, arg2, block);
394 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
395 CallType callType) {
396 DynamicMethod method = searchMethod(name);
397 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
398 return RuntimeHelpers.callMethodMissing(context, self, method, name, context.getFrameSelf(), callType, Block.NULL_BLOCK);
400 return method.call(context, self, this, name);
403 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name) {
404 DynamicMethod method = searchMethod(name);
405 if (method.isUndefined()) {
406 return RuntimeHelpers.callMethodMissing(context, self, method, name, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
408 return method.call(context, self, this, name);
411 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
412 IRubyObject[] args, CallType callType) {
413 assert args != null;
414 DynamicMethod method = searchMethod(name);
415 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
416 return RuntimeHelpers.callMethodMissing(context, self, method, name, args, context.getFrameSelf(), callType, Block.NULL_BLOCK);
418 return method.call(context, self, this, name, args);
421 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
422 IRubyObject[] args) {
423 assert args != null;
424 DynamicMethod method = searchMethod(name);
425 if (method.isUndefined()) {
426 return RuntimeHelpers.callMethodMissing(context, self, method, name, args, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
428 return method.call(context, self, this, name, args);
431 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
432 IRubyObject arg, CallType callType) {
433 DynamicMethod method = searchMethod(name);
434 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
435 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, context.getFrameSelf(), callType, Block.NULL_BLOCK);
437 return method.call(context, self, this, name, arg);
440 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
441 IRubyObject arg) {
442 DynamicMethod method = searchMethod(name);
443 if (method.isUndefined()) {
444 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
446 return method.call(context, self, this, name, arg);
449 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
450 IRubyObject arg0, IRubyObject arg1, CallType callType) {
451 DynamicMethod method = searchMethod(name);
452 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
453 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, context.getFrameSelf(), callType, Block.NULL_BLOCK);
455 return method.call(context, self, this, name, arg0, arg1);
458 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
459 IRubyObject arg0, IRubyObject arg1) {
460 DynamicMethod method = searchMethod(name);
461 if (method.isUndefined()) {
462 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
464 return method.call(context, self, this, name, arg0, arg1);
467 public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
468 IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, CallType callType) {
469 DynamicMethod method = searchMethod(name);
470 if (method.isUndefined() || notVisibleAndNotMethodMissing(method, name, context.getFrameSelf(), callType)) {
471 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, context.getFrameSelf(), callType, Block.NULL_BLOCK);
473 return method.call(context, self, this, name, arg0, arg1, arg2);
476 public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
477 IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
478 DynamicMethod method = searchMethod(name);
479 if (method.isUndefined()) {
480 return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
482 return method.call(context, self, this, name, arg0, arg1, arg2);
485 public IRubyObject invokeInherited(ThreadContext context, IRubyObject self, IRubyObject subclass) {
486 DynamicMethod method = getMetaClass().searchMethod("inherited");
488 if (method.isUndefined()) {
489 return RuntimeHelpers.callMethodMissing(context, self, method, "inherited", subclass, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
492 return method.call(context, self, getMetaClass(), "inherited", subclass, Block.NULL_BLOCK);
495 /** rb_class_new_instance
498 public IRubyObject newInstance(ThreadContext context, IRubyObject[] args, Block block) {
499 IRubyObject obj = allocate();
500 baseCallSites[CS_IDX_INITIALIZE].call(context, obj, args, block);
501 return obj;
504 // TODO: replace this with a smarter generated invoker that can handle 0-N args
505 public static class SpecificArityNew extends JavaMethod {
506 public SpecificArityNew(RubyModule implClass, Visibility visibility) {
507 super(implClass, visibility);
509 public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
510 RubyClass cls = (RubyClass)self;
511 IRubyObject obj = cls.allocate();
512 cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, args, block);
513 return obj;
515 public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
516 RubyClass cls = (RubyClass)self;
517 IRubyObject obj = cls.allocate();
518 cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, block);
519 return obj;
521 public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
522 RubyClass cls = (RubyClass)self;
523 IRubyObject obj = cls.allocate();
524 cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, block);
525 return obj;
527 public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
528 RubyClass cls = (RubyClass)self;
529 IRubyObject obj = cls.allocate();
530 cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, arg1, block);
531 return obj;
533 public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
534 RubyClass cls = (RubyClass)self;
535 IRubyObject obj = cls.allocate();
536 cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, arg1, arg2, block);
537 return obj;
541 /** rb_class_initialize
544 @JRubyMethod(name = "initialize", optional = 1, frame = true, visibility = Visibility.PRIVATE)
545 public IRubyObject initialize(IRubyObject[] args, Block block) {
546 if (superClass != null) {
547 throw getRuntime().newTypeError("already initialized class");
550 IRubyObject superObject;
551 if (args.length == 0) {
552 superObject = getRuntime().getObject();
553 } else {
554 superObject = args[0];
555 checkInheritable(superObject);
558 RubyClass superClazz = (RubyClass) superObject;
560 superClass = superClazz;
561 allocator = superClazz.allocator;
562 makeMetaClass(superClazz.getMetaClass());
564 marshal = superClazz.marshal;
566 superClazz.addSubclass(this);
568 super.initialize(block);
570 inherit(superClazz);
572 return this;
575 /** rb_class_init_copy
578 @JRubyMethod(name = "initialize_copy", required = 1)
579 @Override
580 public IRubyObject initialize_copy(IRubyObject original){
581 if (superClass != null) throw runtime.newTypeError("already initialized class");
582 if (original instanceof MetaClass) throw getRuntime().newTypeError("can't copy singleton class");
584 super.initialize_copy(original);
585 allocator = ((RubyClass)original).allocator;
586 return this;
589 // TODO: Someday, enable.
590 // @JRubyMethod(name = "subclasses", optional = 1)
591 public IRubyObject subclasses(ThreadContext context, IRubyObject[] args) {
592 boolean recursive = false;
593 if (args.length == 1) {
594 if (args[0] instanceof RubyBoolean) {
595 recursive = args[0].isTrue();
596 } else {
597 context.getRuntime().newTypeError(args[0], context.getRuntime().fastGetClass("Boolean"));
601 return RubyArray.newArray(context.getRuntime(), subclasses(recursive)).freeze(context);
604 public Collection subclasses(boolean includeDescendants) {
605 if (subclasses != null) {
606 Collection<RubyClass> mine = new ArrayList<RubyClass>(subclasses);
607 if (includeDescendants) {
608 for (RubyClass i: subclasses) {
609 mine.addAll(i.subclasses(includeDescendants));
613 return mine;
614 } else {
615 return Collections.EMPTY_LIST;
619 public synchronized void addSubclass(RubyClass subclass) {
620 if (subclasses == null) subclasses = new WeakHashSet<RubyClass>();
621 subclasses.add(subclass);
624 public Ruby getClassRuntime() {
625 return runtime;
628 public RubyClass getRealClass() {
629 return this;
632 @JRubyMethod(name = "inherited", required = 1)
633 public IRubyObject inherited(ThreadContext context, IRubyObject arg) {
634 return context.getRuntime().getNil();
637 /** rb_class_inherited (reversed semantics!)
640 public void inherit(RubyClass superClazz) {
641 if (superClazz == null) superClazz = getRuntime().getObject();
643 superClazz.invokeInherited(getRuntime().getCurrentContext(), superClazz, this);
646 /** Return the real super class of this class.
648 * rb_class_superclass
651 @JRubyMethod(name = "superclass")
652 public IRubyObject superclass(ThreadContext context) {
653 RubyClass superClazz = superClass;
655 if (superClazz == null) throw context.getRuntime().newTypeError("uninitialized class");
657 if (isSingleton()) superClazz = metaClass;
658 while (superClazz != null && superClazz.isIncluded()) superClazz = superClazz.superClass;
660 return superClazz != null ? superClazz : context.getRuntime().getNil();
663 /** rb_check_inheritable
666 public static void checkInheritable(IRubyObject superClass) {
667 if (!(superClass instanceof RubyClass)) {
668 throw superClass.getRuntime().newTypeError("superclass must be a Class (" + superClass.getMetaClass() + " given)");
670 if (((RubyClass)superClass).isSingleton()) {
671 throw superClass.getRuntime().newTypeError("can't make subclass of virtual class");
675 public final ObjectMarshal getMarshal() {
676 return marshal;
679 public final void setMarshal(ObjectMarshal marshal) {
680 this.marshal = marshal;
683 public final void marshal(Object obj, MarshalStream marshalStream) throws IOException {
684 getMarshal().marshalTo(getRuntime(), obj, this, marshalStream);
687 public final Object unmarshal(UnmarshalStream unmarshalStream) throws IOException {
688 return getMarshal().unmarshalFrom(getRuntime(), this, unmarshalStream);
691 public static void marshalTo(RubyClass clazz, MarshalStream output) throws java.io.IOException {
692 output.registerLinkTarget(clazz);
693 output.writeString(MarshalStream.getPathFromClass(clazz));
696 public static RubyClass unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
697 String name = RubyString.byteListToString(input.unmarshalString());
698 RubyClass result = UnmarshalStream.getClassFromPath(input.getRuntime(), name);
699 input.registerLinkTarget(result);
700 return result;
703 protected static final ObjectMarshal DEFAULT_OBJECT_MARSHAL = new ObjectMarshal() {
704 public void marshalTo(Ruby runtime, Object obj, RubyClass type,
705 MarshalStream marshalStream) throws IOException {
706 IRubyObject object = (IRubyObject)obj;
708 marshalStream.registerLinkTarget(object);
709 marshalStream.dumpVariables(object.getVariableList());
712 public Object unmarshalFrom(Ruby runtime, RubyClass type,
713 UnmarshalStream unmarshalStream) throws IOException {
714 IRubyObject result = type.allocate();
716 unmarshalStream.registerLinkTarget(result);
718 unmarshalStream.defaultVariablesUnmarshal(result);
720 return result;