Skip to content

Commit

Permalink
Rework super to use __dep__ and __inc__ from classes
Browse files Browse the repository at this point in the history
  • Loading branch information
adambeynon committed Sep 25, 2013
1 parent 5fe86a1 commit 2f65eed
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 42 deletions.
4 changes: 2 additions & 2 deletions corelib/enumerable.rb
Expand Up @@ -539,7 +539,7 @@ def max(&block)
var param = arguments.length == 1 ?
arguments[0] : $slice.call(arguments);
var modules = param.$class().$included_modules;
var modules = param.$class().__inc__;
if (modules == undefined || modules.length == 0 || modules.indexOf(Opal.Comparable) == -1) {
arg_error = true;
Expand Down Expand Up @@ -597,7 +597,7 @@ def min(&block)
var param = arguments.length == 1 ?
arguments[0] : $slice.call(arguments);
var modules = param.$class().$included_modules;
var modules = param.$class().__inc__;
if (modules == undefined || modules.length == 0 || modules.indexOf(Opal.Comparable) == -1) {
arg_error = true;
Expand Down
1 change: 1 addition & 0 deletions corelib/kernel.rb
Expand Up @@ -443,6 +443,7 @@ def singleton_class
// class' singleton should also go to subclasses?)
meta._proto = #{self}.constructor.prototype;
meta._isSingleton = true;
meta.__inc__ = [];
meta._scope = #{self}._scope;
Expand Down
34 changes: 10 additions & 24 deletions corelib/module.rb
Expand Up @@ -54,10 +54,7 @@ def ancestors
while (parent) {
result.push(parent);
if (parent.$included_modules) {
result = result.concat(parent.$included_modules)
}
result = result.concat(parent.__inc__);
parent = parent._super;
}
Expand All @@ -68,25 +65,18 @@ def ancestors

def append_features(klass)
%x{
var module = #{self};
if (!klass.$included_modules) {
klass.$included_modules = [];
}
var module = #{self}, included = klass.__inc__;
for (var idx = 0, length = klass.$included_modules.length; idx < length; idx++) {
if (klass.$included_modules[idx] === module) {
// check if this module is already included in the klass
for (var idx = 0, length = included.length; idx < length; idx++) {
if (included[idx] === module) {
return;
}
}
klass.$included_modules.push(module);
included.push(module);
if (!module._included_in) {
module._included_in = [];
}
module._included_in.push(klass);
module.__dep__.push(klass);
var donator = module._proto,
prototype = klass._proto,
Expand All @@ -97,11 +87,7 @@ def append_features(klass)
prototype[method] = donator[method];
}
// if (prototype._smethods) {
// prototype._smethods.push.apply(prototype._smethods, methods);
//}
if (klass._included_in) {
if (klass.__dep__) {
$opal.donate(klass, methods.slice(), true);
}
}
Expand All @@ -110,8 +96,8 @@ def append_features(klass)
end

def attr_accessor(*names)
attr_reader *names
attr_writer *names
attr_reader(*names)
attr_writer(*names)
end

def attr_reader(*names)
Expand Down
61 changes: 54 additions & 7 deletions corelib/runtime.js
Expand Up @@ -145,6 +145,8 @@
prototype.constructor = OpalModule;
prototype._super = superklass;
prototype._methods = [];
prototype.__inc__ = [];
prototype.__parent = superklass;

var klass = new OpalModule();

Expand All @@ -153,7 +155,7 @@
klass._proto = {};

klass._mod$ = true;
klass._included_in = [];
klass.__dep__ = [];

return klass;
}
Expand Down Expand Up @@ -192,6 +194,8 @@
prototype._super = superklass;
prototype.constructor = RubyClass;
prototype._methods = [];
prototype.__inc__ = [];
prototype.__parent = superklass;

var result = new RubyClass();
klass.prototype._klass = result;
Expand Down Expand Up @@ -229,6 +233,8 @@
prototype.constructor = OpalClass;
prototype._super = superklass;
prototype._methods = [];
prototype.__inc__ = [];
prototype.__parent = superklass;

var result = new OpalClass();
constructor.prototype._klass = result;
Expand All @@ -249,6 +255,7 @@
constructor._super = Object;
constructor.constructor = Class;
constructor._methods = [];
constructor.__inc__ = [];

bridged_classes.push(klass);

Expand Down Expand Up @@ -299,20 +306,25 @@

// Arity count error dispatcher
Opal.ac = function(actual, expected, object, meth) {
var inspect = ((typeof(object) !== 'function') ? object._klass._name + '#' : object._name + '.') + meth;
var inspect = (object._isClass ? object._name + '.' : object._klass._name + '#') + meth;
var msg = '[' + inspect + '] wrong number of arguments(' + actual + ' for ' + expected + ')';
throw Opal.ArgumentError.$new(msg);
};

// Super dispatcher
Opal.dispatch_super = function(obj, jsid, args, iter, defs) {
Opal.dispatch_super = function(obj, jsid, current_func, args, iter, defs) {
var dispatcher;

if (defs) {
dispatcher = obj._isClass ? defs._super : obj._klass._proto;
}
else {
dispatcher = obj._isClass ? obj._klass : obj._klass._super._proto;
if (obj._isClass) {
dispatcher = obj._klass;
}
else {
dispatcher = find_obj_super_dispatcher(obj, jsid, current_func);
}
}

dispatcher = dispatcher['$' + jsid];
Expand All @@ -321,6 +333,41 @@
return dispatcher.apply(obj, args);
};

var find_obj_super_dispatcher = function(obj, jsid, current_func) {
var klass = obj._klass;

// current method we are inside
var current;

while (klass) {
if (klass._proto['$' + jsid] === current_func) {
// ok
break;
}

klass = klass.__parent;
}

// if we arent in a class, we couldnt find current?
if (!klass) {
throw new Error("could not find current class for super()");
}

klass = klass.__parent;

// else, let's find the next one
while (klass) {
if (klass._proto['$' + jsid]) {
// ok
break;
}

klass = klass.__parent;
}

return klass._proto;
};

// return helper
Opal.$return = function(val) {
Opal.returner.$v = val;
Expand Down Expand Up @@ -408,7 +455,7 @@
* Donate methods for a class/module
*/
Opal.donate = function(klass, defined, indirect) {
var methods = klass._methods, included_in = klass._included_in;
var methods = klass._methods, included_in = klass.__dep__;

// if (!indirect) {
klass._methods = methods.concat(defined);
Expand All @@ -424,7 +471,7 @@
dest[method] = klass._proto[method];
}

if (includee._included_in) {
if (includee.__dep__) {
Opal.donate(includee, defined, true);
}
}
Expand Down Expand Up @@ -464,7 +511,7 @@
Opal.donate(this, [mid]);
};

var bridged_classes = ObjectClass._included_in = [];
var bridged_classes = ObjectClass.__dep__ = [];

Opal.base = ObjectClass;
BasicObjectClass._scope = ObjectClass._scope = Opal;
Expand Down
6 changes: 3 additions & 3 deletions lib/opal/parser.rb
Expand Up @@ -2085,13 +2085,13 @@ def process_super(sexp, level)

if @scope.type == :def
@scope.uses_block!
@scope.identify!
scope = @scope.identify!
cls_name = @scope.parent.name || "#{current_self}._klass._proto"

if @scope.defs
[f("$opal.dispatch_super(this, #{@scope.mid.to_s.inspect},", sexp), args, f(", "), iter, f(", #{cls_name})", sexp)]
[f("$opal.dispatch_super(this, #{@scope.mid.to_s.inspect}, #{scope},", sexp), args, f(", "), iter, f(", #{cls_name})", sexp)]
else
[f("$opal.dispatch_super(#{current_self}, #{@scope.mid.to_s.inspect}, ", sexp), args, f(", "), iter, f(")", sexp)]
[f("$opal.dispatch_super(#{current_self}, #{@scope.mid.to_s.inspect}, #{scope}, ", sexp), args, f(", "), iter, f(")", sexp)]
end

elsif @scope.type == :iter
Expand Down
8 changes: 2 additions & 6 deletions spec/filters/bugs/language.rb
Expand Up @@ -270,17 +270,13 @@
fails "A singleton class raises a TypeError for symbols"
fails "A singleton class raises a TypeError for Fixnum's"

fails "The super keyword searches class methods including modules"
fails "The super keyword calls the correct method when the method visibility is modified"
fails "The super keyword passes along modified rest args when they were originally empty"
fails "The super keyword passes along modified rest args when they weren't originally empty"
fails "The super keyword sees the included version of a module a method is alias from"
fails "The super keyword can't be used with implicit arguments from a method defined with define_method"
fails "The super keyword raises an error error when super method does not exist"
fails "The super keyword calls the correct method when the method visibility is modified"
fails "The super keyword searches class methods including modules"
fails "The super keyword calls the method on the calling class"
fails "The super keyword searches the full inheritence chain"
fails "The super keyword calls the method on the calling class including modules"
fails "The super keyword searches the full inheritence chain including modules"
fails "The super keyword calls the correct method when the superclass argument list is different from the subclass"
fails "The super keyword respects the original module a method is aliased from"

Expand Down
43 changes: 43 additions & 0 deletions spec/opal/language/super_spec.rb
Expand Up @@ -109,3 +109,46 @@ def obj.meth; "foo " + super; end
end
end
end


module OpalSuperSpecs
class A
def foo
'a'
end

def self.bar
'x'
end
end

class B < A
def foo
super + 'b'
end

def self.bar
super + 'y'
end
end

class C < B
def foo
super + 'c'
end

def self.bar
super + 'z'
end
end
end

describe "Super chains" do
it "searches entire class hierarchys for instance methods" do
OpalSuperSpecs::C.new.foo.should == 'abc'
end

it "searches entire class hierarchys for class methods" do
OpalSuperSpecs::C.bar.should == 'xyz'
end
end

0 comments on commit 2f65eed

Please sign in to comment.