Skip to content

Commit

Permalink
First cut of support for automatically calling BUILD and initializing…
Browse files Browse the repository at this point in the history
… public attributes based on named arguments to .new/.bless. Doesn't yet handle default values or auto-vivifying type objects.
  • Loading branch information
jnthn committed Jul 4, 2011
1 parent 1f421d3 commit a0d429e
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/Perl6/Metamodel/BOOTSTRAP.pm
Expand Up @@ -26,6 +26,7 @@ my class BOOTSTRAPATTR {
method name() { $!name }
method type() { $!type }
method box_target() { $!box_target }
method has_accessor() { 0 }
method is_generic() { $!type.HOW.is_generic($!type) }
method instantiate_generic($type_environment) {
my $ins := $!type.HOW.instantiate_generic($!type, $type_environment);
Expand Down
51 changes: 51 additions & 0 deletions src/Perl6/Metamodel/ClassHOW.pm
Expand Up @@ -13,6 +13,7 @@ class Perl6::Metamodel::ClassHOW
{
has @!does_list;
has $!composed;
has @!BUILDPLAN;

method new_type(:$name = '<anon>', :$repr = 'P6opaque', :$ver, :$auth) {
my $metaclass := self.new(:name($name), :ver($ver), :auth($auth));
Expand Down Expand Up @@ -63,6 +64,9 @@ class Perl6::Metamodel::ClassHOW
# Install Parrot v-table mappings.
self.publish_parrot_vtable_mapping($obj);
self.publish_parrot_vtable_handler_mapping($obj);

# Create BUILDPLAN.
self.create_BUILDPLAN($obj);

$obj
}
Expand Down Expand Up @@ -155,4 +159,51 @@ class Perl6::Metamodel::ClassHOW
}
0
}

# Creates the plan for building up the object. This works
# out what we'll need to do up front, so we can just zip
# through the "todo list" each time we need to make an object.
# The plan is an array of arrays. The first element of each
# nested array is an "op" representing the task to perform:
# 0 code = call specified BUILD method
# 1 class name attr_name = try to find initialization value
# 2 class attr_name code = call default value closure if needed
method create_BUILDPLAN($obj) {
# Get MRO, then work from least derived to most derived.
my @plan;
my @mro := self.mro($obj);
my $i := +@mro;
while $i > 0 {
# Get current class to consider and its attrs.
$i := $i - 1;
my $class := @mro[$i];
my @attrs := $class.HOW.attributes($class, :local(1));

# Does it have its own BUILD?
my $build := $class.HOW.find_method($class, 'BUILD');
if $build {
# We'll call the custom one.
@plan[+@plan] := [0, $build];
}
else {
# No custom BUILD. Rather than having an actual BUILD
# in Mu, we produce ops here per attribute that may
# need initializing.
for @attrs {
if $_.has_accessor {
my $attr_name := $_.name;
my $name := pir::substr__SSi($attr_name, 2);
@plan[+@plan] := [1, $class, $name, $attr_name];
}
}
}

# XXX TODO: Default values.
}
@!BUILDPLAN := @plan;
}

method BUILDPLAN($obj) {
@!BUILDPLAN
}
}
40 changes: 36 additions & 4 deletions src/core/Mu.pm
Expand Up @@ -38,12 +38,44 @@ my class Mu {
my $cand := nqp::istype($candidate, Whatever) ??
nqp::create(self) !!
$candidate;

$cand
$cand.BUILDALL(@autovivs, %attrinit);
}

method BUILDALL(@autovivs, %attrinit) {
# Get the build plan. Note that we do this "low level" to
# avoid the NQP type getting mapped to a Rakudo one, which
# would get expensive.
my $build_plan := pir::find_method__PPs(self.HOW, 'BUILDPLAN')(self.HOW, self);
my int $count = nqp::elems($build_plan);
my int $i = 0;
while nqp::islt_i($i, $count) {
my $task := nqp::atpos($build_plan, $i);
if nqp::iseq_i(nqp::atpos($task, 0), 0) {
# Custom BUILD call.
nqp::atpos($task, 1)(self, |%attrinit);
}
elsif nqp::iseq_i(nqp::atpos($task, 0), 1) {
# See if we have a value to initialize this attr
# with.
my $key_name := nqp::p6box_s(nqp::atpos($task, 2));
if %attrinit.exists($key_name) {
# XXX Should not really need the decontainerize, but seems
# that slurpy hashes sometimes lead to double containers
# somehow...
nqp::getattr(self, nqp::atpos($task, 1),
nqp::atpos($task, 3)) = pir::nqp_decontainerize__PP(%attrinit{$key_name});
}
}
else {
die "Invalid BUILDPLAN";
}
$i = nqp::add_i($i, 1);
}
self
}

proto method Numeric(|$) { * }
multi method Numeric(Mu:U:) {
proto method Numeric(|$) { * }
multi method Numeric(Mu:U:) {
note 'Use of uninitialized value in numeric context';
0
}
Expand Down

0 comments on commit a0d429e

Please sign in to comment.