blob: 97983bcaadca8e3313f0081b4c9de60fadc755d9 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
use strict;
use warnings;
package Clownfish::Binding::Core;
use Clownfish::Util qw( a_isa_b verify_args );
use Clownfish::Binding::Core::File;
use Clownfish::Binding::Core::Aliases;
use File::Spec::Functions qw( catfile );
use Fcntl;
our %new_PARAMS = (
hierarchy => undef,
dest => undef,
header => undef,
footer => undef,
);
sub new {
my $either = shift;
verify_args( \%new_PARAMS, @_ ) or confess $@;
my $self = bless { %new_PARAMS, @_ }, ref($either) || $either;
# Validate.
for ( keys %new_PARAMS ) {
confess("Missing required param '$_'") unless defined $self->{$_};
}
confess("Not a Hierarchy")
unless a_isa_b( $self->{hierarchy}, "Clownfish::Hierarchy" );
return $self;
}
sub write_all_modified {
my ( $self, $modified ) = @_;
my $hierarchy = $self->{hierarchy};
my $header = $self->{header};
my $footer = $self->{footer};
my $dest = $self->{dest};
$modified = $hierarchy->propagate_modified($modified);
# Iterate over all File objects, writing out those which don't have
# up-to-date auto-generated files.
my %written;
for my $file ( @{ $hierarchy->files } ) {
next unless $file->get_modified;
my $source_class = $file->get_source_class;
next if $written{$source_class};
$written{$source_class} = 1;
Clownfish::Binding::Core::File->write_h(
file => $file,
dest => $dest,
header => $header,
footer => $footer,
);
Clownfish::Binding::Core::File->write_c(
file => $file,
dest => $dest,
header => $header,
footer => $footer,
);
}
# If any class definition changed, rewrite the boil.h file.
$self->_write_boil_h if $modified;
return $modified;
}
# Write the "boil.h" header file, which contains common symbols needed by all
# classes, plus typedefs for all class structs.
sub _write_boil_h {
my $self = shift;
my $ordered = $self->{hierarchy}->ordered_classes;
my $typedefs = "";
# Declare object structs for all instantiable classes.
for my $class (@$ordered) {
next if $class->inert;
my $full_struct = $class->full_struct_sym;
$typedefs .= "typedef struct $full_struct $full_struct;\n";
}
# Create Clownfish aliases if necessary.
my $aliases = Clownfish::Binding::Core::Aliases->c_aliases;
my $filepath = catfile( $self->{dest}, "boil.h" );
unlink $filepath;
sysopen( my $fh, $filepath, O_CREAT | O_EXCL | O_WRONLY )
or confess("Can't open '$filepath': $!");
print $fh <<END_STUFF;
$self->{header}
#ifndef BOIL_H
#define BOIL_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include "charmony.h"
$aliases
$typedefs
/* Refcount / host object */
typedef union {
size_t count;
void *host_obj;
} cfish_ref_t;
/* Generic method pointer.
*/
typedef void
(*cfish_method_t)(const void *vself);
/* Access the function pointer for a given method from the vtable.
*/
#define LUCY_METHOD(_vtable, _class_nick, _meth_name) \\
cfish_method(_vtable, \\
Lucy_ ## _class_nick ## _ ## _meth_name ## _OFFSET)
static CHY_INLINE cfish_method_t
cfish_method(const void *vtable, size_t offset) {
union { char *cptr; cfish_method_t *fptr; } ptr;
ptr.cptr = (char*)vtable + offset;
return ptr.fptr[0];
}
/* Access the function pointer for the given method in the superclass's
* vtable. */
#define LUCY_SUPER_METHOD(_vtable, _class_nick, _meth_name) \\
cfish_super_method(_vtable, \\
Lucy_ ## _class_nick ## _ ## _meth_name ## _OFFSET)
extern size_t cfish_VTable_offset_of_parent;
static CHY_INLINE cfish_method_t
cfish_super_method(const void *vtable, size_t offset) {
char *vt_as_char = (char*)vtable;
cfish_VTable **parent_ptr
= (cfish_VTable**)(vt_as_char + cfish_VTable_offset_of_parent);
return cfish_method(*parent_ptr, offset);
}
/* Return a boolean indicating whether a method has been overridden.
*/
#define LUCY_OVERRIDDEN(_self, _class_nick, _meth_name, _micro_name) \\
(cfish_method(*((cfish_VTable**)_self), \\
Lucy_ ## _class_nick ## _ ## _meth_name ## _OFFSET )\\
!= (cfish_method_t)lucy_ ## _class_nick ## _ ## _micro_name )
#ifdef CFISH_USE_SHORT_NAMES
#define METHOD LUCY_METHOD
#define SUPER_METHOD LUCY_SUPER_METHOD
#define OVERRIDDEN LUCY_OVERRIDDEN
#endif
typedef struct cfish_Callback {
const char *name;
size_t name_len;
cfish_method_t func;
size_t offset;
} cfish_Callback;
#ifdef __cplusplus
}
#endif
#endif /* BOIL_H */
$self->{footer}
END_STUFF
}
1;
__END__
__POD__
=head1 NAME
Clownfish::Binding::Core - Generate core C code for a Clownfish::Hierarchy.
=head1 SYNOPSIS
my $hierarchy = Clownfish::Hierarchy->new(
source => '/path/to/clownfish/files',
dest => 'autogen',
);
$hierarchy->build;
my $core_binding = Clownfish::Binding::Core->new(
hierarchy => $hierarchy,
dest => 'autogen',
header => "/* Auto-generated file. */\n",
footer => $copyfoot,
);
my $modified = $core_binding->write_all_modified($modified);
=head1 DESCRIPTION
A Clownfish::Hierarchy describes an abstract specifiction for a class
hierarchy; Clownfish::Binding::Core is responsible for auto-generating C
code which implements that specification.
=head1 METHODS
=head2 new
my $binding = Clownfish::Binding::Core->new(
hierarchy => $hierarchy, # required
dest => '/path/to/autogen', # required
header => $header, # required
footer => $footer, # required
);
=over
=item * B<hierarchy> - A L<Clownfish::Hierarchy>.
=item * B<dest> - The directory where C output files will be written.
=item * B<header> - Text which will be prepended to each generated C file --
typically, an "autogenerated file" warning.
=item * B<footer> - Text to be appended to the end of each generated C file --
typically copyright information.
=back
=head2 write_all_modified
Call C<< $hierarchy->propagate_modified >> to establish which classes do not
have up-to-date generated .c and .h files, then traverse the hierarchy writing
all necessary files.
=cut