From 84ca8b13ef8b6cfb12847ad4e7d307b59dce18cb Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Tue, 6 Oct 2026 16:47:28 -0700 Subject: [PATCH 1/5] Remove the Perl front end, dfepl Co-Authored-By: Claude Opus 5.5 --- .gitignore | 3 - Makefile | 13 +- doc/CHANGELOG.md | 4 + doc/Makefile | 17 +- doc/daikon.texinfo | 590 +--------------- doc/developer.texinfo | 15 +- doc/images/dfepl-flow.dot | 32 - doc/images/dfepl-flow.txt | 55 -- examples/perl-examples/Birthday.accessors | 2 - examples/perl-examples/Birthday.pm | 95 --- examples/perl-examples/Makefile | 44 -- examples/perl-examples/standalone.pl | 170 ----- examples/perl-examples/test_bday.pl | 35 - front-end/perl/README | 90 --- front-end/perl/lib/B/DeparseDaikon.pm | 817 ---------------------- front-end/perl/lib/Daikon/Output.pm | 443 ------------ front-end/perl/lib/Daikon/PerlType.pm | 690 ------------------ front-end/perl/lib/daikon_runtime.pm | 392 ----------- scripts/dfepl | 439 ------------ scripts/dtrace-perl | 43 -- scripts/dtype-perl | 47 -- 21 files changed, 19 insertions(+), 4017 deletions(-) delete mode 100644 doc/images/dfepl-flow.dot delete mode 100644 doc/images/dfepl-flow.txt delete mode 100644 examples/perl-examples/Birthday.accessors delete mode 100644 examples/perl-examples/Birthday.pm delete mode 100644 examples/perl-examples/Makefile delete mode 100755 examples/perl-examples/standalone.pl delete mode 100644 examples/perl-examples/test_bday.pl delete mode 100644 front-end/perl/README delete mode 100644 front-end/perl/lib/B/DeparseDaikon.pm delete mode 100644 front-end/perl/lib/Daikon/Output.pm delete mode 100644 front-end/perl/lib/Daikon/PerlType.pm delete mode 100644 front-end/perl/lib/daikon_runtime.pm delete mode 100755 scripts/dfepl delete mode 100755 scripts/dtrace-perl delete mode 100755 scripts/dtype-perl diff --git a/.gitignore b/.gitignore index ea776aa37a..1d03160bc4 100644 --- a/.gitignore +++ b/.gitignore @@ -110,9 +110,6 @@ doc/developer.vr doc/developer/ doc/developer_manual_html/ doc/images/daikon-logo.pdf -doc/images/dfepl-flow.eps -doc/images/dfepl-flow.jpg -doc/images/dfepl-flow.pdf doc/invariants-doc.texinfo doc/www/pubs diff --git a/Makefile b/Makefile index 3783aa641d..84f74e4a33 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ JAVA_RELEASE_NUMBER := $(shell java -version 2>&1 | head -1 | cut -d'"' -f2 | se # note that for right now, we are only copying the html and texinfo # versions of the developer manual (though the PDF version is also built) -IMAGE_FILES := daikon-logo.gif daikon-logo.png daikon-logo.eps dfepl-flow.dot +IMAGE_FILES := daikon-logo.gif daikon-logo.png daikon-logo.eps IMAGE_PARTIAL_PATHS := $(addprefix images/,${IMAGE_FILES}) DOC_FILES_NO_IMAGES := Makefile index.html daikon.texinfo \ config-options.texinfo invariants-doc.texinfo \ @@ -61,7 +61,6 @@ DIST_VERSION_FILES := ${README_PATHS} doc/daikon.texinfo doc/developer.texinfo \ # from the 'Installation' section of the Daikon manual. SCRIPT_FILES := Makefile \ daikon.bashrc \ - dfepl dtrace-perl dtype-perl \ java-cpp \ kvasir-dtrace \ convertcsv.pl \ @@ -660,10 +659,6 @@ daikon.tar daikon.zip: kvasir ${README_PATHS} ${DAIKON_JAVA_FILES} java/Makefile # Keep .java files, delete everything else (cd ${TMPDIR}/daikon && find examples/java-examples -name '*.java' -prune -o \( -type f -o -name daikon-output -o -name daikon-java -o -name daikon-instrumented \) -print0 | xargs -0 rm -rf) - # Perl example files - mkdir ${TMPDIR}/daikon/examples/perl-examples - (cd examples/perl-examples && cp -p Birthday.accessors Birthday.pm standalone.pl test_bday.pl ${TMPDIR}/daikon/examples/perl-examples) - # C example files for Kvasir mkdir ${TMPDIR}/daikon/examples/c-examples mkdir ${TMPDIR}/daikon/examples/c-examples/bzip2 @@ -687,12 +682,6 @@ daikon.tar daikon.zip: kvasir ${README_PATHS} ${DAIKON_JAVA_FILES} java/Makefile (cd ${TMPDIR}/daikon/java; ${RM_TEMP_FILES}) ## Front ends - mkdir ${TMPDIR}/daikon/front-end - - # Perl front end - # mkdir ${TMPDIR}/daikon/front-end/perl - cp -pR front-end/perl ${TMPDIR}/daikon/front-end - (cd ${TMPDIR}/daikon/front-end/perl; ${RM_TEMP_FILES} ) # Kvasir C front end # We use the --filter option twice with rsync to exclude unneeded files. diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 6db6daed42..7e08adc777 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -21,6 +21,10 @@ Further documentation can be found in: `make -C $DAIKONDIR/java javadoc` It is also available at . +## Version ?? + +Removed the Perl front end, dfepl. + ## Version 5.9.0 (September 1, 2026) * All Daikon tools now work with Java 8-26. diff --git a/doc/Makefile b/doc/Makefile index 2aea137e6c..ec3d0f7808 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -43,21 +43,13 @@ endif images/%.eps: images/%.dot dot -Tps $< >$@ -# Create a jpg version of the dfepl flow, blow up by 4X to get cleaner -# output. -images/dfepl-flow.jpg: images/dfepl-flow.eps - pstopnm -portrait -stdout -xsize 2000 $< \ - | pnmcrop \ - | pnmscale 0.25 \ - | pnmmargin -white 20 | pnmtojpeg > images/dfepl-flow.jpg - images/%.pdf: images/%.eps epstopdf $< --outfile=$@ .PHONY: html html: daikon.html developer.html -daikon.html: daikon.texinfo config-options.texinfo invariants-doc.texinfo images/dfepl-flow.jpg +daikon.html: daikon.texinfo config-options.texinfo invariants-doc.texinfo ${MAKE} daikon-html # Just builds .html from .texinfo. @@ -91,7 +83,7 @@ html-chap: daikon/index.html developer/index.html # The "subdir" flag does not copy over images, so the # pathnames become incorrect. We need to copy them over ourselves. -daikon/index.html: daikon.texinfo config-options.texinfo invariants-doc.texinfo images/dfepl-flow.jpg ../.utils/html-tools ${CRONIC} +daikon/index.html: daikon.texinfo config-options.texinfo invariants-doc.texinfo ../.utils/html-tools ${CRONIC} rm -rf daikon/* ${CRONIC} ${TEXI2HTML} --split=chapter -o daikon $< # Fixup 'bad' href(s) inserted by makeinfo. @@ -99,7 +91,6 @@ daikon/index.html: daikon.texinfo config-options.texinfo invariants-doc.texinfo perl -pi -e 's|href="CHANGELOG.md"|href="../CHANGELOG.md"|;' daikon/*.html mkdir daikon/images cp images/*.png daikon/images - cp images/*.jpg daikon/images cd daikon && ../${HTMLTOOLS}/html-add-favicon . ../daikon-favicon.png developer/index.html: developer.texinfo ../.utils/html-tools ${CRONIC} @@ -109,10 +100,9 @@ developer/index.html: developer.texinfo ../.utils/html-tools ${CRONIC} perl -pi -e 's|../dir/index.html|../index.html|;' developer/index.html mkdir developer/images cp images/*.png developer/images - cp images/*.jpg developer/images cd developer && ../${HTMLTOOLS}/html-add-favicon . ../daikon-favicon.png -pdf: daikon.pdf images/dfepl-flow.eps developer.pdf +pdf: daikon.pdf developer.pdf daikon.pdf: daikon.texinfo config-options.texinfo invariants-doc.texinfo ${CRONIC} ${CRONIC} makeinfo --pdf $< @@ -132,7 +122,6 @@ clean: texi-clean rm -f developer.tmp \ developer.info developer.info-[0-9] \ developer.log developer.dvi developer.pdf developer.html - rm -f images/dfepl-flow.{eps,pdf,jpg} rm -f *.bak texi-clean: diff --git a/doc/daikon.texinfo b/doc/daikon.texinfo index 2c7d2f72e6..4e3fb43cdd 100644 --- a/doc/daikon.texinfo +++ b/doc/daikon.texinfo @@ -148,7 +148,7 @@ program computes, and then reports properties that were true over the observed executions. @c Daikon's output, which is sometimes called an ``operational abstraction'', @c looks like a formal specification, or the contents of assert statements. -Daikon can detect properties in C, C++, C#, Eiffel, F#, Java, Perl, and +Daikon can detect properties in C, C++, C#, Eiffel, F#, Java, and Visual Basic programs; in spreadsheet files; and in other data sources. (Dynamic invariant detection is a machine learning technique that can be applied to @@ -346,7 +346,7 @@ the Simplify theorem prover (@pxref{Installing Simplify}). @node Example usage -@chapter Example usage for Java, C/C++, C#/F#/Visual Basic, Perl, and Eiffel +@chapter Example usage for Java, C/C++, C#/F#/Visual Basic, and Eiffel Detecting invariants involves two steps: @enumerate @@ -367,7 +367,7 @@ with a variety of tools. @end enumerate This section briefly describes how to obtain data traces for Java, C, -C#, Perl, and Eiffel programs, and how to run Daikon. For detailed +C#, and Eiffel programs, and how to run Daikon. For detailed information about these and other front ends that are available for Daikon, @pxref{Front ends and instrumentation}. @@ -375,7 +375,6 @@ Daikon, @pxref{Front ends and instrumentation}. * Detecting invariants in Java programs:: * Detecting invariants in C/C++ programs:: * Detecting invariants in C# programs:: -* Detecting invariants in Perl programs:: * Detecting invariants in Eiffel programs:: * Detecting invariants in Simulink/Stateflow programs:: @end menu @@ -955,196 +954,6 @@ Please see its documentation at @url{https://github.com/codespecs/@/daikon-dot-net-front-end}. -@node Detecting invariants in Perl programs -@section Detecting invariants in Perl programs - -The Daikon front end for Perl is called @command{dfepl}. - -Using the Perl front end is a two-pass -process: first you must run the annotated program so that the runtime -system can dynamically infer the kind of data stored in each variable, -and then you must re-annotate and re-run the program with the added -type information. -This is necessary because Perl programs do not contain type declarations. - -@command{dfepl} requires version 5.8 or later of Perl. - -@menu -* Instrumenting Perl programs:: -* Perl examples:: -@end menu - - -@node Instrumenting Perl programs -@subsection Instrumenting Perl programs - -Perl programs must be instrumented twice. First they must be -instrumented without type information. Then, once the first -instrumented version has been run to produce type information, they -must be instrumented again taking the type information into account. - -To instrument a stand-alone Perl program, invoke @command{dfepl} with -the name of the program as an argument. - -@exampleindent 1 -@example -dfepl program.pl -@end example - -To instrument a Perl module or a collection of modules, invoke -@command{dfepl} either with the name of each module, or with the name -of a directory containing the modules. To instrument all the modules -in the current directory, give @command{dfepl} the argument @file{.}. -For instance, if the current directory contains a module -@code{Acme::Trampoline} in @file{Acme/Trampoline.pm} and another -module @code{Acme::Date} in @file{Acme/Date.pm}, they can be annotated -by either of the following two commands: - -@example -dfepl Acme/Trampoline.pm Acme/Date.pm -dfepl . -@end example - -Once type information is available, run the instrumentation command -again with the @option{-T} or @option{-t} options added to use the -produced type information. - -For more information about @command{dfepl}, see @ref{dfepl}. - - -@node Perl examples -@subsection Perl examples - -The Daikon distribution includes sample Perl programs suitable for use -with Daikon in the @file{examples/perl-examples} directory. - -Here are step-by-step instructions for examining a simple module, -@file{Birthday.pm}, as used by a test script @file{test-bday.pl}. - -@exampleindent 1 -@enumerate -@item Change to the directory containing the @file{Birthday.pm} -module. -@example -cd examples/perl-examples -@end example - -@item Instrument the @file{Birthday.pm} file. -@example -dfepl Birthday.pm -@end example - -@noindent -This command creates a directory @file{daikon-untyped}, and puts -the instrumented version of @file{Birthday.pm} into -@file{daikon-untyped/Birthday.pm}. As the directory name implies, this -instrumented version doesn't contain type information. - -@item Run a test suite using the instrumented @file{Birthday.pm} -file. -@example -dtype-perl test_bday.pl 10 -@end example - -@noindent -The @file{dtype-perl} is a script that runs Perl with the appropriate -command line options to find the modules used by the Daikon Perl -runtime tracing modules, and to use the instrumented versions of -modules in @file{daikon-untyped} in preference to their original -ones. The number 10 is an argument to the @file{test_bday.pl} script -telling it to run a relatively short test. - -This will also generate a file -@file{daikon-instrumented/Birthday.types} recording the type of each -variable seen during the execution of the instrumented program. - -@item Re-annotate the module using the type information. -@example -dfepl -T Birthday.pm -@end example - -@noindent -This step repeats step 2, except that the @option{-T} flag to -@command{dfepl} tells it to use the type information generated in the -previous step, and to put the output in the directory -@file{daikon-instrumented}. @command{dfepl} also converts the -type information into a file @file{daikon-output/Birthday.decls} -containing subroutine declarations suitable for Daikon. - -@item Run the full test suite with the type-instrumented -@file{Birthday.pm}. -@example -dtrace-perl test_bday.pl 30 -@end example - -@noindent -Here we run another test suite, which happens to be the same -@file{test_bday.pl}, but running for longer. (The example will also -work with a smaller number). The script @command{dtrace-perl} is -similar to @command{dtype-perl} mentioned earlier, but looks for -instrumented source files in @file{daikon-instrumented}. - -This creates @file{daikon-output/test_bday-combined.dtrace}, a trace -file containing the values of variables at each invocation. (The -file name is formed from the name of the test program, with -@file{-combined} appended because it contains the trace information -from all the instrumented modules invoked from the program). - -@item Change to the @file{daikon-output} directory to analyze the -output. -@example -cd daikon-output -@end example - -@item Run Daikon on the trace file -@example -java -cp $@{DAIKONDIR@}/daikon.jar daikon.Daikon Birthday.decls test_bday-combined.dtrace -@end example - -@item -Examine the invariants. They are printed to standard output, and -they are also saved to file @file{Birthday.inv.gz}, which you can -manipulate with the @command{PrintInvariants} program and other Daikon tools. -For example: -@example -java -cp $@{DAIKONDIR@}/daikon.jar daikon.PrintInvariants Birthday.inv.gz -@end example - -Invariants produced from Perl programs can be examined using the same -tools as other Daikon invariants. -@end enumerate -@exampleindent 4 - -In the example above, the script @file{test_bday.pl} was not itself -instrumented; it was only used to test the instrumented code. The -Perl front end can also be used to instrument stand-alone Perl -programs. The following sequence of commands, similar to those above, -show how Daikon can be used with the stand-alone program -@file{standalone.pl}, also in the @file{examples/perl-examples} -directory. - -@exampleindent 1 -@example -dfepl standalone.pl -dtype-perl daikon-untyped/standalone.pl -dfepl -T standalone.pl -dtrace-perl daikon-instrumented/standalone.pl -cd daikon-output -java -cp $@{DAIKONDIR@}/daikon.jar daikon.Daikon -o standalone.inv standalone-main.decls \ - standalone-combined.dtrace -@end example -@exampleindent 4 - -@noindent -Note two differences when running a stand-alone program. First, the -instrumented versions of the program, in the @file{daikon-untyped} or -@file{daikon-instrumented} directory, are run directly. Second, the -declarations file is named after the package in which the subroutines -were declared, but since every stand-alone program uses the -@code{main} package, the name of the program is prepended to the -@file{.decls} file name to avoid collisions. - - @node Detecting invariants in Eiffel programs @section Detecting invariants in Eiffel programs @@ -3138,7 +2947,6 @@ ends,,,developer,Daikon Developer Manual}. * DynComp for Java:: * Kvasir:: * Celeriac:: -* dfepl:: * convertcsv.pl:: * Other front ends:: @end menu @@ -6057,386 +5865,6 @@ use Scout (previously called @nospellcheck{Contract Inserter}): @uref{https://bitbucket.org/michael.ernst/scout-contract-inserter/}. -@node dfepl -@section Perl front end dfepl - -@cindex front end for Perl -@cindex Perl front end -@cindex dfepl (front end for Perl) -@cindex instrumentation, of Perl programs - -This section contains details about @command{dfepl}, the Daikon front end for -Perl. For a brief introduction to @command{dfepl}, see @ref{Perl examples} and -@ref{Instrumenting Perl programs}. - -@command{dfepl} works with Perl versions 5.8 and later. (To be precise, Perl -programs instrumented with @command{dfepl} can also be run with Perl 5.6, but -the instrumentation engine, which is itself written in Perl, requires -version 5.8). @command{dfepl} reads the source code for Perl modules or -programs, and writes out instrumented versions of that code that -keep track of function parameters, and make calls to routines in the -@file{daikon_runtime} package whenever an instrumented subroutine is -entered or exited. - -The instrumentation engine recognizes parameters as those variables -that are declared with @code{my(...)} or @code{local(...)} and, in the -same expression, assigned to from a value related to the argument -array @code{@@_}, but only among the first contiguous series of such -assignments in the body of a subroutine. This will capture the most -common assignment idioms, such as @code{my $self = shift;} (where -@code{shift} is short for @code{shift @@_}), @code{my $x = $_[0];}, and -@code{my($x, $y, @@a) = @@_;}, but the arguments to subroutines which -access them only directly through @code{@@_}, or that perform other -operations before reading their arguments, will not be recognized. - -If the uninstrumented code requested warnings via the @code{use -warnings} pragma or by adding the @option{-w} flag on the @code{#!} -line, the instrumented code will also request warnings. In this case, -or if @option{-w} is specified on the command line when running it, the -instrumented code may produce warnings that the original code did -not. There are several situations in which the instrumented code -produced by @command{dfepl}, while functionally equivalent to the original, -generates more warnings. The most common such problem, which arises -from code that captures the scalar-context return value of a -subroutine that returns a list, has been avoided in the current -version by disabling the warning in question. Other warnings which -are known to be produced innocuously in this way include -@samp{Ambiguous call resolved as CORE::foo(), qualify as such or use -&} (caused by code that uses @code{CORE::} to distinguish a built-in -function from a user subroutine of the same name), and @samp{Constant -subroutine foo redefined} (caused by loading both instrumented and -uninstrumented versions of a file). Though some such warnings -represent deficiencies in the instrumentation engine, they can be -safely ignored when they occur. - -Because Perl programs do not contain static type information to -distinguish, for instance, between strings and numbers, the Perl -front end incorporates an additional dynamic analysis to infer these -types. This type guessing, which occurs as a first pass before the -program can be instrumented to produce output for Daikon, operates in -a manner somewhat analogous to Daikon itself: watching the execution -of a program, the runtime system chooses the most restrictive type for -a variable that is not contradicted during that execution. These -types indicate, for instance, whether a scalar value always holds an -integer, a possibly fractional numeric value, or a reference to -another object. It should not be necessary to examine or modify this -type information directly, but for the curious, the syntax of the type -information is described in comments in the @file{Daikon::PerlType} -module. - -The safest course is to infer types for variables using exactly the -same program executions (e.g., test cases) which will later be used to -generate traces for Daikon, as this guarantees that the type -information will match the actual data written to the trace file. -However, because the type-guessing-instrumented versions of programs -run fairly slowly in the current version, you may be tempted to use a -subset of the input data for type guessing. Doing so is possible, but -it will only work correctly if the smaller tests exercise all of the -instrumented subroutines and exit points with all the types of data -they will later be used with. If the trace runtime tries to output a -data value that doesn't match the inferred type, the value may -silently be converted according to Perl's usual conventions (for -instance, a non-numeric string may be treated as the number zero), or -it may cause an error during tracing (for instance, trying to -dereference a supposed array reference that isn't). Also, if a -subroutine exit point is traced but was never encountered during type -guessing, the generated @file{.decls} and @file{.dtrace} files will be -incompatible in a way that will cause Daikon to abort with an error -message of the form @samp{Program point foo():::EXIT22 appears in @file{.dtrace} -file but not in any @file{.decls} file}. - -@float Figure,dfepl-flow -@center @image{images/dfepl-flow,4in,} -@caption{Workflow of instrumenting Perl code with @command{dfepl}.} -@end float - -@command{dfepl} works by reading one or more Perl programs -or modules, and writing out new versions of those files, instrumented -to capture information about their execution, by default to another -directory. @command{dfepl} is used in two passes: first, before type -information is available, instrumented versions are written to a -directory @file{daikon-untyped}. These untyped programs, when run, -will write -files containing dynamically inferred type information (with the -extension @file{.types}), by default to the @file{daikon-instrumented} -directory. When @command{dfepl} is rerun with this type information, it -produces type-aware instrumented code in the -@file{daikon-instrumented} directory, which when run produces -execution traces in files with the extension @file{.dtrace} in the -directory @file{daikon-output}. - -@menu -* dfepl options:: -@end menu - -@node dfepl options -@subsection dfepl options - -@table @option -@item --absolute -@itemx --no-absolute -@option{--absolute} stores the absolute path to the output directories -(by default named @file{daikon-untyped}, @file{daikon-instrumented} or -@file{daikon-output}) in the instrumented programs, so that no matter -where the instrumented program is run, the output will go to a fixed -location. Even if these directories are given as relative paths (as is -the default), @option{--absolute} specifies that they should always be -taken as relative to the directory that was the working directory when -@command{dfepl} was run. - -@option{--no-absolute} specifies the opposite, causing the output paths -to be interpreted relative to the current working directory each time -the instrumented program is invoked. The default, when neither option -is specified, is for @file{.types} files to use an absolute path, but -all others to use relative path, so that the @file{.types} files will -always be in the same place as the instrumented source files that -generated them, but the @file{daikon-output} directory will be created -in the current directory when the program runs. - -@item --accessor-depth=@var{num} -Controls the number of nested invocations of object accessor methods -to examine. For instance, suppose that the @code{Person} class has a -method @code{mother()} that returns another person (and has been -specified to @command{dfepl} as an accessor), and that @code{$me} is -an instrumented variable. If the accessor depth is 1, only -@code{$me->mother()} will be examined. If the depth is 2, -@code{$me->mother()->mother()} will also be examined. Specifying large -accessor depths is generally not advisable, especially with many -accessor methods, as the number of variables examined can be too many -for Daikon to process efficiently. - -By default, the Daikon Perl trace runtime will examine at most a -single level of accessors. - -@item -A -@itemx --accessors-dir=@var{directory} -Look for files containing accessor lists in @var{directory}, or the -current directory if @var{directory} is omitted. For a class -@code{Acme::Foo}, accessors are methods that return information about -an object but do not modify it. @command{dfepl} cannot determine on -its own which methods are accessors, but when a list of them is -provided, it can call an object's accessors when examining a variable -of that class to obtain more information about the object. To tell -@command{dfepl} about the accessors for @code{Acme::Foo}, make a file -listing the names of each accessor method, one per line with no other -punctuation, named @file{Acme/Foo.accessors} in the same directory as -@file{Acme/Foo.pm}. - -@item --decls-dir=@var{directory} -Put generated declaration files in @var{directory} and its -subdirectories. The default is @file{daikon-output}. - -@item --decls-style=@var{style} -@var{style} should be one of @option{combined}, @option{flat}, or -@option{tree}. A style of @option{combined} specifies that the -declarations for all packages should be merged, in a file named -@file{prog-combined.decls} where @file{prog} is the name of the -program. A style of @option{flat} specifies that the declarations for -each package should be in a separate file named after the package, but -that these files should go in a single directory; for instance, the -declarations for @code{Acme::Trampoline} and -@code{Acme::Skates::Rocket} would go in files named -@file{Acme::Trampoline.decls} and @file{Acme::Skates::Rocket.decls}. A -style of @option{tree} specifies that each package should have its own -declarations file, and that those files should be arranged in -directories whose structure matches the structure of their package -names; in the example above, the files would be -@file{Acme/Trampoline.decls} and @file{Acme/Skates/Rocket.decls}. - -The default is @option{tree}. Note that @option{--decls-style} and -@option{--types-style} are currently constrained to be the same; if -one is specified, the other will use the same value. - -@item --dtrace-append -@itemx --no-dtrace-append -When @option{--dtrace-append} is specified, the instrumented program -will append trace information to the appropriate -@file{.dtrace} file each time it runs. When -@option{--no-dtrace-append} is specified, it will overwrite the file -instead. - -The default behavior is to overwrite. This choice can also be -overridden, when the program is run, to always append by setting the -environment variable @env{DTRACEAPPEND} to 1. - -When appending to a @file{.dtrace} file, no declaration information is -ever produced, because it would be redundant to do so and Daikon does -not permit re-declarations of program points. - -@item --dtrace-dir=@var{directory} -Put generated trace files in @var{directory} and its -subdirectories. The default is @file{daikon-output}. - -@item --dtrace-style=@var{style} -@var{style} should be one of @option{combined}, @option{flat}, or -@option{tree}. A style of @option{combined} specifies that the traces -for all packages should be merged, in a file named -@file{prog-combined.dtrace}, where @file{prog} is the name of the -program. A style of @option{flat} specifies that the -traces for each package should be in a separate file named after the -package, but that these files should go in a single directory; for -instance, the declarations for @code{Acme::Trampoline} and -@code{Acme::Skates::Rocket} would go in files named -@file{Acme::Trampoline.dtrace} and -@file{Acme::Skates::Rocket.dtrace}. A style of @option{tree} specifies -that each package should have its own trace file, and that -those files should be arranged in directories whose structure matches -the structure of their package names; in the example above, the files -would be @file{Acme/Trampoline.dtrace} and -@file{Acme/Skates/Rocket.dtrace}. - -The default is @option{combined}. - -@item --help -Print a short options summary. - -@item --instr-dir=@var{directory} -@c dfec option -- do not rename (yet) -@itemx --instrsourcedir=@var{directory} -Put instrumented source files in @var{directory} and its -subdirectories. The default is @file{daikon-untyped}, or -@file{daikon-instrumented} if type information is available. - -@item --list-depth=@var{DEPTH} -Consider as many as @var{DEPTH} of the first elements of a list to be -distinct entities, for the purpose of guessing their types. When -subroutines return a list of values, each value may have a distinct -meaning, or the list may be homogeneous. When trying to assign types -to the elements of a list, the Daikon Perl trace runtime will try -making separate guesses about the types of the elements of a short -list, but it would be inefficient to retain this distinction for -many elements. This parameter controls how many elements of a list -will be examined individually; all the others will be treated -uniformly. - -The default is 3. - -@item --output-dir=@var{directory} -Put all of the files that are the output of the tracing process (and -therefore input to the Daikon invariant detection engine) in -@var{directory} and its subdirectories. This option is a shorthand -equivalent to setting both @option{--decls-dir} and -@option{--dtrace-dir} to the same value. - -The default behavior is as if @option{--output-dir=daikon-output} had -been specified. - -@item --perl=@var{path} -Use @var{path} as the location of Perl when calling the annotation -back end (a module named @code{B::DeparseDaikon}), rather than the -version of Perl under which @command{dfepl} itself is running, which -is probably the first @command{perl} that occurs on your path. For -instance, if the first version of @command{perl} on your path isn't -version 5.8 or later, you should use this option to specify another -@command{perl} program that is. - -@item --nesting-depth=@var{num} -When examining nested data structures, traverse as many as @var{num} -nested references. For instance, suppose that @code{@@a} is the array - -@example -@@a = (@{1 => [2, 3]@}, @{5 => [4, 2]@}) -@end example - -If the depth is 0, then when examining @code{@@a}, Daikon's Perl trace -runtime will consider it to be an array whose elements are references, -but it won't examine what those references point to. If the depth is -1, it will consider it to be an array of references to hashes whose -keys are integers and whose values are references, but it won't -examine what @emph{those} references point to. Finally, if the depth -is 2 or more, it will consider @code{@@a} to be an array of references -to hashes whose keys are integers and whose values are references to -arrays of integers. - -The default nesting depth is 3. - -When referenced objects have accessor methods, or when accessors -return references, the @option{--accessor-depth} and -@option{--nesting-depth} options interact. Specifically, if these -depths are A and R, the behavior is as if the runtime has a budget of -1 unit, which it can use either on accessors which cost 1/A or -references which cost 1/R@. It may thus sometimes be useful to specify -fractional values for @option{--accessor-depth} and -@option{--nesting-depth}; in fact, the default accessor depth is -1.5. - -@item --types-append -@itemx --no-types-append -When @option{--types-append} is specified, the instrumented program -will append type information to the appropriate -@file{.types} file each time it runs. When -@option{--no-types-append} is specified, it will overwrite the file -instead. - -The default behavior is to append. If @option{--no-types-append} is -specified, however, this choice can also be overridden, when the -program is run, to append by setting the environment variable -@env{TYPESAPPEND} to 1. There is no way to use environment variables -to force the runtime to overwrite a types file, but an equivalent -effect can be obtained by simply removing the previous types file -before each run. - -@item -T -@itemx --types-dir=@var{directory} -Look for @file{.types} files in @var{directory}, or -@file{daikon-instrumented} if @var{directory} is omitted. When -instrumenting a module @code{Acme::Trampoline}, used in a program -@file{coyote.pl}, @command{dfepl} will look for -files named @file{coyote-combined.types}, @file{Acme::Trampoline.types}, and -@file{Acme/Trampoline.types}, corresponding to the possible choices of -@option{--types-style}. Once discovered, the files are used in the -same way as for @option{-t}. - -@item --types-file=@var{file} -@itemx -t @var{file} -Include type information from @var{file} when instrumenting programs -or modules. Since Daikon needs to know the types of variables when -they are declared, useful @file{.decls} and @file{.dtrace} files can -only be produced by source code instrumented with type -information. Since Perl programs don't include this information to -begin with, and it would be cumbersome to produce by hand, type -information must usually be produced by running a version of the -program that has itself been annotated, but without type -information. The Daikon Perl trace runtime will automatically decide -whether to output types, or declarations and traces, depending on -whether the source was instrumented without or with types. This option -may occur multiple times, to read information from multiple types -files (irrelevant type information will be ignored). - -@item --types-basedir=@var{directory} -Put files containing type information in @var{directory} and its -subdirectories. By default, this is whatever @option{--instr-dir} -is, usually @file{daikon-instrumented}. - -@item --types-style=@var{style} -@var{style} should be one of @option{combined}, @option{flat}, or -@option{tree}. A style of @option{combined} specifies that the types -for all packages should be merged, in a file named -@file{prog-combined.types}, where @file{prog} is the name of the -program. A style of @option{flat} specifies that the -types for each package should be in a separate file named after the -package, but that these files should go in a single directory; for -instance, the declarations for @code{Acme::Trampoline} and -@code{Acme::Skates::Rocket} would go in files named -@file{Acme::Trampoline.types} and -@file{Acme::Skates::Rocket.types}. A style of @option{tree} specifies -that each package should have its own trace file, and that -those files should be arranged in directories whose structure matches -the structure of their package names; in the example above, the files -would be @file{Acme/Trampoline.types} and -@file{Acme/Skates/Rocket.types}. - -The default is @option{tree}. Note that @option{--types-style} and -@option{--decls-style} are currently constrained to be the same; if -one is specified, the other will use the same value. - -@item --verbose -@itemx -v -Print additional information about what @command{dfepl} is doing, -including external commands invoked. -@end table - - @node convertcsv.pl @section Comma-separated-value front end convertcsv.pl @@ -8931,21 +8359,11 @@ neither required nor entitled to use the Daikon invariant detector itself under these other licenses. @menu -* dfepl license:: * Kvasir license:: * Celeriac license:: @end menu -@node dfepl license -@subsubsection dfepl license - -The Daikon Perl front end @command{dfepl} may be used and distributed under the -regular Daikon license or, at your option, either the GNU General -Public License or the Perl Artistic License (that is, under the same -terms as Perl itself). - - @node Kvasir license @subsubsection Kvasir license @@ -9189,7 +8607,7 @@ of, or in addition to, referencing this manual and the Daikon website @c LocalWords: inlined makeinfo daikonemail setchapternewpage ifinfo hboxes @c LocalWords: finalout titlepage titlefont eps pdf txt png jpg vskip pt img @c LocalWords: filll src alt ifnothtml dir ifnottex GUIs ifhtml detailmenu pl -@c LocalWords: StackAr Diff orig dfepl CSV convertcsv dfej dfec gcc cd +@c LocalWords: StackAr Diff orig CSV convertcsv dfej dfec gcc cd @c LocalWords: applets EDG spinfo InvariantChecker LogicalCompare dtrace inv @c LocalWords: runtimechecker javac Lackwit pptname uref abs lst treenode IOA @c LocalWords: myArray PathFinder pxref cindex samp classpath xref var wget diff --git a/doc/developer.texinfo b/doc/developer.texinfo index 0951936384..ccc23e50f2 100644 --- a/doc/developer.texinfo +++ b/doc/developer.texinfo @@ -2080,7 +2080,7 @@ the Texinfo source files in the @file{doc/} directory against the previous versions: @smallexample -if [ -z "$@{DAIKONDIR@}" ] && [ -d "front-end/" ] ; then DAIKONDIR=$(pwd) ; fi +if [ -z "$@{DAIKONDIR@}" ] && [ -d "java/daikon/" ] ; then DAIKONDIR=$(pwd) ; fi cd $@{DAIKONDIR@}/doc && \ diff -b -u -s --from-file /cse/web/research/plse/daikon/download/doc *.texinfo @end smallexample @@ -2090,7 +2090,7 @@ Another good source of information is the repository logs since the last release: @smallexample -if [ -z "$@{DAIKONDIR@}" ] && [ -d "front-end/" ] ; then DAIKONDIR=$(pwd) ; fi +if [ -z "$@{DAIKONDIR@}" ] && [ -d "java/daikon/" ] ; then DAIKONDIR=$(pwd) ; fi DAIKONVERSION=`wget -q http://plse.cs.washington.edu/daikon/download/doc/VERSION \ -O - | xargs echo -n` && \ cd $@{DAIKONDIR@} && \ @@ -2109,7 +2109,7 @@ git log v$@{DAIKONVERSION@}..HEAD In addition, you should run the same command in your Fjalar clone: @smallexample -if [ -z "$@{DAIKONDIR@}" ] && [ -d "front-end/" ] ; then DAIKONDIR=$(pwd) ; fi +if [ -z "$@{DAIKONDIR@}" ] && [ -d "java/daikon/" ] ; then DAIKONDIR=$(pwd) ; fi DAIKONVERSION=`wget -q http://plse.cs.washington.edu/daikon/download/doc/VERSION \ -O - | xargs echo -n` && \ @{ cd $@{DAIKONDIR@}/fjalar || cd $@{DAIKONDIR@}/../fjalar ; @} && \ @@ -2790,8 +2790,7 @@ Instead of placing both declarations and data trace records in a single file, it is permitted to place the declarations in one or more @file{.decls} @dfn{declaration files} while leaving the data trace records in the @file{.dtrace} file. This can be convenient for tools that -perform a separate instrumentation step, such as -@command{dfepl} (@pxref{dfepl,,,daikon,Daikon User Manual}). +perform a separate instrumentation step. Such a tool takes as input a target program to be analyzed, and produces two outputs: a @file{.decls} file and an instrumented program. Executing the @@ -3532,8 +3531,8 @@ variables (@pxref{Variable names,,,daikon,Daikon User Manual}). Here are portions of two files @file{StackArTester.decls} and @file{StackArTester.dtrace}, for a Java class that implements a stack of integers using an array as the underlying data structure. You can see -many more examples by simply running an existing front end on some Java, -C, or Perl programs and viewing the resulting files. +many more examples by simply running an existing front end on some Java +or C programs and viewing the resulting files. @menu * Example declaration file:: @@ -4297,7 +4296,7 @@ detection,,,daikon,Daikon User Manual}. @c LocalWords: INST Xiaoxia regex printf DiscardInfo instanceof UpperBound Xu @c LocalWords: DiscardCode Kapfhammer GB ListImplementors pptname rt toc @c LocalWords: VarComparability instrumenter struct Hatcliff Sinha Vibha xref -@c LocalWords: dfepl uninstrumented classname subexit inline dec hashcode len +@c LocalWords: uninstrumented classname subexit inline dec hashcode len @c LocalWords: hashCode param dups NonZero rel varname topOfStack MyInteger @c LocalWords: hasDuplicates hasOrder hasNull nullTerminated isParam DynComp @c LocalWords: Hashcodes birthyear topLevel bottomLevel pptInfo toplevel conf diff --git a/doc/images/dfepl-flow.dot b/doc/images/dfepl-flow.dot deleted file mode 100644 index 9d7902b72f..0000000000 --- a/doc/images/dfepl-flow.dot +++ /dev/null @@ -1,32 +0,0 @@ -digraph dfepl { -size="7,6" -"dfepl" [shape=box] -"dfepl -T" [shape=box] -"dtype-perl" [shape=box] -"dtrace-perl" [shape=box] -"Daikon" [shape=box] -node [shape=plaintext] -source [label="source code to instrument\nModule.pm, program.pl"] -tests [label="other source code, test cases\nprogram.pl, test.t"] -untyped [label="untyped instrumented code\ndaikon-untyped/Module.pm"] -typed [label="type-aware instrumented code\ndaikon-instrumented/Module.pm"] -decls [label="program point declarations\ndaikon-output/package.decls"] -types [label="type information\ndaikon-instrumented/package.types"] -dtrace [label="trace files\ndaikon-output/program-combined.dtrace"] - -source -> "dfepl" -tests -> "dtype-perl" -tests -> "dtrace-perl" -"dfepl" -> untyped -untyped -> "dtype-perl" -"dtype-perl" -> types -types -> "dfepl -T" -source -> "dfepl -T" -"dfepl -T" -> typed -typed -> "dtrace-perl" -"dfepl -T" -> decls -"dtrace-perl" -> dtrace -decls -> "Daikon" -dtrace -> "Daikon" -"Daikon" -> "invariants" -} diff --git a/doc/images/dfepl-flow.txt b/doc/images/dfepl-flow.txt deleted file mode 100644 index f34b608a94..0000000000 --- a/doc/images/dfepl-flow.txt +++ /dev/null @@ -1,55 +0,0 @@ - - source code to instrument - Module.pm, program.pl - | -- - | \-- - | \V - \ +-------+ - | | dfepl | - | +----\--+ - | \ - \ \ - | untyped instrumented code - | daikon-untyped/Module.pm other source code, test cases - | \ program.pl, test.t - \ \ -------------| - | V V-----------/ | - | +------------+ | - | | dtype-perl | | - | +-----+------+ | - \ | | - | V | - | type information | - | daikon-instrumented/package.types | - \ ---- | - | -------/ | - V V--/ | - +----------+ | - | dfepl -T | | - +----+-----+- | - | \--------- | - | \----V | - | type-aware instrumented code / - | daikon-instrumented/Module.pm / - \ | -/ - | | / - | V / - V +-----+-------+ -/ - program point declarations | dtrace-perl |< - daikon-output/package.decls +-----+-------+ - \ | - \ | - \ | - \ V - \- trace files - \ daikon-output/program-combined.dtrace - \ --- - \ ---/ - \ ---/ - V V-/ - +--------+ - | Daikon | - +----+---+ - | - | - invariants diff --git a/examples/perl-examples/Birthday.accessors b/examples/perl-examples/Birthday.accessors deleted file mode 100644 index 649d4edbe2..0000000000 --- a/examples/perl-examples/Birthday.accessors +++ /dev/null @@ -1,2 +0,0 @@ -month_number -leap_year diff --git a/examples/perl-examples/Birthday.pm b/examples/perl-examples/Birthday.pm deleted file mode 100644 index d2bd159bff..0000000000 --- a/examples/perl-examples/Birthday.pm +++ /dev/null @@ -1,95 +0,0 @@ -package Birthday; - -use strict; - -my @months = qw(January February March April May June July August - September October November December); - -my %month_numbers; -@month_numbers{@months} = 1 .. 12; - -sub new { - my $class = shift; - my($month, $day, $year) = @_; - die unless exists $month_numbers{$month}; - return bless {month => $month, day => $day, year => $year}, $class; -} - -sub month { - my $self = shift; - return $self->{'month'}; -} - -sub month_number { - my $self = shift; - return $month_numbers{$self->month}; -} - -sub day { - my $self = shift; - return $self->{'day'}; -} - -sub year { - my $self = shift; - return $self->{'year'}; -} - -sub is_today { - my($self) = shift; - my(undef,undef,undef,$day,$mon) = localtime(time); - return $self->month eq $months[$mon] && $self->day eq $day; -} - -my @dividers = (19, 18, 20, 20, 20, 21, 22, 23, 22, 22, 21, 21); -my @signs = qw(Capricorn Aquarius Pisces Aries Taurus Gemini Cancer - Leo Virgo Libra Scorpio Sagittarius); -sub zodiac { - my $self = shift; - my $m = $month_numbers{$self->month} - 1; - if ($self->day <= $dividers[$m]) { - return $signs[$m]; - } else { - return $signs[($m + 1) % 12]; - } -} - -sub leap_year { - my $self = shift; - my $y = $self->year; - return ($y % 4 == 0 and $y % 100 != 0) || $y % 400 == 0; -} - -my @last_day = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); -sub days_in_month { - my $self = shift; - if ($self->leap_year and $self->month eq "February") { - return 29; - } else { - return $last_day[$self->month_number - 1]; - } -} - -sub next_day { - my $self = shift; - $self->{'day'}++; - if ($self->day > $self->days_in_month) { - $self->{'day'} = 1; - $self->next_month; - } -} - -sub next_month { - my $self = shift; - if ($self->month ne "December") { - $self->{'month'} = $months[$self->month_number + 1 - 1]; - } else { - $self->{'month'} = "January"; - $self->next_year; - } -} - -sub next_year { - my $self = shift; - $self->{'year'}++; -} diff --git a/examples/perl-examples/Makefile b/examples/perl-examples/Makefile deleted file mode 100644 index f458fcce19..0000000000 --- a/examples/perl-examples/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -JAVA = java -Xmx7g -XX:MaxPermSize=768M -# DAIKON = ${JAVA} daikon.Daikon --no_text_output --show_progress --suppress_redundant -DAIKON = ${JAVA} daikon.Daikon --no_text_output --show_progress - -.PHONY: all -all: birthday.inv standalone.inv - -.PHONY: clean -clean: - rm -rf birthday.inv standalone.inv daikon-untyped daikon-instrumented daikon-output - -.PHONY: test -test: all - -birthday.inv: daikon-output/Birthday.decls daikon-output/test_bday-combined.dtrace - ${DAIKON} $^ -o $@ - -standalone.inv: daikon-output/standalone-main.decls daikon-output/standalone-combined.dtrace - ${DAIKON} $^ -o $@ - - -daikon-output/test_bday-combined.dtrace: daikon-instrumented/Birthday.pm test_bday.pl - dtrace-perl test_bday.pl 30 - -daikon-output/standalone-combined.dtrace: daikon-instrumented/standalone.pl - dtrace-perl $< - -daikon-output/Birthday.decls daikon-instrumented/Birthday.pm: daikon-instrumented/Birthday.types Birthday.pm - dfepl -T Birthday.pm - -daikon-output/standalone-main.decls daikon-instrumented/standalone.pl: daikon-instrumented/standalone-main.types standalone.pl - dfepl -T standalone.pl - -daikon-instrumented/Birthday.types: daikon-untyped/Birthday.pm test_bday.pl - dtype-perl test_bday.pl 5 - -daikon-instrumented/standalone-main.types: daikon-untyped/standalone.pl - dtype-perl $< - -daikon-untyped/Birthday.pm: Birthday.pm Birthday.accessors - dfepl -A Birthday.pm - -daikon-untyped/standalone.pl: standalone.pl - dfepl $< diff --git a/examples/perl-examples/standalone.pl b/examples/perl-examples/standalone.pl deleted file mode 100755 index 25dd4d5bc1..0000000000 --- a/examples/perl-examples/standalone.pl +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env perl - -sub my_abs { - my($x) = @_; - - if ($x == 0) { - return 0; - } elsif ($x < 0) { - return -$x; - } else { - return $x; - } -} - -sub my_uc { - my($s) = @_; - - if (!length $s) { - return $s; - } - - $s = ucfirst(substr($s, 0, 1)) . my_uc(substr($s, 1)); - - return $s; -} - -sub my_sort { - my(@x) = @_; - my $divider = 0; - my(@left, @right); - if (@x < 2) { - return @x; - } - $divider = shift @x; - @left = grep($_ <= $divider, @x); - @right = grep($_ > $divider, @x); - @left = my_sort(@left); - @right = my_sort(@right); - return (@left, $divider, @right); -} - -sub num2bits { - my($n) = @_; - my @a = reverse split //, unpack("b*", pack("L", $n)); - shift @a while @a > 1 and $a[0] == 0; - return [@a]; -} - -sub bits2num { - my(@a) = @_; - my $lsb = pop @a; - if (@a == 0) { - return $lsb; - } else { - return $lsb + 2*bits2num(@a); - } -} - -sub odd { - my($x) = @_; - ($x & 1) == 1; -} - -sub param_f { - my($x, @a) = @_; - $x++; - push @a, 5; - return $x + @a + 2; -} - -sub param_g { - my($ar) = @_; - push @$ar, 5; - return 1 + @$ar; -} - -sub true { 1 == 1 } -sub false { 1 == 0 } - -sub aref_compare { - my($a, $b) = @_; - if ($a == $b) { - return true; - } elsif (@$a != @$b) { - return false; - } else { - for my $i (0 .. $#$a) { - return false if $a->[$i] != $b->[$i]; - } - return true; - } -} - -sub maybe_maybe_int { - my($x) = @_; - my $which = $x % 4; - if ($which == 0) { - return ""; - } elsif ($which == 1) { - return \ ""; - } elsif ($which == 2) { - return \$x; - } else { - return \ ($x + 1); - } -} - -for my $v (5,1,12,-3,2,13,-5,-1,2,55,0,1,-4,-1,42,32768,-32768,3,5) { - print "|$v| = ", my_abs($v), "\n"; -} - -for my $s ("cat", "Cat", "CAT", "pAg", "pAG", "42", "home-run", "!@` ", - " ", "!a", "\0", "the quick Brown FOX jumps over the lazy dog") { - print "uc($s) = ", my_uc($s), "\n"; -} - -for my $a ([], [1], [1,2], [2,1], [1,2,3], [3,2,1], [3,1,2], [2,1,3], [2,3,1], - [1,3,2], [3,1,4,1,5,9,2,6,2], [1,1,2,2], [2,2,1,1], [0,0,0,0], - [2,7,1,8,2,8,1,8,2,8,4,5,9], [-1,-2,-3], [-42,42]) { - print "sort(", join(",", @$a), ") = (", join(",", my_sort(@$a)), ")\n"; -} - -for my $i (qw(0 1 2 3 4 5 7 13 24 84 323 523 65535 65536 1234234)) { - my @a = @{num2bits($i)}; - print "$i: ", join(",", @a), ", ", 0+bits2num(@a), "\n"; -} - -sub first_defined { - my(@a) = @_; - foreach my $e (@a) { - return $e if defined($e); - } -} - -print first_defined(0, 5, undef); -print first_defined(5, 0, undef); -print first_defined(undef, 0, 5); -print first_defined(undef, undef, 2, 3, 4); - -print join(",", map(odd($_), -2,-1,0,1,2,3,4,5,6,7,8,9)), "\n"; - -param_f(1, 4, 2); -param_f(-2, 2, 34, 4, 4, 4); -param_f(5, 5); -param_f(6); -param_f(23, 23); -param_f(-1, -2, -4); - -@a = (3); -param_g(\@a); -param_g(\@a); -param_g(\@a); -param_g([2, 3]); -param_g([-1]); -param_g([]); - -@refs = ([], [-1], [0], [4], [5], [5], [4], [1, 2], [0, 0], [0, 1], [1, 0], - [2, 0], [-4, 2], [0, 1], [1, 2, 4], [3, 2, 1], [0, 0, 0], [-1, 0, 1], - [1, 3, 4], [1, 2, 4], [1, 5, 10, 15], [-5, -19, -5, 0], - [3, 1, 4, 1, 6], [2, 1, 7, 1, 8, 2, 8]); - -for my $i (0 .. $#refs) { - for my $j (0 .. $#refs) { - aref_compare($refs[$i], $refs[$j]); - } -} - -for my $i (0 .. 20) { - scalar maybe_maybe_int($i); -} diff --git a/examples/perl-examples/test_bday.pl b/examples/perl-examples/test_bday.pl deleted file mode 100644 index 1dae151430..0000000000 --- a/examples/perl-examples/test_bday.pl +++ /dev/null @@ -1,35 +0,0 @@ -use Birthday; - -my $size = $ARGV[0] || 10; - -my $bday = new Birthday("February", 23, 1975); - -for (1 .. $size * 100) { - my $sign = $bday->zodiac; - if ($bday->is_today) { - print "Happy birthday!\n"; - } -# print "$sign ", %{$bday}, "\n"; - $bday->next_day; -} - -for my $year (1994 .. 1994 + 10 * $size) { - my $day = new Birthday("January", 1, $year); - print "$year is " . ($day->leap_year ? "" : "not ") . "a leap year.\n"; -} - -# my $h; - -# $h = new Birthday("February", 18, 1975); -# print $h->month . " " . $h->day . " " . $h->zodiac . "\n"; -# $h = new Birthday("February", 19, 1975); -# print $h->month . " " . $h->day . " " . $h->zodiac . "\n"; - -# $h = new Birthday("June", 20, 1975); -# print $h->month . " " . $h->day . " " . $h->zodiac . "\n"; -# $h = new Birthday("June", 21, 1975); -# print $h->month . " " . $h->day . " " . $h->zodiac . "\n"; -# $h = new Birthday("June", 22, 1975); -# print $h->month . " " . $h->day . " " . $h->zodiac . "\n"; -# $h = new Birthday("June", 23, 1975); -# print $h->month . " " . $h->day . " " . $h->zodiac . "\n"; diff --git a/front-end/perl/README b/front-end/perl/README deleted file mode 100644 index 09c00e1f2d..0000000000 --- a/front-end/perl/README +++ /dev/null @@ -1,90 +0,0 @@ -(For user documentation of dfepl, the simplified interface to the Daikon -Perl front-end, see the Daikon manual.) - -Usage of the Perl front-end is much like dfej and dfec, except that: - -* A separate pass is needed to collect information about the types - of variables, since Perl isn't statically typed. - -* The .decls file is generated at the same time as the .dtrace output, - rather than when the source is annotated. - -The steps look like: - -1. Generate an instrumented version of the program. -2. Run the instrumented version of the program to guess variable types. -3. Generate a new instrumented version of the program with the types -included. -4. Run the new instrumented version to generate .decls and .dtrace -files. -5. Run daikon on the .decls and .dtrace files. - ----------------------------------------------------------------------- -NOTE: -The rest of this file is a description of B::DeparseDaikon, the Perl -compiler back-end that instruments Perl programs for Daikon. While the -description is still accurate, there's now an easier-to-use front-end -script named "dfepl" that most people will want to use instead. dfepl -is documented in the regular Daikon manual. ----------------------------------------------------------------------- - -* Setting up your environment - -The front-end works with version 5.8.0 of Perl. (Instrumented programs -can also run with 5.6.* versions, but the instrumenter itself won't.) -"perl -v" will tell you which version of perl you're using. (On the -PAG machines, version 5.8.0 is in /usr/local/bin/perl). - -The modules used are in $INV/front-end/perl/lib, so you'll either want -to add "-I$INV/front-end/perl/lib" to the perl command line or add the -directory to the PERLLIB environment variable. (dfepl and dtrace-perl -do this for you). - -* Instrumenter - -To instrument a file "foo.pl" when you don't have any type -information, say - - perl -MO=DeparseDaikon foo.pl >foo_instrumented.pl - -Note that without the redirection, the instrumented version would go -to the standard output. To include type information from a file named -"foo.types", instead say: - - perl -MO=DeparseDaikon,-t,foo.types foo.pl >foo_instrumented.pl - -It's important that there be no space around the commas -- the entire -string "-MO=DeparseDaikon,-t,foo.types" has to be one argument from -the shell's point of view. - -* Running instrumented programs - -Instrumented programs, or programs that use instrumented libraries, -can be run in the usual way. They will generate three output files for -each instrumented package "foo" (recall that a program without a -package declaration will be in the package "main"): - -- "foo.types" - This file includes a guess at the type of any variables whose type - was unknown when the package was instrumented, but for which - examples were seen in this execution. Information is always appended - to this file, and if a type occurs more than once, the instrumenter - will use the least-upper-bound the occurrences, so using the output - of multiple runs is safe. - -- "daikon-output/foo.decls" - Declarations for all the instrumented program points that were - encountered. If the DTRACEAPPEND environment variable is set, these - will be appended to the file rather than overwriting it. The - "daikon-output" directory will be created if it does not exist. - -- "daikon-output/foo.dtrace" - Traces the values of variables every time one of the instrumented - program points is reached. If the DTRACEAPPEND environment variable - is set, these will be appended to the file rather than overwriting - it. If the DTRACEFILE environment variable is set, that filename - will be used instead. - -Note that these paths are all relative to the current directory at the -point where they are created; watch out for scripts and makefiles that -call chdir(). diff --git a/front-end/perl/lib/B/DeparseDaikon.pm b/front-end/perl/lib/B/DeparseDaikon.pm deleted file mode 100644 index 8efca5e153..0000000000 --- a/front-end/perl/lib/B/DeparseDaikon.pm +++ /dev/null @@ -1,817 +0,0 @@ -package B::DeparseDaikon; - -# After Perl has parsed and byte-compiled some code, translate the OPs -# back into Perl source, adding tracing functions for Daikon along the -# way. - -# This file is part of the Daikon distribution. It may be used and -# redistributed under the same terms as the rest of Daikon or, at your -# option, under the same terms as Perl itself, following either the -# GNU General Public License or the Perl Artistic License. The Daikon -# Perl front end, of which this file is a part, and the Daikon dynamic -# invariant detection tool are separate programs, neither derived from -# the other, which are merely aggregated for convenience of -# distribution. As such, licensing the Perl front end under the terms -# of the GPL neither requires nor entitles you to license other parts -# of the Daikon distribution under the same terms. - -use B::Deparse; - -# OPpCONST_ARYBASE and CVf_LOCKED removed from Perl > 5.10 (markro) -# use B qw(main_root main_start main_cv SVf_POK CVf_METHOD CVf_LOCKED -# CVf_LVALUE OPpCONST_ARYBASE class); - -use B qw(main_root main_start main_cv SVf_POK CVf_METHOD CVf_LVALUE class); - -use Daikon::PerlType qw(parse_type unparse_type type_lub - unparse_to_code read_types_into); - -use Carp 'croak', 'cluck', 'carp', 'confess'; - -@ISA = ("B::Deparse"); - -# We use this enough that I just copied it from Deparse.pm rather than say -# B::Deparse::null everywhere. -sub null { - my $op = shift; - return class($op) eq "NULL"; -} - -use strict; - -# This could be any name that isn't used in the code being annotated. -# Something with Daikon in the name is a pretty good bet not to be -# used, except if we're trying to annotate some already annotated -# code, which won't work right anyway. -my $dv_name = '@Daikon_variables'; - -# indexed by {program point name}{variable name} -my %types; - -# Read a .types file from the given filename, and incorporate that -# information into %types for use in annotating. -sub read_types { - my($fname) = @_; - open(TYPES, "<$fname") or die "Can't open $fname: $!"; - read_types_into(\*TYPES, \%types); # in Daikon::PerlType - close TYPES; -} - -# Read and return the method names in a given file, skipping blank -# lines and comments. -sub read_accessors { - my($fname) = @_; - open(ACC, "<$fname") or die "Can't open $fname: $!"; - my $l; - my @acc = (); - while ($l = ) { - $l =~ s/#.*$//; - next if $l =~ /^\s*$/; - chomp $l; - if ($l !~ /^\w+$/) { - warn "`$l' is a strange name for a method\n"; - } - push @acc, $l; - } - close ACC; - return @acc; -} - -# This is the entry point called first when you run this module with -# -MO=DeparseDaikon. Mainly, it returns a subroutine to call after the -# program has been compiled (into an internal form by the perl -# interpreter) -sub compile { - my(@args) = @_; - my @output_style = (); - my @accessors = (); - my @depths = (); - my $saw_types = 0; - return sub { - # Parse and remove any Daikon-specific args from @args - for my $i (0 .. $#args) { - next unless defined($args[$i]); - if ($args[$i] eq "-a") { - # -a: read a list of accessor methods from a given file - my $fname = $args[$i+1]; - push @accessors, read_accessors($fname); - $args[$i] = $args[$i+1] = undef; - } elsif ($args[$i] eq "-t") { - # -t: read type information from a given file - my $fname = $args[$i+1]; - read_types($fname); - $args[$i] = $args[$i+1] = undef; - $saw_types = 1; - } elsif ($args[$i] eq "-o") { - # -o: write output to a given file, rather than STDOUT - my $fname = $args[$i+1]; - open(STDOUT, ">", "$fname") or die "Can't open $fname: $!\n"; - $args[$i] = $args[$i+1] = undef; - } elsif ($args[$i] eq "-O") { - # -O: control how generated code outputs results - @output_style = @args[$i + 1 .. $i + 6]; - @args[$i .. $i + 6] = (undef) x (1 + 6); - } elsif ($args[$i] eq "-D") { - # -D: set data structure tracing depths - @depths = @args[$i + 1 .. $i + 3]; - @args[$i .. $i + 3] = (undef) x (1 + 3); - } - } - @args = grep(defined($_), @args); - my $self = B::DeparseDaikon->new(@args); - # As the first thing in the output, put in a hook to our - # tracing routines. - print "use daikon_runtime;\n"; - if (@output_style) { - print "BEGIN { daikon_runtime::set_output_style("; - print join(", ", map(qq/"$_"/, @output_style)); - print "); }\n"; - } - if (@depths) { - print "BEGIN { daikon_runtime::set_depths("; - print join(", ", @depths); - print "); }\n"; - } - print "BEGIN { daikon_runtime::set_tracing(", - ($saw_types ? 1 : 0), "); }\n"; - $self->deparse_program(); - if (@accessors) { - print "sub DAIKON_ACCESSORS { ("; - print join(", ", map(qq/"$_"/, @accessors)); - print "); }\n"; - } - } -} - -# Used to be unchanged, but now slightly modified to turn off "void -# context" warnings. -sub deparse_program { - my $self = shift; - # First deparse command-line args - if (defined $^I) { # deparse -i - print q(BEGIN { $^I = ).perlstring($^I).qq(; }\n); - } - ### - if ($^W) { - # deparse -w, or more likely for us the file said - # "#!/bin/perl -w" - - # The following code turns off the "useless use of foo in void - # context" warning, which we produce a lot for the scalar - # context return of lists. Unlike the simpler "no warnings - # 'void'", we turn all the other warnings back on, this time - # in the lexically scoped way, so that we don't lose other - # warnings, if the user requested them. Also, we still set $^W - # to duplicate the effect of the -w switch for other modules, - # if any, that don't use lexical warnings. - print 'BEGIN { require warnings; ', - 'warnings->import(); warnings->unimport("void"); ', - '$^W = 1; }', "\n"; - } else { - # Even if the author of this file didn't ask for warnings, to - # match the behavior of the uninstrumented program we still - # want to emit warnings if we're being used from another file - # that had -w set. The following code only works if $^W was - # set before this file was loaded, but that's the most common - # case, covering both a -w flag on the main program and a - # BEGIN { $^W = 1 } added by DeparseDaikon operating on - # another file from the above branch. - print 'BEGIN { if ($^W) { require warnings; ', - 'warnings->import(); warnings->unimport("void"); ', - '} }', "\n"; - } - ### - - if ($/ ne "\n" or defined $O::savebackslash) { # deparse -l and -0 - my $fs = perlstring($/) || 'undef'; - my $bs = perlstring($O::savebackslash) || 'undef'; - print qq(BEGIN { \$/ = $fs; \$\\ = $bs; }\n); - } - my @BEGINs = B::begin_av->isa("B::AV") ? B::begin_av->ARRAY : (); - my @CHECKs = B::check_av->isa("B::AV") ? B::check_av->ARRAY : (); - my @INITs = B::init_av->isa("B::AV") ? B::init_av->ARRAY : (); - my @ENDs = B::end_av->isa("B::AV") ? B::end_av->ARRAY : (); - for my $block (@BEGINs, @CHECKs, @INITs, @ENDs) { - $self->todo($block, 0); - } - $self->stash_subs(); - $self->{'curcv'} = main_cv; - $self->{'curcvlex'} = undef; - print $self->print_protos; - @{$self->{'subs_todo'}} = - sort {$a->[0] <=> $b->[0]} @{$self->{'subs_todo'}}; - print $self->indent($self->deparse(main_root, 0)), "\n" - unless null main_root; - my @text; - while (scalar(@{$self->{'subs_todo'}})) { - push @text, $self->next_todo; - } - print $self->indent(join("", @text)), "\n" if @text; - - # Print __DATA__ section, if necessary - no strict 'refs'; - my $laststash = defined $self->{'curcop'} - ? $self->{'curcop'}->stash->NAME : $self->{'curstash'}; - if (defined *{$laststash."::DATA"}{IO}) { - print "__DATA__\n"; - print readline(*{$laststash."::DATA"}); - } -} - -# Mainly unchanged -sub todo { - my $self = shift; - my($cv, $is_form) = @_; - ### - # Throw out subs from other files, but (changed from 5.8 version) - # keep constant subs that show up as being from "op.c" - return unless ($cv->FILE eq $0 || exists $self->{files}{$cv->FILE} - || $cv->FILE eq "op.c"); - ### - my $seq; - if (!null($cv->START) and B::Deparse::is_state($cv->START)) { - $seq = $cv->START->cop_seq; - } else { - $seq = 0; - } - push @{$self->{'subs_todo'}}, [$seq, $cv, $is_form]; - unless ($is_form || class($cv->STASH) eq 'SPECIAL') { - $self->{'subs_deparsed'}{$cv->STASH->NAME."::".$cv->GV->NAME} = 1; - } -} - -# This is the main driver routine that's called recursively on every -# OP in the tree, and returns a representation of that OP as a -# slightly marked-up string. We piggyback a test here to find OPs -# whose next OP is the leavesub at the end of a subroutine (perhaps -# with intervening leave(loop|try)s for enclosing blocks) to wrap -# trace_return()s around them. -sub deparse { - my $self = shift; - my($op, $cx, $flags) = @_; - - Carp::confess("Null op in deparse") if !defined($op) - || class($op) eq "NULL"; - my $meth = "pp_" . $op->name; - my $add_trace_return = 0; - if ($self->{'leavesub'}) { - # We're on the lookout for an OP whose next OP (roughly) is - # $self->{'leavesub'}. - my $o = $op; - $o = $o->next while !null($op->next) and - $o->next->name =~ /^leave(loop|try)?$/; - if (${$o->next} == ${$self->{'leavesub'}}) { - $self->{'returns_count'}++ if $meth eq "pp_return"; - if ($meth !~ /^pp_(return|leave(loop|try)?)$/) { - # This is the one we want. - $add_trace_return = 1; - } - } - } - if ($add_trace_return and $cx == 0) { - # Put the return value in a low-precedence (since we're going - # to parenthesize it) but non-statement context. For instance, - # we want to get "a and b" rather than "b if a". - $cx = 1; - } - local($self->{'leavesub'}) = "" if $add_trace_return; # avoid nesting - my @flags = (); - if (B::Deparse::is_scope($op)) { - @flags = ($flags); - } - my $val = $self->$meth($op, $cx); - if ($add_trace_return) { - $self->{'returns_count'}++; - # Wrap it. - return $self->make_trace_return($val, $op); - } else { - return $val; - } -} - -# Given the string representing a piece of code and the OP that -# generated that, return a string in which the code is appropriately -# wrapped in a call to trace_return(). -sub make_trace_return { - my $self = shift; - my($val, $op) = @_; - my $ppt = $self->{'ppt_base'}; - $ppt .= ":::EXIT"; - (my $ppt_s = $ppt) =~ s/\(\)/_s()/; - (my $ppt_l = $ppt) =~ s/\(\)/_l()/; - - my $scalar_type; - if (exists $types{$ppt_s}{"return"}) { - $scalar_type = unparse_type($types{$ppt_s}{"return"}); - } else { - $scalar_type = "unknown"; - } - my $list_type; - if (exists $types{$ppt_l}{"return"}) { - $list_type = unparse_type($types{$ppt_l}{"return"}); - } else { - $list_type = "unknown"; - } - # We use scalar() to ensure we're forcing scalar context before - # taking the reference. This is needed because the \ operator - # doesn't itself force scalar context. (In fact \(@a), note the - # parens, is the same as map(\$_, @a)!) - my $scalar = "\\scalar($val)"; - # For the list context version, just wrap the list in an anonymous - # array. - my $list = "[$val]"; - # Name this exit point by its op sequence number relative to the - # sequence number of the beginning of this sub. Note that the op - # sequence number is only 16 bits, so it might wrap around on - # large programs, causing weird looking but hopefully still - # correct output. This number is easier to find on successive runs - # than a line number, but it may change between different versions - # of perl looking at the same code. - - # UNDONE: - # $op->seq has been removed in Perl > 5.10. I have not been able - # to locate a suitable replacement as yet. (markro) -# my $seq = $op->seq - $self->{'op_seq_base'}; - my $seq = 0; - # Evaluate only the appropriate version, depending on the runtime - # context. - my $ret_val = "(wantarray() ? $list : $scalar)"; - return "daikon_runtime::trace_return" . - "($ret_val, '$scalar_type', '$list_type', $seq, $dv_name)"; -} - -# Mainly unchanged from B::Deparse -sub next_todo { - my $self = shift; - my $ent = shift @{$self->{'subs_todo'}}; - my $cv = $ent->[1]; - my $gv = $cv->GV; - my $name = $self->gv_name($gv); - #### - # save the name of the subroutine for later use - local($self->{'sub_name'}) = $name; - # But throw out our own subs (like our constants) - return "" if $name =~ /^(B|Daikon)::/; - #### - if ($ent->[2]) { - return "format $name =\n" - . $self->deparse_format($ent->[1]). "\n"; - } else { - $self->{'subs_declared'}{$name} = 1; - if ($name eq "BEGIN") { - my $use_dec = $self->begin_is_use($cv); - if (defined ($use_dec)) { - return () if 0 == length($use_dec); - return $use_dec; - } - } - my $l = ''; - if ($self->{'linenums'}) { - my $line = $gv->LINE; - my $file = $gv->FILE; - $l = "\n\f#line $line \"$file\"\n"; - } - return "${l}sub $name " . $self->deparse_sub($cv); - } -} - -# Deparse all but the name of a named or anonymous subroutine. The -# effort to catch the last statement in the subroutine is mainly -# dispatched from here. -sub deparse_sub { - my $self = shift; - my $cv = shift; - my $proto = ""; - if ($self->{'sub_name'} =~ /^BEGIN|END|INIT|CHECK$/ or null $cv->ROOT) { - # Don't try to trace special blocks, or undefined subs, - # or constant subs - return B::Deparse::deparse_sub($self, $cv, @_); - } - Carp::confess("NULL in deparse_sub") if !defined($cv)||$cv->isa("B::NULL"); - Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL"); - local $self->{'curcop'} = $self->{'curcop'}; - if ($cv->FLAGS & SVf_POK) { - $proto = "(". $cv->PV . ") "; - } - -# CVf_LOCKED removed from Perl > 5.10 (markro) -# if ($cv->CvFLAGS & (CVf_METHOD|CVf_LOCKED|CVf_LVALUE)) { - if ($cv->CvFLAGS & (CVf_METHOD|CVf_LVALUE)) { - $proto .= ": "; - $proto .= "lvalue " if $cv->CvFLAGS & CVf_LVALUE; -# $proto .= "locked " if $cv->CvFLAGS & CVf_LOCKED; - $proto .= "method " if $cv->CvFLAGS & CVf_METHOD; - } - - # The part of the program point name before the ::: - my $ppt_base = $cv->GV->STASH->NAME . "." . $cv->GV->SAFENAME . "()"; - local($self->{'ppt_base'}) = $ppt_base; - - # The lowest OP sequence number in this sub, to which all others - # will be compared - - # UNDONE: - # $op->seq has been removed in Perl > 5.10. I have not been able - # to locate a suitable replacement as yet. (markro) -# local($self->{'op_seq_base'}) = $cv->START->seq; - local($self->{'op_seq_base'}) = 0; - - local($self->{'curcv'}) = $cv; - local($self->{'curcvlex'}); - local(@$self{qw'curstash warnings hints'}) - = @$self{qw'curstash warnings hints'}; - my $body; - # This is the op whose predecessors we're interested - local($self->{'leavesub'}) = $cv->ROOT; - # Keep track of the number of statements we wrap as being the - # last in the sub, so that if we don't find any, we can put an - # empty return at the end. - local($self->{'returns_count'}) = 0; - my $lineseq = $cv->ROOT->first; - if ($lineseq->name eq "lineseq") { - my @ops; - for(my$o=$lineseq->first; $$o; $o=$o->sibling) { - push @ops, $o; - } - { - # We treat the top level sequence of statements in the - # sub specially. - local($self->{'sub_top_level'}) = 1; - $body = $self->lineseq(undef, @ops).";"; - } - if ($self->{'returns_count'} == 0) { - # If all else fails, add a trace of an empty - # return. This can happen, for instance, if the last - # statement in the subroutine is a loop. - $body .= "\n" . $self->make_trace_return("()", $cv->ROOT); - } - my $scope_en = $self->find_scope_en($lineseq); - if (defined $scope_en) { - my $subs = join"", $self->seq_subs($scope_en); - $body .= ";\n$subs" if length($subs); - } - } - else { - # $body = $self->deparse($cv->ROOT->first, 0); - $body = ""; - } - return $proto ."{\n\t$body\n\b}" ."\n"; -} - -# This predicate guesses whether its op argument is @_ or some related -# value containing our arguments. -sub is_args { - my $self = shift; - my($op) = @_; - if ($op->name eq "rv2av") { - # @_ itself? - return $self->pp_rv2av($op, 0) eq '@_'; - } elsif ($op->name eq "null" and not null($op->first) - and $op->first->name eq "null" and not null($op->first->first) - and $op->first->first->name eq "aelemfast") { - # $_[0], $_[1], etc.? - return $self->gv_or_padgv($op->first->first)->SAFENAME - eq "_"; - } elsif ($op->name eq "null" and not null($op->first) - and not null($op->first->sibling) - and $op->first->sibling->name eq "entersub") { - # Subroutine call, check args recursively - my $k = $op->first->sibling->first; - return 0 unless $$k; - if ($k->can("first")) { - # Usual subroutine case - $k = $k->first; - } else { - # Can happen with methods; we're already at the right level - } - for (; $$k; $k = $k->sibling) { - # If any of the args is @_, that counts. - return 1 if $self->is_args($k); - } - } else { - return 0; - } -} - -# This guesses whether a statement is an assignment whose right-hand -# side is @_ or some related value, which implies that its left-hand -# side is a list of our arguments. -sub is_arg_assign { - my $self = shift; - my($op) = @_; - # only assignments count - return 0 if $op->name ne "sassign" and $op->name ne "aassign"; - my $rhs = $op->first; - my $lhs = $rhs->sibling; - return 1 if $rhs->name eq "shift"; # my $self = shift; - return 1 if $self->is_args($rhs); # my $x = $_[0]; - if ($rhs->name eq "null" and not null($rhs->first) - and $rhs->first->name eq "pushmark" - and $self->is_args($rhs->first->sibling)) { - # my($x, $y) = @_; - return 1; - } - return 0; -} - -# Given an assignment op (for which is_arg_assign() would have -# returned true), get a list of the names of the my() (or local()) -# variables declared in it. -sub get_args { - my $self = shift; - my($op) = @_; - my $rhs = $op->first; - my $lhs = $rhs->sibling; - if ($lhs->name eq "padsv") { - # my $x = ... - my $name = $self->pp_padsv($lhs, 0); - $name =~ s/^my //; - return $name; - } elsif ($lhs->name eq "null" and not null($lhs->first)) { - if ($lhs->first->name eq "gvsv") { - # local $x = (...) - my $name = $self->pp_null($lhs, 0); - $name =~ s/^local //; - return $name; - } elsif ($lhs->first->name eq "pushmark") { - my @ret = (); - # my($x, $y) = ... - for (my $kid = $lhs->first->sibling; $$kid; $kid = $kid->sibling) { - my $name = $self->deparse($kid, 0); - $name =~ s/^(my|local) //; - push @ret, $name; - } - return @ret; - } - } - return (); -} - -# In general, this routine handles OPs that represent a sequence of -# statements like a block, subroutine body, etc. We've messed with it -# in the particular case of the a subroutine body, to find the -# variables that look like arguments so we can trace them. -sub lineseq { - my($self, $root, @ops) = @_; - my($expr, @exprs); - - # We set $sub_top_level if we're the top-level subroutine body - my $sub_top_level = $self->{'sub_top_level'}; - local($self->{'sub_top_level'}) = 0; - - my $out_cop = $self->{'curcop'}; - my $out_seq = defined($out_cop) ? $out_cop->cop_seq : undef; - my $limit_seq; - if (defined $root) { - $limit_seq = $out_seq; - my $nseq = $self->find_scope_st($root->sibling) if ${$root->sibling}; - $limit_seq = $nseq if !defined($limit_seq) - or defined($nseq) && $nseq < $limit_seq; - } - $limit_seq = $self->{'limit_seq'} - if defined($self->{'limit_seq'}) - && (!defined($limit_seq) || $self->{'limit_seq'} < $limit_seq); - local $self->{'limit_seq'} = $limit_seq; - # $in_args is true as long as we've only seen statements that look - # like argument assignments, and false afterwards. - my $in_args = $sub_top_level; - my @args = (); - for (my $i = 0; $i < @ops; $i++) { - $expr = ""; - if (B::Deparse::is_state $ops[$i]) { - $expr = $self->deparse($ops[$i], 0); - $i++; - if ($i > $#ops) { - push @exprs, $expr; - last; - } - } - if (!B::Deparse::is_state $ops[$i] and (my $ls = $ops[$i+1]) and - !null($ops[$i+1]) and $ops[$i+1]->name eq "lineseq") - { - if ($ls->first && !null($ls->first) - && B::Deparse::is_state($ls->first) - && (my $sib = $ls->first->sibling)) { - if (!null($sib) && $sib->name eq "leaveloop") { - push @exprs, $expr . $self->for_loop($ops[$i], 0); - $i++; - next; - } - } - } - if ($in_args) { - if ($self->is_arg_assign($ops[$i])) { - # If $ops[$i] is an argument assignment, snarf the - # arguments - push @args, $self->get_args($ops[$i]); - } else { - # Otherwise, we've reached the end of the arguments, - # so output code to populate the @Daikon_variables - # array and call trace_enter(). - $expr .= "my $dv_name = ("; - for my $arg (@args) { - my $type = $types{$self->{'ppt_base'}.":::UNION"}{$arg} - || "unknown"; - $type = unparse_type($type); - # Escape all the backslashes used for reference - # type constructors - $type =~ s/\\/\\\\/g; - $expr .= "['$arg', \\$arg, '$type'], "; - } - $expr .= ");\n"; - $expr .= "daikon_runtime::trace_enter($dv_name);\n"; - $in_args = 0; - } - } - $expr .= $self->deparse($ops[$i], 0, (@ops != 1)); - $expr =~ s/;\n?\z//; - push @exprs, $expr; - } - my $body = join(";\n", grep {length} @exprs); - my $subs = ""; - if (defined $root && defined $limit_seq && !$self->{'in_format'}) { - $subs = join "\n", $self->seq_subs($limit_seq); - } - return join(";\n", grep {length} $body, $subs); -} - -# Mostly unmodified from B::Deparse -sub pp_return { - my $self = shift; - my($op, $cx) = @_; - my(@exprs); - my $kid = $op->first->sibling; - if (null $kid) { - #### - # scalar() is an error (too few args to scalar), but - # scalar(()) is undef. This is convenient, since return with - # no arguments from a subroutine in scalar context also - # produces undef. Thus we can treat "return;" as if it were - # "return(());". - @exprs = ("()"); - #### - } else { - push @exprs, $self->deparse($kid, 6); - $kid = $kid->sibling; - for (; !null($kid); $kid = $kid->sibling) { - push @exprs, $self->deparse($kid, 6); - } - } - my $val; - $val = join(", ", @exprs); - #### - # Here we also add a trace_return - return "return(" . $self->make_trace_return($val, $op) .")"; - #### -} - -# Mostly unmodified from B::Deparse -sub pp_cond_expr { - my $self = shift; - my($op, $cx) = @_; - my $cond = $op->first; - my $true = $cond->sibling; - my $false = $true->sibling; - my $cuddle = $self->{'cuddle'}; - unless ($cx == 0 and (B::Deparse::is_scope($true) - and $true->name ne "null") and - (B::Deparse::is_scope($false) - || B::Deparse::is_ifelse_cont($false)) - and $self->{'expand'} < 7) { - $cond = $self->deparse($cond, 8); - # XXX Need to think about precedence and associativity more here - # maybe a bug in Deparse (which had 8 on the next line) - $true = $self->deparse($true, 6); - $false = $self->deparse($false, 8); - return $self->maybe_parens("$cond ? $true : $false", $cx, 8); - } - - $cond = $self->deparse($cond, 1); - $true = $self->deparse($true, 0); - my $head = "if ($cond) {\n\t$true\n\b}"; - my @elsifs; - my $newop; - while (!null($false) and B::Deparse::is_ifelse_cont($false)) { - $newop = $false->first; - my $newcond = $newop->first; - my $newtrue = $newcond->sibling; - $false = $newtrue->sibling; # last in chain is OP_AND => no else - $newcond = $self->deparse($newcond, 1); - $newtrue = $self->deparse($newtrue, 0); - push @elsifs, "elsif ($newcond) {\n\t$newtrue\n\b}"; - } - if (!null($false)) { - $false = $cuddle . "else {\n\t" . - $self->deparse($false, 0) . "\n\b}\cK"; - } else { - $false = "\cK"; - #### - # When the last statement in a subroutine is an - # if-elsif-elsif-... with no else, we have to catch the last - # elsif here because the relevant OP (an OP_AND) wouldn't - # normally be passed to deparse(). - if ($newop and ${$newop->next} == ${$self->{'leavesub'}} ) { - $false = "\n" . $self->make_trace_return("()", $newop); - } - #### - } - return $head . join($cuddle, "", @elsifs) . $false; -} - -# Mostly unchanged -sub dq { - my $self = shift; - my $op = shift; - my $type = $op->name; - if ($type eq "const") { - -# OPpCONST_ARYBASE removed from Perl > 5.10 (markro) -# return '$[' if $op->private & OPpCONST_ARYBASE; - return B::Deparse::uninterp(B::Deparse::escape_str(B::Deparse::unback($self->const_sv($op)->as_string))); - } elsif ($type eq "concat") { - my $first = $self->dq($op->first); - my $last = $self->dq($op->last); - - #### - # Fix in the next statement: handle "$a\::foo" correctly - # This was also fixed (independently) in bleadperl #19127 - - # Disambiguate "${foo}bar", "${foo}{bar}", "${foo}[1]" - ($last =~ /^[A-Z\\\^\[\]_?]/ && - $first =~ s/([\$@])\^$/${1}{^}/) # "${^}W" etc - || ($last =~ /^([{\[\w_]|::)/ && - $first =~ s/([\$@])([A-Za-z_]\w*)$/${1}{$2}/); - ### - - return $first . $last; - } elsif ($type eq "uc") { - return '\U' . $self->dq($op->first->sibling) . '\E'; - } elsif ($type eq "lc") { - return '\L' . $self->dq($op->first->sibling) . '\E'; - } elsif ($type eq "ucfirst") { - return '\u' . $self->dq($op->first->sibling); - } elsif ($type eq "lcfirst") { - return '\l' . $self->dq($op->first->sibling); - } elsif ($type eq "quotemeta") { - return '\Q' . $self->dq($op->first->sibling) . '\E'; - } elsif ($type eq "join") { - return $self->deparse($op->last, 26); # was join($", @ary) - } else { - return $self->deparse($op, 26); - } -} - -# This isn't a method in older versions, so we have to replace all the -# methods that call it. -sub const { - my $self = shift; - my($sv, $cx) = @_; - if (class($sv) eq "SPECIAL") { - # In the 5.8.0 version of Deparse, the string for sv_no was - # '0', '""' isn't quite right either, in fact. - # sv_undef, sv_yes, sv_no - return ('undef', '1', $self->maybe_parens("!1", $cx, 21))[$$sv-1]; - } else { - if ($B::Deparse::VERSION <= 0.63) { - return B::Deparse::const($sv); - } else { - return $self->SUPER::const($sv, $cx); - } - } -} - -# Unchanged, except for the call to "const" -sub pp_const { - my $self = shift; - my($op, $cx) = @_; - -# OPpCONST_ARYBASE removed from Perl > 5.10 (markro) -# -# if ($op->private & OPpCONST_ARYBASE) { -# return '$['; -# } -# if ($op->private & OPpCONST_BARE) { # trouble with `=>' autoquoting -# return $self->const_sv($op)->PV; -# } - my $sv = $self->const_sv($op); -# return const($sv); - my $c = $self->const($sv, $cx); - return $c =~ /^-\d/ ? $self->maybe_parens($c, $cx, 21) : $c; -} - -# Unchanged, except for the call to "const" -sub pp_rv2av { - my $self = shift; - my($op, $cx) = @_; - my $kid = $op->first; - if ($kid->name eq "const") { # constant list - my $av = $self->const_sv($kid); - return "(" . join(", ", map($self->const($_, $cx), $av->ARRAY)) . ")"; - } else { - return $self->maybe_local($op, $cx, $self->rv2x($op, $cx, "\@")); - } - } - - - -1; diff --git a/front-end/perl/lib/Daikon/Output.pm b/front-end/perl/lib/Daikon/Output.pm deleted file mode 100644 index 0b65875a45..0000000000 --- a/front-end/perl/lib/Daikon/Output.pm +++ /dev/null @@ -1,443 +0,0 @@ -package Daikon::Output; - -# Routines for printing Perl values in the format that Daikon likes. - -# This file is part of the Daikon distribution. It may be used and -# redistributed under the same terms as the rest of Daikon or, at your -# option, under the same terms as Perl itself, following either the -# GNU General Public License or the Perl Artistic License. The Daikon -# Perl front end, of which this file is a part, and the Daikon dynamic -# invariant detection tool are separate programs, neither derived from -# the other, which are merely aggregated for convenience of -# distribution. As such, licensing the Perl front end under the terms -# of the GPL neither requires nor entitles you to license other parts -# of the Daikon distribution under the same terms. - -use strict; -use Exporter; -use Carp ('carp', 'cluck', 'croak', 'confess'); -use vars qw(@ISA @EXPORT_OK); -@ISA = 'Exporter'; -@EXPORT_OK = qw(declare_var output_var); - -use Daikon::PerlType qw(parse_type is_zero read_types_into); - -# Convert an integer (given a reference to it) to a string form, -# warning if it wasn't really an integer. -sub output_int { - no warnings 'uninitialized'; - my($x_ref) = @_; - confess "Arg to output must be a reference" unless ref($x_ref); - my $x = $$x_ref; - if ($x == 0 and length $x and !is_zero($x)) { - carp "`$x' isn't a number, you said it was an int"; - } - $x = $x + 0; - if ($x != int($x)) { - carp "`$x' isn't an integer, you said it was an int"; - } - return sprintf("%d", $x); -} - -sub output_num { - no warnings 'uninitialized'; - my($x_ref) = @_; - confess "Arg to output must be a reference" unless ref($x_ref); - my $x = $$x_ref; - if ($x == 0 and length $x and !is_zero($x)) { - carp "`$x' isn't a number, you said it was an int"; - } - $x = $x + 0; - return sprintf("%g", $x); -} - -# Like 0 + $x for a reference, but avoids calling overload routines -# if $x has them. The squeamish might want to avert their eyes. -# Based on overload::StrVal -sub address { - my $package = ref $_[0]; - return 0 + $_[0] unless $package; - return 0 + $_[0] if $package =~ /^SCALAR|REF|LVALUE|ARRAY|HASH\z/; - bless $_[0], "overload::Fake"; # Non-overloaded package - my $val = 0 + $_[0]; - bless $_[0], $package; # Back - return $val; -} - -# Backslash escape a character (', ", \r, or \n) in the way that -# Daikon likes. -sub escape_char { - my($c) = @_; - if ($c eq "\n") { - return '\\n'; - } elsif ($c eq "\r") { - return '\\r'; - } elsif ($c eq '"' or $c eq '\\') { - return "\\$c"; - } else { - return $c; - } -} - -# Convert an arbitrary string into a quoted string in a -# Daikon-friendly format. -sub output_string { - no warnings 'uninitialized'; # Silently treat undef as "" - my($s_ref) = @_; - my $s = $$s_ref; - $s =~ s/([\\\"\r\n])/escape_char($1)/eg; - return qq'"$s"'; -} - -# Given a type, figure out how to print it for Daikon. - -# Returns a list with one element for each daikon-variable that'll -# correspond to this one perl variable. -# Each element in the list is a tuple: -# [$prefix, $suffix, $type, $rep_type, \&output] -# where $prefix is text to write at the beginning of the variable -# name, $suffix is text to write at the end, $type is the type to tell -# Daikon, $rep_type is the representation type to tell Daikon, and -# &output is a sub to convert the value (passed by reference) into a -# string. Because Perl has pretty high subroutine call overhead, the -# use of closures to represent the output code isn't especially fast: -# performance could perhaps be improved, at the expense of elegance, by -# generating textual code and eval-ing it. - -# If the $single argument is true, this output is already going to go -# in a sequence, so we can't output a nested sequence. We can however -# output any fixed number of fields, which will be distributed over -# the sequence. - -# Note that when passing $prefix and $suffix up between recursive -# calls, you usually want to append to the prefix and prepend to the -# suffix, since the accessors for compound values work from the inside -# out. - -sub daikon_output_spec { - my($t, $single) = @_; - if ($t eq "int") { - return ["", "", "int", "int", \&output_int, {}]; - } elsif ($t eq "bit") { - return ["", "", "int", "int", \&output_int, {}]; - } elsif ($t eq "num") { - return ["", "", "double", "double", \&output_num, {}]; - } elsif ($t eq "str") { - # The Daikon name for a string is "java.lang.String", - # even in languages other than Java. This is most important - # for the rep type, but it will help consistency even for the - # "declared" type. - return ["", "", "java.lang.String", "java.lang.String", - \&output_string, {}]; - } elsif (ref($t)) { - if ($t->[0] eq "array") { - if ($single) { - # We must have gotten here by a reference, so we'll - # have to be content with the address form of the - # reference. - return (); - } else { - my @elt_specs = daikon_output_spec($t->[1], 1); - my @specs; - for my $spec (@elt_specs) { - my($prefix, $suffix, $type, $rep_type, $output, $flags) - = @$spec; - push @specs, - [$prefix, "[]".$suffix, - $type."[]", $rep_type."[]", - sub { "[" . join(" ", map($output->(\$_), @{$_[0]})) - . "]"}, $flags]; - } - return @specs; - } - } elsif ($t->[0] eq "hash") { - return () if $single; - my @specs; - for my $spec (daikon_output_spec($t->[1], 1)) { - my($kprefix, $ksuffix, $ktype, $krep_type, $koutput, $flags) - = @$spec; - push @specs, [$kprefix, ".keys".$ksuffix, $ktype."[]", - $krep_type."[]", - sub { "[" . - join(" ", map($koutput->(\$_), - keys %{$_[0]})) . "]" }, - $flags]; - } - for my $spec (daikon_output_spec($t->[2], 1)) { - my($vprefix, $vsuffix, $vtype, $vrep_type, $voutput, $flags) - = @$spec; - push @specs, [$vprefix, ".values".$vsuffix, $vtype."[]", - $vrep_type."[]", - sub { "[" . - join(" ", map($voutput->(\$_), - values %{$_[0]})) . "]" }, - $flags]; - } - return @specs; - } elsif ($t->[0] eq "list") { - die if $single; # lists should only be at top level - my $num_fixed = @{$t->[1]}; - my @specs; - for my $index (0 .. $num_fixed - 1) { - for my $spec (daikon_output_spec($t->[1][$index], 0)) { - my($prefix, $suffix, $type, $rep_type, $output, $flags) - = @$spec; - push @specs, [$prefix, "[$index]".$suffix, $type, - $rep_type, - sub { $output->(\ ($_[0]->[$index])) }, - $flags]; - } - } - if ($t->[2]) { - my $rest = $t->[2]; - for my $spec (daikon_output_spec($rest, 1)) { - my($prefix, $suffix, $type, $rep_type, $output, $flags) - = @$spec; - my $slice = $num_fixed ? "[$num_fixed..]" : ""; - push @specs, - [$prefix, $suffix . $slice . "[]", $type."[]", - $rep_type."[]", - sub { "[" . - join(" ", - map($output->(\$_), - @{$_[0]}[$num_fixed .. $#{$_[0]}])) - . "]" }, - $flags]; - } - } - return @specs; - } elsif ($t->[0] eq "object") { - my @specs; - # When this was enabled, I'd picked ".Class" to look - # distinct from usual field names that are - # lowercase. But Daikon treats ".class" specially, so - # maybe it should be that. -# push @specs, ["", ".Class", "String", -# "java.lang.String", sub { '"'. ref($_[0]).'"' }]; - for my $k (sort keys %{$t->[1]}) { - for my $spec (daikon_output_spec($t->[1]{$k}, $single)) { - my($prefix, $suffix, $type, $rep_type, $output, $flags) - = @$spec; - push @specs, [$prefix, ".$k$suffix", $type, $rep_type, - sub { exists($_[0]->{$k}) ? - $output->(\ ($_[0]->{$k})) - : $output->(\undef) }, - $flags]; - } - } - for my $k (sort keys %{$t->[2]}) { - for my $spec (daikon_output_spec($t->[2]{$k}, $single)) { - my($prefix, $suffix, $type, $rep_type, $output, $flags) - = @$spec; - push @specs, [$prefix, ".$k()$suffix", $type, - $rep_type, - sub { $output->(\scalar($_[0]->$k)) }, - $flags]; - } - } - return @specs; - } elsif ($t->[0] eq "ref") { - # Some Perl documenters have tried to promulgate "sigil" - # as the name for the funny characters at the front of - # Perl variables. At the moment we can't use them here, - # though, because the complexities of Perl's dereferencing - # syntax are currently too much for Daikon to handle. -# my $sigil; -# if (ref($t->[1]) and $t->[1][0] eq "array") { -# $sigil = '@'; -# } elsif (ref($t->[1]) and $t->[1][0] =~ /^hash|object$/) { -# $sigil = '%'; -# } else { -# $sigil = '$'; -# } - if ($t->[1] eq "top") { - return ["", "", "Object", "hashcode", - sub { output_int(\ (address(${$_[0]}))); }, - {addr => 1}]; - } else { - my @specs; - for my $spec (daikon_output_spec($t->[1], $single)) { - my($prefix, $suffix, $type, $rep_type, $output, $flags) - = @$spec; - push @specs, [$prefix, '.deref'.$suffix, $type, $rep_type, - sub { - die "nonsensical" unless ref ${$_[0]}; - $output->(${$_[0]}) - }, - {%$flags, saw_deref => 1}]; - } - push @specs, ["", "", "Object", "hashcode", - sub { output_int(\ (address(${$_[0]}))); }, - {addr => 1}]; - return @specs; - } - } elsif ($t->[0] eq "maybe") { - my $is_def = ["", ".is_defined", "boolean", "boolean", - sub { defined(${$_[0]}) ? "true" : "false"}, {}]; - if ($t->[1] eq "bottom") { - # 'nothing' is always either the empty string or the - # undefined value, so the only interesting information - # is whether it's defined. - return $is_def; - } - my $is_empty = ["", ".is_empty", "boolean", "boolean", - sub { no warnings 'uninitialized'; - length(${$_[0]})==0 ? "true" : "false"}, {}]; - my @extra_specs = ($is_def, $is_empty); - if ($t->[1] eq "bit") { - # Boolean is a special case, since we want 'bit's to - # show up as integers, but 'bool's to show up as - # booleans. - my $bool = ["", "", "boolean", "boolean", - sub { ${$_[0]} ? "true" : "false"}, {}]; - return ($bool, @extra_specs); - } elsif ($t->[1] eq "str") { - my @specs = daikon_output_spec($t->[1], $single); - # is_empty isn't needed, since the empty string is a - # regular string. - return (@specs, $is_def); - } else { - my @specs = daikon_output_spec($t->[1], $single); - my @lifted_specs; - foreach my $spec (@specs) { - my @spec = @$spec; - $spec[1] = ".value" . $spec[1]; - push @lifted_specs, [@spec]; - } - return (@specs, @extra_specs); - } - } - } - return (); -} - -# A cache of the output specification for a type, indexed by their -# string representations. -my %specs; - -sub simplify_name { - my($name) = @_; - # Turn .deref.foo into .foo, except when foo is "is_defined" or - # "is_empty". Internal derefs usually make the name longer without - # adding information, so we'd like to get rid of them, but in the - # case of a maybe reference to a maybe, they're needed to - # distinguish properties describing the definedness or emptiness - # of the referrer and referent maybes. - $name =~ s/(\.deref)(\.\w+)/ - ($2 eq ".is_defined" || $2 eq ".is_empty") ? $1.$2 : $2/eg; - return $name; -} - -# Format a variable declaration for a .decls file, given the name for -# the variable and list of specifications. Returns a list of lines. -sub declare_var_spec { - my($varname, @specs) = @_; - my @lines = (); - - for my $spec (@specs) { - my($prefix, $suffix, $type, $rep_type, undef, $flags) = @$spec; - my $name = simplify_name("$prefix$varname$suffix"); - my @aux; - if ($varname ne "return" and not $flags->{saw_deref}) { - push @aux, "isParam = true"; - } - if ($flags->{addr}) { - push @aux, "hasNull = false"; - } - my $aux = join(", ", @aux); - $aux = " # $aux" if length $aux; - push @lines, "$name\n"; # Variable name - push @lines, "$type$aux\n"; - push @lines, "$rep_type\n"; - push @lines, "22\n"; # comparability: not available - } - return @lines; -} - -# Format a variable declaration for a .decls file, given the name for -# the variable and an unparsed type. Returns a list of lines. -sub declare_var { - my($varname, $type) = @_; - if (not exists $specs{$type}) { - $specs{$type} = [daikon_output_spec(parse_type($type), 0)]; - } - return declare_var_spec($varname, @{$specs{$type}}); -} - -# Output a variable to a .dtrace file, given the filehandle, the name -# of the variable, a reference to the value of the variable, and a -# list of specifications. -sub output_var_spec { - my($fh, $varname, $value, @specs) = @_; - for my $spec (@specs) { - my($prefix, $suffix, undef, undef, $output, undef) = @$spec; - my $name = simplify_name("$prefix$varname$suffix"); - print $fh "$name\n"; - my $out_str; - eval { - $out_str = $output->($value); - }; - if ($@ =~ /nonsensical/) { - print $fh "nonsensical\n2\n"; - } elsif (length $@) { - die $@; # rethrow exception - } else { - print $fh "$out_str\n"; - print $fh "1\n"; # Pretend always modified - } - } -} - -# Output a variable to a .dtrace file, given the filehandle, the name -# of the variable, a reference to the value of the variable, the type -# of the variable in unparsed form. -sub output_var { - my($fh, $varname, $value, $type) = @_; - if (not exists $specs{$type}) { - $specs{$type} = [daikon_output_spec(parse_type($type), 0)]; - } - output_var_spec($fh, $varname, $value, @{$specs{$type}}); -} - -# Write the type information in $types_r and $order_r into a decls -# file open on $out_fh. -sub types_info_to_decls { - my($types_r, $order_r, $out_fh) = @_; - print $out_fh "\nVarComparability\nnone\n\n"; - for my $ppt_name (sort keys %$types_r) { - next unless $ppt_name =~ /_[sl]\(\)/; - next if $ppt_name =~ /EXIT$/; - my $union_ppt = $ppt_name; - $union_ppt =~ s/_[sl]\(\)/()/; - $union_ppt =~ s/:::.*$/:::UNION/; - (my $exit_union_ppt = $ppt_name) =~ s/:::EXIT(\d+)/:::EXIT/; - my @lines = ("DECLARE\n"); - push @lines, "$ppt_name\n"; - my $vars_ref = $types_r->{$ppt_name}; - for my $var (@{$order_r->{$ppt_name}}) { - my $ppt; - if ($var eq "return") { - $ppt = $exit_union_ppt; - } else { - $ppt = $union_ppt; - } - my @specs = daikon_output_spec($types_r->{$ppt}{$var}); - push @lines, declare_var_spec($var, @specs); - } - print $out_fh @lines; - print $out_fh "\n"; - } -} - -sub types_file_to_decls_file { - my($types_fname, $decls_fname) = @_; - open(TYPES, "<$types_fname") or die "Can't open $types_fname: $!"; - my(%types, %order); - read_types_into(\*TYPES, \%types, \%order); - close TYPES; - open(DECLS, ">$decls_fname") or die "Can't open $decls_fname: $!"; - types_info_to_decls(\%types, \%order, \*DECLS); - close DECLS; -} - -1; diff --git a/front-end/perl/lib/Daikon/PerlType.pm b/front-end/perl/lib/Daikon/PerlType.pm deleted file mode 100644 index 06e8885a96..0000000000 --- a/front-end/perl/lib/Daikon/PerlType.pm +++ /dev/null @@ -1,690 +0,0 @@ -package Daikon::PerlType; - -# Routines to handle our slightly contrived notion of "types" for Perl -# values. We can guess a type for a particular runtime value by -# examining it, take the lub of two such types, or convert between the -# internal form and a string representation. - -# This file is part of the Daikon distribution. It may be used and -# redistributed under the same terms as the rest of Daikon or, at your -# option, under the same terms as Perl itself, following either the -# GNU General Public License or the Perl Artistic License. The Daikon -# Perl front end, of which this file is a part, and the Daikon dynamic -# invariant detection tool are separate programs, neither derived from -# the other, which are merely aggregated for convenience of -# distribution. As such, licensing the Perl front end under the terms -# of the GPL neither requires nor entitles you to license other parts -# of the Daikon distribution under the same terms. - -use strict; - -use vars ('@ISA', '@EXPORT_OK'); -use Exporter; -@ISA = ('Exporter'); - -use Carp 'croak', 'confess', 'cluck', 'carp'; - -@EXPORT_OK = qw(DEPTH_LIMIT LIST_LIMIT - parse_type unparse_type type_lub unparse_to_code - guess_type_ref guess_type_scalar guess_type_array - guess_type_hash guess_type_object guess_type_list - is_zero set_limits read_types_into); - -sub max { $_[0] > $_[1] ? $_[0] : $_[1] } -sub min { $_[0] < $_[1] ? $_[0] : $_[1] } - -# How deep in terms of nested references to go in a linked data -# structure. We don't have any notion of recursive types, so this has -# to be finite to keep from getting stuck in reference loops, and it -# should probably be small because the size of the types we guess can -# potentially be exponential in it. In arbitrary units; the value -# 189000 was chosen because it has a lot of factors, which should -# reduce the chance of floating-point rounding problems. -sub DEPTH_LIMIT () { 189000 } - -# How much traversing a scalar reference adds to our depth -my $ref_depth = DEPTH_LIMIT/6.1; -sub SCALAR_REF_DEPTH { $ref_depth; } - -# How much looking at the output of an accessor method adds to our -# depth -my $accessor_depth = DEPTH_LIMIT/2.99; -sub ACCESSOR_METHOD_DEPTH { $accessor_depth; } - -# The number of elements at the start of a list type to possibly treat -# as distinct entities. After this many, any remaining elements in the -# list go in one "bucket", so to speak. -my $list_limit = 3; -sub LIST_LIMIT { $list_limit } - -# Modify the recursion limits (globally). At some point in the future, -# it would be nice to change this so that the limit can be set -# per-module. -sub set_limits { - my($ref_lim, $accessor_lim, $list_lim) = @_; - $ref_depth = DEPTH_LIMIT/$ref_lim; - $accessor_depth = DEPTH_LIMIT/$accessor_lim; - $list_limit = $list_lim; -} - -# There are several basic scalar types: -# -# type values -# ---- ------ -# nothing the undefined value, or the defined empty string -# bit 0 or 1 -# bool undef/0/"" for false, or 1 for true -# int integers -# num arbitrary floating-point numbers -# ref references -# str arbitrary scalars (which always have a stringified form) - -# These obey bit <= int <= num <= str and ref <= str, where "<=" is -# the subtype relation. For each scalar type that doesn't include the -# empty string or undef, there's also a `lifted' variant that -# does. Generally these are formed with a "?" on the end like int?, -# ref?, num?, and str?. "bit?" is called bool, and "\T?" is written -# "\?T" to make the paser's job easier. - -# There are also some type combiners. Let T be a type. - -# \T is the type of references to values of type T. \T <= ref. - -# [T] is the type of arrays, all of whose elements have type T. The -# elements of arrays are always scalars, so the most general array -# type is [str]. - -# {Tk=>Tv} is the type of hashes whose keys and values have the scalar -# types Tv and Th. - -# is the type of blessed hashes (objects) of any -# class with elements (fields) a of type T1, b of type T2, etc. It's -# okay for any particular instance to be missing some fields, so the -# lub has the union of the field names, eaching having the lub of the -# corresponding types. - -# (T1,T2,...,Ti,T*) is the type of a list whose first i elements have -# types T1, T2, ..., Ti, and remaining elements have type T. Lists -# only exist transiently during expression evaluation, so they can't -# appear inside any other values. If you lub lists with different -# numbers of fixed elements (different values of "i"), the result has -# the smaller number, and all the extras are lub in at the end. This -# tries to capture the common case of returning ($a, $b, @foo), where -# @foo might have any number of elements. The T* part is optional. - -# There are also "code" and "glob" types to represent the other kinds -# of data you can refer to; they have no internal structure. - -# ------------------------------------------------------------------ - -# Internally, types are represented in a subset of perl values -# isomorphic to Lisp s-expressions: array references whose elements -# are either other array references or one of a few strings. For instance, -# \{int=>\num} is represented as - -# ['ref', ['hash', 'int', ['ref', 'num']]] - -# As an exception, object types use a hash table to store the type -# corresponding to a particular field key. The full correspondence: - -# bottom 'bottom' -# bool 'bool' -# ... -# ref ['ref', 'top'] -# T? ['maybe', T] -# bool ['maybe', 'bit'] -# nothing ['maybe', 'bottom'] -# \T ['ref', T] -# \?T ['maybe', ['ref', T]] -# ref? ['maybe', ['ref', 'top']] -# {Tk=>Tv} ['hash', Tk, Tv] -# T3,g->T4> ['object', {'a' => T1, 'b' => T2}, -# {'f' => T3, 'g' => T4}] -# (T1, T2) ['list', [T1, T2]] -# (T1, T2, T*) ['list', [T1, T2], T] - -# There is a generic bottom type named 'bottom', which is no longer -# the same thing as undef. There's also a generic top type, called -# 'top', but it should never show up as a type itself, since scalars, -# arrays, hashes, code, globs, and lists are all syntactically -# segregated in Perl; there's no context where you might have more -# than one. References to all of these things (except lists) are -# possible as scalars, though, so the ref type turns into the -# equivalent of \top internally. - -# Given an internal type representation, return a string representing -# it. Dies if the type is top or otherwise invalid. -sub unparse_type { - my($t) = @_; - if (not ref $t) { - die "Can't print top" if $t eq "top"; - return $t; # 'int' unparses to 'int', etc. - } else { - if ($t->[0] eq "ref") { - if ($t->[1] eq "top") { - return "ref"; - } else { - return "\\" . unparse_type($t->[1]); - } - } elsif ($t->[0] eq "maybe") { - if ($t->[1] eq "bottom") { - return "nothing"; - } elsif ($t->[1] eq "bit") { - return "bool"; - } elsif (ref($t->[1]) and $t->[1]->[0] eq "ref") { - if ($t->[1]->[1] eq "top") { - return "ref?"; - } else { - return "\\?" . unparse_type($t->[1]->[1]); - } - } elsif (not ref($t->[1])) { - return $t->[1] . "?"; - } else { - die "Invalid maybe type"; - } - } elsif ($t->[0] eq "array") { - return "[" . unparse_type($t->[1]) . "]"; - } elsif ($t->[0] eq "hash") { - return "{" . unparse_type($t->[1]) . - "=>" . unparse_type($t->[2]) . "}"; - } elsif ($t->[0] eq "object") { - my @contents = (); - for my $k (sort keys %{$t->[1]}) { - push @contents, $k . ":" . unparse_type($t->[1]{$k}); - } - my @accessors = (); - for my $k (sort keys %{$t->[2]}) { - push @accessors, $k . "->" . unparse_type($t->[2]{$k}); - } - my $acc = ""; - $acc = ";" . join(",", @accessors) if @accessors; - return "<" . join(",", @contents) . "$acc>"; - } elsif ($t->[0] eq "list") { - my @elts = (); - foreach my $elt (@{$t->[1]}) { - push @elts, unparse_type($elt); - } - if ($t->[2]) { - push @elts, unparse_type($t->[2]) . "*"; - } - return "(" . join(",", @elts). ")"; - } else { - die "Invalid type object"; - } - } -} - -# Return a string of Perl code to construct a type representation -# like the argument. This doesn't handle 'object' types. - -# sub unparse_to_code { -# my($t) = @_; -# if (not ref $t) { -# return "'$t'"; -# } else { -# return "[" . join(",",map(unparse_to_code($_), @$t)) . "]"; -# } -# } - -# The main recursive routine for parsing the string representation of -# a type into our internal representation. Takes a string as an -# argument, and returns the representation of the first type named in -# the string, followed by the rest of the string. Dies on various -# syntax errors. -sub parse_type_from { - my($s) = @_; - if ($s =~ /^ref\?(.*)\z/) { - return (['maybe', ['ref', 'top']]); - } elsif ($s =~ /^(int|num|str|ref)\?(.*)\z/) { - return (['maybe', $1], $2); - } elsif ($s =~ /^(bottom|bit|int|num|str|code|glob)(.*)\z/) { - return ($1, $2); - } elsif ($s =~ /^ref(.*)\z/) { - return (['ref', 'top'], $1); - } elsif ($s =~ /^nothing(.*)\z/) { - return (['maybe', 'bottom'], $1); - } elsif ($s =~ /^bool(.*)\z/) { - return (['maybe', 'bit'], $1); - } elsif ($s =~ /^\\\?(.*)\z/) { - my($subtype, $rest) = parse_type_from($1); - return (['maybe', ['ref', $subtype]], $rest); - } elsif ($s =~ /^\\(.*)\z/) { - my($subtype, $rest) = parse_type_from($1); - return (['ref', $subtype], $rest); - } elsif ($s =~ /^\[(.*)\z/) { - my($subtype, $rest) = parse_type_from($1); - $rest =~ s/^\]// or die "expected ] in type"; - return (['array', $subtype], $rest); - } elsif ($s =~ /^\{(.*)\z/) { - my($key_type, $rest) = parse_type_from($1); - $rest =~ s/^=>// or die "expected => in type"; - (my($val_type), $rest) = parse_type_from($rest); - $rest =~ s/^\}// or die "expected } in type"; - return (['hash', $key_type, $val_type], $rest); - } elsif ($s =~ /^\<(.*)\z/) { - my $rest = $1; - my %obj; - while ($rest =~ /^(\w+):(.*)\z/) { - my $key = $1; - (my($val_type), $rest) = parse_type_from($2); - $obj{$key} = $val_type; - if ($rest !~ /^[;>]/) { - $rest =~ s/^,// or die "expected , ; or > in type"; - } - } - my %acc; - if ($rest =~ /^;(.*)\z/) { - $rest = $1; - while ($rest =~ /^(\w+)->(.*)\z/) { - my $func = $1; - (my($ret_type), $rest)= parse_type_from($2); - $acc{$func} = $ret_type; - if ($rest !~ /^>/) { - $rest =~ s/^,// or die "expected , or > in type"; - } - } - } - $rest =~ s/^>// or die "expected field:type, ;, or > in type"; - return (['object', {%obj}, {%acc}], $rest); - } elsif ($s =~ /^\((.*)\z/) { - my $rest = $1; - my @types = (); - while (not $rest =~ /^[\)*]/) { - (my($type), $rest) = parse_type_from($rest); - push @types, $type; - if ($rest !~ /^[\)*]/) { - $rest =~ s/^,// or die "expected , * or ) in list type"; - } - } - my $type; - if ($rest =~ /^\*(.*)\z/) { - $rest = $1; - my $extras = pop @types; - $type = ['list', [@types], $extras]; - } else { - $type = ['list', [@types]]; - } - $rest =~ s/^\)// or die "expected ) in list type"; - return ($type, $rest); - } else { - #use Carp 'confess'; - die "Can't parse type, stuck at $s"; - } -} - -# The entry point for our recursive-descent type parser. Removes -# whitespace to make the recursive routine's job easier, and checks -# that there's no extra junk left in the string after the type. -sub parse_type { - my($s) = @_; - $s =~ tr/ \t\n\f//d; - my($type, $rest) = parse_type_from($s); - if (length $rest) { - die "Junk `$rest' after type"; - } - return $type; -} - -# Compute the least-upper-bound of two types in our internal representation. -sub type_lub { - my($t1, $t2) = @_; - return $t1 if $t1 eq $t2; # efficient special case - return $t1 if $t2 eq "bottom"; # 'bottom' is the bottom - return $t2 if $t1 eq "bottom"; - return "top" if $t1 eq "top" or $t2 eq "top"; # 'top' is the top - # code and glob are incomparable with anything else - return "top" if $t1 eq "code" xor $t2 eq "code"; - return "top" if $t1 eq "glob" xor $t2 eq "glob"; - my $maybe1 = (ref($t1) and $t1->[0] eq "maybe"); - my $maybe2 = (ref($t2) and $t2->[0] eq "maybe"); - if ($maybe1 or $maybe2) { - my $ret = type_lub(($maybe1 ? $t1->[1] : $t1), - ($maybe2 ? $t2->[1] : $t2)); - return "top" if $ret eq "top"; # top? is top, because top is top. - return ["maybe", $ret]; - } - if (ref $t1 and ref $t2) { - # all the other composite types are mutually incomparable - return "top" if $t1->[0] ne $t2->[0]; - if ($t1->[0] eq "array") { - return ["array", type_lub($t1->[1], $t2->[1])]; - } elsif ($t1->[0] eq "ref") { - return ["ref", type_lub($t1->[1], $t2->[1])]; - } elsif ($t1->[0] eq "hash") { - return ["hash", type_lub($t1->[1], $t2->[1]), - type_lub($t1->[2], $t2->[2])]; - } elsif ($t1->[0] eq "object") { - # The keys of the lub object are the union of the keys - my %h = (%{$t1->[1]}, %{$t2->[1]}); - foreach my $k (keys %h) { - # For values that exist in both, lub them - $h{$k} = type_lub($t1->[1]{$k}, $t2->[1]{$k}) - if exists($t1->[1]{$k}) and exists($t2->[1]{$k}); - } - my %acc_union = (%{$t1->[2]}, %{$t2->[2]}); - my %acc; - foreach my $k (keys %acc_union) { - $acc{$k} = type_lub($t1->[2]{$k}, $t2->[2]{$k}) - if exists($t1->[2]{$k}) and exists($t2->[2]{$k}); - } - return ["object", {%h}, {%acc}]; - } elsif ($t1->[0] eq "list") { - my($s1, $s2) = (1 + $#{$t1->[1]}, 1 + $#{$t2->[1]}); - if ($s2 < $s1) { - # Assume WLOG that $s1 <= $s2 - ($t1, $t2) = ($t2, $t1); - ($s1, $s2) = ($s2, $s1); - } - my @fixed = (); - for my $i (0 .. $s1 - 1) { - # lub the elements that fixed in both elementwise - push @fixed, type_lub($t1->[1][$i], $t2->[1][$i]); - } - if ($t1->[2] or $t2->[2] or $s1 != $s2) { - # The T* of $t1, if any... - my $extra = $t1->[2] || 'bottom'; - for my $i ($s1 .. $s2 - 1) { - # ... and the extra fixed types of the longer list - $extra = type_lub($extra, $t2->[1][$i]); - } - if ($t2->[2]) { - # ... and the T* of $t2, if any... - # lub together to give the T* of the lub - $extra = type_lub($extra, $t2->[2]); - } - return ['list', [@fixed], $extra]; - } else { - return ['list', [@fixed]]; - } - } else { - die "Invalid type object"; - } - } elsif (!ref $t1 and !ref $t2) { - # The scalar types form a chain - return "str" if $t1 eq "str" or $t2 eq "str"; - return "num" if $t1 eq "num" or $t2 eq "num"; - return "int" if $t1 eq "int" or $t2 eq "int"; - return "bit" if $t1 eq "bit" or $t2 eq "bit"; - die "Invalid type object"; - } else { - # The only relationship between the simple and composite types - # is that references are scalars - return "str" if ref($t1) and $t1->[0] eq "ref"; - return "str" if ref($t2) and $t2->[0] eq "ref"; - return "top"; - } -} - -# Guess a type for a reference to an object. This is the most general -# entry point to the guessing routines, handling everything except -# lists. -sub guess_type_ref { - my($r) = @_; - die unless ref($r); - my $t; - if (UNIVERSAL::isa($r, "CODE")) { - $t = "code"; - } elsif (UNIVERSAL::isa($r, "GLOB")) { - $t = "glob"; - } elsif (UNIVERSAL::isa($r, "ARRAY")) { - $t = guess_type_array($r); - } elsif (UNIVERSAL::isa($r, "HASH")) { - if (ref($r) eq "HASH") { - # unblessed - $t = guess_type_hash($r); - } else { - # blessed - $t = guess_type_object($r); - } - } elsif (UNIVERSAL::isa($r, "SCALAR") or UNIVERSAL::isa($r, "REF") - or UNIVERSAL::isa($r, "LVALUE")) { - # A quirk of the internal routine that implements ref() and - # UNIVERSAL::isa is that it calls references to references - # "REF" references rather than "SCALAR" references (even - # though references are scalars just as much as numbers and - # strings are), and exposes an internal distinction that - # certain scalars have an lvalue magic that makes funny things - # happen when you assign to them ($r = \substr($s, 0, 1); $$r - # = "hah!";). These all count as scalars for our purposes. - $t = guess_type_scalar($$r); - } else { - die "Can't figure out the type"; - } - return ['ref', $t]; -} - -# We guess that the type of an array (passed by reference) is array-of -# the lub of the types of the elements. -sub guess_type_array { - my($ar) = @_; - my $elt_t = "bottom"; - for my $idx (0 .. $#$ar) { - $elt_t = type_lub($elt_t, guess_type_scalar($ar->[$idx])); - } - return ['array', $elt_t]; -} - -# Like guess_type_array, but we guess separate types for the keys and -# the values. -sub guess_type_hash { - my($hr) = @_; - my($key_t, $value_t) = ("bottom", "bottom"); - my($k, $v); - while (($k, $v) = each %$hr) { - $key_t = type_lub($key_t, guess_type_scalar($k)); - $value_t = type_lub($value_t, guess_type_scalar($v)); - } - return ['hash', $key_t, $value_t]; -} - -{ - # A static variable, to avoid passing it around all the guessing routines - my $depth = 0; - - # For an object, we guess separate types for each field. - sub guess_type_object { - my($hr) = @_; - # We only look at the fields whose names look like identifiers, - # for two reasons: - # 1. It would be a pain to include arbitrary strings in our - # printed type representation - # 2. Weirdly named keys are more likely to be used for strange, - # un-field-like purposes. - my @fields = grep(/^\w+\z/, keys %$hr); - my $field_types = {map(($_ => guess_type_scalar($hr->{$_})), @fields)}; - my @accessors; - if ($hr->can("DAIKON_ACCESSORS")) { - @accessors = $hr->DAIKON_ACCESSORS(); - } - $depth += ACCESSOR_METHOD_DEPTH; - my $acc_types = - {map(($_ => guess_type_scalar(scalar($hr->$_()))), - @accessors)}; - $depth -= ACCESSOR_METHOD_DEPTH; - return ['object', $field_types, $acc_types]; - } - - sub guess_type_scalar { - my($s) = @_; - return ['maybe', 'bottom'] if not defined $s; # nothing - if (ref $s) { - if ($depth > DEPTH_LIMIT) { - # Bottom out by remaining ignorant about what this is - # a reference to. - return ['ref', 'top']; - } else { - my $t; - $depth += SCALAR_REF_DEPTH(); - $t = guess_type_ref($s); - $depth -= SCALAR_REF_DEPTH(); - return $t; - } - } - # The way we recognize numbers is a little tricky, since we're - # trying to reintroduce a distinction that Perl deliberately - # abstracts away. This also works slightly differently than it - # used to, since on further consideration I've decided that a - # float followed by other junk should count as a 'str', not a - # 'num'. This means we can't use the compare-to-zero trick, - # and instead must resort to defining the set of legal numeric - # values as strings with a regex. -SMcC - no warnings 'numeric'; # for $s eq int($s) test - if (not length $s) { - # The empty string is also nothing. - return ['maybe', 'bottom']; - } elsif ($s eq "0" or $s eq "1") { - return 'bit'; - } elsif ($s =~ /^-?\d{1,15}$/) { - # Note this includes numbers that Perl internally - # represents with a double, as long as they're small - # enough to be represented exactly. - # The test "$s eq int($s)" would be equivalent, except for - # the presence of leading zeros. - # The constant 15 is right for IEEE doubles because IEEE - # doubles have 53 bits of mantissa, and 2**53 =~= - # 9*10**15. - return 'int'; - } elsif ($s =~ /^[\d+-.]/) { - # This regex is guarded by the fast one above, since it - # looks ugly. It may not be that bad in practice, though; - # I should benchmark it. - # This might also be an opportune place to sneak a look at - # the internal NOK flag, somehow. That could only be a - # fast yes test, though, not a fast no, since to match the - # expectation of Perl programmers, a value whose string - # representation is a number should count as a number even - # if you haven't done any numeric operations on it: - # consider the case of reading an ASCII float from a file. - if ($s =~ - /^(?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee](?:[+-]?\d+))?$/) { - return 'num'; - } else { - return 'str'; - } - } else { - return 'str'; - } - } -} - -# Guess a type for a list, passed in an array reference. Seeing a -# single value, we can't tell which elements are fixed an which are -# variable, so we just assume that they're all fixed up to a -# predetermined limit. -sub guess_type_list { - my($ar) = @_; - my $fixed_len = min($#$ar + 1, LIST_LIMIT); - my @fixed = map(guess_type_scalar($_), @$ar[0 .. $fixed_len - 1]); - if (@$ar > LIST_LIMIT) { - my $extra = 'bottom'; - for my $i ($fixed_len .. $#$ar) { - $extra = type_lub($extra, guess_type_scalar($ar->[$i])); - } - return ['list', [@fixed], $extra]; - } - return ['list', [@fixed]]; -} - -# Given a string, return a boolean value that's true if the string -# looks like a numeric zero, and false otherwise. -sub is_zero { - my($s) = @_; - return $s =~ /^-?(0+\.?0*|0*\.0+)([eE][-+]?(\d+))?\z$/; -} - -# Read the .types file opened of $fh, and store the types into the -# hash table referenced by $types_r, which should be a nested hash -# indexed first by program point and second by variable name. -# The types file might have multiple entries for a given pair (for instance, it might be the result of -# appending the types for several runs), which we just lub -# together. - -# We also do some lubbing together of type information between places -# that we don't distinguish between while tracing; in particular, -# Daikon expects a variable to always have the same type, so we have -# to cater to that. In the .types file, we keep a separate type for a -# variable at the ENTER program point and at each EXIT program point, -# as well as separately for the scalar and list context versions of a -# sub. For every variable except "return", though, we merge all of -# these together, in a fake program point named -# "...foo():::UNION". For the sake of "return", we also merge all the -# scalar :::EXIT PPTs into a "...foo_s():::EXIT" PPT, and similarly -# for all the list exit points. (Note that this combining isn't -# related to the combining of numbered exit points into a single -# ":::EXIT" program point that Daikon itself does, except that we use -# the same name). -sub read_types_into { - my($fh, $types_r, $order_r) = @_; - my $l; - while ($l = <$fh>) { - my($ppt, $var, $type) = split(' ', $l); - if ($var eq "seen-but-has-no-variables") { - $order_r->{$ppt} = []; - $types_r->{$ppt} = {}; - next; - } - my $parsed = parse_type($type); - if (exists $types_r->{$ppt}{$var}) { - my $t = $types_r->{$ppt}{$var}; - $t = type_lub($t, $parsed); - $types_r->{$ppt}{$var} = $t; - } else { - $types_r->{$ppt}{$var} = $parsed; - push @{$order_r->{$ppt}}, $var if $order_r; - } - my $union_ppt = $ppt; - $union_ppt =~ s/_[sl]\(\)/()/; - $union_ppt =~ s/:::.*$/:::UNION/; - if (not exists $types_r->{$union_ppt}{$var}) { - $types_r->{$union_ppt}{$var} = 'bottom'; - } - $types_r->{$union_ppt}{$var} = type_lub($types_r->{$union_ppt}{$var}, - $types_r->{$ppt}{$var}) - unless $var eq "return"; - if ($ppt =~ /:::EXIT/) { - my $exit_union_ppt = $ppt; - $exit_union_ppt =~ s/:::EXIT(\d+)/:::EXIT/; - if (not exists $types_r->{$exit_union_ppt}{$var}) { - $types_r->{$exit_union_ppt}{$var} = 'bottom'; - } - $types_r->{$exit_union_ppt}{$var} = - type_lub($types_r->{$exit_union_ppt}{$var}, - $types_r->{$ppt}{$var}); - } - } -} - -# Say perl -MDaikon::PerlType -e 'Daikon::PerlType::test_guess()' -# to play with the guessing routines in a read-eval-print-loop way. -sub test_guess { - my $lub = "bottom"; - $|=1; - while (<>) { - if (/^\s*$/) { - $lub = "bottom"; - next; - } - my $type = guess_type_list([eval $_]); - print unparse_type($type); - $lub = type_lub($lub, $type); - print ", lub is ", unparse_type($lub), "\n"; - } -} - -# Say perl -MDaikon::PerlType -e 'Daikon::PerlType::test_parse()' -# to play with the parsing routines in a read-eval-print-loop way. -sub test_parse { - my $lub = "bottom"; - while (<>) { - if (/^\s*$/) { - $lub = "bottom"; - next; - } - my $type = parse_type($_); - $lub = type_lub($lub, $type); - print unparse_type($type), ", lub is ", unparse_type($lub), "\n"; - } -} - -1; diff --git a/front-end/perl/lib/daikon_runtime.pm b/front-end/perl/lib/daikon_runtime.pm deleted file mode 100644 index da835b7481..0000000000 --- a/front-end/perl/lib/daikon_runtime.pm +++ /dev/null @@ -1,392 +0,0 @@ -package daikon_runtime; - -# This is the package of routines that Daikon-annotated Perl code -# uses. - -# This file is part of the Daikon distribution. It may be used and -# redistributed under the same terms as the rest of Daikon or, at your -# option, under the same terms as Perl itself, following either the -# GNU General Public License or the Perl Artistic License. The Daikon -# Perl front end, of which this file is a part, and the Daikon dynamic -# invariant detection tool are separate programs, neither derived from -# the other, which are merely aggregated for convenience of -# distribution. As such, licensing the Perl front end under the terms -# of the GPL neither requires nor entitles you to license other parts -# of the Daikon distribution under the same terms. - -# Among the 5.6 features we use is three-argument open. Note that for -# backwards compatibility it would be silly to say "use 5.6.0", since -# that version syntax wasn't introduced until 5.6. -use 5.006; - -use Exporter; - -use strict; -#use warnings; - -use Carp qw(carp cluck croak confess); -use IO::File; -use File::Path 'mkpath'; # like "mkdir -p" -use File::Basename; - -use vars '@EXPORT_OK'; -use vars '@ISA'; - -@ISA = 'Exporter'; -@EXPORT_OK = qw(trace trace_enter trace_return); - -use Daikon::PerlType qw(LIST_LIMIT type_lub unparse_type - guess_type_ref guess_type_list set_limits); - -use Daikon::Output qw(declare_var output_var); - -# indexed by {package name}. When tracing, holds the filehandle for -# the .dtrace file. When typing, holds a dummy value to remind us to -# output types at the end of the run. -my %filehandles = (); - -# indexed by {package_name}{program point name}{variable name} -my %types; - -# indexed by {package_name}{program point name}, gives an array of the -# variable names in that PPT, in the order in which they will appear -# in the dtrace file. -my %order; - -# indexed by {types|dtrace}{basedir|style|append|combined_fh} -my %output_style; - -# True, if we have types and are ready to make a .dtrace file. -# Defined but false, if we're tracing untyped programs just to get -# their types. -# Initially undefined. -my $tracing = undef; - -sub set_output_style { - my (@styles) = @_; - my $i = 0; - for my $kind ('types', 'dtrace') { - for my $info ('basedir', 'style', 'append') { - if (exists $output_style{$kind}{$info} and - $output_style{$kind}{$info} ne $styles[$i]) { - die "Inconsistent output specification: ". - "conflicting $info for $kind files.\n"; - } - $output_style{$kind}{$info} = $styles[$i]; - $i++; - } - } -} - -sub set_depths { - my @depths = @_; - Daikon::PerlType::set_limits(@depths); -} - -sub set_tracing { - my($arg_tracing) = @_; - if (defined($tracing) and $tracing != $arg_tracing) { - croak "Can't mix type-annotated and non-type-annotated code"; - } else { - $tracing = $arg_tracing; - } -} - -sub program_name { - my $name = $0; - if ($name eq "-e") { - # We aren't yet good at instrumenting -e scripts, but maybe in - # the future... - $name = "minus_e"; - } else { - $name = basename($name, ".plx", ".pl", ".t"); - } - return $name; -} - -# Open (if it isn't already open) the file we'll use to output KIND -# data (where KIND is types, decls or dtrace) generated for the -# package PACKAGE. Returns a file handle to use, and a boolean value -# that tells if the caller should close the file when done writing, or -# leave it open for later. -sub open_for_output { - my($package, $kind) = @_; - my $dir = $output_style{$kind}{basedir}; - if ($package eq "main") { - $package = program_name() . "-main"; - } - my $fname = "$package.$kind"; - my $op; - if ($output_style{$kind}{append} or $ENV{uc($kind)."APPEND"}) { - $op = ">>"; - } else { - $op = ">"; - } - my $fh; - my $close_when_done = 1; - my $combined = 0; - if ($output_style{$kind}{style} eq "tree") { - if ($package =~ /::/) { - (my $asdir = $package) =~ s[::][/]g; - $dir .= "/" . dirname($asdir); - $fname = basename($asdir) . ".$kind"; - } - } elsif ($output_style{$kind}{style} eq "combined") { - $fname = program_name() . "-combined.$kind"; - $combined = 1; - } - my $path = "$dir/$fname"; - if ($ENV{uc($kind)."FILE"}) { - $path = $ENV{uc($kind)."FILE"}; - $combined = 1; - } - if ($combined and exists $output_style{$kind}{combined_fh}) { - $fh = $output_style{$kind}{combined_fh}; - $close_when_done = 0; - } else { - if (not -d $dir) { - mkpath($dir) or die "Couldn't create $dir: $!\n"; - } - $fh = new IO::File; - open($fh, $op, $path) or carp "Can't create $fname: $!"; - if ($combined) { - $output_style{$kind}{combined_fh} = $fh; - $close_when_done = 0; - } - } - return ($fh, $close_when_done); -} - -# Start tracing the subroutines in the given package. It's no longer -# necessary to call this explicitly, since the trace routines call it -# when they're first invoked in a given package. -sub begin_trace { - my($package) = @_; - if (exists $filehandles{$package}) { - carp "Already tracing package $package\n"; - return; - } - if ($tracing) { - my($fh, $close_when_done) = open_for_output($package, "dtrace"); - $filehandles{$package} = $fh; - } else { - $filehandles{$package} = 1; - } -} - -# Finish tracing the subroutines in the given package. This is called -# automatically from an END { } block. We wait until we're done to -# output the .decls file, and any type information we might have -# guessed. (For the type information, we have to wait because our -# guesses might need to be refined as we get more data. For the .decls -# file, we have to wait because we're being lazy and only generating -# declarations when we see a corresponding execution.) -sub end_trace { - my($package) = @_; -# warn "Finishing $package"; - if ($tracing and not exists $output_style{dtrace}{combined_fh}) { - close $filehandles{$package}; - } - delete $filehandles{$package}; - - if (!$tracing and keys %{$types{$package}}) { - my ($fh, $close_when_done) = open_for_output($package, "types"); - for my $ppt (keys %{$types{$package}}) { - my @vars = @{$order{$package}{$ppt}}; - if (@vars == 0) { - print $fh "$ppt seen-but-has-no-variables\n"; - } # else - for my $var (@vars) { - print $fh "$ppt $var "; - print $fh unparse_type($types{$package}{$ppt}{$var}); - print $fh "\n"; - } - } - close $fh if $close_when_done; - } -} - -# Close out tracing and output .decls files for all the packages we -# started tracing. -sub end_trace_all { - for my $p (keys %filehandles) { - end_trace($p); - } - if (exists $output_style{types}{combined_fh}) { - close $output_style{types}{combined_fh}; - delete $output_style{types}{combined_fh}; - } - if (exists $output_style{dtrace}{combined_fh}) { - close $output_style{dtrace}{combined_fh}; - delete $output_style{dtrace}{combined_fh}; - } -} - -# At the end of regular execution, finish any unfinished tracing. -END { end_trace_all(); } - -# This is a little bit strange. We'd like to have a flag that is set -# to 1 inside the tracing routines, and tells re-entrant invocations -# of them not to do anything, to avoid confusing the -# output. (Reentrant calls to trace occur for instance if a value -# we're tracing is actually an overloaded object with a stringify or -# numify method that we're also tracing. They don't represent a real -# part of the execution we're trying to capture, so we'd like to -# ignore them). I'd like to use Perl's local() operator to set a flag -# to 1 in such a way that it will always be reset to zero when I leave -# the tracing routine, even by some exception or other non-local exit. -# Unfortunately, for reasons of avoiding confusing beginning -# programmers and of implementation simplification, Perl doesn't let -# you use local() on a my() variable. It does, however, let you use -# local() an element of any array, my() or not. Therefore I use the -# 0th element of the @trace_mutex array as if it were a variable -# $trace_mutex for this purpose. -my @trace_mutex = (0); - -# The main routine to handle the tracing at a single program -# point. The name argument is something like "ENTER" or "EXIT42", and -# @vars is a list of variable specifications like ['$x', \$x, 'int'] -# (variable name, reference to value, type). -sub trace_name { - my($name, @vars) = @_; - if ($trace_mutex[0]) { - if ($ENV{"DAIKON_PERL_DEBUG_REENTRANT"}) { - $trace_mutex[0] = 0; # Prevent looping during the backtrace - cluck "Whoa! Re-entering trace!\n"; - $SIG{__DIE__} = 0; - use POSIX; - POSIX::_exit(0); # Really die, even in an eval - } else { - return; - } - } - local($trace_mutex[0]) = 1; - # These calls to caller() are the way we extract information about - # where the trace routine was called, to save the caller from - # passing it explicitly. caller(i) gives you information about the - # invocation i-levels up the call stack, but the return convention - # is a bit tricky. For a given level, $package and $line (really - # the 0th and 2th elements) are the package and line number of the - # caller of that subroutine, while $subname and $wantlist (3th and - # 5th) are the name and the scalar/list context of the subroutine - # itself. Thus we need two calls. Also we have to be careful that - # trace_name is always the same number of levels down the stack - # from the code whenever it's called. For the values below, this - # works if the routine being traced calls foo() and foo() calls - # trace_name(). - my(undef, undef, undef, $subname, undef, $wantlist) = caller(2); - my($package, undef, $line, undef, undef, undef) = caller(1); - $subname =~ s/.*:://; # Remove leading package qualifiers - if ($wantlist) { - $subname .= "_l"; - } else { - $subname .= "_s"; - } -# my $javaish_package = $package; -# $javaish_package =~ s/::/./g; - - # The parens in the next line are important to get some tools - # (e.g. the tree GUI) to work right. I guess otherwise they can't - # tell that this is a subroutine. - my $ppt_name = "${package}.${subname}():::$name"; - if (not exists $filehandles{$package}) { - begin_trace($package); - } - if ($tracing) { - my $fh = $filehandles{$package}; - print $fh "$ppt_name\n"; - for my $v (@vars) { - # Output a real value - if ($v->[2] eq "unknown") { - cluck("Found an unknown type while tracing!"); - } - output_var($fh, @$v); - } - print $fh "\n"; - } else { - # Do type inference rather than data collection this pass - if (not exists $types{$package}{$ppt_name}) { - # Make sure these exist, even if they're empty, to record - # the fact that we saw this PPT. - $types{$package}{$ppt_name} = {}; - $order{$package}{$ppt_name} = []; - for my $v (@vars) { - confess "Bad variable format" unless ref($v) eq "ARRAY"; - if (not exists $types{$package}{$ppt_name}{$v->[0]}) { - # Every variable starts out at the bottom type, since - # that's the identity for LUB. - $types{$package}{$ppt_name}{$v->[0]} = 'bottom'; - push @{$order{$package}{$ppt_name}}, $v->[0]; - } - } - } - for my $v (@vars) { - croak "Found a type in an untyped file" - if $v->[2] ne "unknown"; - # Type guessing - my $t = $types{$package}{$ppt_name}{$v->[0]}; - if ($v->[0] eq "return" and $ppt_name =~ /_l\(\):::EXIT/) { - # List return types are the only place lists appear - $t = type_lub($t, guess_type_list($v->[1])); - } else { - # Other values are just single. - my $this_type = guess_type_ref($v->[1])->[1]; - $t = type_lub($t, $this_type); - } - $types{$package}{$ppt_name}{$v->[0]} = $t; - } - } -} - -# Trace the entrance to a subroutine. The arguments are a list of -# variable specs. -sub trace_enter { trace_name("ENTER", @_); } - -# Trace the return from a subroutine. This is more complicated than -# entering because we need to distinguish scalar from list context, -# and different exit points from one another. We assume that -# "return(FOO)" will get rewritten into something like - -# return trace_return((wantarray()?[FOO]:\scalar(FOO)),'int','(int),42,...) - -# where FOO returns an int in scalar context and a list containing a -# single int in list ("array") context. Recall that there's no way to -# derive the list context return value from the scalar context value -# or vice versa, so we have to have code paths that capture both, but -# the FOO expression might also have side effects, so we only want -# to capture one value for each call to the subroutine. The scalar and -# list types are in the usual unparsed format; they may not always be -# as similar as they are the example above. "42" is a sequence number -# chosen by the annotator to be differed from all other returns in the -# same subroutine; otherwise it's arbitrary. Any other arguments are -# additional variables to trace the value of, as with the other trace -# routines. - -my $include_exit_points = 0; - -sub trace_return { - my($ret_val_ref, $scalar_type, $list_type, $seq, @vars) = @_; - my $point_type = "int"; - $point_type = "unknown" - if $scalar_type eq "unknown" and $list_type eq "unknown"; - my @ep = (); - @ep = (['daikonEXITpoint', \$seq, $point_type]) - if $include_exit_points; - if ((caller(1))[5]) { - # list context - trace_name("EXIT$seq", @vars, - ['return', $ret_val_ref, $list_type], @ep); - return @$ret_val_ref; - } else { - # scalar (or void) context - trace_name("EXIT$seq", @vars, - ['return', $ret_val_ref, $scalar_type], @ep); - return $$ret_val_ref; - } -} - -# This would be the routine to trace at points other than entrance and -# exit, but that isn't supported at the moment. -# sub trace { trace_name("LINE", @_); } - -1; diff --git a/scripts/dfepl b/scripts/dfepl deleted file mode 100755 index 2cceeba88b..0000000000 --- a/scripts/dfepl +++ /dev/null @@ -1,439 +0,0 @@ -#!/usr/bin/env perl - -# Convenient interface to the Daikon annotation front-end for Perl programs -# $Id$ - -# This file is part of the Daikon distribution. It may be used and -# redistributed under the same terms as the rest of Daikon or, at your -# option, under the same terms as Perl itself, following either the -# GNU General Public License or the Perl Artistic License. The Daikon -# Perl front end, of which this file is a part, and the Daikon dynamic -# invariant detection tool are separate programs, neither derived from -# the other, which are merely aggregated for convenience of -# distribution. As such, licensing the Perl front end under the terms -# of the GPL neither requires nor entitles you to license other parts -# of the Daikon distribution under the same terms. - -use strict; -use 5.006; -use warnings; -BEGIN { - require English; - if ($^V ge 5.8.0) { - English->import("-no_match_vars"); # avoid speed penalty - } else { - English->import(); - } -} - -use Getopt::Long (); -#use Carp ('carp', 'croak', 'cluck', 'confess'); -use File::Path 'mkpath'; -use File::Basename; -use File::Spec::Functions 'rel2abs'; -use File::Find; -use Cwd 'getcwd'; - -my $verbose = 0; -my $perl = $EXECUTABLE_NAME; - -my $use_types = 0; -my $use_accessors = 0; -my @types_files = (); -my $types_search = undef; - -my $accessors_search = undef; - -my $instr_basedir = undef; # defaults to daikon-{untyped,instrumented} - # depending on whether we have type information. -my $types_basedir = undef; # default to daikon-instrumented - -my $decls_basedir = undef; -my $dtrace_basedir = undef; -my $output_dir = undef; - -my $types_style = undef; # defaults to tree -my $decls_style = undef; # now always matches types_style -my $dtrace_style = "combined"; - -my $types_append = 1; -my $dtrace_append = 0; - -my $absolute = undef; -my $instr_absolute = undef; -my $types_absolute = undef; -my $decls_absolute = undef; -my $dtrace_absolute = undef; - -my $reference_depth = 3; -my $accessor_depth = 1.5; -my $list_depth = 3; - -my $do_usage = 0; - -sub careful_system { - my @cmd = @_; - die unless @cmd > 1; # Avoid the shell - if ($verbose) { - my @nice_args = @cmd; - # So that our output looks like make or shell script output, - # do an approximation to shell quoting on the verbose - # output. Note that because we always use the multi-arg form - # of system, we don't have to worry about shell-escaping the - # actual args we pass to the command, which is convenient. - for my $arg (@nice_args) { - $arg = "'$arg'" if $arg =~ / /; - } - print join(" ", @nice_args), "\n"; - } - my $result = system(@cmd); - if ($result != 0) { - die "External command failed: " . join(" ", @cmd); - } - return $result; -} - -sub file_id { - my($fname) = @_; - my($dev, $inode) = stat($fname) or die "Couldn't stat $fname: $!\n"; - return "$dev:$inode"; -} - -my @found_modules = (); - -sub is_module { - my ($dev,$ino,$mode,$nlink,$uid,$gid); - - if ((($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) && - -f _ && /^.*\.pm\z/s) { - push @found_modules, $File::Find::name; - print "Found module $File::Find::name\n" if $verbose; - return 1; - } -} - -use File::Basename; -my $dirname = dirname(__FILE__); - -my $opt_parse = new Getopt::Long::Parser; -$opt_parse->configure("bundling_override"); -my $parsed = - # Some deprecated (outdated) option names are still supported. - $opt_parse->getoptions("instrsourcedir|instr-basedir|instr-dir=s" - => \$instr_basedir, - "types-basedir=s" => \$types_basedir, - "decls-basedir|decls-dir=s" => \$decls_basedir, - "dtrace-basedir|dtrace_dir=s" => \$dtrace_basedir, - "output-dir=s", => \$output_dir, -# "instr-style=s" => \$instr_style, - "types-style=s" => \$types_style, - "decls-style=s" => \$decls_style, - "dtrace-style=s" => \$dtrace_style, - "types-append!" => \$types_append, - "dtrace-append!" => \$dtrace_append, - "use-types|T" => \$use_types, - "types-file|t=s" => \@types_files, - "types-dir:s" => \$types_search, - "use-accessors|A" => \$use_accessors, - "accessors-dir:s" => \$accessors_search, - "verbose|v+" => \$verbose, - "absolute!" => \$absolute, - "reference-depth|nesting-depth=f", - => \$reference_depth, - "accessor-depth=f", => \$accessor_depth, - "list-depth=n", => \$list_depth, -# This is probably more control than anyone needs -# "types-absolute!" => \$types_absolute, -# "decls-absolute!" => \$decls_absolute, -# "dtrace-absolute!" => \$dtrace_absolute, - "perl=s", \$perl, - "help|h" => \$do_usage, - ); - -exit(1) unless $parsed; - -if ($do_usage) { - print < \&is_module}, $arg); - for my $file (@found_modules) { - process_file_relative_to($file, $arg); - } - @found_modules = (); - } -} diff --git a/scripts/dtrace-perl b/scripts/dtrace-perl deleted file mode 100755 index 956ff5c067..0000000000 --- a/scripts/dtrace-perl +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env perl - -# Convenient interface to running Perl on Daikon-annotated perl programs - -# $Id$ - -use strict; -use 5.006; -use warnings; -BEGIN { - require English; - if ($^V ge 5.8.0) { - English->import("-no_match_vars"); # avoid speed penalty - } else { - English->import(); - } -} - -use File::Basename; -my $dirname = dirname(__FILE__); - -my @locs = ('daikon-instrumented', 'lib/daikon-instrumented', - 'blib/lib/daikon-instrumented', '../blib/lib/daikon-instrumented'); - -my $perl = $EXECUTABLE_NAME; -my $perl_lib = "-I$dirname/front-end/perl/lib"; - -my @args = ($perl_lib, @ARGV); - -for my $loc (@locs) { - if (-e $loc) { - unshift @args, "-I$loc"; - last; - } -} - -unshift @args, $perl; - -print STDERR join(" ", @args), "\n"; - -my $result = system(@args); - -exit $result; diff --git a/scripts/dtype-perl b/scripts/dtype-perl deleted file mode 100755 index 61c1e3d898..0000000000 --- a/scripts/dtype-perl +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env perl - -# Convenient interface to running Perl on Daikon-annotated perl -# programs that don't yet have types. - -# This is currently the same as "dtrace-perl", except that it looks in -# different directories for annotated .pm files. - -# $Id$ - -use strict; -use 5.006; -use warnings; -BEGIN { - require English; - if ($^V ge 5.8.0) { - English->import("-no_match_vars"); # avoid speed penalty - } else { - English->import(); - } -} - -use File::Basename; -my $dirname = dirname(__FILE__); - -my @locs = ('daikon-untyped', 'lib/daikon-untyped', - 'blib/lib/daikon-untyped', '../blib/lib/daikon-untyped'); - -my $perl = $EXECUTABLE_NAME; -my $perl_lib = "-I$dirname/front-end/perl/lib"; - -my @args = ($perl_lib, @ARGV); - -for my $loc (@locs) { - if (-e $loc) { - unshift @args, "-I$loc"; - last; - } -} - -unshift @args, $perl; - -print STDERR join(" ", @args), "\n"; - -my $result = system(@args); - -exit $result; From b69c8fe57aca43f46ddf34304aa50f2f0de76d02 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Tue, 6 Oct 2026 17:52:29 -0700 Subject: [PATCH 2/5] Remove remaining references to the Perl front end --- Makefile | 2 -- doc/Makefile | 3 --- doc/daikon.texinfo | 2 +- doc/decl_format.txt | 4 ++-- doc/www/download/index.html | 4 ++-- doc/www/index.html | 2 +- 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 84f74e4a33..1be74d6498 100644 --- a/Makefile +++ b/Makefile @@ -681,8 +681,6 @@ daikon.tar daikon.zip: kvasir ${README_PATHS} ${DAIKON_JAVA_FILES} java/Makefile # Don't do ${MAKE} clean which deletes .class files (cd ${TMPDIR}/daikon/java; ${RM_TEMP_FILES}) - ## Front ends - # Kvasir C front end # We use the --filter option twice with rsync to exclude unneeded files. # The first attempts to ignore all files indicated by the contents diff --git a/doc/Makefile b/doc/Makefile index ec3d0f7808..1b61d58fcc 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -40,9 +40,6 @@ invariants-doc.texinfo: .PHONY : invariants-doc.texinfo endif -images/%.eps: images/%.dot - dot -Tps $< >$@ - images/%.pdf: images/%.eps epstopdf $< --outfile=$@ diff --git a/doc/daikon.texinfo b/doc/daikon.texinfo index 4e3fb43cdd..d9af4ef530 100644 --- a/doc/daikon.texinfo +++ b/doc/daikon.texinfo @@ -8616,7 +8616,7 @@ of, or in addition to, referencing this manual and the Daikon website @c LocalWords: CompletionChar PathCompletionChar subsubsection MYDOCU ENV arg @c LocalWords: DataStructures dotfiles ActiveState mypackage MyClass emph Xmx @c LocalWords: StackArTester PrintInvariants theArray topOfStack Ljava gdwarf -@c LocalWords: kvasir decls Valgrind config Rijndael rijndaeltest perl bday +@c LocalWords: kvasir decls Valgrind config Rijndael rijndaeltest perl @c LocalWords: dtype standalone decl asis noversion num conf nohierarchy ppt @c LocalWords: dbg mem stat postprocessed itemx mux pre val classname regexp @c LocalWords: ListImplementors dataflow getLogger JML jml hashcode DBC Jtest diff --git a/doc/decl_format.txt b/doc/decl_format.txt index fa515c052b..13b878b41f 100644 --- a/doc/decl_format.txt +++ b/doc/decl_format.txt @@ -498,7 +498,7 @@ File format - Input language record. Specifies the language of the target program. - input-language |C|C++|Perl|Java + input-language |C|C++|Java - The type of comparability used in the file @@ -879,7 +879,7 @@ Resolved Issues: other languages. For instance, as mentioned above it would be nice to use "*" for dereferences in C (though we don't at the moment). In Perl, there's a class of variables whose names start - with "*" (though dfepl doesn't support them at the moment) and + with "*" and there's also a global variable for just about every punctuation character, including $*. Perhaps a multi-character marker would be more robust? (smcc) diff --git a/doc/www/download/index.html b/doc/www/download/index.html index 0b7c269048..20d7c42348 100644 --- a/doc/www/download/index.html +++ b/doc/www/download/index.html @@ -51,9 +51,9 @@

Daikon invariant detector distribution

  • Front ends
    A front end enables Daikon to - work on a specific programming language, such as Java, C/C++, C#, Eiffel, or Perl. + work on a specific programming language, such as Java, C/C++, C#, or Eiffel. The Daikon distribution contains front ends that are sufficient for - most people's purposes (including front ends for Java, C, C++, Perl, + most people's purposes (including front ends for Java, C, C++, and other languages). Here we provide additional downloads for unusual circumstances.
      diff --git a/doc/www/index.html b/doc/www/index.html index e4c41bc307..cef73641ad 100644 --- a/doc/www/index.html +++ b/doc/www/index.html @@ -48,7 +48,7 @@

      The Daikon invariant detector

      Dynamic invariant detection runs a program, observes the values that the program computes, and then reports properties that were true over the observed executions. -Daikon can detect properties in C, C++, C#, Eiffel, F#, Java, Perl, and +Daikon can detect properties in C, C++, C#, Eiffel, F#, Java, and Visual Basic programs; in spreadsheet files; and in other data sources. (Dynamic invariant detection is a machine learning technique that can be applied to From e708723599823ca6af6997396d63fb76076455a5 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Tue, 6 Oct 2026 18:09:18 -0700 Subject: [PATCH 3/5] Remove graphviz, netpbm, and eps2png-mono-alpha.zsh; documentation fixes Graphviz and netpbm were used only to build the dfepl-flow image. Also restore an attributed historical note, format the changelog entry as a bullet, and clarify that the input-language string is arbitrary. --- .github/workflows/checkbashisms.yaml | 2 - doc/CHANGELOG.md | 2 +- doc/daikon.texinfo | 6 +-- doc/decl_format.txt | 7 ++-- doc/developer.texinfo | 2 +- prek.toml | 1 - scripts/Dockerfile-rockylinux-jdk21-plus | 3 -- scripts/Dockerfile-rockylinux-jdk25-plus | 3 -- scripts/Dockerfile-rockylinux-jdk27-plus | 3 -- scripts/Dockerfile-rockylinux.m4 | 3 -- scripts/Dockerfile-ubuntu-jdk11-plus | 2 - scripts/Dockerfile-ubuntu-jdk17-plus | 2 - scripts/Dockerfile-ubuntu-jdk21-plus | 2 - scripts/Dockerfile-ubuntu-jdk25-plus | 2 - scripts/Dockerfile-ubuntu-jdk27-plus | 2 - scripts/Dockerfile-ubuntu.m4 | 2 - scripts/eps2png-mono-alpha.zsh | 48 ------------------------ 17 files changed, 9 insertions(+), 83 deletions(-) delete mode 100755 scripts/eps2png-mono-alpha.zsh diff --git a/.github/workflows/checkbashisms.yaml b/.github/workflows/checkbashisms.yaml index e75debcb82..3467ec017a 100644 --- a/.github/workflows/checkbashisms.yaml +++ b/.github/workflows/checkbashisms.yaml @@ -26,8 +26,6 @@ jobs: sh_checker_shellcheck_disable: true # Space-separated list of regexes matched against each file name. # povray is third-party code that is not maintained here. - # checkbashisms cannot check zsh scripts. sh_checker_exclude: >- gradlew tests/kvasir-tests/povray/ - scripts/eps2png-mono-alpha[.]zsh diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 7e08adc777..a52e9bf8d3 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -23,7 +23,7 @@ Further documentation can be found in: ## Version ?? -Removed the Perl front end, dfepl. +* Removed the Perl front end, dfepl. ## Version 5.9.0 (September 1, 2026) diff --git a/doc/daikon.texinfo b/doc/daikon.texinfo index d9af4ef530..4f69de917a 100644 --- a/doc/daikon.texinfo +++ b/doc/daikon.texinfo @@ -338,7 +338,7 @@ the Simplify theorem prover (@pxref{Installing Simplify}). @c export DAIKONDIR=$(cd daikon-5.9.1 && pwd) @c source $@{DAIKONDIR@}/scripts/daikon.bashrc @c -@c apt -qqy install openjdk-11-jdk gcc ctags git graphviz make netpbm \ +@c apt -qqy install openjdk-11-jdk gcc ctags git make \ @c texlive texlive-generic-recommended texinfo \ @c autoconf automake binutils-dev m4 zlib1g-dev @c @@ -8719,8 +8719,8 @@ of, or in addition to, referencing this manual and the Daikon website @c LocalWords: dimensionality consequents UNRETURNED getter globals pwd @c LocalWords: typedefs celeriacArg Inserter comparator roundoff bitwise @c LocalWords: Kellen Florian Laure Raytheon pathname customizable ctags -@c LocalWords: myclasspath runtime Year2 typedef ubuntu openjdk graphviz -@c LocalWords: netpbm texlive autoconf automake m4 zlib1g fno ClassFile +@c LocalWords: myclasspath runtime Year2 typedef ubuntu openjdk +@c LocalWords: texlive autoconf automake m4 zlib1g fno ClassFile @c LocalWords: example1c Ldaikon sayHello FunctionalInterface valgrind @c LocalWords: Bourne accessors subparts MERCHANTABILITY merchantability @c LocalWords: uniq Cemal Akcaba Marinov McArthur Rayside Pickard Thaine diff --git a/doc/decl_format.txt b/doc/decl_format.txt index 13b878b41f..2fbe3df4cf 100644 --- a/doc/decl_format.txt +++ b/doc/decl_format.txt @@ -496,9 +496,10 @@ File format are: - Input language record. Specifies the language of the target - program. + program. The language string is arbitrary (for example, C, C++, + C#, or Java) and is not used by Daikon. - input-language |C|C++|Java + input-language - The type of comparability used in the file @@ -879,7 +880,7 @@ Resolved Issues: other languages. For instance, as mentioned above it would be nice to use "*" for dereferences in C (though we don't at the moment). In Perl, there's a class of variables whose names start - with "*" and + with "*" (though dfepl doesn't support them at the moment) and there's also a global variable for just about every punctuation character, including $*. Perhaps a multi-character marker would be more robust? (smcc) diff --git a/doc/developer.texinfo b/doc/developer.texinfo index ccc23e50f2..3bbc25a5ec 100644 --- a/doc/developer.texinfo +++ b/doc/developer.texinfo @@ -4308,7 +4308,7 @@ detection,,,daikon,Daikon User Manual}. @c LocalWords: paragraphindent firstparagraphindent codequotebacktick v1 @c LocalWords: ifclear evenheading thispage thischaptername oddheading @c LocalWords: thischapternum thissectionnum thissectionname sudo jdk v2 -@c LocalWords: everyfooting exampleindent openjdk ctags graphviz netpbm +@c LocalWords: everyfooting exampleindent openjdk ctags @c LocalWords: texlive Makefile automake autoconf binutils dev dfn AMD64 @c LocalWords: UnaryInvariant BinaryInvariant TernaryInvariant EM64T www @c LocalWords: runtime var1 PptSlice1 over1 config EXIT99 var2 var3 val1 diff --git a/prek.toml b/prek.toml index 79e0a69064..580b26f8b2 100644 --- a/prek.toml +++ b/prek.toml @@ -223,7 +223,6 @@ hooks = [ "SCRIPTDIR", ], exclude = { glob = [ "cronic-orig", - "scripts/eps2png-mono-alpha.zsh", "share/csail-athena-tickets.bash", "tests/kvasir-tests/**/*", ] } }, diff --git a/scripts/Dockerfile-rockylinux-jdk21-plus b/scripts/Dockerfile-rockylinux-jdk21-plus index 965a4089d3..56734b0be9 100644 --- a/scripts/Dockerfile-rockylinux-jdk21-plus +++ b/scripts/Dockerfile-rockylinux-jdk21-plus @@ -53,9 +53,6 @@ RUN dnf -q -y upgrade && dnf -q -y install \ ctags \ devscripts-checkbashisms \ gcc-c++ \ - graphviz \ - netpbm \ - netpbm-progs \ python3 \ python3-distutils-extra \ ShellCheck \ diff --git a/scripts/Dockerfile-rockylinux-jdk25-plus b/scripts/Dockerfile-rockylinux-jdk25-plus index 84aaf433af..e6d38f4668 100644 --- a/scripts/Dockerfile-rockylinux-jdk25-plus +++ b/scripts/Dockerfile-rockylinux-jdk25-plus @@ -54,9 +54,6 @@ RUN dnf -q -y upgrade && dnf -q -y install \ ctags \ devscripts-checkbashisms \ gcc-c++ \ - graphviz \ - netpbm \ - netpbm-progs \ python3 \ python3-distutils-extra \ ShellCheck \ diff --git a/scripts/Dockerfile-rockylinux-jdk27-plus b/scripts/Dockerfile-rockylinux-jdk27-plus index 6be3958981..b2987a34f0 100644 --- a/scripts/Dockerfile-rockylinux-jdk27-plus +++ b/scripts/Dockerfile-rockylinux-jdk27-plus @@ -58,9 +58,6 @@ RUN dnf -q -y upgrade && dnf -q -y install \ ctags \ devscripts-checkbashisms \ gcc-c++ \ - graphviz \ - netpbm \ - netpbm-progs \ python3 \ python3-distutils-extra \ ShellCheck \ diff --git a/scripts/Dockerfile-rockylinux.m4 b/scripts/Dockerfile-rockylinux.m4 index 07180ff6b4..82efb31cc5 100644 --- a/scripts/Dockerfile-rockylinux.m4 +++ b/scripts/Dockerfile-rockylinux.m4 @@ -58,9 +58,6 @@ RUN dnf -q -y upgrade && dnf -q -y install \ ctags \ devscripts-checkbashisms \ gcc-c++ \ - graphviz \ - netpbm \ - netpbm-progs \ python3 \ python3-distutils-extra \ ShellCheck \ diff --git a/scripts/Dockerfile-ubuntu-jdk11-plus b/scripts/Dockerfile-ubuntu-jdk11-plus index 0a5b8c4b39..648a5e9d55 100644 --- a/scripts/Dockerfile-ubuntu-jdk11-plus +++ b/scripts/Dockerfile-ubuntu-jdk11-plus @@ -68,9 +68,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy install \ devscripts \ g++ \ - graphviz \ libc6-dbg \ - netpbm \ python3-setuptools \ shellcheck \ shfmt \ diff --git a/scripts/Dockerfile-ubuntu-jdk17-plus b/scripts/Dockerfile-ubuntu-jdk17-plus index fb035f9caa..2c1d65c63a 100644 --- a/scripts/Dockerfile-ubuntu-jdk17-plus +++ b/scripts/Dockerfile-ubuntu-jdk17-plus @@ -68,9 +68,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy install \ devscripts \ g++ \ - graphviz \ libc6-dbg \ - netpbm \ python3-setuptools \ shellcheck \ shfmt \ diff --git a/scripts/Dockerfile-ubuntu-jdk21-plus b/scripts/Dockerfile-ubuntu-jdk21-plus index 4c943bec7e..a997466554 100644 --- a/scripts/Dockerfile-ubuntu-jdk21-plus +++ b/scripts/Dockerfile-ubuntu-jdk21-plus @@ -68,9 +68,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy install \ devscripts \ g++ \ - graphviz \ libc6-dbg \ - netpbm \ python3-setuptools \ shellcheck \ shfmt \ diff --git a/scripts/Dockerfile-ubuntu-jdk25-plus b/scripts/Dockerfile-ubuntu-jdk25-plus index 8ef974758f..6254e5e048 100644 --- a/scripts/Dockerfile-ubuntu-jdk25-plus +++ b/scripts/Dockerfile-ubuntu-jdk25-plus @@ -69,9 +69,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy install \ devscripts \ g++ \ - graphviz \ libc6-dbg \ - netpbm \ python3-setuptools \ shellcheck \ shfmt \ diff --git a/scripts/Dockerfile-ubuntu-jdk27-plus b/scripts/Dockerfile-ubuntu-jdk27-plus index 64260e8820..e9d7ec91d0 100644 --- a/scripts/Dockerfile-ubuntu-jdk27-plus +++ b/scripts/Dockerfile-ubuntu-jdk27-plus @@ -69,9 +69,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy install \ devscripts \ g++ \ - graphviz \ libc6-dbg \ - netpbm \ python3-setuptools \ shellcheck \ shfmt \ diff --git a/scripts/Dockerfile-ubuntu.m4 b/scripts/Dockerfile-ubuntu.m4 index 806d6c7bc1..b0e0b2d2b5 100644 --- a/scripts/Dockerfile-ubuntu.m4 +++ b/scripts/Dockerfile-ubuntu.m4 @@ -71,9 +71,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get -qqy install \ devscripts \ g++ \ - graphviz \ libc6-dbg \ - netpbm \ python3-setuptools \ shellcheck \ shfmt \ diff --git a/scripts/eps2png-mono-alpha.zsh b/scripts/eps2png-mono-alpha.zsh deleted file mode 100755 index 100772117b..0000000000 --- a/scripts/eps2png-mono-alpha.zsh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env zsh -# Old shebang line: #!/bin/zsh -f - -# Convert a black on white Postscript image (as produced by dot) into -# a black-on-transparent PNG of the specified width, with height -# computed to preserve the aspect ratio. We do this by rendering the -# .eps file into a bitmap 4 times larger (in each dimension) than the -# desired output, shrinking it by a factor of 4 to get an anti-aliased -# grayscale image, and then using that as an alpha mask along with an -# all-black image of the same size to make a black-on-transparent -# result. Also, we use Perl to overwrite the useless "number of -# significant bits" chunk that pnmtopng writes with a chunk that -# specifies that the background color should be white. This is for the -# benefit of Netscape 4.x, which can't handle true transparency and -# would otherwise render the image as black on black background. - -psfile=$1 -width=$2 -temp=/tmp/eps2png$$.pgm -# Default to whatever's on our path -pnmtopng=`which pnmtopng` -pnmtopng238=`which pnmtopng-2.38` -if [ -x /g6/users/smcc/bin/pnmtopng ]; then - # We need at least version 2.38, not 2.37.5 as in Red Hat. - pnmtopng=/g6/users/smcc/bin/pnmtopng -elif [ -n "$pnmtopng238" ]; then - # This is what we've called it on our Debian boxes, which as of - # now also have a broken version in /usr/bin (Debian bug #274907) - pnmtopng=$pnmtopng238 -fi -# echo Using $pnmtopng - -if [ ! -x $pnmtopng ]; then - echo "Can't find pnmtopng!" >&2 - exit 1; -fi - -pstopnm -portrait -stdout -xsize $[4 * $width] $psfile \ - | pnmcrop \ - | pnmscale 0.25 \ - | pnmmargin -white 20 \ - | ppmtopgm >$temp && \ -$pnmtopng -alpha <(pnminvert $temp) \ - <(pbmmake -black $(pnmfile $temp \ - | perl -ne 'print "$1 $2" if /(\d+) by (\d+)/')) \ - | perl -0777 -pe 's/sBIT\x1\cH\204\.\375M/bKGD\0\377\207\217\314\277/' \ - >${psfile%.eps}.png && \ -rm $temp From 9d8aee1db725df479a636162c70861ed2216c2da Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Tue, 6 Oct 2026 18:14:12 -0700 Subject: [PATCH 4/5] Address code review findings: input-language token, CHANGELOG header, dead Perl references and spellcheck words --- Makefile | 12 +----------- doc/CHANGELOG.md | 2 +- doc/daikon.texinfo | 14 +++++++------- doc/decl_format.txt | 10 +++++----- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index 1be74d6498..b7f00dd9a2 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ README_PATHS := README doc/README fjalar/README DIST_VERSION_FILES := ${README_PATHS} doc/daikon.texinfo doc/developer.texinfo \ doc/index.html doc/www/download/index.html -# Scripts, such as Perl programs, that are included in the Daikon distribution. +# Scripts that are included in the Daikon distribution. # Why not just include all of them? (Maybe to avoid problems with # accidentally including things in the user's checkout that are not needed # by most users, but why not include everything that's in repository?) @@ -141,16 +141,6 @@ endif # For deterministic sorting export LC_ALL=C -## Examples of better ways to get the lists: -# PERL_MODULES := $(wildcard *.pm) -# PERL_SCRIPTS := $(wildcard *.pl) -# PERL_SCRIPTS += em_analyze em_reports cppp -# PERL_MODULE_TEXI := $(patsubst %.pm,%.texi,${PERL_MODULES}) -# PERL_MODULE_INFO := $(patsubst %.pm,%.info,${PERL_MODULES}) -# PERL_MODULE_MAN := $(patsubst %.pm,%.man,${PERL_MODULES}) -# PERL_MODULE_HTML := $(patsubst %.pm,%.html,${PERL_MODULES}) - - ########################################################################### ### Rules ### diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index a52e9bf8d3..7e4e71f09f 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -21,7 +21,7 @@ Further documentation can be found in: `make -C $DAIKONDIR/java javadoc` It is also available at . -## Version ?? +## Version 5.9.1 (???, 2026) * Removed the Perl front end, dfepl. diff --git a/doc/daikon.texinfo b/doc/daikon.texinfo index 4f69de917a..ac2db2aa4c 100644 --- a/doc/daikon.texinfo +++ b/doc/daikon.texinfo @@ -8617,7 +8617,7 @@ of, or in addition to, referencing this manual and the Daikon website @c LocalWords: DataStructures dotfiles ActiveState mypackage MyClass emph Xmx @c LocalWords: StackArTester PrintInvariants theArray topOfStack Ljava gdwarf @c LocalWords: kvasir decls Valgrind config Rijndael rijndaeltest perl -@c LocalWords: dtype standalone decl asis noversion num conf nohierarchy ppt +@c LocalWords: decl asis noversion num conf nohierarchy ppt @c LocalWords: dbg mem stat postprocessed itemx mux pre val classname regexp @c LocalWords: ListImplementors dataflow getLogger JML jml hashcode DBC Jtest @c LocalWords: Parasoft's Quant ESC pre-state int seq elt Javadoc JVML param @@ -8629,8 +8629,8 @@ of, or in addition to, referencing this manual and the Daikon website @c LocalWords: autoload myA myList hashcodes MyA HashSetLinear HslIterator IA @c LocalWords: topAndPop init util DTRACEFILE DTRACEAPPEND Valgrind's AMD gz @c LocalWords: Athlon fomit Kvasir's programname vars gzip fifo AFS csv rec -@c LocalWords: stdout stderr disambig myprog Debian libfoo inst pragma prog -@c LocalWords: workflow accessor instr instrsourcedir TYPESAPPEND basedir cp +@c LocalWords: stdout stderr disambig myprog Debian libfoo inst prog +@c LocalWords: workflow instr cp @c LocalWords: myfile dfej's classfiles jikes QueueAr QueueArTester ln nn gdb @c LocalWords: classhier decldir instrdir uninst csal apdir cpdir Mangel's va @c LocalWords: ComparablePairsDescFileReader runnables dumpdir rm isprint cmd @@ -8698,14 +8698,14 @@ of, or in addition to, referencing this manual and the Daikon website @c LocalWords: oddheading thissectionnum thissectionname everyfooting d1 @c LocalWords: QuickTime QTSystem QTJava arg1 arg2 arg3 MyMain Makefile @c LocalWords: originalclasspath grep EXIT79 EXIT32 prepend Xmx7g var1 -@c LocalWords: prepended computeConfidence numsamples getName var2 var3 +@c LocalWords: computeConfidence numsamples getName var2 var3 @c LocalWords: jcc Java2 seq1 CSharpContract myString otherString EXIT48 @c LocalWords: unsplit if'' condition1 condition2 condition3 procedure1 @c LocalWords: replacement1 procedure2 replacement2 arg0 EXIT33 pack1 d2 @c LocalWords: pack2 online'' EXIT11 o1 o2 SIGSEGV O0 fjalar gcc3 EXIT0 @c LocalWords: I' C' S' A' P' superclasses vv1 bzip2 Xmx7g bfd dev ABI @c LocalWords: binutils AMD64 EM64T amd64 CeleriacLauncher celeriacArg1 -@c LocalWords: celeriacArg2 EXIT22 checkargs Takuan WS BPEL file1 file2 +@c LocalWords: celeriacArg2 checkargs Takuan WS BPEL file1 file2 @c LocalWords: readlink Hynger HYbrid iNvariant GEneratoR SLSF Bak Udon @c LocalWords: Drager I1 I2 invSortComparator1 invSortComparator2 prover @c LocalWords: Myprog2 isDaikonInstrumented getDaikonInvariants GeoPoint @@ -8715,14 +8715,14 @@ of, or in addition to, referencing this manual and the Daikon website @c LocalWords: SecurityManager setSecurityManager omitpattern Software'' @c LocalWords: MyProblemMethod Liu Galeotti Mangala Gowri ABB LVTT LVT @c LocalWords: ClassFormatError javautil ArrayList17 Simulink Stateflow -@c LocalWords: untyped splitter filename substring boolean splitters CVC +@c LocalWords: splitter filename substring boolean splitters CVC @c LocalWords: dimensionality consequents UNRETURNED getter globals pwd @c LocalWords: typedefs celeriacArg Inserter comparator roundoff bitwise @c LocalWords: Kellen Florian Laure Raytheon pathname customizable ctags @c LocalWords: myclasspath runtime Year2 typedef ubuntu openjdk @c LocalWords: texlive autoconf automake m4 zlib1g fno ClassFile @c LocalWords: example1c Ldaikon sayHello FunctionalInterface valgrind -@c LocalWords: Bourne accessors subparts MERCHANTABILITY merchantability +@c LocalWords: Bourne subparts MERCHANTABILITY merchantability @c LocalWords: uniq Cemal Akcaba Marinov McArthur Rayside Pickard Thaine @c LocalWords: Massimiliano Menarini Tatyana Nikolova Hellendoorn Xing @c LocalWords: Weitian Yoav Zibin daikonparentpath OpenAPI AGORA qqy diff --git a/doc/decl_format.txt b/doc/decl_format.txt index 2fbe3df4cf..faa0e39e04 100644 --- a/doc/decl_format.txt +++ b/doc/decl_format.txt @@ -496,8 +496,9 @@ File format are: - Input language record. Specifies the language of the target - program. The language string is arbitrary (for example, C, C++, - C#, or Java) and is not used by Daikon. + program. The language is an arbitrary single token that contains + no whitespace (for example, C, C++, C#, or Java) and is not used + by Daikon. input-language @@ -880,9 +881,8 @@ Resolved Issues: other languages. For instance, as mentioned above it would be nice to use "*" for dereferences in C (though we don't at the moment). In Perl, there's a class of variables whose names start - with "*" (though dfepl doesn't support them at the moment) and - there's also a global variable for just about every punctuation - character, including $*. Perhaps a multi-character marker would + with "*" and there's also a global variable for just about every + punctuation character, including $*. Perhaps a multi-character marker would be more robust? (smcc) I'd forgotten about '*' in C. I've changed the proposal to use From b5d593929c207ccb432a4e6242eaa7cfd567678e Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Sat, 10 Oct 2026 17:59:44 -0700 Subject: [PATCH 5/5] Rewrap paragraph in decl_format.txt --- doc/decl_format.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/decl_format.txt b/doc/decl_format.txt index faa0e39e04..afa119a4cc 100644 --- a/doc/decl_format.txt +++ b/doc/decl_format.txt @@ -882,8 +882,8 @@ Resolved Issues: nice to use "*" for dereferences in C (though we don't at the moment). In Perl, there's a class of variables whose names start with "*" and there's also a global variable for just about every - punctuation character, including $*. Perhaps a multi-character marker would - be more robust? (smcc) + punctuation character, including $*. Perhaps a multi-character + marker would be more robust? (smcc) I'd forgotten about '*' in C. I've changed the proposal to use '..' instead. We could also use '...', or the somewhat less