Skip to content

Commit

Permalink
fix #186 improving abstract class based closure support
Browse files Browse the repository at this point in the history
  • Loading branch information
baroquebobcat committed May 14, 2012
1 parent 40a0c94 commit 5b1c8b1
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 9 deletions.
22 changes: 17 additions & 5 deletions lib/mirah/ast/structure.rb
Expand Up @@ -143,15 +143,23 @@ def initialize(parent, position, &block)


def prepare(typer, method) def prepare(typer, method)
mirah = typer.transformer mirah = typer.transformer
interface = method.argument_types[-1] interface_or_abstract_class = method.argument_types[-1]
outer_class = scope.defining_class outer_class = scope.defining_class


binding = scope.binding_type(mirah) binding = scope.binding_type(mirah)


name = "#{outer_class.name}$#{mirah.tmp}" name = "#{outer_class.name}$#{mirah.tmp}"


klass = mirah.define_closure(position, name, outer_class) klass = mirah.define_closure(position, name, outer_class)
klass.interfaces = [interface] case
when interface_or_abstract_class.interface?
klass.interfaces = [interface_or_abstract_class]
when interface_or_abstract_class.abstract?
klass.superclass = interface_or_abstract_class
else
raise "#{interface_or_abstract_class.name} isn't an interface or abstract"
end

klass.define_constructor(position, klass.define_constructor(position,
['binding', binding]) do |c| ['binding', binding]) do |c|
mirah.eval("@binding = binding", '-', c, 'binding') mirah.eval("@binding = binding", '-', c, 'binding')
Expand Down Expand Up @@ -205,7 +213,7 @@ def add_methods(klass, binding, typer)


def build_method(klass, binding, typer) def build_method(klass, binding, typer)
# find all methods which would not otherwise be on java.lang.Object # find all methods which would not otherwise be on java.lang.Object
impl_methods = find_methods(klass.interfaces).select do |m| impl_methods = find_abstract_methods(klass).select do |m|
begin begin
# Very cumbersome. Not sure how it got this way. # Very cumbersome. Not sure how it got this way.
mirror = BiteScript::ASM::ClassMirror.for_name('java.lang.Object') mirror = BiteScript::ASM::ClassMirror.for_name('java.lang.Object')
Expand Down Expand Up @@ -241,14 +249,18 @@ def build_method(klass, binding, typer)
end end
end end


def find_methods(interfaces) def find_abstract_methods(klass)
methods = [] methods = []
interfaces = interfaces.dup interfaces = klass.interfaces.dup
until interfaces.empty? until interfaces.empty?
interface = interfaces.pop interface = interfaces.pop
methods += interface.declared_instance_methods.select {|m| m.abstract?} methods += interface.declared_instance_methods.select {|m| m.abstract?}
interfaces.concat(interface.interfaces) interfaces.concat(interface.interfaces)
end end

if klass.superclass && klass.superclass.abstract?
methods += klass.superclass.declared_instance_methods.select{|m| m.abstract? }
end
methods methods
end end
end end
Expand Down
1 change: 1 addition & 0 deletions lib/mirah/jvm/method_lookup.rb
Expand Up @@ -21,6 +21,7 @@ def log(msg); end


def find_method(mapped_type, name, mapped_params, meta) def find_method(mapped_type, name, mapped_params, meta)
raise ArgumentError if mapped_params.any? {|p| p.nil?} raise ArgumentError if mapped_params.any? {|p| p.nil?}

if name == 'new' if name == 'new'
if meta if meta
name = "<init>" name = "<init>"
Expand Down
8 changes: 6 additions & 2 deletions lib/mirah/jvm/types/type.rb
Expand Up @@ -52,6 +52,10 @@ def interface?
# mirrors for all incoming types without blowing up on e.g. 'boolean' or 'int' # mirrors for all incoming types without blowing up on e.g. 'boolean' or 'int'
(@type || BiteScript::ASM::ClassMirror.for_name(@name)).interface? rescue nil (@type || BiteScript::ASM::ClassMirror.for_name(@name)).interface? rescue nil
end end

def abstract?
(@type || BiteScript::ASM::ClassMirror.for_name(@name)).abstract? rescue nil
end


def dynamic? def dynamic?
false false
Expand All @@ -76,7 +80,7 @@ def assignable_from?(other)
return true if other.error? || other.unreachable? return true if other.error? || other.unreachable?


# TODO should we allow more here? # TODO should we allow more here?
return interface? if other.block? return interface? || abstract? if other.block?


return true if jvm_type && (jvm_type == other.jvm_type) return true if jvm_type && (jvm_type == other.jvm_type)


Expand Down Expand Up @@ -170,4 +174,4 @@ def aload(builder)
end end
end end
end end
end end
2 changes: 1 addition & 1 deletion lib/mirah/transform/helper.rb
Expand Up @@ -762,4 +762,4 @@ def transform_annotation(node, parent)
end end
end end
end end
end end
18 changes: 17 additions & 1 deletion test/jvm/blocks_test.rb
Expand Up @@ -24,7 +24,7 @@ def setup
def parse_and_type code, name=tmp_script_name def parse_and_type code, name=tmp_script_name
parse_and_resolve_types name, code parse_and_resolve_types name, code
end end

#this should probably be a core test #this should probably be a core test
def test_empty_block_parses_and_types_without_error def test_empty_block_parses_and_types_without_error
assert_nothing_raised do assert_nothing_raised do
Expand Down Expand Up @@ -259,4 +259,20 @@ def foo(a:Bar)
end end
CODE CODE
end end

def test_method_requiring_subclass_of_abstract_class_finds_abstract_method
cls, = compile(<<-EOF)
import java.io.OutputStream
def foo x:OutputStream
x.write byte(1)
rescue
end
foo do |b:int|
puts "writing"
end
EOF
assert_output "writing\n" do
cls.main(nil)
end
end
end end

0 comments on commit 5b1c8b1

Please sign in to comment.