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: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to