Author: rjung
Date: Mon Nov 9 18:36:41 2015
New Revision: 1713507
URL: http://svn.apache.org/viewvc?rev=1713507&view=rev
Log:
Add a script to check consistency of native
C and Java API.
There probably exists a more clever approach
but it seems to work for the time being.
Currently in trunk all methods that exists in
both worlds have consistent signatures, but a
lot of methods are missing in one of the two
worlds.
I have not yet completely checked the result,
but the current reported inconsistencies are:
Missing Java API:
In Library:
boolean has(int)
boolean initialize()
int size(int)
int version(int)
In Lock:
int cleanup(long)
In Pool:
long palloc(long,int)
long pcalloc(long,int)
long unmanaged()
In SSL:
boolean generateRSATempKey(int)
int getHandshakeCount(long)
boolean loadDSATempKey(int,String)
In SSLContext:
boolean setCertificateRaw(long,byte\[\],byte\[\],int)
In Socket:
long get(long,int)
In Status:
boolean is(int,int)
Missing C API:
For Local:
long accept(long)
int bind(long,long)
int connect(long,long)
long create(String,long)
int listen(long,int)
For OS:
String expand(String)
int info(long[])
void syslog(int,String)
void sysloginit(String)
For Registry:
int close(long)
long create(int,String,int,long)
int deleteKey(int,String,boolean)
int deleteValue(long,String)
String[] enumKeys(long)
String[] enumValues(long)
int getSize(long,String)
int getType(long,String)
String[] getValueA(long,String)
byte[] getValueB(long,String)
int getValueI(long,String)
long getValueJ(long,String)
String getValueS(long,String)
long open(int,String,int,long)
int setValueA(long,String,String[])
int setValueB(long,String,byte[])
int setValueE(long,String,String)
int setValueI(long,String,int)
int setValueJ(long,String,long)
int setValueS(long,String,String)
For SSL:
void flushBIO(long)
Added:
tomcat/native/trunk/native/build/api.pl (with props)
Added: tomcat/native/trunk/native/build/api.pl
URL:
http://svn.apache.org/viewvc/tomcat/native/trunk/native/build/api.pl?rev=1713507&view=auto
==============================================================================
--- tomcat/native/trunk/native/build/api.pl (added)
+++ tomcat/native/trunk/native/build/api.pl Mon Nov 9 18:36:41 2015
@@ -0,0 +1,367 @@
+#!/usr/bin/perl
+
+# 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.
+
+# -----------------------------------------------------------------------------
+# Compare the native API with native methods in the Java API.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# TODO:
+# - use method name plus return type plus args as key instead of
+# only method name
+# -----------------------------------------------------------------------------
+#
+
+use strict;
+
+# Modules needed, should all be standard
+
+# Commandline option parsing
+use Getopt::Std;
+# dirname function
+use File::Basename;
+# Traverse through directory structure and execute
+# callback for each file
+use File::Find;
+# Platform agnostic concatenation of directory and file names
+use File::Spec::Functions;
+
+################### BEGIN VARIABLES WHICH MUST BE MAINTAINED
#####################
+
+# Script version, printed via getopts with "--version"
+our $VERSION = '1.0';
+
+# Sub directories containing c native API and Java classes.
+# Path relative to tcnative main project directory.
+my $C_NATIVE_SUBDIR = 'native/src';
+my $JAVA_API_SUBDIR = 'java';
+
+# Macro used for marking C native API
+my $C_NATIVE_DECLARATION = 'TCN_IMPLEMENT_CALL';
+
+# Macro used for marking C native API arguments
+my $C_NATIVE_ARGUMENTS = 'TCN_STDARGS';
+
+################### END VARIABLES WHICH MUST BE MAINTAINED
#####################
+
+# Global data variables
+
+# Print verbose output?
+my $verbose = 0;
+
+# Signature structures
+my %cSignature;
+my %javaSignature;
+
+
+# Usage/Help
+sub HELP_MESSAGE {
+ my $fh = shift;
+ print $fh "Usage:: $0 [ -h ] [ -v ] [ -d DIRECTORY ]\n";
+ print $fh " -h: Print help\n";
+ print $fh " -v: Verbose output\n";
+ print $fh " -d DIRECTORY: Path to tcnative project main directory.\n";
+ print $fh " Default is '..\\..' relative to the script
directory \n";
+}
+
+
+# Parse arguments:
+# -h: help
+# -v: verbose
+# -d: tcnative project main directory
+
+$Getopt::Std::STANDARD_HELP_VERSION = 1;
+our ($opt_h, $opt_v, $opt_d);
+getopts('hvd:');
+
+if ($opt_h) {
+ HELP_MESSAGE(*STDOUT);
+ exit 1;
+}
+
+if ($opt_v) {
+ $verbose = 1;
+}
+
+my $directory = $opt_d;
+if (!defined($directory) || $directory eq '') {
+ $directory = dirname($0);
+ $directory = catfile($directory, ('..', '..'));
+}
+
+if (! -d $directory) {
+ print STDERR "Invalid tcnative project directory '$directory' -
Aborting!\n";
+ exit 1;
+}
+
+sub mapTypes {
+ my $types = shift;
+ # Replace "Array" by "[]"
+ $types =~ s/Array/\\[\\]/g;
+ # Remove leading "j" from scalar types
+ $types =~ s/j(byte|short|int|long|boolean)/$1/g;
+ # Replace string type
+ $types =~ s/jstring/String/g;
+ # Replace object type with pattern
+ $types =~ s/jobject/([A-Z][A-Za-z0-9]*|[a-z]+\\[\\])/g;
+ return $types;
+}
+
+# Extract C native API from one file
+sub cNativeApi {
+
+ # Only check C source files
+ return if $_ !~ /\.c$/;
+
+ my $file = $_;
+ my ($m, $signature, $type, $args, $ret, $class, $method);
+
+ print "Checking C API file $file\n" if $verbose;
+ open(IN, "<$file") or do {
+ print "ERROR: Ignoring file '$file', could not open file for read:
$!\n";
+ return;
+ };
+
+ while(<IN>) {
+
+ # Example declaration:
+ #TCN_IMPLEMENT_CALL(jlong, Socket, create)(TCN_STDARGS, jint family,
+ # jint type, jint protocol,
+ # jlong pool)
+ if ($_ =~ /^\s*$C_NATIVE_DECLARATION\s*/o) {
+ chomp();
+ $signature = $_;
+ # Concat next line until signature is complete
+ while($signature !~ /\([^\)]*\)\s*\([^\)]*\)/ && $signature !~ /;/
&& ($_ = <IN>)) {
+ chomp();
+ $signature .= $_;
+ }
+
+ # Strip C-style comments. See: "perldoc -q comment"
+ $signature =~
s#/\*[^*]*\*+([^/*][^*]*\*+)*/|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined
$2 ? $2 : ""#gse;
+
+ # Save one declaration
+ if ($signature =~
/^\s*$C_NATIVE_DECLARATION\s*\(([^\)]*)\)\s*\(([^\)]*)\)/) {
+ ($type, $args) = ($1, $2);
+
+ # Normalize return type and method name
+ # Collapse multiple spaces
+ $type =~ s/\s+/ /g;
+ # Remove spaces around commas
+ $type =~ s/, /,/g;
+ $type =~ s/ ,/,/g;
+ # Trim spaces at start and end
+ $type =~ s/^ //;
+ $type =~ s/ $//;
+
+ ($ret, $class, $method) = split(/,/, $type);
+
+ # Map return type
+ $ret = mapTypes($ret);
+
+ # Normalize argument list
+ # Collapse multiple spaces
+ $args =~ s/\s+/ /g;
+ # Remove spaces around commas
+ $args =~ s/, /,/g;
+ $args =~ s/ ,/,/g;
+ # Remove argument names, only types are needed
+ $args =~ s/ [^, ]+//g;
+ # Trim spaces at start and end
+ $args =~ s/^ //;
+ $args =~ s/ $//;
+ # Remove leading JNI arguments macro
+ $args =~ s/^$C_NATIVE_ARGUMENTS,?//o;
+
+ # Map argument list types
+ $args = mapTypes($args);
+
+ $m = $cSignature{$class};
+ if (!defined($m)) {
+ $m = $cSignature{$class} = {};
+ }
+ if (exists($m->{$method}) && ($m->{$method}->{return} ne $ret
|| $m->{$method}->{args} ne $args)) {
+ print "ERROR: C native file $file method $method in class
$class will be overwritten!";
+ print "\tOld signature: '$m->{$method}->{return}
$m->{$method}->{method}($m->{$method}->{args})\n";
+ print "\tNew signature: '$ret $method($args)\n";
+ }
+ # XXX Use method plus ret plus args as key instead
+ $m = $m->{$method} = {};
+ $m->{method} = $method;
+ $m->{return} = $ret;
+ $m->{args} = $args;
+ print "\tFound C API in $file class $class: '$m->{return}
$m->{method}($m->{args})'\n" if $verbose;
+ } else {
+ print "ERROR: Incomplete C signature in file $file ignored in
'$signature'";
+ }
+ }
+ }
+
+ close(IN);
+}
+
+# Extract native methods in Java API from one file
+sub javaNativeApi {
+
+ # Only check Java source files
+ return if $_ !~ /\.java$/;
+
+ my $file = $_;
+ my ($m, $signature, $type, $args, $ret, $class, $method);
+
+ print "Checking Java API file $file\n" if $verbose;
+ open(IN, "<$_") or do {
+ print "ERROR: Ignoring file '$file', could not open file for read:
$!\n";
+ return;
+ };
+
+ $class = $file;
+ $class =~ s/\.java$//;
+
+ while(<IN>) {
+
+ # Example declaration
+ #public static native long uid(String username, long p)
+ # throws Error;
+ if ($_ =~ /^\s*public\s+([^\(]*)/ && ($type = $1) && $type =~
/\snative\s/) {
+ chomp();
+ $signature = $_;
+ # Concat next line until signature is complete
+ while($signature !~ /\([^\)]*\)/ && $signature !~ /;/ && ($_ =
<IN>)) {
+ chomp();
+ $signature .= $_;
+ }
+
+ # Save one declaration
+ if ($signature =~ /^\s*([^\(]+)\(([^\)]*)\)/) {
+ ($type, $args) = ($1, $2);
+
+ # Normalize return type and method name
+ # Remove unused specifiers
+ $type =~ s/\bpublic\b//g;
+ $type =~ s/\bnative\b//g;
+ $type =~ s/\bstatic\b//g;
+ # Collapse multiple spaces
+ $type =~ s/\s+/ /g;
+ # Trim spaces at start and end
+ $type =~ s/^ //;
+ $type =~ s/ $//;
+
+ ($ret, $method) = split(/\s+/, $type);
+
+ # Normalize argument list
+ # Collapse multiple spaces
+ $args =~ s/\s+/ /g;
+ # Remove spaces around commas
+ $args =~ s/, /,/g;
+ $args =~ s/ ,/,/g;
+ # Remove spaces in front of array "[]"
+ $args =~ s/ \[\]/[]/g;
+ # Remove argument names, only types are needed
+ $args =~ s/ [^, ]+//g;
+ # Trim spaces at start and end
+ $args =~ s/^ //;
+ $args =~ s/ $//;
+
+ $m = $javaSignature{$class};
+ if (!defined($m)) {
+ $m = $javaSignature{$class} = {};
+ }
+ if (exists($m->{$method})) {
+ print "ERROR: Java file $file method $method in class
$class will be overwritten!";
+ print "\tOld signature: '$m->{$method}->{return}
$m->{$method}->{method}($m->{$method}->{args})\n";
+ print "\tNew signature: '$ret $method($args)\n";
+ }
+ # XXX Use method plus ret plus args as key instead
+ $m = $m->{$method} = {};
+ $m->{method} = $method;
+ $m->{return} = $ret;
+ $m->{args} = $args;
+ print "\tFound native Java API in $file class $class:
'$m->{return} $m->{method}($m->{args})'\n" if $verbose;
+ } else {
+ print "ERROR: Incomplete Java signature in file $file ignored
in '$signature'";
+ }
+ }
+ }
+
+ close(IN);
+}
+
+# Search for C native API
+find(\&cNativeApi, catfile($directory, $C_NATIVE_SUBDIR));
+# Search for native methods in Java API
+find(\&javaNativeApi, catfile($directory, $JAVA_API_SUBDIR));
+
+print "Comparing C and Java APIs...\n";
+
+# Check whether all native methods have correct Java counterparts
+for my $class (sort keys %cSignature) {
+ my $c = $cSignature{$class};
+ my $j = $javaSignature{$class};
+ if (!defined($j)) {
+ print "ERROR: No Java API defined for class '$class'\n";
+ for my $method (sort keys %{$c}) {
+ my $m = $c->{$method};
+ print "\tMissing Java method: $m->{return}
$m->{method}($m->{args})\n";
+ }
+ } else {
+ for my $method (sort keys %{$c}) {
+ my $m = $c->{$method};
+ if (!exists($j->{$method})) {
+ print "ERROR: Missing Java method in class '$class':
$m->{return} $m->{method}($m->{args})\n";
+ } else {
+ my $n = $j->{$method};
+ if ($n->{return} !~ /^$m->{return}$/ || $n->{args} !~
/^$m->{args}$/) {
+ print "ERROR: Incompatible API in class '$class'!\n";
+ print "\tC signature: $m->{return}
$m->{method}($m->{args})\n";
+ print "\tJava signature: $n->{return}
$n->{method}($n->{args})\n";
+ }
+ delete($j->{$method});
+ }
+ }
+ }
+}
+
+# Check whether any native Java API methods remain
+for my $class (sort keys %javaSignature) {
+ my $j = $javaSignature{$class};
+ my $c = $cSignature{$class};
+ if (!defined($c)) {
+ print "ERROR: No C API defined for class '$class'!\n";
+ for my $method (sort keys %{$j}) {
+ my $m = $j->{$method};
+ print "\tMissing C method: $m->{return}
$m->{method}($m->{args})\n";
+ }
+ } else {
+ for my $method (sort keys %{$j}) {
+ my $m = $j->{$method};
+ if (!exists($c->{$method})) {
+ print "ERROR: Missing C method in class '$class': $m->{return}
$m->{method}($m->{args})\n";
+ } else {
+ my $n = $c->{$method};
+ if ($m->{return} !~ /^$n->{return}$/ || $m->{args} !~
/^$n->{args}$/) {
+ print "ERROR: Incompatible API in class '$class'!\n";
+ print "\tC signature: $n->{return}
$n->{method}($n->{args})\n";
+ print "\tJava signature: $m->{return}
$m->{method}($m->{args})\n";
+ }
+ delete($c->{$method});
+ }
+ }
+ }
+}
+
+print "Done.\n";
Propchange: tomcat/native/trunk/native/build/api.pl
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: tomcat/native/trunk/native/build/api.pl
------------------------------------------------------------------------------
svn:executable = *
Propchange: tomcat/native/trunk/native/build/api.pl
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]