/** * Copyright 2005-2007 Xue Yong Zhi, Ye Zheng * Distributed under the BSD License */ package com.xruby.runtime.lang; import com.xruby.runtime.builtin.RubyArray; import com.xruby.runtime.lang.annotation.DummyMethod; import com.xruby.runtime.lang.annotation.RubyAllocMethod; import com.xruby.runtime.lang.annotation.RubyLevelClass; import com.xruby.runtime.lang.annotation.RubyLevelMethod; @RubyLevelClass(name="Class", superclass="Module", dummy={ @DummyMethod(name="inherited", privateMethod=true) } ) public class RubyClass extends RubyModule { private static MethodCache cache = new MethodCache(); public static void resetCache() { cache.reset(); } private int objectAddress; public RubyClass(String name, RubyClass superclass, RubyModule owner) { super(name, owner); superclass_ = superclass; this.objectAddress = super.hashCode(); } public boolean isRealModule() { return false; } public boolean isRealClass() { return true; } public boolean isSingleton() { return false; } public String getName() { RubyClass realClass = this.getRealClass(); return (realClass != null) ? realClass.name_ : null; } public RubyClass getRealClass() { RubyClass klass = this; while (klass != null && !klass.isRealClass()) { klass = klass.superclass_; } return klass; } @RubyLevelMethod(name="new") public RubyValue newInstance(RubyBlock block) { RubyValue v = this.allocObject(); callInitializeMethod(v, block); return v; } @RubyLevelMethod(name="new") public RubyValue newInstance(RubyValue arg, RubyBlock block) { RubyValue v = this.allocObject(); callInitializeMethod(v, arg, block); return v; } @RubyLevelMethod(name="new") public RubyValue newInstance(RubyArray args, RubyBlock block) { RubyValue v = this.allocObject(); callInitializeMethod(v, args, block); return v; } private void callInitializeMethod(RubyValue v, RubyBlock block) { RubyAPI.callNoArgMethod(v, block, RubyID.initializeId); } private void callInitializeMethod(RubyValue v, RubyValue arg, RubyBlock block) { RubyAPI.callOneArgMethod(v, arg, block, RubyID.initializeId); } private void callInitializeMethod(RubyValue v, RubyArray args, RubyBlock block) { RubyAPI.callMethod(v, args, block, RubyID.initializeId); } @RubyLevelMethod(name="initialize") public RubyValue initialize(RubyBlock block) { return this.initialize(RubyRuntime.ObjectClass, block); } @RubyLevelMethod(name="initialize") public RubyValue initialize(RubyValue arg, RubyBlock block) { if (this.superclass_ != null) { throw new RubyException(RubyRuntime.TypeErrorClass, "already initialized class"); } this.superclass_ = checkInheritable(arg); new RubySingletonClass(this, this.superclass_.getRubyClass(), null); ClassFactory.inheritedClass(this.superclass_, this); this.initializeModule(block); return this; } @RubyAllocMethod public static RubyClass allocClass(RubyValue receiver) { return ClassFactory.createBootClass(null); } private RubyClass checkInheritable(RubyValue superclass) { if (!(superclass instanceof RubyClass)) { throw new RubyException(RubyRuntime.TypeErrorClass, "superclass must be a Class (" + superclass.getClass().getName() + " given)"); } RubyClass klass = (RubyClass)superclass; if (klass.isSingleton()) { throw new RubyException(RubyRuntime.TypeErrorClass, "can't make subclass of virtual class"); } return klass; } public int objectAddress() { return this.objectAddress; } public RubyClass getSuperClass() { return superclass_; } public void setSuperClass(RubyClass superclass) { this.superclass_ = superclass; } @RubyLevelMethod(name="superclass") public RubyValue superclass() { RubyClass c = this.superclass_; if (null != c) { c = c.getRealClass(); } return (null == c) ? RubyConstant.QNIL : c; } @RubyLevelMethod(name="allocate") public RubyValue allocate() { //TODO this implmentation does not look right... return new RubyObject(this); } public void defineAllocMethod(RubyMethod m) { m.setAccess(RubyMethod.PRIVATE); this.getRubyClass().addMethod(RubyID.ID_ALLOCATOR, m, RubyMethod.PRIVATE); } public void defineModuleMethod(String name, RubyMethod m) { throw new Error("should not reach here!"); } public void setInherited(RubyClass klass) { RubyAPI.callOneArgMethod(this, klass, null, RubyID.inheritedID); } private RubyValue allocObject() { RubyValue value = RubyAPI.callNoArgMethod(this, null, RubyID.ID_ALLOCATOR); if (value.getRubyClass().getRealClass() != this.getRealClass()) { throw new RubyException(RubyRuntime.TypeErrorClass, "wrong instance allocation"); } return value; } boolean isMyParent(final RubyClass superclass) { return superclass_ == superclass; } protected RubyMethod findSuperMethod(RubyID mid) { return null != this.superclass_ ? this.superclass_.findOwnMethod(mid) : null; } public RubyMethod findOwnMethod(RubyID mid) { MethodCache.CacheEntry entry = cache.getMethod(this, mid); if (entry.klass == this && entry.mid == mid) { if (entry.method == null) { return null; } else { return entry.method; } } RubyMethod m = super.findOwnMethod(mid); if (null != m) { cache.putMethod(this, mid, m); } return m; } public RubyMethod findOwnPublicMethod(RubyID mid) { MethodCache.CacheEntry entry = cache.getMethod(this, mid); if (entry.klass == this && entry.mid == mid) { RubyMethod em = entry.method; if (em != null && RubyMethod.PUBLIC == em.getAccess()) { return em; } else { return null; } } RubyMethod m = super.findOwnPublicMethod(mid); if (null != m) { cache.putMethod(this, mid, m); } return m; } public void collectClassMethodNames(RubyArray a, int mode) { RubyClass klass = this; while (klass != null) { klass.collectOwnMethodNames(a, mode); klass = klass.superclass_; } } protected RubyValue addMethod(RubyID id, RubyMethod method, int attribute) { cache.removeMethod(id); return super.addMethod(id, method, attribute); } }