blob: 55e32ab444d18627a5a40b3cc3261cbeea854616 [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::Perl::Subroutine;
use Carp;
use Scalar::Util qw( blessed );
use Clownfish::Class;
use Clownfish::Function;
use Clownfish::Method;
use Clownfish::Variable;
use Clownfish::ParamList;
use Clownfish::Util qw( verify_args );
our %new_PARAMS = (
param_list => undef,
alias => undef,
class_name => undef,
retval_type => undef,
use_labeled_params => undef,
);
sub new {
my $either = shift;
verify_args( \%new_PARAMS, @_ ) or confess $@;
my $self = bless { %new_PARAMS, @_, }, ref($either) || $either;
for (qw( param_list class_name alias retval_type )) {
confess("$_ is required") unless defined $self->{$_};
}
return $self;
}
sub get_class_name { shift->{class_name} }
sub use_labeled_params { shift->{use_labeled_params} }
sub perl_name {
my $self = shift;
return "$self->{class_name}::$self->{alias}";
}
sub c_name {
my $self = shift;
my $c_name = "XS_" . $self->perl_name;
$c_name =~ s/:+/_/g;
return $c_name;
}
sub c_name_list {
my $self = shift;
return $self->{param_list}->name_list;
}
my %params_hash_vals_map = (
NULL => 'undef',
true => 1,
false => 0,
);
sub params_hash_def {
my $self = shift;
return unless $self->{use_labeled_params};
my $params_hash_name = $self->perl_name . "_PARAMS";
my $arg_vars = $self->{param_list}->get_variables;
my $vals = $self->{param_list}->get_initial_values;
my @pairs;
for ( my $i = 1; $i < @$arg_vars; $i++ ) {
my $var = $arg_vars->[$i];
my $val = $vals->[$i];
if ( !defined $val ) {
$val = 'undef';
}
elsif ( exists $params_hash_vals_map{$val} ) {
$val = $params_hash_vals_map{$val};
}
push @pairs, $var->micro_sym . " => $val,";
}
if (@pairs) {
my $list = join( "\n ", @pairs );
return qq|\%$params_hash_name = (\n $list\n);\n|;
}
else {
return qq|\%$params_hash_name = ();\n|;
}
}
my %prim_type_to_allot_macro = (
double => 'ALLOT_F64',
float => 'ALLOT_F32',
int => 'ALLOT_INT',
short => 'ALLOT_SHORT',
long => 'ALLOT_LONG',
size_t => 'ALLOT_SIZE_T',
uint64_t => 'ALLOT_U64',
uint32_t => 'ALLOT_U32',
uint16_t => 'ALLOT_U16',
uint8_t => 'ALLOT_U8',
int64_t => 'ALLOT_I64',
int32_t => 'ALLOT_I32',
int16_t => 'ALLOT_I16',
int8_t => 'ALLOT_I8',
chy_bool_t => 'ALLOT_BOOL',
);
sub _allot_params_arg {
my ( $type, $label, $required ) = @_;
confess("Not a Clownfish::Type")
unless blessed($type) && $type->isa('Clownfish::Type');
my $len = length($label);
my $req_string = $required ? 'true' : 'false';
if ( $type->is_object ) {
my $struct_sym = $type->get_specifier;
my $vtable = uc($struct_sym);
if ( $struct_sym =~ /^[a-z_]*(Obj|CharBuf)$/ ) {
# Share buffers rather than copy between Perl scalars and
# Clownfish string types.
return qq|ALLOT_OBJ(\&$label, "$label", $len, $req_string, |
. qq|$vtable, alloca(cfish_ZCB_size()))|;
}
else {
return qq|ALLOT_OBJ(\&$label, "$label", $len, $req_string, |
. qq|$vtable, NULL)|;
}
}
elsif ( $type->is_primitive ) {
if ( my $allot = $prim_type_to_allot_macro{ $type->to_c } ) {
return qq|$allot(\&$label, "$label", $len, $req_string)|;
}
}
confess( "Missing typemap for " . $type->to_c );
}
sub build_allot_params {
my $self = shift;
my $param_list = $self->{param_list};
my $arg_inits = $param_list->get_initial_values;
my $arg_vars = $param_list->get_variables;
my $params_hash = $self->perl_name . "_PARAMS";
my $allot_params = "";
# Declare variables and assign default values.
for ( my $i = 1; $i <= $#$arg_vars; $i++ ) {
my $arg_var = $arg_vars->[$i];
my $val = $arg_inits->[$i];
if ( !defined($val) ) {
$val = $arg_var->get_type->is_object ? 'NULL' : '0';
}
$allot_params .= $arg_var->local_c . " = $val;\n ";
}
# Iterate over args in param list.
$allot_params .= qq|chy_bool_t args_ok = XSBind_allot_params(\n|
. qq| &(ST(0)), 1, items, "$params_hash",\n|;
for ( my $i = 1; $i <= $#$arg_vars; $i++ ) {
my $var = $arg_vars->[$i];
my $val = $arg_inits->[$i];
my $required = defined $val ? 0 : 1;
my $name = $var->micro_sym;
my $type = $var->get_type;
$allot_params .= " "
. _allot_params_arg( $type, $name, $required ) . ",\n";
}
$allot_params .= qq| NULL);
if (!args_ok) {
CFISH_RETHROW(LUCY_INCREF(cfish_Err_get_error()));
}|;
return $allot_params;
}
sub xsub_def { confess "Abstract method" }
1;
__END__
__POD__
=head1 NAME
Clownfish::Binding::Perl::Subroutine - Abstract base binding for a
Clownfish::Function.
=head1 SYNOPSIS
# Abstract base class.
=head1 DESCRIPTION
This class is used to generate binding code for invoking Clownfish's
functions and methods across the Perl/C barrier.
=head1 METHODS
=head2 new
my $binding = $subclass->SUPER::new(
param_list => $param_list, # required
alias => 'pinch', # required
class_name => 'Crustacean::Claw', # required
retval_type => $type, # required
use_labeled_params => 1, # default: false
);
Abstract constructor.
=over
=item * B<param_list> - A L<Clownfish::ParamList>.
=item * B<alias> - The local, unqualified name for the Perl subroutine that
will be used to invoke the function.
=item * B<class_name> - The name of the Perl class that the subroutine belongs
to.
=item * B<retval_type> - The return value's L<Type|Clownfish::Type>.
=item * B<use_labeled_params> - True if the binding should take hash-style
labeled parameters, false if it should take positional arguments.
=back
=head2 xsub_def
Abstract method which must return C code (not XS code) defining the Perl XSUB.
=head2 get_class_name use_labeled_params
Accessors.
=head2 perl_name
Returns the fully-qualified perl sub name.
=head2 c_name
Returns the fully-qualified name of the C function that implements the XSUB.
=head2 c_name_list
Returns a string containing the names of arguments to feed to bound C
function, joined by commas.
=head2 params_hash_def
Return Perl code initializing a package-global hash where all the keys are the
names of labeled params. The hash's name consists of the the binding's
perl_name() plus "_PARAMS".
=cut