/** Every class in opal is an instance of RClass @param {RClass} klass @param {RClass} superklass */ var RClass = Rt.RClass = function(klass, superklass) { this.$id = yield_hash(); this.$super = superklass; if (superklass) { var mtor = function() {}; mtor.prototype = new superklass.$m_tor(); this.$m_tbl = mtor.prototype; this.$m_tor = mtor; var cctor = function() {}; cctor.prototype = superklass.$c_prototype; var c_tor = function(){}; c_tor.prototype = new cctor(); this.$c = new c_tor(); this.$c_prototype = c_tor.prototype; } else { var mtor = function() {}; this.$m_tbl = mtor.prototype; this.$m_tor = mtor; var ctor = function() {}; this.$c = new ctor(); this.$c_prototype = ctor.prototype; } this.$method_table = {}; this.$const_table = {}; return this; }; // RClass prototype for minimizing var Rp = RClass.prototype; /** Every RClass instance is just a T_CLASS. */ Rp.$flags = T_CLASS; /** RClass truthiness */ Rp.$r = true; /** Every object in opal (except toll free objects) are instances of RObject @param {RClass} klass */ var RObject = Rt.RObject = function(klass) { this.$id = yield_hash(); this.$klass = klass; this.$m = klass.$m_tbl; return this; }; // For minimizing var Bp = RObject.prototype; /** Every RObject is a T_OBJECT */ Bp.$flags = T_OBJECT; /** RObject truthiness */ Bp.$r = true; /** The hash of all objects and classes is sinple its id */ Bp.$hash = Rp.$hash = function() { return this.$id; }; /** Like boot_defclass but for root object only (i.e. BasicObject) */ function boot_defrootclass(id) { var cls = new RClass(null, null); cls.$flags = T_CLASS; name_class(cls, id); const_set((cObject || cls), id, cls); return cls; } /** Boots core classes - Object, Module and Class */ function boot_defclass(id, superklass) { var cls = class_boot(superklass); name_class(cls, id); const_set((cObject || cls), id, cls); return cls; } function class_boot(superklass) { if (superklass) { var ctor = function() {}; ctor.prototype = superklass.constructor.prototype; var result = function() { RClass.call(this, null, superklass); return this; }; result.prototype = new ctor(); var klass = new result(); klass.$klass = cClass; return klass; } else { var result = new RClass(null, null); return result; } } function class_real(klass) { while (klass.$flags & FL_SINGLETON) { klass = klass.$super; } return klass; }; Rt.class_real = class_real; /** Name the class with the given id. */ function name_class(klass, id) { klass.__classid__ = id; }; /** Make metaclass for the given class */ function make_metaclass(klass, super_class) { if (klass.$flags & T_CLASS) { if ((klass.$flags & T_CLASS) && (klass.$flags & FL_SINGLETON)) { return make_metametaclass(klass); } else { // FIXME this needs fixinfg to remove hacked stuff now in make_singleton_class var meta = class_boot(super_class); // remove this??! meta.$m = meta.$klass.$m_tbl; meta.$c = meta.$klass.$c_prototype; meta.$flags |= FL_SINGLETON; meta.__classid__ = "#<Class:" + klass.__classid__ + ">"; klass.$klass = meta; klass.$m = meta.$m_tbl; meta.$c = klass.$c; singleton_class_attached(meta, klass); // console.log("meta id: " + klass.__classid__); return meta; } } else { // if we want metaclass of an object, do this return make_singleton_class(klass); } }; function make_singleton_class(obj) { var orig_class = obj.$klass; var klass = class_boot(orig_class); klass.$flags |= FL_SINGLETON; obj.$klass = klass; obj.$m = klass.$m_tbl; // make methods we define here actually point to instance // FIXME: we could just take advantage of $bridge_prototype like we // use for bridged classes?? means we can make more instances... klass.$bridge_prototype = obj; singleton_class_attached(klass, obj); klass.$klass = class_real(orig_class).$klass; klass.$m = klass.$klass.$m_tbl; klass.__classid__ = "#<Class:#<" + orig_class.__classid__ + ":" + klass.$id + ">>"; return klass; }; function singleton_class_attached(klass, obj) { if (klass.$flags & FL_SINGLETON) { klass.__attached__ = obj; } }; function make_metametaclass(metaclass) { var metametaclass, super_of_metaclass; if (metaclass.$klass == metaclass) { metametaclass = class_boot(null); metametaclass.$klass = metametaclass; } else { metametaclass = class_boot(null); metametaclass.$klass = metaclass.$klass.$klass == metaclass.$klass ? make_metametaclass(metaclass.$klass) : metaclass.$klass.$klass; } metametaclass.$flags |= FL_SINGLETON; singleton_class_attached(metametaclass, metaclass); metaclass.$klass = metametaclass; metaclsss.$m = metametaclass.$m_tbl; super_of_metaclass = metaclass.$super; metametaclass.$super = super_of_metaclass.$klass.__attached__ == super_of_metaclass ? super_of_metaclass.$klass : make_metametaclass(super_of_metaclass); return metametaclass; }; function boot_defmetametaclass(klass, metametaclass) { klass.$klass.$klass = metametaclass; }; // Holds an array of all prototypes that are bridged. Any method defined on // Object in ruby will also be added to the bridge classes. var bridged_classes = []; /** Define toll free bridged class */ function bridge_class(prototype, flags, id, super_class) { var klass = define_class(id, super_class); prototype.$klass = klass; prototype.$m = klass.$m_tbl; prototype.$flags = flags; prototype.$r = true; prototype.$hash = function() { return flags + '_' + this; }; return klass; }; Rt.native_prototype = function(cls, proto) { proto.$klass = cls; proto.$m = cls.$m_tbl; proto.$flags = T_OBJECT; proto.$r = true; proto.$hash = function() { return this.$id || (this.$id = yield_hash()); }; return cls; }; /** Define a new class (normal way), with the given id and superclass. Will be top level. */ function define_class(id, super_klass) { return define_class_under(cObject, id, super_klass); }; function define_class_under(base, id, super_klass) { var klass; if (const_defined(base, id)) { klass = const_get(base, id); if (!(klass.$flags & T_CLASS)) { throw new Error(id + " is not a class!"); } if (klass.$super != super_klass && super_klass != cObject) { throw new Error("Wrong superclass given for " + id); } return klass; } klass = define_class_id(id, super_klass); if (base == cObject) { name_class(klass, id); } else { name_class(klass, base.__classid__ + '::' + id); } const_set(base, id, klass); klass.$parent = base; // Class#inherited hook - here is a good place to call. We check method // is actually defined first (incase we are calling it during boot). We // can't do this earlier as an error will cause constant names not to be // set etc (this is the last place before returning back to scope). if (super_klass.m$inherited) { super_klass.m$inherited(klass); } return klass; }; Rt.define_class_under = define_class_under; /** Actually create class */ function define_class_id(id, super_klass) { var klass; if (!super_klass) { super_klass = cObject; } klass = class_create(super_klass); name_class(klass, id); make_metaclass(klass, super_klass.$klass); return klass; }; function class_create(super_klass) { return class_boot(super_klass); }; /** Get singleton class of obj */ function singleton_class(obj) { var klass; if (obj.$flags & T_OBJECT) { if ((obj.$flags & T_NUMBER) || (obj.$flags & T_SYMBOL)) { raise(eTypeError, "can't define singleton"); } } if ((obj.$klass.$flags & FL_SINGLETON) && obj.$klass.__attached__ == obj) { klass = obj.$klass; } else { var class_id = obj.$klass.__classid__; klass = make_metaclass(obj, obj.$klass); } return klass; }; Rt.singleton_class = singleton_class;