source: josm/trunk/tools/japicc/modules/Internals/APIDump.pm @ 12872

Last change on this file since 12872 was 12872, checked in by Don-vip, 6 years ago

update to JAPICC 2.3

File size: 32.1 KB
Line 
1###########################################################################
2# A module to create API dump from disassembled code
3#
4# Copyright (C) 2016-2017 Andrey Ponomarenko's ABI Laboratory
5#
6# Written by Andrey Ponomarenko
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License or the GNU Lesser
10# General Public License as published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# and the GNU Lesser General Public License along with this program.
19# If not, see <http://www.gnu.org/licenses/>.
20###########################################################################
21use strict;
22use IPC::Open3;
23
24my $ExtractCounter = 0;
25
26my %MName_Mid;
27my %Mid_MName;
28
29my $T_ID = 0;
30my $M_ID = 0;
31my $U_ID = 0;
32
33# Aliases
34my (%MethodInfo, %TypeInfo, %TName_Tid) = ();
35
36foreach (1, 2)
37{
38    $MethodInfo{$_} = $In::API{$_}{"MethodInfo"};
39    $TypeInfo{$_} = $In::API{$_}{"TypeInfo"};
40    $TName_Tid{$_} = $In::API{$_}{"TName_Tid"};
41}
42
43sub createAPIDump($)
44{
45    my $LVer = $_[0];
46   
47    readArchives($LVer);
48   
49    if(not keys(%{$MethodInfo{$LVer}})) {
50        printMsg("WARNING", "empty dump");
51    }
52   
53    $In::API{$LVer}{"LibraryVersion"} = $In::Desc{$LVer}{"Version"};
54    $In::API{$LVer}{"LibraryName"} = $In::Opt{"TargetLib"};
55    $In::API{$LVer}{"Language"} = "Java";
56}
57
58sub readArchives($)
59{
60    my $LVer = $_[0];
61    my @ArchivePaths = getArchives($LVer);
62    if($#ArchivePaths==-1) {
63        exitStatus("Error", "Java archives are not found in ".$In::Desc{$LVer}{"Version"});
64    }
65    printMsg("INFO", "Reading classes ".$In::Desc{$LVer}{"Version"}." ...");
66   
67    $T_ID = 0;
68    $M_ID = 0;
69    $U_ID = 0;
70   
71    %MName_Mid = ();
72    %Mid_MName = ();
73   
74    foreach my $ArchivePath (sort {length($a)<=>length($b)} @ArchivePaths) {
75        readArchive($LVer, $ArchivePath);
76    }
77    foreach my $TName (keys(%{$TName_Tid{$LVer}}))
78    {
79        my $Tid = $TName_Tid{$LVer}{$TName};
80        if(not $TypeInfo{$LVer}{$Tid}{"Type"})
81        {
82            if($TName=~/\A(void|boolean|char|byte|short|int|float|long|double)\Z/) {
83                $TypeInfo{$LVer}{$Tid}{"Type"} = "primitive";
84            }
85            else {
86                $TypeInfo{$LVer}{$Tid}{"Type"} = "class";
87            }
88        }
89    }
90}
91
92sub getArchives($)
93{
94    my $LVer = $_[0];
95    my @Paths = ();
96    foreach my $Path (keys(%{$In::Desc{$LVer}{"Archives"}}))
97    {
98        if(not -e $Path) {
99            exitStatus("Access_Error", "can't access \'$Path\'");
100        }
101       
102        foreach (getArchivePaths($Path, $LVer)) {
103            push(@Paths, $_);
104        }
105    }
106    return @Paths;
107}
108
109sub readArchive($$)
110{ # 1, 2 - library, 0 - client
111    my ($LVer, $Path) = @_;
112   
113    $Path = getAbsPath($Path);
114    my $JarCmd = getCmdPath("jar");
115    if(not $JarCmd) {
116        exitStatus("Not_Found", "can't find \"jar\" command");
117    }
118    my $ExtractPath = join_P($In::Opt{"Tmp"}, $ExtractCounter);
119    if(-d $ExtractPath) {
120        rmtree($ExtractPath);
121    }
122    mkpath($ExtractPath);
123    chdir($ExtractPath);
124    system($JarCmd." -xf \"$Path\"");
125    if($?) {
126        exitStatus("Error", "can't extract \'$Path\'");
127    }
128    chdir($In::Opt{"OrigDir"});
129    my @Classes = ();
130    foreach my $ClassPath (cmdFind($ExtractPath, "", "*\\.class"))
131    {
132        if($In::Opt{"OS"} ne "windows") {
133            $ClassPath=~s/\.class\Z//g;
134        }
135       
136        my $ClassName = getFilename($ClassPath);
137        if($ClassName=~/\$\d/ or $ClassName eq "module-info") {
138            next;
139        }
140       
141        $ClassPath = cutPrefix($ClassPath, $ExtractPath); # javap decompiler accepts relative paths only
142       
143        my $ClassDir = getDirname($ClassPath);
144        if($ClassDir=~/\./)
145        { # jaxb-osgi.jar/1.0/org/apache
146            next;
147        }
148       
149        my $Package = getPFormat($ClassDir);
150        if($LVer)
151        {
152            if(skipPackage($Package, $LVer))
153            { # internal packages
154                next;
155            }
156        }
157       
158        push(@Classes, $ClassPath);
159    }
160   
161    if($#Classes!=-1)
162    {
163        foreach my $PartRef (divideArray(\@Classes))
164        {
165            if($LVer) {
166                readClasses($PartRef, $LVer, getFilename($Path));
167            }
168            else {
169                readClasses_Usage($PartRef);
170            }
171        }
172    }
173   
174    $ExtractCounter += 1;
175   
176    if($LVer)
177    {
178        foreach my $SubArchive (cmdFind($ExtractPath, "", "*\\.jar"))
179        { # recursive step
180            readArchive($LVer, $SubArchive);
181        }
182    }
183   
184    rmtree($ExtractPath);
185}
186
187sub sepParams($$$)
188{
189    my ($Params, $Comma, $Sp) = @_;
190    my @Parts = ();
191    my %B = ( "("=>0, "<"=>0, ")"=>0, ">"=>0 );
192    my $Part = 0;
193    foreach my $Pos (0 .. length($Params) - 1)
194    {
195        my $S = substr($Params, $Pos, 1);
196        if(defined $B{$S}) {
197            $B{$S} += 1;
198        }
199        if($S eq "," and
200        $B{"("}==$B{")"} and $B{"<"}==$B{">"})
201        {
202            if($Comma)
203            { # include comma
204                $Parts[$Part] .= $S;
205            }
206            $Part += 1;
207        }
208        else {
209            $Parts[$Part] .= $S;
210        }
211    }
212    if(not $Sp)
213    { # remove spaces
214        foreach (@Parts)
215        {
216            s/\A //g;
217            s/ \Z//g;
218        }
219    }
220    return @Parts;
221}
222
223sub simpleDecl($$)
224{
225    my ($Line, $LVer) = @_;
226   
227    my %B = ( "<"=>0, ">"=>0 );
228    my @Chars = split("", $Line);
229   
230    my $Extends = undef;
231    my ($Pre, $Post) = ("", "");
232    my @Replace = ();
233   
234    foreach my $Pos (0 .. $#Chars)
235    {
236        my $S = $Chars[$Pos];
237        if(defined $B{$S}) {
238            $B{$S} += 1;
239        }
240       
241        if($B{"<"}!=0)
242        {
243            if(defined $Extends)
244            {
245                my $E = 0;
246               
247                if($S eq ",")
248                {
249                    if($B{"<"}-$B{">"}==$Extends) {
250                        $E = 1;
251                    }
252                }
253                elsif($S eq ">")
254                {
255                    if($B{"<"}==$B{">"} or $B{"<"}-$B{">"}+1==$Extends) {
256                        $E = 1;
257                    }
258                }
259               
260                if($E)
261                {
262                    if($Post) {
263                        push(@Replace, $Post);
264                    }
265                   
266                    $Extends = undef;
267                    ($Pre, $Post) = ("", "");
268                }
269            }
270            elsif($B{"<"}!=$B{">"})
271            {
272                if(substr($Pre, -9) eq " extends ")
273                {
274                    $Extends = $B{"<"}-$B{">"};
275                }
276            }
277        }
278       
279        $Pre .= $S;
280        if(defined $Extends) {
281            $Post .= $S;
282        }
283    }
284   
285    my %Tmpl = ();
286   
287    foreach my $R (@Replace)
288    {
289        if($Line=~s/([A-Za-z\d\?]+) extends \Q$R\E/$1/) {
290            $Tmpl{$1} = registerType($R, $LVer);
291        }
292    }
293   
294    return ($Line, \%Tmpl);
295}
296
297sub readClasses($$$)
298{
299    my ($Paths, $LVer, $ArchiveName) = @_;
300   
301    my $JavapCmd = getCmdPath("javap");
302    if(not $JavapCmd) {
303        exitStatus("Not_Found", "can't find \"javap\" command");
304    }
305   
306    my $TmpDir = $In::Opt{"Tmp"};
307   
308    # ! private info should be processed
309    my @Cmd = ($JavapCmd, "-s", "-private");
310    if(not $In::Opt{"Quick"}) {
311        @Cmd = (@Cmd, "-c", "-verbose");
312    }
313   
314    @Cmd = (@Cmd, @{$Paths});
315   
316    chdir($TmpDir."/".$ExtractCounter);
317    my $Pid = open3(*IN, *OUT, *ERR, @Cmd);
318    close(IN);
319   
320    my (%TypeAttr, $CurrentMethod, $CurrentPackage, $CurrentClass, $CurrentClass_Short) = ();
321    my ($InParamTable, $InVarTypeTable, $InExceptionTable, $InCode) = (0, 0, 0, 0);
322   
323    my $InAnnotations = undef;
324    my $InAnnotations_Class = undef;
325    my $InAnnotations_Method = undef;
326    my %AnnotationName = ();
327    my %AnnotationNum = (); # support for Java 7
328   
329    my ($ParamPos, $FieldPos) = (0, 0);
330    my ($LINE, $Stay, $Run, $NonEmpty) = (undef, 0, 1, undef);
331   
332    my $DContent = "";
333    my $Debug = (defined $In::Opt{"Debug"});
334   
335    while($Run)
336    {
337        if(not $Stay)
338        {
339            $LINE = <OUT>;
340           
341            if(not defined $NonEmpty and $LINE) {
342                $NonEmpty = 1;
343            }
344           
345            if($Debug) {
346                $DContent .= $LINE;
347            }
348        }
349       
350        if(not $LINE)
351        {
352            $Run = 0;
353            last;
354        }
355       
356        $Stay = 0;
357       
358        if($LINE=~/\A\s*const/) {
359            next;
360        }
361       
362        if(index($LINE, 'Start  Length')!=-1
363        or index($LINE, 'Compiled from')!=-1
364        or index($LINE, 'Last modified')!=-1
365        or index($LINE, 'MD5 checksum')!=-1
366        or index($LINE, 'Classfile /')==0
367        or index($LINE, 'Classfile jar')==0)
368        {
369            next;
370        }
371       
372        if(index($LINE, '=')!=-1)
373        {
374            if(index($LINE, ' stack=')!=-1
375            or index($LINE, 'frame_type =')!=-1
376            or index($LINE, 'offset_delta =')!=-1)
377            {
378                next;
379            }
380        }
381       
382        if(index($LINE, ':')!=-1)
383        {
384            if(index($LINE, ' LineNumberTable:')!=-1
385            or index($LINE, 'SourceFile:')==0
386            or index($LINE, ' StackMapTable:')!=-1
387            or index($LINE, ' Exceptions:')!=-1
388            or index($LINE, 'Constant pool:')!=-1
389            or index($LINE, 'minor version:')!=-1
390            or index($LINE, 'major version:')!=-1
391            or index($LINE, ' AnnotationDefault:')!=-1)
392            {
393                next;
394            }
395        }
396       
397        if(index($LINE, " of ")!=-1
398        or index($LINE, "= [")!=-1) {
399            next;
400        }
401       
402        if($LINE=~/ line \d+:|\[\s*class|\$\d|\._\d/)
403        { # artificial methods and code
404            next;
405        }
406       
407        # $LINE=~s/ \$(\w)/ $1/g;
408       
409        if(index($LINE, '$')!=-1)
410        {
411            if(index($LINE, ' class$')!=-1
412            or index($LINE, '$eq')!=-1
413            or index($LINE, '.$')!=-1
414            or index($LINE, '/$')!=-1
415            or index($LINE, '$$')!=-1
416            or index($LINE, '$(')!=-1
417            or index($LINE, '$:')!=-1
418            or index($LINE, '$.')!=-1
419            or index($LINE, '$;')!=-1) {
420                next;
421            }
422           
423            if($LINE=~/ (\w+\$|)\w+\$\w+[\(:]/) {
424                next;
425            }
426           
427            if(not $InParamTable and not $InVarTypeTable)
428            {
429                if(index($LINE, ' $')!=-1) {
430                    next;
431                }
432            }
433           
434            $LINE=~s/\$([\> ]|\s*\Z)/$1/g;
435        }
436       
437        my $EndBr = ($LINE eq "}\n" or $LINE eq "}\r\n");
438       
439        if($EndBr) {
440            $InAnnotations_Class = 1;
441        }
442       
443        if($EndBr or $LINE eq "\n" or $LINE eq "\r\n")
444        {
445            $CurrentMethod = undef;
446            $InCode = 0;
447            $InAnnotations_Method = 0;
448            $InParamTable = 0;
449            $InVarTypeTable = 0;
450            next;
451        }
452       
453        if(index($LINE, '#')!=-1)
454        {
455            if($LINE=~/\A\s*#(\d+)/)
456            { # Constant pool
457                my $CNum = $1;
458                if($LINE=~/\s+([^ ]+?);/)
459                {
460                    my $AName = $1;
461                    $AName=~s/\AL//;
462                    $AName=~s/\$/./g;
463                    $AName=~s/\//./g;
464                   
465                    $AnnotationName{$CNum} = $AName;
466                   
467                    if(defined $AnnotationNum{$CNum})
468                    { # support for Java 7
469                        if($InAnnotations_Class) {
470                            $TypeAttr{"Annotations"}{registerType($AName, $LVer)} = 1;
471                        }
472                        delete($AnnotationNum{$CNum});
473                    }
474                }
475               
476                next;
477            }
478           
479            if(index($LINE, ": #")!=-1 and index($LINE, "//")!=-1) {
480                next;
481            }
482        }
483       
484        my $TmplP = undef;
485       
486        # Java 7: templates
487        if(index($LINE, "<")!=-1)
488        { # <T extends java.lang.Object>
489          # <KEYIN extends java.lang.Object ...
490            if($LINE=~/<[A-Z\d\?]+ /i) {
491                ($LINE, $TmplP) = simpleDecl($LINE, $LVer);
492            }
493        }
494       
495        if(index($LINE, ',')!=-1) {
496            $LINE=~s/\s*,\s*/,/g;
497        }
498       
499        if(index($LINE, '$')!=-1) {
500            $LINE=~s/\$/#/g;
501        }
502       
503        if(index($LINE, "LocalVariableTable")!=-1) {
504            $InParamTable += 1;
505        }
506        elsif(index($LINE, "LocalVariableTypeTable")!=-1) {
507            $InVarTypeTable += 1;
508        }
509        elsif(index($LINE, "Exception table")!=-1) {
510            $InExceptionTable = 1;
511        }
512        elsif(index($LINE, " Code:")!=-1)
513        {
514            $InCode += 1;
515            $InAnnotations = undef;
516        }
517        elsif(index($LINE, ':')!=-1
518        and $LINE=~/\A\s*\d+:\s*/)
519        { # read Code
520            if($InCode==1)
521            {
522                if($CurrentMethod)
523                {
524                    if(index($LINE, "invoke")!=-1)
525                    {
526                        if($LINE=~/ invoke(\w+) .* \/\/\s*(Method|InterfaceMethod)\s+(.+?)\s*\Z/)
527                        { # 3:   invokevirtual   #2; //Method "[Lcom/sleepycat/je/Database#DbState;".clone:()Ljava/lang/Object;
528                            my ($InvokeType, $InvokedName) = ($1, $3);
529                           
530                            if($InvokedName!~/\A(\w+:|java\/(lang|util|io)\/)/
531                            and index($InvokedName, '"<init>":')!=0)
532                            {
533                                $InvokedName=~s/#/\$/g;
534                               
535                                my $ID = undef;
536                                if($In::Opt{"Reproducible"}) {
537                                    $ID = getMd5($InvokedName);
538                                }
539                                else {
540                                    $ID = ++$U_ID;
541                                }
542                               
543                                $In::API{$LVer}{"MethodUsed"}{$ID}{"Name"} = $InvokedName;
544                                $In::API{$LVer}{"MethodUsed"}{$ID}{"Used"}{$CurrentMethod} = $InvokeType;
545                            }
546                        }
547                    }
548                    # elsif($LINE=~/ (getstatic|putstatic) .* \/\/\s*Field\s+(.+?)\s*\Z/)
549                    # {
550                    #     my $UsedFieldName = $2;
551                    #     $In::API{$LVer}{"FieldUsed"}{$UsedFieldName}{$CurrentMethod} = 1;
552                    # }
553                }
554            }
555            elsif(defined $InAnnotations)
556            {
557                if($LINE=~/\A\s*\d+\:\s*#(\d+)/)
558                {
559                    if(my $AName = $AnnotationName{$1})
560                    {
561                        if($InAnnotations_Class) {
562                            $TypeAttr{"Annotations"}{registerType($AName, $LVer)} = 1;
563                        }
564                        elsif($InAnnotations_Method) {
565                            $MethodInfo{$LVer}{$MName_Mid{$CurrentMethod}}{"Annotations"}{registerType($AName, $LVer)} = 1;
566                        }
567                    }
568                    else
569                    { # suport for Java 7
570                        $AnnotationNum{$1} = 1;
571                    }
572                }
573            }
574        }
575        elsif($InParamTable==1 and $LINE=~/\A\s+\d/)
576        { # read parameter names from LocalVariableTable
577            if($CurrentMethod and $LINE=~/\A\s+0\s+\d+\s+\d+\s+(\#?)(\w+)/)
578            {
579                my $Art = $1;
580                my $PName = $2;
581               
582                if(($PName ne "this" or $Art) and $PName=~/[a-z]/i)
583                {
584                    if($CurrentMethod)
585                    {
586                        my $ID = $MName_Mid{$CurrentMethod};
587                       
588                        if(defined $MethodInfo{$LVer}{$ID}
589                        and defined $MethodInfo{$LVer}{$ID}{"Param"}
590                        and defined $MethodInfo{$LVer}{$ID}{"Param"}{$ParamPos}
591                        and defined $MethodInfo{$LVer}{$ID}{"Param"}{$ParamPos}{"Type"})
592                        {
593                            $MethodInfo{$LVer}{$ID}{"Param"}{$ParamPos}{"Name"} = $PName;
594                            $ParamPos++;
595                        }
596                    }
597                }
598            }
599        }
600        elsif($InVarTypeTable==1 and $LINE=~/\A\s+\d/)
601        {
602            # skip
603        }
604        elsif($CurrentClass and index($LINE, '(')!=-1
605        and $LINE=~/(\A|\s+)([^\s]+)\s+([^\s]+)\s*\((.*)\)\s*(throws\s*([^\s]+)|)\s*;\s*\Z/)
606        { # attributes of methods and constructors
607            my (%MethodAttr, $ParamsLine, $Exceptions) = ();
608           
609            $InParamTable = 0; # read the first local variable table
610            $InVarTypeTable = 0;
611            $InCode = 0; # read the first code
612            $InAnnotations_Method = 1;
613            $InAnnotations_Class = 0;
614           
615            ($MethodAttr{"Return"}, $MethodAttr{"ShortName"}, $ParamsLine, $Exceptions) = ($2, $3, $4, $6);
616            $MethodAttr{"ShortName"}=~s/#/./g;
617           
618            if($Exceptions)
619            {
620                foreach my $E (split(/,/, $Exceptions)) {
621                    $MethodAttr{"Exceptions"}{registerType($E, $LVer)} = 1;
622                }
623            }
624            if($LINE=~/(\A|\s+)(public|protected|private)\s+/) {
625                $MethodAttr{"Access"} = $2;
626            }
627            else {
628                $MethodAttr{"Access"} = "package-private";
629            }
630            $MethodAttr{"Class"} = registerType($TypeAttr{"Name"}, $LVer);
631            if($MethodAttr{"ShortName"}=~/\A(|(.+)\.)(\Q$CurrentClass\E|\Q$CurrentClass_Short\E)\Z/)
632            {
633                if($2)
634                {
635                    $MethodAttr{"Package"} = $2;
636                    $CurrentPackage = $MethodAttr{"Package"};
637                    $MethodAttr{"ShortName"} = $CurrentClass;
638                }
639                $MethodAttr{"Constructor"} = 1;
640                delete($MethodAttr{"Return"});
641            }
642            else
643            {
644                $MethodAttr{"Return"} = registerType($MethodAttr{"Return"}, $LVer);
645            }
646           
647            my @Params = sepParams($ParamsLine, 0, 1);
648           
649            $ParamPos = 0;
650            foreach my $ParamTName (@Params)
651            {
652                %{$MethodAttr{"Param"}{$ParamPos}} = ("Type"=>registerType($ParamTName, $LVer), "Name"=>"p".($ParamPos+1));
653                $ParamPos++;
654            }
655            $ParamPos = 0;
656            if(not $MethodAttr{"Constructor"})
657            { # methods
658                if($CurrentPackage) {
659                    $MethodAttr{"Package"} = $CurrentPackage;
660                }
661                if($LINE=~/(\A|\s+)abstract\s+/) {
662                    $MethodAttr{"Abstract"} = 1;
663                }
664                if($LINE=~/(\A|\s+)final\s+/) {
665                    $MethodAttr{"Final"} = 1;
666                }
667                if($LINE=~/(\A|\s+)static\s+/) {
668                    $MethodAttr{"Static"} = 1;
669                }
670                if($LINE=~/(\A|\s+)native\s+/) {
671                    $MethodAttr{"Native"} = 1;
672                }
673                if($LINE=~/(\A|\s+)synchronized\s+/) {
674                    $MethodAttr{"Synchronized"} = 1;
675                }
676            }
677           
678            my $LINE_N = <OUT>;
679           
680            if($Debug) {
681                $DContent .= $LINE_N;
682            }
683           
684            # $LINE_N=~s/ \$(\w)/ $1/g;
685            $LINE_N=~s/\$([\> ]|\s*\Z)/$1/g;
686           
687            # read the Signature
688            if(index($LINE_N, ": #")==-1
689            and $LINE_N=~/(Signature|descriptor):\s*(.+?)\s*\Z/i)
690            { # create run-time unique name ( java/io/PrintStream.println (Ljava/lang/String;)V )
691                my $SignParams = $2;
692               
693                # Generic classes
694                my $ShortClass = $CurrentClass;
695                $ShortClass=~s/<.*>//g;
696               
697                if($MethodAttr{"Constructor"}) {
698                    $CurrentMethod = $ShortClass.".\"<init>\":".$SignParams;
699                }
700                else {
701                    $CurrentMethod = $ShortClass.".".$MethodAttr{"ShortName"}.":".$SignParams;
702                }
703                if(my $PackageName = getSFormat($CurrentPackage)) {
704                    $CurrentMethod = $PackageName."/".$CurrentMethod;
705                }
706            }
707            else {
708                exitStatus("Error", "internal error - can't read method signature");
709            }
710           
711            $MethodAttr{"Archive"} = $ArchiveName;
712            if($CurrentMethod)
713            {
714                my $ID = undef;
715                if($In::Opt{"Reproducible"}) {
716                    $ID = getMd5($CurrentMethod);
717                }
718                else {
719                    $ID = ++$M_ID;
720                }
721               
722                $MName_Mid{$CurrentMethod} = $ID;
723               
724                if(defined $Mid_MName{$ID} and $Mid_MName{$ID} ne $CurrentMethod) {
725                    printMsg("ERROR", "md5 collision on \'$ID\', please increase ID length (MD5_LEN in Basic.pm)");
726                }
727               
728                $Mid_MName{$ID} = $CurrentMethod;
729               
730                $MethodAttr{"Name"} = $CurrentMethod;
731                $MethodInfo{$LVer}{$ID} = \%MethodAttr;
732            }
733        }
734        elsif($CurrentClass and $LINE=~/(\A|\s+)([^\s]+)\s+(\w+);\s*\Z/)
735        { # fields
736            my ($TName, $FName) = ($2, $3);
737            $TypeAttr{"Fields"}{$FName}{"Type"} = registerType($TName, $LVer);
738            if($LINE=~/(\A|\s+)final\s+/) {
739                $TypeAttr{"Fields"}{$FName}{"Final"} = 1;
740            }
741            if($LINE=~/(\A|\s+)static\s+/) {
742                $TypeAttr{"Fields"}{$FName}{"Static"} = 1;
743            }
744            if($LINE=~/(\A|\s+)transient\s+/) {
745                $TypeAttr{"Fields"}{$FName}{"Transient"} = 1;
746            }
747            if($LINE=~/(\A|\s+)volatile\s+/) {
748                $TypeAttr{"Fields"}{$FName}{"Volatile"} = 1;
749            }
750            if($LINE=~/(\A|\s+)(public|protected|private)\s+/) {
751                $TypeAttr{"Fields"}{$FName}{"Access"} = $2;
752            }
753            else {
754                $TypeAttr{"Fields"}{$FName}{"Access"} = "package-private";
755            }
756           
757            $TypeAttr{"Fields"}{$FName}{"Pos"} = $FieldPos++;
758           
759            my $LINE_NP = <OUT>;
760            if($Debug) {
761                $DContent .= $LINE_NP;
762            }
763           
764            # read the Signature
765            if(index($LINE_NP, ": #")==-1
766            and $LINE_NP=~/(Signature|descriptor):\s*(.+?)\s*\Z/i)
767            {
768                my $FSignature = $2;
769                if(my $PackageName = getSFormat($CurrentPackage)) {
770                    $TypeAttr{"Fields"}{$FName}{"Mangled"} = $PackageName."/".$CurrentClass.".".$FName.":".$FSignature;
771                }
772            }
773           
774            $LINE_NP = <OUT>;
775            if($Debug) {
776                $DContent .= $LINE_NP;
777            }
778           
779            if($LINE_NP=~/flags:/i)
780            { # flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ANNOTATION
781                $LINE_NP = <OUT>;
782                if($Debug) {
783                    $DContent .= $LINE_NP;
784                }
785            }
786            else
787            {
788                $LINE = $LINE_NP;
789                $Stay = 1;
790            }
791           
792            # read the Value
793            if($LINE_NP=~/Constant\s*value:\s*([^\s]+)\s(.*?)\s*\Z/i)
794            {
795              # Java 6: Constant value: ...
796              # Java 7: ConstantValue: ...
797                my ($TName, $Value) = ($1, $2);
798                if($Value)
799                {
800                    if($Value=~s/Deprecated:\s*true\Z//g) {
801                        # deprecated values: ?
802                    }
803                    $TypeAttr{"Fields"}{$FName}{"Value"} = $Value;
804                }
805                elsif($TName eq "String") {
806                    $TypeAttr{"Fields"}{$FName}{"Value"} = "\@EMPTY_STRING\@";
807                }
808            }
809            else
810            {
811                $LINE = $LINE_NP;
812                $Stay = 1;
813            }
814        }
815        elsif($LINE=~/(\A|\s+)(class|interface)\s+([^\s\{]+)(\s+|\{|\s*\Z)/)
816        { # properties of classes and interfaces
817            if($TypeAttr{"Name"})
818            { # register previous
819                %{$TypeInfo{$LVer}{registerType($TypeAttr{"Name"}, $LVer)}} = %TypeAttr;
820            }
821           
822            %TypeAttr = ("Type"=>$2, "Name"=>$3); # reset previous class
823            %AnnotationName = (); # reset annotations of the class
824            %AnnotationNum = (); # support for Java 7
825            $InAnnotations_Class = 1;
826           
827            $FieldPos = 0; # reset field position
828            $CurrentMethod = ""; # reset current method
829            $TypeAttr{"Archive"} = $ArchiveName;
830            if($TypeAttr{"Name"}=~/\A(.+)\.([^.]+)\Z/)
831            {
832                $CurrentClass = $2;
833                $TypeAttr{"Package"} = $1;
834                $CurrentPackage = $TypeAttr{"Package"};
835            }
836            else
837            {
838                $CurrentClass = $TypeAttr{"Name"};
839                $CurrentPackage = "";
840            }
841           
842            if($CurrentClass=~s/#/./g)
843            { # javax.swing.text.GlyphView.GlyphPainter <=> GlyphView$GlyphPainter
844                $TypeAttr{"Name"}=~s/#/./g;
845            }
846           
847            $CurrentClass_Short = $CurrentClass;
848            if(index($CurrentClass_Short, "<")!=-1) {
849                $CurrentClass_Short=~s/<.+>//g;
850            }
851           
852            if($LINE=~/(\A|\s+)(public|protected|private)\s+/) {
853                $TypeAttr{"Access"} = $2;
854            }
855            else {
856                $TypeAttr{"Access"} = "package-private";
857            }
858            if($LINE=~/\s+extends\s+([^\s\{]+)/)
859            {
860                my $Extended = $1;
861               
862                if($TypeAttr{"Type"} eq "class")
863                {
864                    if($Extended ne $CurrentPackage.".".$CurrentClass) {
865                        $TypeAttr{"SuperClass"} = registerType($Extended, $LVer);
866                    }
867                }
868                elsif($TypeAttr{"Type"} eq "interface")
869                {
870                    my @Elems = sepParams($Extended, 0, 0);
871                    foreach my $SuperInterface (@Elems)
872                    {
873                        if($SuperInterface ne $CurrentPackage.".".$CurrentClass) {
874                            $TypeAttr{"SuperInterface"}{registerType($SuperInterface, $LVer)} = 1;
875                        }
876                       
877                        if($SuperInterface eq "java.lang.annotation.Annotation") {
878                            $TypeAttr{"Annotation"} = 1;
879                        }
880                    }
881                }
882            }
883            if($LINE=~/\s+implements\s+([^\s\{]+)/)
884            {
885                my $Implemented = $1;
886                my @Elems = sepParams($Implemented, 0, 0);
887               
888                foreach my $SuperInterface (@Elems) {
889                    $TypeAttr{"SuperInterface"}{registerType($SuperInterface, $LVer)} = 1;
890                }
891            }
892            if($LINE=~/(\A|\s+)abstract\s+/) {
893                $TypeAttr{"Abstract"} = 1;
894            }
895            if($LINE=~/(\A|\s+)final\s+/) {
896                $TypeAttr{"Final"} = 1;
897            }
898            if($LINE=~/(\A|\s+)static\s+/) {
899                $TypeAttr{"Static"} = 1;
900            }
901           
902            if($TmplP) {
903                $TypeAttr{"GenericParam"} = $TmplP;
904            }
905        }
906        elsif(index($LINE, "Deprecated: true")!=-1
907        or index($LINE, "Deprecated: length")!=-1)
908        { # deprecated method or class
909            if($CurrentMethod) {
910                $MethodInfo{$LVer}{$MName_Mid{$CurrentMethod}}{"Deprecated"} = 1;
911            }
912            elsif($CurrentClass) {
913                $TypeAttr{"Deprecated"} = 1;
914            }
915        }
916        elsif(index($LINE, "RuntimeInvisibleAnnotations")!=-1
917        or index($LINE, "RuntimeVisibleAnnotations")!=-1)
918        {
919            $InAnnotations = 1;
920            $InCode = 0;
921        }
922        elsif(defined $InAnnotations and index($LINE, "InnerClasses")!=-1) {
923            $InAnnotations = undef;
924        }
925        else
926        {
927            # unparsed
928        }
929    }
930   
931    if($TypeAttr{"Name"})
932    { # register last
933        %{$TypeInfo{$LVer}{registerType($TypeAttr{"Name"}, $LVer)}} = %TypeAttr;
934    }
935   
936    waitpid($Pid, 0);
937    chdir($In::Opt{"OrigDir"});
938   
939    if(my $Err = $?>>8) {
940        exitStatus("Error", "failed to run javap");
941    }
942   
943    if(not $NonEmpty) {
944        exitStatus("Error", "internal error in parser");
945    }
946   
947    if($Debug) {
948        appendFile(getDebugDir($LVer)."/class-dump.txt", $DContent);
949    }
950}
951
952sub registerType($$)
953{
954    my ($TName, $LVer) = @_;
955   
956    if(not $TName) {
957        return 0;
958    }
959   
960    $TName=~s/#/./g;
961    if($TName_Tid{$LVer}{$TName}) {
962        return $TName_Tid{$LVer}{$TName};
963    }
964   
965    if(not $TName_Tid{$LVer}{$TName})
966    {
967        my $ID = undef;
968        if($In::Opt{"Reproducible"}) {
969            $ID = getMd5($TName);
970        }
971        else {
972            $ID = ++$T_ID;
973        }
974        $TName_Tid{$LVer}{$TName} = "$ID";
975    }
976   
977    my $Tid = $TName_Tid{$LVer}{$TName};
978    $TypeInfo{$LVer}{$Tid}{"Name"} = $TName;
979    if($TName=~/(.+)\[\]\Z/)
980    {
981        if(my $BaseTypeId = registerType($1, $LVer))
982        {
983            $TypeInfo{$LVer}{$Tid}{"BaseType"} = $BaseTypeId;
984            $TypeInfo{$LVer}{$Tid}{"Type"} = "array";
985        }
986    }
987    elsif($TName=~/(.+)\.\.\.\Z/)
988    {
989        if(my $BaseTypeId = registerType($1, $LVer))
990        {
991            $TypeInfo{$LVer}{$Tid}{"BaseType"} = $BaseTypeId;
992            $TypeInfo{$LVer}{$Tid}{"Type"} = "variable-arity";
993        }
994    }
995   
996    return $Tid;
997}
998
999sub readClasses_Usage($)
1000{
1001    my $Paths = $_[0];
1002   
1003    my $JavapCmd = getCmdPath("javap");
1004    if(not $JavapCmd) {
1005        exitStatus("Not_Found", "can't find \"javap\" command");
1006    }
1007   
1008    my $Input = join(" ", @{$Paths});
1009    if($In::Opt{"OS"} ne "windows")
1010    { # on unix ensure that the system does not try and interpret the $, by escaping it
1011        $Input=~s/\$/\\\$/g;
1012    }
1013   
1014    my $TmpDir = $In::Opt{"Tmp"};
1015   
1016    chdir($TmpDir."/".$ExtractCounter);
1017    open(CONTENT, "$JavapCmd -c -private $Input 2>\"$TmpDir/warn\" |");
1018    while(<CONTENT>)
1019    {
1020        if(/\/\/\s*(Method|InterfaceMethod)\s+(.+)\s*\Z/)
1021        {
1022            my $M = $2;
1023            $In::Opt{"UsedMethods_Client"}{$M} = 1;
1024           
1025            if($M=~/\A(.*)+\.\w+\:\(/)
1026            {
1027                my $C = $1;
1028                $C=~s/\//./g;
1029                $In::Opt{"UsedClasses_Client"}{$C} = 1;
1030            }
1031        }
1032        elsif(/\/\/\s*Field\s+(.+)\s*\Z/)
1033        {
1034            # my $FieldName = $1;
1035            # if(/\s+(putfield|getfield|getstatic|putstatic)\s+/) {
1036            #     $UsedFields_Client{$FieldName} = $1;
1037            # }
1038        }
1039        elsif(/ ([^\s]+) [^: ]+\(([^()]+)\)/)
1040        {
1041            my ($Ret, $Params) = ($1, $2);
1042           
1043            $Ret=~s/\[\]//g; # quals
1044            $In::Opt{"UsedClasses_Client"}{$Ret} = 1;
1045           
1046            foreach my $Param (split(/\s*,\s*/, $Params))
1047            {
1048                $Param=~s/\[\]//g; # quals
1049                $In::Opt{"UsedClasses_Client"}{$Param} = 1;
1050            }
1051        }
1052        elsif(/ class /)
1053        {
1054            if(/extends ([^\s{]+)/)
1055            {
1056                foreach my $Class (split(/\s*,\s*/, $1)) {
1057                    $In::Opt{"UsedClasses_Client"}{$Class} = 1;
1058                }
1059            }
1060           
1061            if(/implements ([^\s{]+)/)
1062            {
1063                foreach my $Interface (split(/\s*,\s*/, $1)) {
1064                    $In::Opt{"UsedClasses_Client"}{$Interface} = 1;
1065                }
1066            }
1067        }
1068    }
1069    close(CONTENT);
1070    chdir($In::Opt{"OrigDir"});
1071}
1072
1073return 1;
Note: See TracBrowser for help on using the repository browser.