Android: Remove enable_incremental_javac and jmake
enable_incremental_javac uses jmake which does not work with java annotation processors (e.g. Dagger etc) that we are now using. It is not worth the engineering time and complexity to maintain and is currently broken. Removing it in favour of spending more dev time and resources to optimizes javac usage. Also simplify javac.py logic. Bug: 930737 Change-Id: Ic31d1b806a81601145696970249e762ed7b3251a Reviewed-on: https://chromium-review.googlesource.com/c/1478991 Commit-Queue: Peter Wen <wnwen@chromium.org> Reviewed-by: Andrew Grieve <agrieve@chromium.org> Auto-Submit: Peter Wen <wnwen@chromium.org> Cr-Commit-Position: refs/heads/master@{#633890}
This commit is contained in:
.gn
build
docs
third_party
jmake
BUILD.gnLICENSEOWNERSREADME.chromium
src
org
pantsbuild
jmake
Base64.javaBinaryFileReader.javaBinaryFileWriter.javaBinaryProjectDatabaseReader.javaBinaryProjectDatabaseWriter.javaClassFileReader.javaClassInfo.javaClassPath.javaCompatibilityChecker.javaMain.javaPCDContainer.javaPCDEntry.javaPCDManager.javaPrivateException.javaPublicExceptions.javaRefClassFinder.javaTextProjectDatabaseReader.javaTextProjectDatabaseWriter.javaUtils.java
robolectric
1
.gn
1
.gn
@ -440,7 +440,6 @@ check_targets = [
|
||||
"//third_party/isimpledom/*",
|
||||
"//third_party/javax_inject/*",
|
||||
"//third_party/jinja2/*",
|
||||
"//third_party/jmake/*",
|
||||
"//third_party/jsoncpp/*",
|
||||
"//third_party/jsr-305/*",
|
||||
"//third_party/jstemplate/*",
|
||||
|
@ -183,22 +183,6 @@ def _ExtractClassFiles(jar_path, dest_dir, java_files):
|
||||
shutil.copystat(jar_path, path)
|
||||
|
||||
|
||||
def _ConvertToJMakeArgs(javac_cmd, pdb_path):
|
||||
new_args = ['bin/jmake', '-pdb', pdb_path, '-jcexec', javac_cmd[0]]
|
||||
if md5_check.PRINT_EXPLANATIONS:
|
||||
new_args.append('-Xtiming')
|
||||
|
||||
do_not_prefix = ('-classpath', '-bootclasspath')
|
||||
skip_next = False
|
||||
for arg in javac_cmd[1:]:
|
||||
if not skip_next and arg not in do_not_prefix:
|
||||
arg = '-C' + arg
|
||||
new_args.append(arg)
|
||||
skip_next = arg in do_not_prefix
|
||||
|
||||
return new_args
|
||||
|
||||
|
||||
def _ParsePackageAndClassNames(java_file):
|
||||
package_name = ''
|
||||
class_names = []
|
||||
@ -257,7 +241,7 @@ def _ProcessInfo(java_file, package_name, class_names, source, chromium_code):
|
||||
assert not chromium_code or len(class_names) == 1, (
|
||||
'Chromium java files must only have one class: {}'.format(source))
|
||||
if chromium_code:
|
||||
# This check is necessary to ensure JMake works.
|
||||
# This check is not necessary but nice to check this somewhere.
|
||||
_CheckPathMatchesClassName(java_file, package_name, class_names[0])
|
||||
|
||||
|
||||
@ -307,11 +291,8 @@ def _CreateJarFile(jar_path, provider_configurations, additional_jar_files,
|
||||
logging.info('Completed jar file: %s', jar_path)
|
||||
|
||||
|
||||
def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs,
|
||||
classpath):
|
||||
def _OnStaleMd5(options, javac_cmd, java_files, classpath):
|
||||
logging.info('Starting _OnStaleMd5')
|
||||
# Don't bother enabling incremental compilation for non-chromium code.
|
||||
incremental = options.incremental and options.chromium_code
|
||||
|
||||
# Compiles with Error Prone take twice as long to run as pure javac. Thus GN
|
||||
# rules run both in parallel, with Error Prone only used for checks.
|
||||
@ -323,42 +304,12 @@ def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs,
|
||||
classes_dir = os.path.join(temp_dir, 'classes')
|
||||
os.makedirs(classes_dir)
|
||||
|
||||
changed_paths = None
|
||||
# jmake can handle deleted files, but it's a rare case and it would
|
||||
# complicate this script's logic.
|
||||
if incremental and changes.AddedOrModifiedOnly():
|
||||
changed_paths = set(changes.IterChangedPaths())
|
||||
# Do a full compile if classpath has changed.
|
||||
# jmake doesn't seem to do this on its own... Might be that ijars mess up
|
||||
# its change-detection logic.
|
||||
if any(p in changed_paths for p in classpath_inputs):
|
||||
changed_paths = None
|
||||
|
||||
if options.incremental:
|
||||
pdb_path = options.jar_path + '.pdb'
|
||||
|
||||
if incremental:
|
||||
# jmake is a compiler wrapper that figures out the minimal set of .java
|
||||
# files that need to be rebuilt given a set of .java files that have
|
||||
# changed.
|
||||
# jmake determines what files are stale based on timestamps between .java
|
||||
# and .class files. Since we use .jars, .srcjars, and md5 checks,
|
||||
# timestamp info isn't accurate for this purpose. Rather than use jmake's
|
||||
# programatic interface (like we eventually should), we ensure that all
|
||||
# .class files are newer than their .java files, and convey to jmake which
|
||||
# sources are stale by having their .class files be missing entirely
|
||||
# (by not extracting them).
|
||||
javac_cmd = _ConvertToJMakeArgs(javac_cmd, pdb_path)
|
||||
|
||||
if save_outputs:
|
||||
generated_java_dir = options.generated_dir
|
||||
else:
|
||||
generated_java_dir = os.path.join(temp_dir, 'gen')
|
||||
|
||||
# Incremental means not all files will be extracted, so don't bother
|
||||
# clearing out stale generated files.
|
||||
if not incremental:
|
||||
shutil.rmtree(generated_java_dir, True)
|
||||
shutil.rmtree(generated_java_dir, True)
|
||||
|
||||
srcjar_files = {}
|
||||
if srcjars:
|
||||
@ -366,12 +317,8 @@ def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs,
|
||||
build_utils.MakeDirectory(generated_java_dir)
|
||||
jar_srcs = []
|
||||
for srcjar in options.java_srcjars:
|
||||
if changed_paths:
|
||||
changed_paths.update(os.path.join(generated_java_dir, f)
|
||||
for f in changes.IterChangedSubpaths(srcjar))
|
||||
extracted_files = build_utils.ExtractAll(
|
||||
srcjar, no_clobber=not incremental, path=generated_java_dir,
|
||||
pattern='*.java')
|
||||
srcjar, no_clobber=True, path=generated_java_dir, pattern='*.java')
|
||||
for path in extracted_files:
|
||||
# We want the path inside the srcjar so the viewer can have a tree
|
||||
# structure.
|
||||
@ -380,28 +327,8 @@ def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs,
|
||||
jar_srcs.extend(extracted_files)
|
||||
logging.info('Done extracting srcjars')
|
||||
java_files.extend(jar_srcs)
|
||||
if changed_paths:
|
||||
# Set the mtime of all sources to 0 since we use the absence of .class
|
||||
# files to tell jmake which files are stale.
|
||||
for path in jar_srcs:
|
||||
os.utime(path, (0, 0))
|
||||
|
||||
if java_files:
|
||||
if changed_paths:
|
||||
changed_java_files = [p for p in java_files if p in changed_paths]
|
||||
if os.path.exists(options.jar_path):
|
||||
_ExtractClassFiles(options.jar_path, classes_dir, changed_java_files)
|
||||
# Add the extracted files to the classpath. This is required because
|
||||
# when compiling only a subset of files, classes that haven't changed
|
||||
# need to be findable.
|
||||
classpath.append(classes_dir)
|
||||
|
||||
# Can happen when a target goes from having no sources, to having sources.
|
||||
# It's created by the call to build_utils.Touch() below.
|
||||
if incremental:
|
||||
if os.path.exists(pdb_path) and not os.path.getsize(pdb_path):
|
||||
os.unlink(pdb_path)
|
||||
|
||||
# Don't include the output directory in the initial set of args since it
|
||||
# being in a temp dir makes it unstable (breaks md5 stamping).
|
||||
cmd = javac_cmd + ['-d', classes_dir]
|
||||
@ -416,33 +343,14 @@ def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs,
|
||||
f.write(' '.join(java_files))
|
||||
cmd += ['@' + java_files_rsp_path]
|
||||
|
||||
# JMake prints out some diagnostic logs that we want to ignore.
|
||||
# This assumes that all compiler output goes through stderr.
|
||||
stdout_filter = lambda s: ''
|
||||
if md5_check.PRINT_EXPLANATIONS:
|
||||
stdout_filter = None
|
||||
|
||||
logging.debug('Build command %s', cmd)
|
||||
attempt_build = lambda: build_utils.CheckOutput(
|
||||
build_utils.CheckOutput(
|
||||
cmd,
|
||||
print_stdout=options.chromium_code,
|
||||
stdout_filter=stdout_filter,
|
||||
stderr_filter=ProcessJavacOutput)
|
||||
try:
|
||||
attempt_build()
|
||||
except build_utils.CalledProcessError as e:
|
||||
# Work-around for a bug in jmake (http://crbug.com/551449).
|
||||
if ('project database corrupted' not in e.output
|
||||
and 'jmake: internal Java exception' not in e.output):
|
||||
raise
|
||||
logging.error(
|
||||
'Applying work-around for jmake project database corrupted '
|
||||
'(http://crbug.com/551449).')
|
||||
os.unlink(pdb_path)
|
||||
attempt_build()
|
||||
logging.info('Finished build command')
|
||||
|
||||
if options.incremental or save_outputs:
|
||||
if save_outputs:
|
||||
# Creating the jar file takes the longest, start it first on a separate
|
||||
# process to unblock the rest of the post-processing steps.
|
||||
jar_file_worker = multiprocessing.Process(
|
||||
@ -460,10 +368,6 @@ def _OnStaleMd5(changes, options, javac_cmd, java_files, classpath_inputs,
|
||||
else:
|
||||
build_utils.Touch(options.jar_path + '.info')
|
||||
|
||||
if options.incremental and (not java_files or not incremental):
|
||||
# Make sure output exists.
|
||||
build_utils.Touch(pdb_path)
|
||||
|
||||
if jar_file_worker:
|
||||
jar_file_worker.join()
|
||||
logging.info('Completed all steps in _OnStaleMd5')
|
||||
@ -499,11 +403,6 @@ def _ParseOptions(argv):
|
||||
'--interface-classpath',
|
||||
action='append',
|
||||
help='Classpath to use when no annotation processors are present.')
|
||||
parser.add_option(
|
||||
'--incremental',
|
||||
action='store_true',
|
||||
help='Whether to re-use .class files rather than recompiling them '
|
||||
'(when possible).')
|
||||
parser.add_option(
|
||||
'--processors',
|
||||
action='append',
|
||||
@ -668,6 +567,7 @@ def main(argv):
|
||||
|
||||
classpath_inputs = (options.bootclasspath + options.interface_classpath +
|
||||
options.processorpath)
|
||||
|
||||
# GN already knows of java_files, so listing them just make things worse when
|
||||
# they change.
|
||||
depfile_deps = [javac_path] + classpath_inputs + options.java_srcjars
|
||||
@ -677,25 +577,16 @@ def main(argv):
|
||||
options.jar_path,
|
||||
options.jar_path + '.info',
|
||||
]
|
||||
if options.incremental:
|
||||
output_paths.append(options.jar_path + '.pdb')
|
||||
|
||||
# An escape hatch to be able to check if incremental compiles are causing
|
||||
# problems.
|
||||
force = int(os.environ.get('DISABLE_INCREMENTAL_JAVAC', 0))
|
||||
|
||||
# List python deps in input_strings rather than input_paths since the contents
|
||||
# of them does not change what gets written to the depsfile.
|
||||
build_utils.CallAndWriteDepfileIfStale(
|
||||
lambda changes: _OnStaleMd5(changes, options, javac_cmd, java_files,
|
||||
classpath_inputs, classpath),
|
||||
lambda: _OnStaleMd5(options, javac_cmd, java_files, classpath),
|
||||
options,
|
||||
depfile_deps=depfile_deps,
|
||||
input_paths=input_paths,
|
||||
input_strings=javac_cmd + classpath,
|
||||
output_paths=output_paths,
|
||||
force=force,
|
||||
pass_changes=True,
|
||||
add_pydeps=False)
|
||||
logging.info('Script complete: %s', __file__)
|
||||
|
||||
|
@ -201,9 +201,6 @@ if (is_android || is_chromeos) {
|
||||
# Required for Android M+ due to SELinux policies (stronger sandboxing).
|
||||
disable_incremental_isolated_processes = false
|
||||
|
||||
# Speeds up incremental compiles by compiling only changed files.
|
||||
enable_incremental_javac = false
|
||||
|
||||
# Build incremental targets whenever possible.
|
||||
# Ex. with this arg set to true, the chrome_public_apk target result in
|
||||
# chrome_public_apk_incremental being built.
|
||||
@ -252,11 +249,9 @@ if (is_android || is_chromeos) {
|
||||
use_hashed_jni_names = !is_java_debug
|
||||
}
|
||||
|
||||
# Neither of these should ever be used for release builds since they are
|
||||
# somewhat experimental and dx --incremental is known to not produce
|
||||
# byte-for-byte identical output.
|
||||
# This should not be used for release builds since dx --incremental is known
|
||||
# to not produce byte-for-byte identical output.
|
||||
assert(!(enable_incremental_dx && !is_java_debug))
|
||||
assert(!(enable_incremental_javac && !is_java_debug))
|
||||
|
||||
# Path to where selected build variables are written to.
|
||||
android_build_vars = "$root_build_dir/build_vars.txt"
|
||||
|
@ -2762,10 +2762,6 @@ if (enable_java_templates) {
|
||||
# additional_jar_files: Optional list of files to copy into the resulting
|
||||
# .jar file (by default, only .class files are put there). Each entry
|
||||
# has the 'srcPath:dstPath' format.
|
||||
# enable_incremental_javac_override: Optional. If provided, determines
|
||||
# whether incremental javac compilation (based on jmake) is enabled.
|
||||
# Otherwise, decision is based on the global enable_incremental_javac
|
||||
# build arg variable.
|
||||
# enable_errorprone: Optional. If True, use the errorprone compiler to
|
||||
# check for error-prone constructs in the language. If not provided,
|
||||
# whether this is enabled depends on chromium_code and the global
|
||||
@ -2803,14 +2799,6 @@ if (enable_java_templates) {
|
||||
_additional_jar_files = invoker.additional_jar_files
|
||||
}
|
||||
|
||||
if (defined(invoker.enable_incremental_javac_override)) {
|
||||
# Use invoker-specified override.
|
||||
_enable_incremental_javac = invoker.enable_incremental_javac_override
|
||||
} else {
|
||||
# Default to build arg if not overridden.
|
||||
_enable_incremental_javac = enable_incremental_javac
|
||||
}
|
||||
|
||||
_srcjar_deps = []
|
||||
if (defined(invoker.srcjar_deps)) {
|
||||
_srcjar_deps += invoker.srcjar_deps
|
||||
@ -2867,12 +2855,6 @@ if (enable_java_templates) {
|
||||
if (defined(invoker.srcjar_filearg)) {
|
||||
args += [ "--java-srcjars=${invoker.srcjar_filearg}" ]
|
||||
}
|
||||
if (_enable_incremental_javac) {
|
||||
args += [ "--incremental" ]
|
||||
deps += [ "//third_party/jmake($default_toolchain)" ]
|
||||
inputs += [ "$root_build_dir/bin/jmake" ]
|
||||
outputs += [ "${invoker.javac_jar_path}.pdb" ]
|
||||
}
|
||||
if (invoker.requires_android) {
|
||||
args += [ "--bootclasspath=@FileArg($_rebased_build_config:android:sdk_interface_jars)" ]
|
||||
}
|
||||
@ -3407,7 +3389,6 @@ if (enable_java_templates) {
|
||||
_compile_java_forward_variables = [
|
||||
"additional_jar_files",
|
||||
"apk_name",
|
||||
"enable_incremental_javac_override",
|
||||
"processor_args_javac",
|
||||
"provider_configurations",
|
||||
"javac_args",
|
||||
|
@ -1383,8 +1383,6 @@ if (enable_java_templates) {
|
||||
#
|
||||
# chromium_code: If true, extra analysis warning/errors will be enabled.
|
||||
# enable_errorprone: If true, enables the errorprone compiler.
|
||||
# enable_incremental_javac_override: Overrides the global
|
||||
# enable_incremental_javac.
|
||||
#
|
||||
# jar_excluded_patterns: List of patterns of .class files to exclude.
|
||||
# jar_included_patterns: List of patterns of .class files to include.
|
||||
|
@ -353,11 +353,6 @@ Args that affect build speed:
|
||||
* What it does: Uses multiple `.so` files instead of just one (faster links)
|
||||
* `is_java_debug = true` *(default=`is_debug`)*
|
||||
* What it does: Disables ProGuard (slow build step)
|
||||
* `enable_incremental_javac = true` *(default=`false`)*
|
||||
* What it does: Tries to compile only a subset of `.java` files within an
|
||||
`android_library` for subsequent builds.
|
||||
* Can cause infrequent (once a month-ish) failures due to not recompiling a
|
||||
class that should be recompiled.
|
||||
|
||||
#### Incremental Install
|
||||
"Incremental install" uses reflection and side-loading to speed up the edit
|
||||
|
31
third_party/jmake/BUILD.gn
vendored
31
third_party/jmake/BUILD.gn
vendored
@ -1,31 +0,0 @@
|
||||
# Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//build/config/android/rules.gni")
|
||||
|
||||
java_binary("jmake") {
|
||||
java_files = [
|
||||
"src/org/pantsbuild/jmake/CompatibilityChecker.java",
|
||||
"src/org/pantsbuild/jmake/BinaryFileWriter.java",
|
||||
"src/org/pantsbuild/jmake/PCDEntry.java",
|
||||
"src/org/pantsbuild/jmake/TextProjectDatabaseWriter.java",
|
||||
"src/org/pantsbuild/jmake/Base64.java",
|
||||
"src/org/pantsbuild/jmake/PCDContainer.java",
|
||||
"src/org/pantsbuild/jmake/Main.java",
|
||||
"src/org/pantsbuild/jmake/ClassFileReader.java",
|
||||
"src/org/pantsbuild/jmake/ClassPath.java",
|
||||
"src/org/pantsbuild/jmake/BinaryFileReader.java",
|
||||
"src/org/pantsbuild/jmake/PrivateException.java",
|
||||
"src/org/pantsbuild/jmake/ClassInfo.java",
|
||||
"src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java",
|
||||
"src/org/pantsbuild/jmake/PCDManager.java",
|
||||
"src/org/pantsbuild/jmake/TextProjectDatabaseReader.java",
|
||||
"src/org/pantsbuild/jmake/RefClassFinder.java",
|
||||
"src/org/pantsbuild/jmake/Utils.java",
|
||||
"src/org/pantsbuild/jmake/PublicExceptions.java",
|
||||
"src/org/pantsbuild/jmake/BinaryProjectDatabaseReader.java",
|
||||
]
|
||||
main_class = "org.pantsbuild.jmake.Main"
|
||||
enable_incremental_javac_override = false
|
||||
}
|
341
third_party/jmake/LICENSE
vendored
341
third_party/jmake/LICENSE
vendored
@ -1,341 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
4
third_party/jmake/OWNERS
vendored
4
third_party/jmake/OWNERS
vendored
@ -1,4 +0,0 @@
|
||||
agrieve@chromium.org
|
||||
yfriedman@chromium.org
|
||||
|
||||
# COMPONENT: Build
|
17
third_party/jmake/README.chromium
vendored
17
third_party/jmake/README.chromium
vendored
@ -1,17 +0,0 @@
|
||||
Name: JMake
|
||||
URL: https://github.com/pantsbuild/jmake
|
||||
Version: 0
|
||||
Revision: 7761ee3e1537ccc61820c0d30061eb09edaf1c93
|
||||
License: GPL 2.0
|
||||
License File: NOT_SHIPPED
|
||||
Security Critical: No
|
||||
License Android Compatible: No
|
||||
|
||||
Description:
|
||||
Formerly known as Javamake, jmake is a compiler wrapper that figures out the
|
||||
minimal set of .java files that need to be rebuilt given a set of .java files
|
||||
that have changed.
|
||||
|
||||
Local Modifications:
|
||||
* Removed unneeded files
|
||||
* Added BUILD.gn
|
@ -1,80 +0,0 @@
|
||||
/* Copyright (c) 2002-2013 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* JMake needs to run against old versions of Java, that may not have JAXB's
|
||||
* javax.xml.bind.DatatypeConverter. And we don't want JMake to depend on third-party external libraries,
|
||||
* especially not just for this. So we implement a lightweight Base64 converter here ourselves.
|
||||
|
||||
* Note that sun.misc.BASE64Encoder is not official API and can go away at any time. Plus it inserts
|
||||
* line breaks into its emitted string, which is not what we want. So we can't use that either.
|
||||
*/
|
||||
|
||||
public class Base64 {
|
||||
// The easiest way to grok this code is to think of Base64 as the following chain of
|
||||
// conversions (ignoring padding issues):
|
||||
// 3 bytes -> 24 bits -> 4 6-bit nibbles -> 4 indexes from 0-63 -> 4 characters.
|
||||
private static final char[] indexToDigit =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
|
||||
private static final int[] digitToIndex = new int[128];
|
||||
static {
|
||||
assert(indexToDigit.length == 64);
|
||||
Arrays.fill(digitToIndex, -1);
|
||||
for (int i = 0; i < indexToDigit.length; i++) digitToIndex[(int)indexToDigit[i]] = i;
|
||||
}
|
||||
|
||||
private Base64() {}
|
||||
|
||||
public static char[] encode(byte[] in) {
|
||||
char[] ret = new char[(in.length + 2) / 3 * 4];
|
||||
int p = 0;
|
||||
int i = 0;
|
||||
while (i < in.length) {
|
||||
// Lowest 24 bits count.
|
||||
int bits = (in[i++] & 0xff) << 16 | (i < in.length ? in[i++] & 0xff : 0) << 8 | (i < in.length ? in[i++] & 0xff : 0);
|
||||
ret[p++] = indexToDigit[(bits & 0xfc0000) >> 18];
|
||||
ret[p++] = indexToDigit[(bits & 0x3f000) >> 12];
|
||||
ret[p++] = indexToDigit[(bits & 0xfc0) >> 6];
|
||||
ret[p++] = indexToDigit[bits & 0x3f];
|
||||
}
|
||||
assert(p == ret.length);
|
||||
int padding = (3 - in.length % 3) % 3;
|
||||
for (int j = ret.length - padding; j < ret.length; j++) ret[j] = '=';
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] decode(char[] in) {
|
||||
if (in.length % 4 != 0) throw new IllegalArgumentException("Base64-encoded string must be of length that is a multiple of 4.");
|
||||
int len = in.length;
|
||||
while(len > 0 && in[len - 1] == '=') len--;
|
||||
int padding = in.length - len;
|
||||
byte[] ret = new byte[in.length / 4 * 3 - padding];
|
||||
int i = 0;
|
||||
int p = 0;
|
||||
while (i < len) {
|
||||
char c0 = in[i++];
|
||||
char c1 = in[i++];
|
||||
char c2 = i < len ? in[i++] : 'A';
|
||||
char c3 = i < len ? in[i++] : 'A';
|
||||
if (c0 > 127 || c1 > 127 || c2 > 127 || c3 > 127) throw new IllegalArgumentException("Invalid Base64 digit in: " + c0 + c1 + c2 + c3);
|
||||
int n0 = digitToIndex[c0];
|
||||
int n1 = digitToIndex[c1];
|
||||
int n2 = digitToIndex[c2];
|
||||
int n3 = digitToIndex[c3];
|
||||
if (n0 < 0 || n1 < 0 || n2 < 0 || n3 < 0) throw new IllegalArgumentException("Invalid Base64 digit in: " + c0 + c1 + c2 + c3);
|
||||
int bits = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3;
|
||||
ret[p++] = (byte)((bits & 0xff0000) >> 16);
|
||||
if (p < ret.length) ret[p++] = (byte)((bits & 0xff00) >> 8);
|
||||
if (p < ret.length) ret[p++] = (byte)(bits & 0xff);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Basic operations for reading a byte array representing a binary file.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 10 November 2001
|
||||
*/
|
||||
public class BinaryFileReader {
|
||||
|
||||
protected byte[] buf;
|
||||
protected int curBufPos;
|
||||
protected String fileFullPath; // Required only for nice error reports
|
||||
|
||||
protected void initBuf(byte[] buf, String fileFullPath) {
|
||||
this.buf = buf;
|
||||
curBufPos = 0;
|
||||
this.fileFullPath = fileFullPath;
|
||||
}
|
||||
|
||||
protected char nextChar() {
|
||||
return (char) (((buf[curBufPos++] & 255) << 8) + (buf[curBufPos++] & 255));
|
||||
}
|
||||
|
||||
protected char getChar(int bufPos) {
|
||||
return (char) (((buf[bufPos] & 255) << 8) + (buf[bufPos+1] & 255));
|
||||
}
|
||||
|
||||
protected int nextInt() {
|
||||
return ((buf[curBufPos++] & 255) << 24) + ((buf[curBufPos++] & 255) << 16) +
|
||||
((buf[curBufPos++] & 255) << 8) + (buf[curBufPos++] & 255);
|
||||
}
|
||||
|
||||
protected int getInt(int bufPos) {
|
||||
return ((buf[bufPos] & 255) << 24) + ((buf[bufPos+1] & 255) << 16) +
|
||||
((buf[bufPos+2] & 255) << 8) + (buf[bufPos+3] & 255);
|
||||
}
|
||||
|
||||
protected long nextLong() {
|
||||
long res = getLong(curBufPos);
|
||||
curBufPos += 8;
|
||||
return res;
|
||||
}
|
||||
|
||||
protected long getLong(int bufPos) {
|
||||
DataInputStream bufin =
|
||||
new DataInputStream(new ByteArrayInputStream(buf, bufPos, 8));
|
||||
try {
|
||||
return bufin.readLong();
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected float nextFloat() {
|
||||
float res = getFloat(curBufPos);
|
||||
curBufPos += 4;
|
||||
return res;
|
||||
}
|
||||
|
||||
protected float getFloat(int bufPos) {
|
||||
DataInputStream bufin =
|
||||
new DataInputStream(new ByteArrayInputStream(buf, bufPos, 4));
|
||||
try {
|
||||
return bufin.readFloat();
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected double nextDouble() {
|
||||
double res = getDouble(curBufPos);
|
||||
curBufPos += 8;
|
||||
return res;
|
||||
}
|
||||
|
||||
protected double getDouble(int bufPos) {
|
||||
DataInputStream bufin =
|
||||
new DataInputStream(new ByteArrayInputStream(buf, bufPos, 8));
|
||||
try {
|
||||
return bufin.readDouble();
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
/**
|
||||
* Basic operations for writing to a byte array representing a binary file.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 30 January 2002
|
||||
*/
|
||||
public class BinaryFileWriter {
|
||||
|
||||
protected byte[] buf;
|
||||
protected int curBufSize, bufInc, curBufPos, threshold;
|
||||
private boolean bufferIncreaseAllowed = true;
|
||||
|
||||
protected void initBuf(int initSize) {
|
||||
buf = new byte[initSize];
|
||||
curBufSize = initSize;
|
||||
bufInc = initSize / 5;
|
||||
curBufPos = 0;
|
||||
threshold = curBufSize - bufInc;
|
||||
}
|
||||
|
||||
protected void increaseBuf() {
|
||||
if (!bufferIncreaseAllowed) {
|
||||
return;
|
||||
}
|
||||
byte newBuf[] = new byte[curBufSize + bufInc];
|
||||
System.arraycopy(buf, 0, newBuf, 0, curBufPos);
|
||||
buf = newBuf;
|
||||
curBufSize = buf.length;
|
||||
threshold = curBufSize - bufInc;
|
||||
}
|
||||
|
||||
// This should be called with false only when we are sure that we set the exact size of the buffer
|
||||
// and there is no need to increase it.
|
||||
protected void setBufferIncreaseMode(boolean increaseMode) {
|
||||
bufferIncreaseAllowed = increaseMode;
|
||||
}
|
||||
|
||||
public byte[] getBuffer() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
protected void writeByte(byte b) {
|
||||
if (curBufPos > threshold) {
|
||||
increaseBuf();
|
||||
}
|
||||
buf[curBufPos++] = b;
|
||||
}
|
||||
|
||||
protected void writeChar(int ch) {
|
||||
buf[curBufPos++] = (byte) ((ch >> 8) & 255);
|
||||
buf[curBufPos++] = (byte) (ch & 255);
|
||||
if (curBufPos > threshold) {
|
||||
increaseBuf();
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeInt(int i) {
|
||||
buf[curBufPos++] = (byte) ((i >> 24) & 255);
|
||||
buf[curBufPos++] = (byte) ((i >> 16) & 255);
|
||||
buf[curBufPos++] = (byte) ((i >> 8) & 255);
|
||||
buf[curBufPos++] = (byte) (i & 255);
|
||||
if (curBufPos > threshold) {
|
||||
increaseBuf();
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeLong(long l) {
|
||||
buf[curBufPos++] = (byte) ((l >> 56) & 255);
|
||||
buf[curBufPos++] = (byte) ((l >> 48) & 255);
|
||||
buf[curBufPos++] = (byte) ((l >> 40) & 255);
|
||||
buf[curBufPos++] = (byte) ((l >> 32) & 255);
|
||||
buf[curBufPos++] = (byte) ((l >> 24) & 255);
|
||||
buf[curBufPos++] = (byte) ((l >> 16) & 255);
|
||||
buf[curBufPos++] = (byte) ((l >> 8) & 255);
|
||||
buf[curBufPos++] = (byte) (l & 255);
|
||||
if (curBufPos > threshold) {
|
||||
increaseBuf();
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeFloat(float f) {
|
||||
int i = Float.floatToIntBits(f);
|
||||
writeInt(i);
|
||||
}
|
||||
|
||||
protected void writeDouble(double d) {
|
||||
long l = Double.doubleToLongBits(d);
|
||||
writeLong(l);
|
||||
}
|
||||
}
|
@ -1,281 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class creates the internal representation of the project database from a byte array.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 2 March 2005
|
||||
*/
|
||||
public class BinaryProjectDatabaseReader extends BinaryFileReader {
|
||||
|
||||
private String stringTable[];
|
||||
private Map<String,PCDEntry> pcd;
|
||||
private int nOfEntries;
|
||||
private int pdbFormat; // Currently supported values: 0x01030300 (jmake 1.3.3 and newer versions); 1 (all older versions)
|
||||
// These are defined in Utils as PDB_FORMAT_CODE_LATEST and PDB_FORMAT_CODE_OLD
|
||||
|
||||
public Map<String,PCDEntry> readProjectDatabaseFromFile(File infile) {
|
||||
byte buf[] = Utils.readFileIntoBuffer(infile);
|
||||
return readProjectDatabase(buf, infile.toString());
|
||||
}
|
||||
|
||||
public Map<String,PCDEntry> readProjectDatabase(byte[] pdbFile,
|
||||
String pdbFileFullPath) {
|
||||
initBuf(pdbFile, pdbFileFullPath);
|
||||
|
||||
readPreamble();
|
||||
readStringTable();
|
||||
pcd = new LinkedHashMap<String,PCDEntry>(nOfEntries * 4 / 3);
|
||||
|
||||
for (int i = 0; i < nOfEntries; i++) {
|
||||
PCDEntry entry = readPCDEntry();
|
||||
pcd.put(entry.className, entry);
|
||||
}
|
||||
|
||||
stringTable = null; // Help the GC
|
||||
return pcd;
|
||||
}
|
||||
|
||||
private void readPreamble() {
|
||||
if (buf.length < Utils.magicLength + 8) {
|
||||
pdbCorruptedException("file too short");
|
||||
}
|
||||
|
||||
for (int i = 0; i < Utils.magicLength; i++) {
|
||||
if (buf[i] != Utils.MAGIC[i]) {
|
||||
pdbCorruptedException("wrong project database header");
|
||||
}
|
||||
}
|
||||
|
||||
curBufPos += Utils.magicLength;
|
||||
pdbFormat = nextInt();
|
||||
if (pdbFormat != Utils.PDB_FORMAT_CODE_OLD && pdbFormat != Utils.PDB_FORMAT_CODE_LATEST) {
|
||||
pdbCorruptedException("wrong version number");
|
||||
}
|
||||
|
||||
int pdbSize = nextInt();
|
||||
if (buf.length != Utils.MAGIC.length + 8 + pdbSize) {
|
||||
pdbCorruptedException("file size does not match stored value");
|
||||
}
|
||||
|
||||
nOfEntries = nextInt();
|
||||
}
|
||||
|
||||
private void readStringTable() {
|
||||
int size = nextInt();
|
||||
stringTable = new String[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
stringTable[i] = nextString();
|
||||
}
|
||||
}
|
||||
|
||||
private PCDEntry readPCDEntry() {
|
||||
String className = nextStringRef();
|
||||
String javaFileFullPath = nextStringRef();
|
||||
long classFileLastModified = nextLong();
|
||||
long classFileFingerprint = nextLong();
|
||||
ClassInfo classInfo = readClassInfo();
|
||||
|
||||
return new PCDEntry(className, javaFileFullPath, classFileLastModified, classFileFingerprint, classInfo);
|
||||
}
|
||||
|
||||
private ClassInfo readClassInfo() {
|
||||
int i, j, len;
|
||||
ClassInfo res = new ClassInfo();
|
||||
|
||||
res.name = nextStringRef();
|
||||
if (pdbFormat >= Utils.PDB_FORMAT_CODE_133) {
|
||||
res.javacTargetRelease = nextInt();
|
||||
} else {
|
||||
res.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST;
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
String cpoolRefsToClasses[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
cpoolRefsToClasses[i] = nextStringRef();
|
||||
}
|
||||
res.cpoolRefsToClasses = cpoolRefsToClasses;
|
||||
boolean isRefClassArray[] = new boolean[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
isRefClassArray[i] = (buf[curBufPos++] != 0);
|
||||
}
|
||||
res.isRefClassArray = isRefClassArray;
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
String cpoolRefsToFieldClasses[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
cpoolRefsToFieldClasses[i] = nextStringRef();
|
||||
}
|
||||
res.cpoolRefsToFieldClasses = cpoolRefsToFieldClasses;
|
||||
String cpoolRefsToFieldNames[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
cpoolRefsToFieldNames[i] = nextStringRef();
|
||||
}
|
||||
res.cpoolRefsToFieldNames = cpoolRefsToFieldNames;
|
||||
String cpoolRefsToFieldSignatures[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
cpoolRefsToFieldSignatures[i] = nextStringRef();
|
||||
}
|
||||
res.cpoolRefsToFieldSignatures = cpoolRefsToFieldSignatures;
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
String cpoolRefsToMethodClasses[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
cpoolRefsToMethodClasses[i] = nextStringRef();
|
||||
}
|
||||
res.cpoolRefsToMethodClasses = cpoolRefsToMethodClasses;
|
||||
String cpoolRefsToMethodNames[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
cpoolRefsToMethodNames[i] = nextStringRef();
|
||||
}
|
||||
res.cpoolRefsToMethodNames = cpoolRefsToMethodNames;
|
||||
String cpoolRefsToMethodSignatures[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
cpoolRefsToMethodSignatures[i] = nextStringRef();
|
||||
}
|
||||
res.cpoolRefsToMethodSignatures = cpoolRefsToMethodSignatures;
|
||||
}
|
||||
|
||||
res.accessFlags = nextChar();
|
||||
res.isNonMemberNestedClass = (buf[curBufPos++] != 0);
|
||||
if (!"java/lang/Object".equals(res.name)) {
|
||||
res.superName = nextStringRef();
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
String interfaces[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
interfaces[i] = nextStringRef();
|
||||
}
|
||||
res.interfaces = interfaces;
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
String fieldNames[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
fieldNames[i] = nextStringRef();
|
||||
}
|
||||
res.fieldNames = fieldNames;
|
||||
String fieldSignatures[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
fieldSignatures[i] = nextStringRef();
|
||||
}
|
||||
res.fieldSignatures = fieldSignatures;
|
||||
char fieldAccessFlags[] = new char[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
fieldAccessFlags[i] = nextChar();
|
||||
}
|
||||
res.fieldAccessFlags = fieldAccessFlags;
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
Object primitiveConstantInitValues[] = new Object[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
byte code = buf[curBufPos++];
|
||||
switch (code) {
|
||||
case 1:
|
||||
primitiveConstantInitValues[i] = nextStringRef();
|
||||
break;
|
||||
case 2:
|
||||
primitiveConstantInitValues[i] = Integer.valueOf(nextInt());
|
||||
break;
|
||||
case 3:
|
||||
primitiveConstantInitValues[i] = Long.valueOf(nextLong());
|
||||
break;
|
||||
case 4:
|
||||
primitiveConstantInitValues[i] = Float.valueOf(nextFloat());
|
||||
break;
|
||||
case 5:
|
||||
primitiveConstantInitValues[i] =
|
||||
Double.valueOf(nextDouble());
|
||||
break;
|
||||
default: // Nothing to do
|
||||
}
|
||||
}
|
||||
res.primitiveConstantInitValues = primitiveConstantInitValues;
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
String methodNames[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
methodNames[i] = nextStringRef();
|
||||
}
|
||||
res.methodNames = methodNames;
|
||||
String methodSignatures[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
methodSignatures[i] = nextStringRef();
|
||||
}
|
||||
res.methodSignatures = methodSignatures;
|
||||
char methodAccessFlags[] = new char[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
methodAccessFlags[i] = nextChar();
|
||||
}
|
||||
res.methodAccessFlags = methodAccessFlags;
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
String checkedExceptions[][] = new String[len][];
|
||||
for (i = 0; i < len; i++) {
|
||||
int len1 = nextChar();
|
||||
if (len1 > 0) {
|
||||
checkedExceptions[i] = new String[len1];
|
||||
for (j = 0; j < len1; j++) {
|
||||
checkedExceptions[i][j] = nextStringRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
res.checkedExceptions = checkedExceptions;
|
||||
}
|
||||
|
||||
len = nextChar();
|
||||
if (len > 0) {
|
||||
String nestedClasses[] = new String[len];
|
||||
for (i = 0; i < len; i++) {
|
||||
nestedClasses[i] = nextStringRef();
|
||||
}
|
||||
res.nestedClasses = nestedClasses;
|
||||
}
|
||||
|
||||
res.initializeImmediateTransientFields();
|
||||
return res;
|
||||
}
|
||||
|
||||
private String nextString() {
|
||||
int length = nextChar();
|
||||
if (buf.length < curBufPos + length) {
|
||||
pdbCorruptedException("data error");
|
||||
}
|
||||
String res = (new String(buf, curBufPos, length)).intern();
|
||||
curBufPos += length;
|
||||
return res;
|
||||
}
|
||||
|
||||
private String nextStringRef() {
|
||||
return stringTable[nextInt()];
|
||||
}
|
||||
|
||||
private void pdbCorruptedException(String message) {
|
||||
throw new PrivateException(new PublicExceptions.PDBCorruptedException(message));
|
||||
}
|
||||
}
|
@ -1,363 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class implements writing into a byte array representing a project database
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 2 March 2005
|
||||
*/
|
||||
public class BinaryProjectDatabaseWriter extends BinaryFileWriter {
|
||||
|
||||
private Map<String, PCDEntry> pcd = null;
|
||||
private int nOfEntries;
|
||||
private byte[] stringBuf;
|
||||
private int curStringBufPos, stringBufInc, curStringBufWatermark, stringCount;
|
||||
private StringHashTable stringHashTable = null;
|
||||
|
||||
public void writeProjectDatabaseToFile(File outfile, Map<String, PCDEntry> pcd) {
|
||||
try {
|
||||
byte[] buf = new BinaryProjectDatabaseWriter().writeProjectDatabase(pcd);
|
||||
FileOutputStream out = new FileOutputStream(outfile);
|
||||
out.write(buf);
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] writeProjectDatabase(Map<String, PCDEntry> pcd) {
|
||||
this.pcd = pcd;
|
||||
nOfEntries = pcd.size();
|
||||
|
||||
// So far the constant here is chosen rather arbitrarily
|
||||
initBuf(nOfEntries * 1000);
|
||||
|
||||
stringBuf = new byte[nOfEntries * 300];
|
||||
stringBufInc = stringBuf.length / 5;
|
||||
curStringBufWatermark = stringBuf.length - 20;
|
||||
stringHashTable = new StringHashTable(stringBuf.length / 8);
|
||||
|
||||
for (PCDEntry entry : pcd.values()) {
|
||||
writePCDEntry(entry);
|
||||
}
|
||||
|
||||
// Now we have the string buffer and the main buffer. Write the end result
|
||||
byte[] mainBuf = buf;
|
||||
int mainBufSize = curBufPos;
|
||||
int preambleSize = Utils.MAGIC.length + 8;
|
||||
int stringBufSize = curStringBufPos;
|
||||
int pdbSize = stringBufSize + mainBufSize + 8; // 8 is for nOfEntries and string table size
|
||||
initBuf(preambleSize + pdbSize);
|
||||
setBufferIncreaseMode(false);
|
||||
|
||||
writePreamble(pdbSize);
|
||||
writeStringTable(stringBufSize);
|
||||
System.arraycopy(mainBuf, 0, buf, curBufPos, mainBufSize);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private void writePreamble(int pdbSize) {
|
||||
System.arraycopy(Utils.MAGIC, 0, buf, 0, Utils.MAGIC.length);
|
||||
curBufPos += Utils.MAGIC.length;
|
||||
|
||||
writeInt(Utils.PDB_FORMAT_CODE_LATEST); // Version number
|
||||
writeInt(pdbSize);
|
||||
writeInt(pcd.size());
|
||||
}
|
||||
|
||||
private void writeStringTable(int stringBufSize) {
|
||||
writeInt(stringCount);
|
||||
System.arraycopy(stringBuf, 0, buf, curBufPos, stringBufSize);
|
||||
curBufPos += stringBufSize;
|
||||
}
|
||||
|
||||
private void writePCDEntry(PCDEntry entry) {
|
||||
writeStringRef(entry.className);
|
||||
writeStringRef(entry.javaFileFullPath);
|
||||
writeLong(entry.oldClassFileLastModified);
|
||||
writeLong(entry.oldClassFileFingerprint);
|
||||
writeClassInfo(entry.oldClassInfo);
|
||||
}
|
||||
|
||||
private void writeClassInfo(ClassInfo ci) {
|
||||
int i, j, len;
|
||||
|
||||
writeStringRef(ci.name);
|
||||
writeInt(ci.javacTargetRelease);
|
||||
|
||||
len = ci.cpoolRefsToClasses != null ? ci.cpoolRefsToClasses.length : 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
String cpoolRefsToClasses[] = ci.cpoolRefsToClasses;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(cpoolRefsToClasses[i]);
|
||||
}
|
||||
boolean isRefClassArray[] = ci.isRefClassArray;
|
||||
for (i = 0; i < len; i++) {
|
||||
byte b = isRefClassArray[i] ? (byte) 1 : (byte) 0;
|
||||
writeByte(b);
|
||||
}
|
||||
}
|
||||
|
||||
len = ci.cpoolRefsToFieldClasses != null ? ci.cpoolRefsToFieldClasses.length
|
||||
: 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
String cpoolRefsToFieldClasses[] = ci.cpoolRefsToFieldClasses;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(cpoolRefsToFieldClasses[i]);
|
||||
}
|
||||
String cpoolRefsToFieldNames[] = ci.cpoolRefsToFieldNames;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(cpoolRefsToFieldNames[i]);
|
||||
}
|
||||
String cpoolRefsToFieldSignatures[] = ci.cpoolRefsToFieldSignatures;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(cpoolRefsToFieldSignatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
len = ci.cpoolRefsToMethodClasses != null ? ci.cpoolRefsToMethodClasses.length
|
||||
: 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
String cpoolRefsToMethodClasses[] = ci.cpoolRefsToMethodClasses;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(cpoolRefsToMethodClasses[i]);
|
||||
}
|
||||
String cpoolRefsToMethodNames[] = ci.cpoolRefsToMethodNames;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(cpoolRefsToMethodNames[i]);
|
||||
}
|
||||
String cpoolRefsToMethodSignatures[] =
|
||||
ci.cpoolRefsToMethodSignatures;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(cpoolRefsToMethodSignatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
writeChar(ci.accessFlags);
|
||||
byte b = ci.isNonMemberNestedClass ? (byte) 1 : (byte) 0;
|
||||
writeByte(b);
|
||||
if (!"java/lang/Object".equals(ci.name)) {
|
||||
writeStringRef(ci.superName);
|
||||
}
|
||||
|
||||
len = ci.interfaces != null ? ci.interfaces.length : 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
String interfaces[] = ci.interfaces;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(interfaces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
len = ci.fieldNames != null ? ci.fieldNames.length : 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
String fieldNames[] = ci.fieldNames;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(fieldNames[i]);
|
||||
}
|
||||
String fieldSignatures[] = ci.fieldSignatures;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(fieldSignatures[i]);
|
||||
}
|
||||
char fieldAccessFlags[] = ci.fieldAccessFlags;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeChar(fieldAccessFlags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
len = ci.primitiveConstantInitValues != null ? ci.primitiveConstantInitValues.length
|
||||
: 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
Object primitiveConstantInitValues[] =
|
||||
ci.primitiveConstantInitValues;
|
||||
for (i = 0; i < len; i++) {
|
||||
Object pc = primitiveConstantInitValues[i];
|
||||
if (pc != null) {
|
||||
if (pc instanceof String) {
|
||||
writeByte((byte)1);
|
||||
writeStringRef((String) pc);
|
||||
} else if (pc instanceof Integer) {
|
||||
writeByte((byte)2);
|
||||
writeInt(((Integer) pc).intValue());
|
||||
} else if (pc instanceof Long) {
|
||||
writeByte((byte)3);
|
||||
writeLong(((Long) pc).longValue());
|
||||
} else if (pc instanceof Float) {
|
||||
writeByte((byte)4);
|
||||
writeFloat(((Float) pc).floatValue());
|
||||
} else if (pc instanceof Double) {
|
||||
writeByte((byte)5);
|
||||
writeDouble(((Double) pc).doubleValue());
|
||||
}
|
||||
} else {
|
||||
writeByte((byte)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
len = ci.methodNames != null ? ci.methodNames.length : 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
String methodNames[] = ci.methodNames;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(methodNames[i]);
|
||||
}
|
||||
String methodSignatures[] = ci.methodSignatures;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(methodSignatures[i]);
|
||||
}
|
||||
char methodAccessFlags[] = ci.methodAccessFlags;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeChar(methodAccessFlags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
len = ci.checkedExceptions != null ? ci.checkedExceptions.length : 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
String checkedExceptions[][] = ci.checkedExceptions;
|
||||
for (i = 0; i < len; i++) {
|
||||
int lenl = checkedExceptions[i] != null ? checkedExceptions[i].length
|
||||
: 0;
|
||||
writeChar(lenl);
|
||||
if (lenl > 0) {
|
||||
for (j = 0; j < lenl; j++) {
|
||||
writeStringRef(checkedExceptions[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
len = ci.nestedClasses != null ? ci.nestedClasses.length : 0;
|
||||
writeChar(len);
|
||||
if (len > 0) {
|
||||
String nestedClasses[] = ci.nestedClasses;
|
||||
for (i = 0; i < len; i++) {
|
||||
writeStringRef(nestedClasses[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeString(String s) {
|
||||
byte sb[] = s.getBytes();
|
||||
int len = sb.length;
|
||||
if (curStringBufPos + len > curStringBufWatermark) {
|
||||
// May need to adapt stringBufInc
|
||||
if (len >= stringBufInc) {
|
||||
stringBufInc = (stringBufInc + len) * 2;
|
||||
} else {
|
||||
stringBufInc = (stringBufInc * 5) / 4; // Still increase a little - observations show that otherwise we usually get here 20 more times
|
||||
}
|
||||
byte newStringBuf[] = new byte[stringBuf.length + stringBufInc];
|
||||
System.arraycopy(stringBuf, 0, newStringBuf, 0, curStringBufPos);
|
||||
stringBuf = newStringBuf;
|
||||
curStringBufWatermark = stringBuf.length - 20;
|
||||
}
|
||||
stringBuf[curStringBufPos++] = (byte) ((len >> 8) & 255);
|
||||
stringBuf[curStringBufPos++] = (byte) (len & 255);
|
||||
System.arraycopy(sb, 0, stringBuf, curStringBufPos, len);
|
||||
curStringBufPos += len;
|
||||
}
|
||||
|
||||
private void writeStringRef(String s) {
|
||||
int stringRef = stringHashTable.get(s);
|
||||
if (stringRef == -1) {
|
||||
stringHashTable.add(s, stringCount);
|
||||
stringRef = stringCount;
|
||||
writeString(s);
|
||||
stringCount++;
|
||||
}
|
||||
writeInt(stringRef);
|
||||
}
|
||||
|
||||
/** Maps Strings to integer numbers (their positions in String table) */
|
||||
static class StringHashTable {
|
||||
|
||||
String keys[];
|
||||
int values[];
|
||||
int size, nOfElements, watermark;
|
||||
|
||||
StringHashTable(int size) {
|
||||
size = makeLikePrimeNumber(size);
|
||||
this.size = size;
|
||||
keys = new String[size];
|
||||
values = new int[size];
|
||||
nOfElements = 0;
|
||||
watermark = size * 3 / 4;
|
||||
}
|
||||
|
||||
final int get(String key) {
|
||||
int pos = (key.hashCode() & 0x7FFFFFFF) % size;
|
||||
|
||||
while (keys[pos] != null && !keys[pos].equals(key)) {
|
||||
pos = (pos + 3) % size; // Relies on the fact that size % 3 != 0
|
||||
}
|
||||
if (key.equals(keys[pos])) {
|
||||
return values[pos];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
final void add(String key, int value) {
|
||||
if (nOfElements > watermark) {
|
||||
rehash();
|
||||
}
|
||||
|
||||
int pos = (key.hashCode() & 0x7FFFFFFF) % size;
|
||||
while (keys[pos] != null) {
|
||||
pos = (pos + 3) % size; // Relies on the fact that size % 3 != 0
|
||||
}
|
||||
keys[pos] = key;
|
||||
values[pos] = value;
|
||||
nOfElements++;
|
||||
}
|
||||
|
||||
private final void rehash() {
|
||||
String oldKeys[] = keys;
|
||||
int oldValues[] = values;
|
||||
int oldSize = size;
|
||||
size = makeLikePrimeNumber(size * 3 / 2);
|
||||
keys = new String[size];
|
||||
values = new int[size];
|
||||
nOfElements = 0;
|
||||
watermark = size * 3 / 4;
|
||||
|
||||
for (int i = 0; i < oldSize; i++) {
|
||||
if (oldKeys[i] != null) {
|
||||
add(oldKeys[i], oldValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final int makeLikePrimeNumber(int no) {
|
||||
no = (no / 2) * 2 + 1; // Make it an odd number
|
||||
// Find the nearest "approximately prime" number
|
||||
boolean prime = false;
|
||||
do {
|
||||
no += 2;
|
||||
prime =
|
||||
(no % 3 != 0 && no % 5 != 0 && no % 7 != 0 && no % 11 != 0 &&
|
||||
no % 13 != 0 && no % 17 != 0 && no % 19 != 0 && no % 23 != 0 &&
|
||||
no % 29 != 0 && no % 31 != 0 && no % 37 != 0 && no % 41 != 0);
|
||||
} while (!prime);
|
||||
return no;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,595 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements reading a byte array representing a class file and converting it into ClassInfo.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 2 March 2005
|
||||
*/
|
||||
public class ClassFileReader extends BinaryFileReader {
|
||||
|
||||
public static final int JAVA_MAGIC = -889275714; // 0xCAFEBABE
|
||||
public static final int JAVA_MINOR_VERSION = 0;
|
||||
public static final int JAVA_MIN_MAJOR_VERSION = 45;
|
||||
public static final int JAVA_MIN_MINOR_VERSION = 3;
|
||||
public static final int DEFAULT_MAJOR_VERSION = 46;
|
||||
public static final int DEFAULT_MINOR_VERSION = 0;
|
||||
public static final int JDK14_MAJOR_VERSION = 48;
|
||||
public static final int JDK15_MAJOR_VERSION = 49;
|
||||
public static final int JDK16_MAJOR_VERSION = 50;
|
||||
public static final int JDK17_MAJOR_VERSION = 51;
|
||||
public static final int JDK18_MAJOR_VERSION = 52;
|
||||
public static final int CONSTANT_Utf8 = 1;
|
||||
public static final int CONSTANT_Unicode = 2;
|
||||
public static final int CONSTANT_Integer = 3;
|
||||
public static final int CONSTANT_Float = 4;
|
||||
public static final int CONSTANT_Long = 5;
|
||||
public static final int CONSTANT_Double = 6;
|
||||
public static final int CONSTANT_Class = 7;
|
||||
public static final int CONSTANT_String = 8;
|
||||
public static final int CONSTANT_Fieldref = 9;
|
||||
public static final int CONSTANT_Methodref = 10;
|
||||
public static final int CONSTANT_InterfaceMethodref = 11;
|
||||
public static final int CONSTANT_NameandType = 12;
|
||||
public static final int CONSTANT_MethodHandle = 15;
|
||||
public static final int CONSTANT_MethodType = 16;
|
||||
public static final int CONSTANT_InvokeDynamic = 18;
|
||||
private ClassInfo classInfo = null;
|
||||
private int cpOffsets[];
|
||||
private Object cpObjectCache[];
|
||||
private byte cpTags[];
|
||||
|
||||
public void readClassFile(byte[] classFile, ClassInfo classInfo, String classFileFullPath, boolean readFullInfo) {
|
||||
initBuf(classFile, classFileFullPath);
|
||||
this.classInfo = classInfo;
|
||||
|
||||
readPreamble();
|
||||
readConstantPool(readFullInfo);
|
||||
readIntermediate();
|
||||
if (readFullInfo) {
|
||||
readFields();
|
||||
readMethods();
|
||||
readAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
private int versionWord(int major, int minor) {
|
||||
return major * 1000 + minor;
|
||||
}
|
||||
|
||||
private void readPreamble() {
|
||||
int magic = nextInt();
|
||||
if (magic != JAVA_MAGIC) {
|
||||
throw classFileParseException("Illegal start of class file");
|
||||
}
|
||||
int minorVersion = nextChar();
|
||||
int majorVersion = nextChar();
|
||||
if (majorVersion > JDK14_MAJOR_VERSION ||
|
||||
versionWord(majorVersion, minorVersion) <
|
||||
versionWord(JAVA_MIN_MAJOR_VERSION, JAVA_MIN_MINOR_VERSION) ) {
|
||||
if (majorVersion == JDK18_MAJOR_VERSION) {
|
||||
classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_18;
|
||||
} else if (majorVersion == JDK17_MAJOR_VERSION) {
|
||||
classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_17;
|
||||
} else if (majorVersion == JDK16_MAJOR_VERSION) {
|
||||
classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_16;
|
||||
} else if (majorVersion == JDK15_MAJOR_VERSION) {
|
||||
classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_15;
|
||||
} else {
|
||||
throw classFileParseException("Wrong version: " + majorVersion + "." + minorVersion);
|
||||
}
|
||||
} else {
|
||||
classInfo.javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST;
|
||||
}
|
||||
}
|
||||
|
||||
private void readConstantPool(boolean readFullInfo) {
|
||||
int classRefsNo = 0;
|
||||
int fieldRefsNo = 0;
|
||||
int methodRefsNo = 0;
|
||||
|
||||
cpOffsets = new int[nextChar()];
|
||||
cpTags = new byte[cpOffsets.length];
|
||||
int ofs, len, classIdx, nameAndTypeIdx, nameIdx, sigIdx, utf8Idx;
|
||||
int i = 1;
|
||||
while (i < cpOffsets.length) {
|
||||
byte tag = buf[curBufPos++];
|
||||
cpOffsets[i] = curBufPos;
|
||||
cpTags[i] = tag;
|
||||
i++;
|
||||
switch (tag) {
|
||||
case CONSTANT_Utf8:
|
||||
len = nextChar();
|
||||
curBufPos += len;
|
||||
break;
|
||||
|
||||
case CONSTANT_Class:
|
||||
classRefsNo++;
|
||||
curBufPos += 2;
|
||||
break;
|
||||
|
||||
case CONSTANT_String:
|
||||
case CONSTANT_MethodType:
|
||||
curBufPos += 2;
|
||||
break;
|
||||
|
||||
case CONSTANT_Fieldref:
|
||||
fieldRefsNo++;
|
||||
curBufPos += 4;
|
||||
break;
|
||||
|
||||
case CONSTANT_Methodref:
|
||||
case CONSTANT_InterfaceMethodref:
|
||||
methodRefsNo++;
|
||||
curBufPos += 4;
|
||||
break;
|
||||
|
||||
case CONSTANT_MethodHandle:
|
||||
curBufPos += 3;
|
||||
break;
|
||||
|
||||
case CONSTANT_NameandType:
|
||||
case CONSTANT_Integer:
|
||||
case CONSTANT_Float:
|
||||
case CONSTANT_InvokeDynamic:
|
||||
curBufPos += 4;
|
||||
break;
|
||||
|
||||
case CONSTANT_Long:
|
||||
case CONSTANT_Double:
|
||||
curBufPos += 8;
|
||||
i++;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw classFileParseException("Bad constant pool tag: " + tag + " at " + Integer.toString(curBufPos - 1));
|
||||
}
|
||||
}
|
||||
|
||||
cpObjectCache = new Object[cpOffsets.length];
|
||||
if (!readFullInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
classInfo.cpoolRefsToClasses = new String[classRefsNo];
|
||||
classInfo.isRefClassArray = new boolean[classRefsNo];
|
||||
classInfo.cpoolRefsToFieldClasses = new String[fieldRefsNo];
|
||||
classInfo.cpoolRefsToFieldNames = new String[fieldRefsNo];
|
||||
classInfo.cpoolRefsToFieldSignatures = new String[fieldRefsNo];
|
||||
classInfo.cpoolRefsToMethodClasses = new String[methodRefsNo];
|
||||
classInfo.cpoolRefsToMethodNames = new String[methodRefsNo];
|
||||
classInfo.cpoolRefsToMethodSignatures = new String[methodRefsNo];
|
||||
|
||||
int curClassRef = 0;
|
||||
int curFieldRef = 0;
|
||||
int curMethodRef = 0;
|
||||
|
||||
for (i = 0; i < cpOffsets.length; i++) {
|
||||
ofs = cpOffsets[i];
|
||||
switch (cpTags[i]) {
|
||||
case CONSTANT_Class:
|
||||
utf8Idx = getChar(ofs);
|
||||
classInfo.cpoolRefsToClasses[curClassRef++] =
|
||||
classNameAtCPIndex(utf8Idx, classInfo.isRefClassArray, curClassRef - 1);
|
||||
//System.out.println("Read cpool ref to class: " + classInfo.cpoolRefsToClasses[curClassRef-1]);
|
||||
break;
|
||||
|
||||
case CONSTANT_Fieldref:
|
||||
classIdx = getChar(ofs);
|
||||
nameAndTypeIdx = getChar(ofs + 2);
|
||||
if (cpTags[classIdx] != CONSTANT_Class || cpTags[nameAndTypeIdx] != CONSTANT_NameandType) {
|
||||
badCPReference(ofs, i);
|
||||
}
|
||||
classInfo.cpoolRefsToFieldClasses[curFieldRef] =
|
||||
classNameAtCPIndex(getChar(cpOffsets[classIdx]));
|
||||
|
||||
ofs = cpOffsets[nameAndTypeIdx];
|
||||
nameIdx = getChar(ofs);
|
||||
sigIdx = getChar(ofs + 2);
|
||||
if (cpTags[nameIdx] != CONSTANT_Utf8 || cpTags[sigIdx] != CONSTANT_Utf8) {
|
||||
badCPReference(ofs, i);
|
||||
}
|
||||
classInfo.cpoolRefsToFieldNames[curFieldRef] =
|
||||
utf8AtCPIndex(nameIdx);
|
||||
classInfo.cpoolRefsToFieldSignatures[curFieldRef] =
|
||||
signatureAtCPIndex(sigIdx);
|
||||
//System.out.println("Read cpool ref to field: " + classInfo.cpoolRefsToFieldNames[curFieldRef] + " " +
|
||||
// classInfo.cpoolRefsToFieldSignatures[curFieldRef]);
|
||||
curFieldRef++;
|
||||
break;
|
||||
|
||||
case CONSTANT_Methodref:
|
||||
case CONSTANT_InterfaceMethodref:
|
||||
classIdx = getChar(ofs);
|
||||
nameAndTypeIdx = getChar(ofs + 2);
|
||||
if (cpTags[classIdx] != CONSTANT_Class || cpTags[nameAndTypeIdx] != CONSTANT_NameandType) {
|
||||
badCPReference(ofs, i);
|
||||
}
|
||||
classInfo.cpoolRefsToMethodClasses[curMethodRef] =
|
||||
classNameAtCPIndex(getChar(cpOffsets[classIdx]));
|
||||
|
||||
ofs = cpOffsets[nameAndTypeIdx];
|
||||
nameIdx = getChar(ofs);
|
||||
sigIdx = getChar(ofs + 2);
|
||||
if (cpTags[nameIdx] != CONSTANT_Utf8 || cpTags[sigIdx] != CONSTANT_Utf8) {
|
||||
badCPReference(ofs, i);
|
||||
}
|
||||
classInfo.cpoolRefsToMethodNames[curMethodRef] =
|
||||
utf8AtCPIndex(nameIdx);
|
||||
classInfo.cpoolRefsToMethodSignatures[curMethodRef] =
|
||||
signatureAtCPIndex(sigIdx);
|
||||
//System.out.println("Read cpool ref to method: " + classInfo.cpoolRefsToMethodNames[curMethodRef] + " " +
|
||||
// classInfo.cpoolRefsToMethodSignatures[curMethodRef]);
|
||||
curMethodRef++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readIntermediate() {
|
||||
int i, classIdx, superClassIdx;
|
||||
|
||||
classInfo.accessFlags = nextChar();
|
||||
classIdx = nextChar();
|
||||
if (cpTags[classIdx] != CONSTANT_Class) {
|
||||
throw classFileParseException("Bad reference to this class name");
|
||||
}
|
||||
classInfo.name = classNameAtCPIndex(getChar(cpOffsets[classIdx]));
|
||||
superClassIdx = nextChar();
|
||||
if (!"java/lang/Object".equals(classInfo.name)) {
|
||||
if (cpTags[superClassIdx] != CONSTANT_Class) {
|
||||
throw classFileParseException("Bad reference to super class name");
|
||||
}
|
||||
classInfo.superName =
|
||||
classNameAtCPIndex(getChar(cpOffsets[superClassIdx]));
|
||||
}
|
||||
|
||||
char intfCount = nextChar();
|
||||
if (intfCount != 0) {
|
||||
classInfo.interfaces = new String[intfCount];
|
||||
for (i = 0; i < intfCount; i++) {
|
||||
classIdx = nextChar();
|
||||
if (cpTags[classIdx] != CONSTANT_Class) {
|
||||
throw classFileParseException("Bad reference to an implemented interface");
|
||||
}
|
||||
classInfo.interfaces[i] =
|
||||
classNameAtCPIndex(getChar(cpOffsets[classIdx]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readFields() {
|
||||
int i, j;
|
||||
|
||||
char definedFieldCount = nextChar();
|
||||
if (definedFieldCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String names[] = new String[definedFieldCount];
|
||||
String signatures[] = new String[definedFieldCount];
|
||||
char accessFlags[] = new char[definedFieldCount];
|
||||
|
||||
// We are not going to record information on private fields which have either primitive or non-project-class
|
||||
// (typically core-class) types. Such fields cannot affect anything except their own class, so we don't need them.
|
||||
int ri = 0;
|
||||
|
||||
for (i = 0; i < definedFieldCount; i++) {
|
||||
char flags = nextChar();
|
||||
String name = utf8AtCPIndex(nextChar());
|
||||
String sig = signatureAtCPIndex(nextChar());
|
||||
|
||||
boolean recordField =
|
||||
!(Modifier.isPrivate(flags) &&
|
||||
(ClassInfo.isPrimitiveFieldSig(sig) || classInfo.isNonProjectClassTypeFieldSig(sig)));
|
||||
|
||||
int attrCount = nextChar();
|
||||
for (j = 0; j < attrCount; j++) {
|
||||
int attrNameIdx = nextChar();
|
||||
int attrLen = nextInt();
|
||||
if (recordField && utf8AtCPIndex(attrNameIdx).equals("ConstantValue") &&
|
||||
Modifier.isFinal(flags)) {
|
||||
if (classInfo.primitiveConstantInitValues == null) {
|
||||
classInfo.primitiveConstantInitValues =
|
||||
new Object[definedFieldCount];
|
||||
}
|
||||
int constValueIdx = nextChar();
|
||||
switch (cpTags[constValueIdx]) {
|
||||
case CONSTANT_String:
|
||||
classInfo.primitiveConstantInitValues[ri] =
|
||||
utf8AtCPIndex(getChar(cpOffsets[constValueIdx]));
|
||||
break;
|
||||
|
||||
case CONSTANT_Integer:
|
||||
classInfo.primitiveConstantInitValues[ri] =
|
||||
Integer.valueOf(getInt(cpOffsets[constValueIdx]));
|
||||
break;
|
||||
|
||||
case CONSTANT_Long:
|
||||
classInfo.primitiveConstantInitValues[ri] =
|
||||
Long.valueOf(getLong(cpOffsets[constValueIdx]));
|
||||
break;
|
||||
|
||||
case CONSTANT_Float:
|
||||
classInfo.primitiveConstantInitValues[ri] =
|
||||
Float.valueOf(getFloat(cpOffsets[constValueIdx]));
|
||||
break;
|
||||
|
||||
case CONSTANT_Double:
|
||||
classInfo.primitiveConstantInitValues[ri] =
|
||||
Double.valueOf(getDouble(cpOffsets[constValueIdx]));
|
||||
break;
|
||||
|
||||
default:
|
||||
badCPEntry(constValueIdx);
|
||||
}
|
||||
|
||||
} else {
|
||||
curBufPos += attrLen;
|
||||
}
|
||||
}
|
||||
|
||||
if (recordField) {
|
||||
names[ri] = name;
|
||||
signatures[ri] = sig;
|
||||
accessFlags[ri] = flags;
|
||||
ri++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ri == definedFieldCount) {
|
||||
classInfo.fieldNames = names;
|
||||
classInfo.fieldSignatures = signatures;
|
||||
classInfo.fieldAccessFlags = accessFlags;
|
||||
} else if (ri > 0) {
|
||||
classInfo.fieldNames = new String[ri];
|
||||
classInfo.fieldSignatures = new String[ri];
|
||||
classInfo.fieldAccessFlags = new char[ri];
|
||||
System.arraycopy(names, 0, classInfo.fieldNames, 0, ri);
|
||||
System.arraycopy(signatures, 0, classInfo.fieldSignatures, 0, ri);
|
||||
System.arraycopy(accessFlags, 0, classInfo.fieldAccessFlags, 0, ri);
|
||||
}
|
||||
}
|
||||
|
||||
private void readMethods() {
|
||||
int i, j;
|
||||
|
||||
char methodCount = nextChar();
|
||||
if (methodCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String names[] = new String[methodCount];
|
||||
String signatures[] = new String[methodCount];
|
||||
char accessFlags[] = new char[methodCount];
|
||||
|
||||
for (i = 0; i < methodCount; i++) {
|
||||
accessFlags[i] = nextChar();
|
||||
names[i] = utf8AtCPIndex(nextChar());
|
||||
signatures[i] = signatureAtCPIndex(nextChar());
|
||||
|
||||
int attrCount = nextChar();
|
||||
for (j = 0; j < attrCount; j++) {
|
||||
int attrNameIdx = nextChar();
|
||||
int attrLen = nextInt();
|
||||
if (utf8AtCPIndex(attrNameIdx).equals("Exceptions")) {
|
||||
if (classInfo.checkedExceptions == null) {
|
||||
classInfo.checkedExceptions = new String[methodCount][];
|
||||
}
|
||||
int nExceptions = nextChar();
|
||||
String exceptions[] = new String[nExceptions];
|
||||
for (int k = 0; k < nExceptions; k++) {
|
||||
int excClassIdx = nextChar();
|
||||
if (cpTags[excClassIdx] != CONSTANT_Class) {
|
||||
badCPEntry(excClassIdx);
|
||||
}
|
||||
exceptions[k] =
|
||||
classNameAtCPIndex(getChar(cpOffsets[excClassIdx]));
|
||||
}
|
||||
classInfo.checkedExceptions[i] = exceptions;
|
||||
} else {
|
||||
curBufPos += attrLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
classInfo.methodNames = names;
|
||||
classInfo.methodSignatures = signatures;
|
||||
classInfo.methodAccessFlags = accessFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method actually reads only the information related to the nested classes, and
|
||||
* records only those of them which are first level nested classes of this class. The class
|
||||
* may also reference other classes which are not package members through the same
|
||||
* InnerClasses attribute - their names would be processed when their respective enclosing
|
||||
* classes are read.
|
||||
*/
|
||||
private void readAttributes() {
|
||||
String nestedClassPrefix = classInfo.name + "$";
|
||||
|
||||
char attrCount = nextChar();
|
||||
|
||||
for (int i = 0; i < attrCount; i++) {
|
||||
int attrNameIdx = nextChar();
|
||||
int attrLen = nextInt();
|
||||
if (utf8AtCPIndex(attrNameIdx).equals("InnerClasses")) {
|
||||
int nOfClasses = nextChar();
|
||||
String nestedClasses[] = new String[nOfClasses];
|
||||
char nestedClassAccessFlags[] = new char[nOfClasses];
|
||||
boolean nestedClassNonMember[] = new boolean[nOfClasses];
|
||||
int curIdx = 0;
|
||||
for (int j = 0; j < nOfClasses; j++) {
|
||||
int innerClassInfoIdx = nextChar();
|
||||
int outerClassInfoIdx = nextChar();
|
||||
int innerClassNameIdx = nextChar();
|
||||
char innerClassAccessFlags = nextChar();
|
||||
|
||||
// Even if a class is private or non-member (innerClassAccessFlags has private bit set or
|
||||
// outerClassInfoIdx == 0), we still should take this class into account, since it may e.g. extend
|
||||
// a public class/implement a public interface, which, in turn, may be changed incompatibly.
|
||||
|
||||
String nestedClassFullName = classNameAtCPIndex(getChar(cpOffsets[innerClassInfoIdx]));
|
||||
|
||||
// We are only interested the nested classes whose enclosing class is this one.
|
||||
if (!nestedClassFullName.startsWith(nestedClassPrefix))
|
||||
continue;
|
||||
|
||||
// We are only interested in the directly nested classes of this class.
|
||||
String nestedClassNameSuffix = nestedClassFullName.substring(nestedClassPrefix.length());
|
||||
|
||||
if (innerClassNameIdx == 0) {
|
||||
// Nested class is anonymous. Suffix must be all digits.
|
||||
if (findFirstNonDigit(nestedClassNameSuffix) != -1)
|
||||
continue;
|
||||
} else {
|
||||
// Nested class is named.
|
||||
String nestedClassSimpleName = utf8AtCPIndex(innerClassNameIdx);
|
||||
// The simple case is Outer$Inner.
|
||||
if (!nestedClassNameSuffix.equals(nestedClassSimpleName)) {
|
||||
// The more complicated case is a local class. In JDK 1.5+ These are named,
|
||||
// e.g., Outer$1Inner. Pre-JDK 1.5 they are named e.g., Outer$1$Inner.
|
||||
int p = findFirstNonDigit(nestedClassNameSuffix);
|
||||
if (p == -1)
|
||||
continue;
|
||||
if (classInfo.javacTargetRelease == Utils.JAVAC_TARGET_RELEASE_OLDEST &&
|
||||
nestedClassNameSuffix.charAt(p++) != '$')
|
||||
continue;
|
||||
if (!nestedClassNameSuffix.substring(p).equals(nestedClassSimpleName))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// The name has passed all checks, so register it.
|
||||
|
||||
nestedClasses[curIdx] = nestedClassFullName;
|
||||
nestedClassAccessFlags[curIdx] = innerClassAccessFlags;
|
||||
nestedClassNonMember[curIdx] = (outerClassInfoIdx == 0);
|
||||
curIdx++;
|
||||
}
|
||||
if (curIdx == nOfClasses) {
|
||||
classInfo.nestedClasses = nestedClasses;
|
||||
classInfo.nestedClassAccessFlags = nestedClassAccessFlags;
|
||||
classInfo.nestedClassNonMember = nestedClassNonMember;
|
||||
} else if (curIdx > 0) {
|
||||
// We found fewer nested classes for this class than we originally expected, but still more than 0.
|
||||
// Create a new array to fit their number exactly.
|
||||
classInfo.nestedClasses = new String[curIdx];
|
||||
classInfo.nestedClassAccessFlags = new char[curIdx];
|
||||
classInfo.nestedClassNonMember = new boolean[curIdx];
|
||||
System.arraycopy(nestedClasses, 0, classInfo.nestedClasses, 0, curIdx);
|
||||
System.arraycopy(nestedClassAccessFlags, 0, classInfo.nestedClassAccessFlags, 0, curIdx);
|
||||
System.arraycopy(nestedClassNonMember, 0, classInfo.nestedClassNonMember, 0, curIdx);
|
||||
}
|
||||
} else {
|
||||
curBufPos += attrLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int findFirstNonDigit(String s) {
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
if (!Character.isDigit(s.charAt(i)))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String utf8AtCPIndex(int idx) {
|
||||
if (cpTags[idx] != CONSTANT_Utf8) {
|
||||
throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
|
||||
}
|
||||
if (cpObjectCache[idx] == null) {
|
||||
int utf8Len = getChar(cpOffsets[idx]);
|
||||
// String interning reduces the size of the disk database very significantly
|
||||
// (by one-third in one observed case), and also speeds up database search.
|
||||
cpObjectCache[idx] =
|
||||
(new String(buf, cpOffsets[idx] + 2, utf8Len)).intern();
|
||||
}
|
||||
return (String) cpObjectCache[idx];
|
||||
}
|
||||
|
||||
private String classNameAtCPIndex(int idx) {
|
||||
return classNameAtCPIndex(idx, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read class name at the given CONSTANT_Utf8 constant pool index, and return it
|
||||
* trimmed of the possible '[' and 'L' prefixes and the ';' suffix.
|
||||
*/
|
||||
private String classNameAtCPIndex(int idx, boolean isRefClassArray[], int isArrayIdx) {
|
||||
if (cpTags[idx] != CONSTANT_Utf8) {
|
||||
throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
|
||||
}
|
||||
boolean isArray = false;
|
||||
if (cpObjectCache[idx] == null) {
|
||||
int utf8Len = getChar(cpOffsets[idx]);
|
||||
int stPos = cpOffsets[idx] + 2;
|
||||
int initStPos = stPos;
|
||||
while (buf[stPos] == '[') {
|
||||
stPos++;
|
||||
}
|
||||
if (stPos != initStPos) {
|
||||
isArray = true;
|
||||
if (buf[stPos] == 'L') {
|
||||
stPos++;
|
||||
utf8Len--; // To get rid of the terminating ';'
|
||||
}
|
||||
}
|
||||
utf8Len = utf8Len - (stPos - initStPos);
|
||||
cpObjectCache[idx] = (new String(buf, stPos, utf8Len)).intern();
|
||||
if (isRefClassArray != null) {
|
||||
isRefClassArray[isArrayIdx] = isArray;
|
||||
}
|
||||
}
|
||||
return (String) cpObjectCache[idx];
|
||||
}
|
||||
|
||||
// We replace all "Lclassname;" in signatures with "@classname#" to simplify signature parsing during reference checking
|
||||
private String signatureAtCPIndex(int idx) {
|
||||
if (cpTags[idx] != CONSTANT_Utf8) {
|
||||
throw classFileParseException("Constant pool entry " + idx + " should be UTF8 constant");
|
||||
}
|
||||
if (cpObjectCache[idx] == null) {
|
||||
int utf8Len = getChar(cpOffsets[idx]);
|
||||
byte tmp[] = new byte[utf8Len];
|
||||
System.arraycopy(buf, cpOffsets[idx] + 2, tmp, 0, utf8Len);
|
||||
boolean inClassName = false;
|
||||
for (int i = 0; i < utf8Len; i++) {
|
||||
if (!inClassName) {
|
||||
if (tmp[i] == 'L') {
|
||||
tmp[i] = '@';
|
||||
inClassName = true;
|
||||
}
|
||||
} else if (tmp[i] == ';') {
|
||||
tmp[i] = '#';
|
||||
inClassName = false;
|
||||
}
|
||||
}
|
||||
cpObjectCache[idx] = (new String(tmp)).intern();
|
||||
}
|
||||
return (String) cpObjectCache[idx];
|
||||
}
|
||||
|
||||
private void badCPReference(int ofs, int i) {
|
||||
throw classFileParseException("Bad constant pool reference: " + ofs + " from entry " + i);
|
||||
}
|
||||
|
||||
private void badCPEntry(int entryNo) {
|
||||
throw classFileParseException("Constant pool entry " + entryNo + " : invalid type");
|
||||
}
|
||||
|
||||
private PrivateException classFileParseException(String msg) {
|
||||
return new PrivateException(new PublicExceptions.ClassFileParseException(
|
||||
"Error reading class file " + fileFullPath + ":\n" + msg));
|
||||
}
|
||||
}
|
@ -1,746 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A reflection of a class, in the form that allows fast checks and information obtaining.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 5 April 2004
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ClassInfo implements Serializable {
|
||||
|
||||
public static final int VER_OLD = 0; // Old version
|
||||
public static final int VER_NEW = 1; // New version
|
||||
public static final int NO_VERSIONS = 2; // Non-project class, no change tracking
|
||||
private transient PCDManager pcdm;
|
||||
transient int verCode; // Version code for this ClassInfo - one of the above.
|
||||
String name = null;
|
||||
transient String packageName; // Package name; restored when database is reloaded
|
||||
int javacTargetRelease = Utils.JAVAC_TARGET_RELEASE_OLDEST; // Can have values from Utils.JAVAC_TARGET_RELEASE_xxx
|
||||
String cpoolRefsToClasses[]; // Directly referenced class names trimmed of array and 'L' prefixes and ';' suffixes
|
||||
boolean isRefClassArray[]; // Indicates if a directly referenced class is actually an array class
|
||||
// In all signatures we replace the 'L' and ';' symbols that enclose non-primitive type names with '@' and '#' respectively,
|
||||
// so that class names inside signatures can be located fast and unambiguously.
|
||||
String cpoolRefsToFieldClasses[]; // Defining classes of referenced fields, trimmed of enclosing 'L' and ';' symbols
|
||||
String cpoolRefsToFieldNames[]; // Names of referenced fields
|
||||
String cpoolRefsToFieldSignatures[]; // Signatures of referenced fields
|
||||
String cpoolRefsToMethodClasses[]; // Defining classes of referenced methods, trimmed of enclosing 'L' and ';' symbols
|
||||
String cpoolRefsToMethodNames[]; // Names of referenced methods
|
||||
String cpoolRefsToMethodSignatures[]; // Signatures of referenced methods
|
||||
char accessFlags; // isInterface flag included
|
||||
boolean isNonMemberNestedClass = false; // True if this is a non-member nested class
|
||||
String superName;
|
||||
String interfaces[];
|
||||
String fieldNames[];
|
||||
String fieldSignatures[];
|
||||
char fieldAccessFlags[];
|
||||
Object primitiveConstantInitValues[];
|
||||
String methodNames[];
|
||||
String methodSignatures[];
|
||||
char methodAccessFlags[];
|
||||
String checkedExceptions[][];
|
||||
transient ClassInfo directSubclasses[]; // Direct subclasses. Created lazily and not preserved on disk.
|
||||
transient String directlyEnclosingClass; // Directly enclosing class name; restored when database is reloaded
|
||||
transient String topLevelEnclosingClass; // Top-level enclosing class name; restored when database is reloaded
|
||||
String nestedClasses[]; // Names of all nested classes. Don't make transient - it's used to check
|
||||
// if nested classes for this class were added/deleted in new version
|
||||
transient char nestedClassAccessFlags[]; // No need to store this information permanently
|
||||
transient boolean nestedClassNonMember[]; // Ditto
|
||||
|
||||
/** Creates new ClassInfo out of a class file. The last parameter is needed only to produce sensible error reports.*/
|
||||
public ClassInfo(byte[] classFileBytes, int verCode, PCDManager pcdm, String classFileFullPath) {
|
||||
this.pcdm = pcdm;
|
||||
this.verCode = verCode;
|
||||
pcdm.getClassFileReader().readClassFile(classFileBytes, this, classFileFullPath, true);
|
||||
packageName = Utils.getPackageName(name);
|
||||
directlyEnclosingClass =
|
||||
Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease);
|
||||
topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a "lightweight" ClassInfo, that contains just the class name, super name, interfaces, flags and verCode.
|
||||
* Used for non-project classes, that don't change themselves, for which we are only interested in type hierarchy structure.
|
||||
*/
|
||||
public ClassInfo(byte[] classFileBytes, PCDManager pcdm, String classFileFullPath) {
|
||||
this.pcdm = pcdm;
|
||||
this.verCode = NO_VERSIONS;
|
||||
pcdm.getClassFileReader().readClassFile(classFileBytes, this, classFileFullPath, false);
|
||||
packageName = Utils.getPackageName(name);
|
||||
directlyEnclosingClass =
|
||||
Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease);
|
||||
topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name);
|
||||
}
|
||||
|
||||
/** Even more lightweight variant - created for a deleted non-project class, to enable minimum possible checks. */
|
||||
public ClassInfo(String name, PCDManager pcdm) {
|
||||
this.pcdm = pcdm;
|
||||
this.verCode = NO_VERSIONS;
|
||||
this.name = name;
|
||||
packageName = Utils.getPackageName(name);
|
||||
directlyEnclosingClass = Utils.getDirectlyEnclosingClass(name, 0);
|
||||
topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name);
|
||||
}
|
||||
|
||||
public ClassInfo() {
|
||||
}
|
||||
|
||||
/** Initialize transient data that can be initialized immediately after this ClassInfo is read from the project database */
|
||||
public void initializeImmediateTransientFields() {
|
||||
verCode = VER_OLD;
|
||||
|
||||
packageName = Utils.getPackageName(name);
|
||||
|
||||
directlyEnclosingClass =
|
||||
Utils.getDirectlyEnclosingClass(name, this.javacTargetRelease);
|
||||
topLevelEnclosingClass = Utils.getTopLevelEnclosingClass(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to restore the pointer to the current PCDManager after this ClassInfo is brought back
|
||||
* from the store.
|
||||
*/
|
||||
public void restorePCDM(PCDManager pcdm) {
|
||||
this.pcdm = pcdm;
|
||||
}
|
||||
|
||||
public boolean isInterface() {
|
||||
return Modifier.isInterface(accessFlags);
|
||||
}
|
||||
|
||||
public boolean isAbstract() {
|
||||
return Modifier.isAbstract(accessFlags);
|
||||
}
|
||||
|
||||
public boolean isPublic() {
|
||||
return Modifier.isPublic(accessFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the superclasses of the given class (transitively), that belong
|
||||
* to the same project, plus those of the superclasses that can be found on the class path
|
||||
* supplied to jmake, and on the boot class path.
|
||||
*/
|
||||
public List<String> getAllSuperclassNames() {
|
||||
List<String> res = new ArrayList<String>();
|
||||
String superName = this.superName;
|
||||
while (superName != null && !"java/lang/Object".equals(superName)) {
|
||||
res.add(superName);
|
||||
ClassInfo classInfo = pcdm.getClassInfoForName(verCode, superName);
|
||||
if (classInfo == null) { // Class not in project (or deleted?). Try to find it and further superclasses in non-project classes
|
||||
ClassPath.getSuperclasses(superName, res, pcdm);
|
||||
break;
|
||||
}
|
||||
superName = classInfo.superName;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of names of the interfaces transitively implemented by the given
|
||||
* class, that belong to the same project.
|
||||
*/
|
||||
public Set<String> getAllImplementedIntfNames() {
|
||||
Set<String> res = new LinkedHashSet<String>();
|
||||
addImplementedInterfaceNames(false, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Add to the given set the names of direct/all interfaces implemented by the given class. */
|
||||
private void addImplementedInterfaceNames(boolean directOnly,
|
||||
Set<String> intfSet) {
|
||||
if (interfaces != null) {
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
String superIntfName = interfaces[i];
|
||||
intfSet.add(superIntfName);
|
||||
if (directOnly) {
|
||||
continue;
|
||||
}
|
||||
ClassInfo superIntfInfo =
|
||||
pcdm.getClassInfoForName(verCode, superIntfName);
|
||||
if (superIntfInfo == null) { // Class not in project
|
||||
ClassPath.addAllImplementedInterfaceNames(superIntfName, intfSet, pcdm);
|
||||
} else {
|
||||
superIntfInfo.addImplementedInterfaceNames(false, intfSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (directOnly || superName == null ||
|
||||
"java/lang/Object".equals(superName)) {
|
||||
return;
|
||||
}
|
||||
ClassInfo superInfo = pcdm.getClassInfoForName(verCode, superName);
|
||||
if (superInfo == null) { // Class not in project
|
||||
ClassPath.addAllImplementedInterfaceNames(superName, intfSet, pcdm);
|
||||
} else {
|
||||
superInfo.addImplementedInterfaceNames(false, intfSet);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the array of all direct subclasses of this class (array of zero length if there are none). */
|
||||
public ClassInfo[] getDirectSubclasses() {
|
||||
if (directSubclasses != null) {
|
||||
return directSubclasses;
|
||||
}
|
||||
|
||||
List<ClassInfo> listRes = new ArrayList<ClassInfo>();
|
||||
|
||||
for (PCDEntry entry : pcdm.entries()) {
|
||||
ClassInfo classInfo = pcdm.getClassInfoForPCDEntry(verCode, entry);
|
||||
if (classInfo == null) {
|
||||
continue; // New or deleted class, depending on verCode
|
||||
}
|
||||
if (classInfo.superName.equals(name)) {
|
||||
listRes.add(classInfo);
|
||||
}
|
||||
}
|
||||
|
||||
directSubclasses = listRes.toArray(new ClassInfo[listRes.size()]);
|
||||
return directSubclasses;
|
||||
}
|
||||
|
||||
/** Check if the initial values for the given primitive constatnts in two classes are the same. */
|
||||
public static boolean constFieldInitValuesEqual(ClassInfo oldClassInfo, int oldFieldNo,
|
||||
ClassInfo newClassInfo, int newFieldNo) {
|
||||
Object oldInitValue = oldClassInfo.primitiveConstantInitValues == null ? null
|
||||
: oldClassInfo.primitiveConstantInitValues[oldFieldNo];
|
||||
Object newInitValue = newClassInfo.primitiveConstantInitValues == null ? null
|
||||
: newClassInfo.primitiveConstantInitValues[newFieldNo];
|
||||
if (oldInitValue == newInitValue) {
|
||||
return true;
|
||||
}
|
||||
if (oldInitValue == null || newInitValue == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldInitValue instanceof Integer) {
|
||||
if (((Integer) oldInitValue).intValue() == ((Integer) newInitValue).intValue()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (oldInitValue instanceof String) {
|
||||
if ( ((String) oldInitValue).equals((String) newInitValue) ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (oldInitValue instanceof Long) {
|
||||
if (((Long) oldInitValue).longValue() == ((Long) newInitValue).longValue()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (oldInitValue instanceof Float) {
|
||||
if (((Float) oldInitValue).floatValue() == ((Float) newInitValue).floatValue()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (oldInitValue instanceof Double) {
|
||||
if (((Double) oldInitValue).doubleValue() == ((Double) newInitValue).doubleValue()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean implementsInterfaceDirectly(String intfName) {
|
||||
if (interfaces == null) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
if (intfName.equals(interfaces[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check if this class implements interface I or any subinterface of I directly */
|
||||
public boolean implementsIntfOrSubintfDirectly(String intfName) {
|
||||
if (interfaces == null) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
if (intfName.equals(interfaces[i])) {
|
||||
return true;
|
||||
}
|
||||
// An interface can have multiple superinterfaces, all of which are listed in its "interfaces" array
|
||||
// (although in the .java source it "extends" them all).
|
||||
ClassInfo superIntfInfo =
|
||||
pcdm.getClassInfoForName(verCode, interfaces[i]);
|
||||
if (superIntfInfo == null) {
|
||||
continue; // Class not in project
|
||||
}
|
||||
if (superIntfInfo.implementsIntfOrSubintfDirectly(intfName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class C implements interface I indirectly, if C or some superclass of C directly implements I
|
||||
* or some subinterface of I.
|
||||
*/
|
||||
public boolean implementsInterfaceDirectlyOrIndirectly(String intfName) {
|
||||
if (interfaces == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (implementsIntfOrSubintfDirectly(intfName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (superName != null) {
|
||||
ClassInfo superInfo = pcdm.getClassInfoForName(verCode, superName);
|
||||
if (superInfo == null) {
|
||||
return false; // Class not in project
|
||||
}
|
||||
return superInfo.implementsInterfaceDirectlyOrIndirectly(intfName);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this class declares a field with the same name and type as
|
||||
* the field number fieldNo in class classInfo.
|
||||
*/
|
||||
public boolean declaresField(ClassInfo classInfo, int fieldNo) {
|
||||
if (fieldNames == null) {
|
||||
return false;
|
||||
}
|
||||
String fieldName = classInfo.fieldNames[fieldNo];
|
||||
String fieldSignature = classInfo.fieldSignatures[fieldNo];
|
||||
|
||||
for (int i = 0; i < fieldNames.length; i++) {
|
||||
if (fieldName.equals(fieldNames[i]) &&
|
||||
fieldSignature.equals(fieldSignatures[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if this class declares a field with the given name, signature and access */
|
||||
public boolean declaresField(String name, String signature, boolean isStatic) {
|
||||
if (fieldNames == null) {
|
||||
return false;
|
||||
}
|
||||
signature = ("@" + signature + "#").intern();
|
||||
for (int i = 0; i < fieldNames.length; i++) {
|
||||
if (name.equals(fieldNames[i]) &&
|
||||
signature.equals(fieldSignatures[i]) &&
|
||||
Modifier.isStatic(fieldAccessFlags[i]) == isStatic) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this class declares a method with the same name and signature as
|
||||
* the method number methodNo in class classInfo.
|
||||
*/
|
||||
public boolean declaresMethod(ClassInfo classInfo, int methodNo) {
|
||||
if (methodNames == null) {
|
||||
return false;
|
||||
}
|
||||
String methodName = classInfo.methodNames[methodNo];
|
||||
String methodSignature = classInfo.methodSignatures[methodNo];
|
||||
|
||||
for (int i = 0; i < methodNames.length; i++) {
|
||||
if (methodName.equals(methodNames[i]) &&
|
||||
methodSignature.equals(methodSignatures[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this class declares a method with the same name and signature as the given method,
|
||||
* return its position. Otherwise, return -1.
|
||||
*/
|
||||
public int getDeclaredMethodPos(ClassInfo classInfo, int methodNo) {
|
||||
if (methodNames == null) {
|
||||
return -1;
|
||||
}
|
||||
String methodName = classInfo.methodNames[methodNo];
|
||||
String methodSignature = classInfo.methodSignatures[methodNo];
|
||||
|
||||
for (int i = 0; i < methodNames.length; i++) {
|
||||
if (methodName.equals(methodNames[i]) &&
|
||||
methodSignature.equals(methodSignatures[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nonnegative number (position in the method array) if this class declares a method with the
|
||||
* name methodName, and -1 otherwise.
|
||||
*/
|
||||
public int declaresSameNameMethod(String methodName) {
|
||||
if (methodNames == null) {
|
||||
return -1;
|
||||
}
|
||||
for (int j = 0; j < methodNames.length; j++) {
|
||||
if (methodName.equals(methodNames[j])) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this class references the given class in different ways, depending on thorDegree parameter.
|
||||
* thorDegree = 0: the given class (but not its array class) directly from the constantpool.
|
||||
*
|
||||
* thorDegree = 1: the given class or its array class directly from the constantpool, as a
|
||||
* type of a data field, as a type in a method signature or a thrown exception, as a directly
|
||||
* implemented interface or a direct superclass
|
||||
*
|
||||
* thorDegree = 2: the given class or its array class directly or indirectly from the
|
||||
* constantpool, as a type of a data field, as a type in a method signature or a thrown exception,
|
||||
* as a directly/indirectly implemented interface or a direct/indirect superclass.
|
||||
*
|
||||
* isRefTypeInterface indicates whether className is an interface.
|
||||
*/
|
||||
public boolean referencesClass(String className, boolean isRefTypeInterface, int thorDegree) {
|
||||
int i;
|
||||
|
||||
if (thorDegree == 0) {
|
||||
if (cpoolRefsToClasses == null) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < cpoolRefsToClasses.length; i++) {
|
||||
if (!isRefClassArray[i] &&
|
||||
className.equals(cpoolRefsToClasses[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isSubclassOf(className, (thorDegree == 1))) {
|
||||
return true;
|
||||
}
|
||||
if (isRefTypeInterface) {
|
||||
if (thorDegree == 1) {
|
||||
if (implementsInterfaceDirectly(className)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Check for indirectly implemented interfaces
|
||||
if (implementsInterfaceDirectlyOrIndirectly(className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cpoolRefsToClasses != null) {
|
||||
for (i = 0; i < cpoolRefsToClasses.length; i++) {
|
||||
if (className.equals(cpoolRefsToClasses[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (thorDegree == 2) {
|
||||
// Check for indirect references from the constantpool
|
||||
if (cpoolRefsToFieldSignatures != null) {
|
||||
for (i = 0; i < cpoolRefsToFieldSignatures.length; i++) {
|
||||
if (signatureIncludesClassName(cpoolRefsToFieldSignatures[i], className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cpoolRefsToMethodNames != null) {
|
||||
for (i = 0; i < cpoolRefsToMethodSignatures.length; i++) {
|
||||
if (signatureIncludesClassName(cpoolRefsToMethodSignatures[i], className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldSignatures != null) {
|
||||
for (i = 0; i < fieldSignatures.length; i++) {
|
||||
if (signatureIncludesClassName(fieldSignatures[i], className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (methodSignatures != null) {
|
||||
for (i = 0; i < methodSignatures.length; i++) {
|
||||
if (signatureIncludesClassName(methodSignatures[i], className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (checkedExceptions != null) {
|
||||
for (i = 0; i < checkedExceptions.length; i++) {
|
||||
if (checkedExceptions[i] != null) {
|
||||
String excArray[] = checkedExceptions[i];
|
||||
for (int j = 0; j < excArray.length; j++) {
|
||||
if (className.equals(excArray[j])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean signatureIncludesClassName(String signature, String className) {
|
||||
int stIndex = signature.indexOf(className);
|
||||
if (stIndex == -1) {
|
||||
return false;
|
||||
}
|
||||
return ((stIndex != 0 && signature.charAt(stIndex - 1) == '@' && signature.charAt(stIndex + className.length()) == '#') ||
|
||||
(stIndex == 0 && signature.length() == className.length()));
|
||||
}
|
||||
|
||||
public boolean isSubclassOf(String className, boolean directOnly) {
|
||||
if (className.equals(superName)) {
|
||||
return true;
|
||||
}
|
||||
if (directOnly) {
|
||||
return false;
|
||||
}
|
||||
String superName = this.superName;
|
||||
while (superName != null) {
|
||||
if (className.equals(superName)) {
|
||||
return true;
|
||||
}
|
||||
ClassInfo classInfo = pcdm.getClassInfoForName(verCode, superName);
|
||||
if (classInfo == null) {
|
||||
break; // Class not in project
|
||||
}
|
||||
superName = classInfo.superName;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this class references field number fieldNo of class fieldDefClassInfo. Let us call
|
||||
* this field C.f. Actual reference contained in the constant pool may be not to C.f itself,
|
||||
* but to Csub.f, where Csub is some subclass of C such that neither Csub nor any other class
|
||||
* located between C and Csub in the class hierarchy redeclares f. We look up both "real"
|
||||
* references C.f and "fake" references such as Csub.f.
|
||||
*/
|
||||
public boolean referencesField(ClassInfo fieldDefClassInfo, int fieldNo) {
|
||||
if (cpoolRefsToFieldNames == null) {
|
||||
return false;
|
||||
}
|
||||
String fieldDefClassName = fieldDefClassInfo.name;
|
||||
String fieldName = fieldDefClassInfo.fieldNames[fieldNo];
|
||||
String fieldSig = fieldDefClassInfo.fieldSignatures[fieldNo];
|
||||
for (int i = 0; i < cpoolRefsToFieldNames.length; i++) {
|
||||
if (fieldName.equals(cpoolRefsToFieldNames[i]) &&
|
||||
fieldSig.equals(cpoolRefsToFieldSignatures[i]) ) {
|
||||
if (fieldDefClassName.equals(cpoolRefsToFieldClasses[i]) ) {
|
||||
return true; // "real" reference
|
||||
} else { // Check if this is a "fake" reference that resolves to the above "real" reference
|
||||
ClassInfo classInThisCpool =
|
||||
pcdm.getClassInfoForName(verCode, cpoolRefsToFieldClasses[i]);
|
||||
if (classInThisCpool == null) {
|
||||
continue; // Class not in project
|
||||
}
|
||||
if (!classInThisCpool.isSubclassOf(fieldDefClassInfo.name, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ok, now check that this field is not actually redeclared in fieldDefClassInfo or
|
||||
// somewhere in between it and classInThisCpool
|
||||
boolean redeclared = false;
|
||||
ClassInfo curClass = classInThisCpool;
|
||||
do {
|
||||
if (curClass.declaresField(fieldDefClassInfo, fieldNo)) {
|
||||
redeclared = true;
|
||||
break;
|
||||
}
|
||||
String superName = curClass.superName;
|
||||
curClass = pcdm.getClassInfoForName(verCode, superName);
|
||||
if (curClass == null) {
|
||||
break;
|
||||
}
|
||||
} while (curClass != fieldDefClassInfo);
|
||||
if (!redeclared) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this class references method number methodNo of class methodDefClassInfo. Let us
|
||||
* call this method C.m. Actual reference contained in the constant pool may be not to C.m
|
||||
* itself, but to Csub.m, where Csub is some subclass of C such that neither Csub nor any
|
||||
* other class located between C and Csub in the class hierarchy redeclares m. We look up
|
||||
* both "real" references C.m and "fake" references such as Csub.m.
|
||||
*/
|
||||
public boolean referencesMethod(ClassInfo methodDefClassInfo, int methodNo) {
|
||||
if (cpoolRefsToMethodNames == null) {
|
||||
return false;
|
||||
}
|
||||
String methodDefClassName = methodDefClassInfo.name;
|
||||
String methodName = methodDefClassInfo.methodNames[methodNo];
|
||||
String methodSig = methodDefClassInfo.methodSignatures[methodNo];
|
||||
for (int i = 0; i < cpoolRefsToMethodNames.length; i++) {
|
||||
if (methodName.equals(cpoolRefsToMethodNames[i]) &&
|
||||
methodSig.equals(cpoolRefsToMethodSignatures[i])) {
|
||||
if (methodDefClassName.equals(cpoolRefsToMethodClasses[i])) {
|
||||
return true; // "real" reference
|
||||
} else { // Check if this is a "fake" reference that resolves to the above "real" reference
|
||||
// Be careful - class in the cpool may be not a project class (e.g. a core class).
|
||||
ClassInfo classInThisCpool =
|
||||
pcdm.getClassInfoForName(verCode, cpoolRefsToMethodClasses[i]);
|
||||
if (classInThisCpool == null) {
|
||||
continue; // Class not in project
|
||||
}
|
||||
if (classInThisCpool.isSubclassOf(methodDefClassInfo.name, false)) {
|
||||
// Ok, now check that this method is not actually redeclared in classInThisCpool (which is
|
||||
// lower in the hierarchy) or somewhere in between it and classInThisCpool
|
||||
boolean redeclared = false;
|
||||
ClassInfo curClass = classInThisCpool;
|
||||
do {
|
||||
if (curClass.declaresMethod(methodDefClassInfo, methodNo)) {
|
||||
redeclared = true;
|
||||
break;
|
||||
}
|
||||
String superName = curClass.superName;
|
||||
curClass =
|
||||
pcdm.getClassInfoForName(verCode, superName);
|
||||
if (curClass == null) {
|
||||
break;
|
||||
}
|
||||
} while (curClass != methodDefClassInfo);
|
||||
if (!redeclared) {
|
||||
return true;
|
||||
}
|
||||
} else if (methodDefClassInfo.isInterface() && classInThisCpool.implementsIntfOrSubintfDirectly(methodDefClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this class has a method that throws the given exception, return its index. Otherwise return -1.
|
||||
* The search starts from method with index startMethodIdx.
|
||||
*/
|
||||
public int hasMethodThrowingException(ClassInfo excClassInfo, int startMethodIdx) {
|
||||
if (checkedExceptions == null) {
|
||||
return -1;
|
||||
}
|
||||
if (startMethodIdx >= checkedExceptions.length) {
|
||||
return -1;
|
||||
}
|
||||
String excName = excClassInfo.name;
|
||||
for (int i = startMethodIdx; i < checkedExceptions.length; i++) {
|
||||
if (checkedExceptions[i] == null) {
|
||||
continue;
|
||||
}
|
||||
String[] exc = checkedExceptions[i];
|
||||
for (int j = 0; j < exc.length; j++) {
|
||||
if (exc[j].equals(excName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static abstract class MethodHandler {
|
||||
|
||||
abstract void handleMethod(ClassInfo ci, int methodIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check this class and all its superclasses (if includeSuperclasses == true) and superinterfaces (if includeInterfaces == true)
|
||||
* for a method with the given name. If such a method is found, call h.handleMethod(classInfo, methodIdx).
|
||||
*/
|
||||
public void findExistingSameNameMethods(String methodName, boolean includeSuperclasses, boolean includeInterfaces, MethodHandler h) {
|
||||
String className = name;
|
||||
ClassInfo classInfo;
|
||||
while (className != null) {
|
||||
classInfo = pcdm.getClassInfoForName(verCode, className);
|
||||
if (classInfo == null) {
|
||||
break; // Class not in project
|
||||
}
|
||||
String mNames[] = classInfo.methodNames;
|
||||
int mNamesLen = mNames != null ? mNames.length : 0;
|
||||
for (int i = 0; i < mNamesLen; i++) {
|
||||
if (methodName.equals(mNames[i])) {
|
||||
h.handleMethod(classInfo, i);
|
||||
}
|
||||
}
|
||||
if (includeInterfaces && classInfo.interfaces != null) {
|
||||
String intfNames[] = classInfo.interfaces;
|
||||
for (int i = 0; i < intfNames.length; i++) {
|
||||
ClassInfo superIntfInfo =
|
||||
pcdm.getClassInfoForName(verCode, intfNames[i]);
|
||||
if (superIntfInfo == null) {
|
||||
continue; // Class not in project
|
||||
}
|
||||
superIntfInfo.findExistingSameNameMethods(methodName, true, includeInterfaces, h);
|
||||
}
|
||||
}
|
||||
if (includeSuperclasses) {
|
||||
className = classInfo.superName;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPrimitiveFieldSig(String fieldSig) {
|
||||
return fieldSig.indexOf('@') == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given signature is of a class type, and that class does not belong to the project.
|
||||
* It used to be a check for just a core type name, but sometimes people use JDK sources as e.g. a test
|
||||
* case - so better perform a universal (and entirely correct, unlike just a core type name) test here.
|
||||
*/
|
||||
public boolean isNonProjectClassTypeFieldSig(String fieldSig) {
|
||||
int stPos = fieldSig.indexOf('@');
|
||||
if (stPos == -1) {
|
||||
return false;
|
||||
}
|
||||
int endPos = fieldSig.indexOf('#');
|
||||
String className = fieldSig.substring(stPos + 1, endPos);
|
||||
return (!pcdm.isProjectClass(verCode, className));
|
||||
}
|
||||
|
||||
/** For debugging. */
|
||||
public String toString() {
|
||||
return name + (verCode == VER_OLD ? " OLD" : " NEW");
|
||||
}
|
||||
}
|
@ -1,448 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* An instance of this class represents a class path, on which binary classes can be looked up.
|
||||
* It also provides several static methods to create and utilize several specific class paths used
|
||||
* throughout jmake.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 12 October 2004
|
||||
*/
|
||||
public class ClassPath {
|
||||
|
||||
private PathEntry[] paths;
|
||||
private static ClassPath projectClassPath; // Class path (currently it can contain only JARs) containing sourceless project classes.
|
||||
// See also the comment to standardClassPath.
|
||||
private static ClassPath standardClassPath; // Class path that the user specifies via the -classpath option. A sum of the
|
||||
// standardClassPath, the projectClassPath, and the virtualPath is passed to the compiler. Each of these
|
||||
// class paths are also used to look up non-project superclasses/superinterfaces of
|
||||
// project classes.
|
||||
private static ClassPath bootClassPath, extClassPath; // Class paths that by default are sun.boot.class.path and all JARs on
|
||||
// java.ext.class.path, respectively. They are used to look up non-project
|
||||
// superclasses/superinterfaces of project classes. Their values can be changed using
|
||||
// setBootClassPath() and setExtDirs().
|
||||
private static ClassPath virtualPath; // Class path that the user specifies via the -vpath option.
|
||||
private static String compilerUserClassPath; // Class path to be passed to the compiler; equals to the sum of values of parameters of
|
||||
// setClassPath() and setProjectClassPath() methods.
|
||||
private static String standardClassPathStr, projectClassPathStr, bootClassPathStr, extDirsStr,
|
||||
virtualPathStr;
|
||||
private static Map<String,ClassInfo> classCache;
|
||||
|
||||
|
||||
static {
|
||||
resetOnFinish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed since some environments, e.g. NetBeans, can keep jmake classes in memory
|
||||
* permanently. Thus unchanged class paths from previous, possibly unrelated invocations
|
||||
* of jmake, may interfere with the current settings.
|
||||
*/
|
||||
public static void resetOnFinish() {
|
||||
projectClassPath = standardClassPath = bootClassPath = extClassPath = virtualPath =
|
||||
null;
|
||||
compilerUserClassPath = null;
|
||||
standardClassPathStr = projectClassPathStr = bootClassPathStr =
|
||||
extDirsStr = virtualPathStr = null;
|
||||
classCache = new LinkedHashMap<String,ClassInfo>();
|
||||
}
|
||||
|
||||
public static void setClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
|
||||
standardClassPathStr = value;
|
||||
standardClassPath = new ClassPath(value, false);
|
||||
}
|
||||
|
||||
public static void setProjectClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
|
||||
projectClassPathStr = value;
|
||||
projectClassPath = new ClassPath(value, true);
|
||||
}
|
||||
|
||||
public static void setBootClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
|
||||
bootClassPathStr = value;
|
||||
bootClassPath = new ClassPath(value, false);
|
||||
}
|
||||
|
||||
public static void setExtDirs(String value) throws PublicExceptions.InvalidCmdOptionException {
|
||||
extDirsStr = value;
|
||||
// Extension class path needs special handling, since it consists of directories, which contain .jars
|
||||
// So we need to find all these .jars in all these dirs and add them to extClassPathElementList
|
||||
List<String> extClassPathElements = new ArrayList<String>();
|
||||
for (StringTokenizer tok =
|
||||
new StringTokenizer(value, File.pathSeparator); tok.hasMoreTokens();) {
|
||||
File extDir = new File(tok.nextToken());
|
||||
String[] extJars = extDir.list(new FilenameFilter() {
|
||||
|
||||
public boolean accept(File dir, String name) {
|
||||
name = name.toLowerCase(Locale.ENGLISH);
|
||||
return name.endsWith(".zip") || name.endsWith(".jar");
|
||||
}
|
||||
});
|
||||
if (extJars == null) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < extJars.length; i++) {
|
||||
extClassPathElements.add(extDir + File.separator + extJars[i]);
|
||||
}
|
||||
}
|
||||
extClassPath = new ClassPath(extClassPathElements, false);
|
||||
}
|
||||
|
||||
public static void setVirtualPath(String value) throws PublicExceptions.InvalidCmdOptionException {
|
||||
if (value == null) {
|
||||
throw new PublicExceptions.InvalidCmdOptionException("null argument");
|
||||
}
|
||||
StringTokenizer st = new StringTokenizer(value, File.pathSeparator);
|
||||
while (st.hasMoreElements()) {
|
||||
String dir = st.nextToken();
|
||||
if ( ! (new File(dir)).isDirectory()) {
|
||||
throw new PublicExceptions.InvalidCmdOptionException("Virtual path must contain only directories." +
|
||||
" Entry " + dir + " is not a directory.");
|
||||
}
|
||||
}
|
||||
virtualPathStr = value;
|
||||
virtualPath = new ClassPath(value, false);
|
||||
}
|
||||
|
||||
public static void initializeAllClassPaths() {
|
||||
// First set the compiler class path value
|
||||
if (standardClassPathStr == null && projectClassPathStr == null) {
|
||||
compilerUserClassPath = ".";
|
||||
} else if (standardClassPathStr == null) {
|
||||
compilerUserClassPath = projectClassPathStr;
|
||||
} else if (projectClassPathStr == null) {
|
||||
compilerUserClassPath = standardClassPathStr;
|
||||
} else {
|
||||
compilerUserClassPath =
|
||||
standardClassPathStr + File.pathSeparator + projectClassPathStr;
|
||||
}
|
||||
|
||||
if (virtualPathStr != null) {
|
||||
compilerUserClassPath += File.pathSeparator + virtualPathStr;
|
||||
}
|
||||
|
||||
if (standardClassPathStr == null) {
|
||||
try {
|
||||
String tmp = ".";
|
||||
if (virtualPathStr != null) {
|
||||
tmp += File.pathSeparator + virtualPathStr;
|
||||
}
|
||||
standardClassPath = new ClassPath(tmp, false);
|
||||
} catch (PublicExceptions.InvalidCmdOptionException ex) { /* Should not happen */ }
|
||||
}
|
||||
if (projectClassPathStr == null) {
|
||||
projectClassPath = new ClassPath();
|
||||
}
|
||||
|
||||
// Create the core class path as a combination of sun.boot.class.path and java.ext.dirs contents
|
||||
if (bootClassPathStr == null) {
|
||||
try {
|
||||
bootClassPath =
|
||||
new ClassPath(System.getProperty("sun.boot.class.path"), false);
|
||||
} catch (PublicExceptions.InvalidCmdOptionException ex) { /* Shouldn't happen */ }
|
||||
// bootClassPathStr should remain null, so that nothing that the user didn't specify is passed to the compiler
|
||||
}
|
||||
|
||||
if (extDirsStr == null) {
|
||||
try {
|
||||
setExtDirs(System.getProperty("java.ext.dirs"));
|
||||
} catch (PublicExceptions.InvalidCmdOptionException ex) { /* Shouldn't happen */ }
|
||||
// extDirsStr should remain null, so that nothing that the user didn't specify is passed to the compiler
|
||||
extDirsStr = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Never returns null - if classpath wasn't set explicitly, returns "." */
|
||||
public static String getCompilerUserClassPath() {
|
||||
return compilerUserClassPath;
|
||||
}
|
||||
|
||||
/** Will return null if boot class path wasn't explicitly specified */
|
||||
public static String getCompilerBootClassPath() {
|
||||
return bootClassPathStr;
|
||||
}
|
||||
|
||||
/** Will return null if extdirs weren't explicitly specified */
|
||||
public static String getCompilerExtDirs() {
|
||||
return extDirsStr;
|
||||
}
|
||||
|
||||
/** Will return null if virtualPath wasn't explicitly specified */
|
||||
public static String getVirtualPath() {
|
||||
return virtualPathStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given class return the list of all of its superclasses (excluding Object), that can be loaded from
|
||||
* projectClassPath or standardClassPath, plus the first superclass that can be loaded from coreClassPath.
|
||||
* The latter is an optimization based on the assumption that core classes never change, or rather the programmer
|
||||
* will recompile everything when they switch to a new JDK version. The optimization prevents us from wasting time
|
||||
* repeatedly loading the same sets of core classes.
|
||||
*/
|
||||
public static void getSuperclasses(String className,
|
||||
Collection<String> res, PCDManager pcdm) {
|
||||
int iterNo = 0;
|
||||
while (!"java/lang/Object".equals(className)) {
|
||||
ClassInfo ci = getClassInfoForName(className, pcdm);
|
||||
if (ci == null) {
|
||||
return;
|
||||
}
|
||||
if (iterNo++ > 0) {
|
||||
res.add(ci.name);
|
||||
}
|
||||
className = ci.superName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the given set the names of all interfaces implemented by the given class, that can be loaded from
|
||||
* projectClassPath or standardClassPath, plus the first interface on each branch that can be loaded from
|
||||
* coreClassPath. It's the same optimization as in getSuperclasses().
|
||||
*/
|
||||
public static void addAllImplementedInterfaceNames(String className,
|
||||
Set<String> intfSet, PCDManager pcdm) {
|
||||
if ("java/lang/Object".equals(className)) {
|
||||
return;
|
||||
}
|
||||
ClassInfo ci = getClassInfoForName(className, pcdm);
|
||||
if (ci == null) {
|
||||
return;
|
||||
}
|
||||
String[] interfaces = ci.interfaces;
|
||||
if (interfaces != null) {
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
intfSet.add(interfaces[i]);
|
||||
addAllImplementedInterfaceNames(interfaces[i], intfSet, pcdm);
|
||||
}
|
||||
}
|
||||
|
||||
String superName = ci.superName;
|
||||
if (superName != null) {
|
||||
addAllImplementedInterfaceNames(superName, intfSet, pcdm);
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getProjectJars() {
|
||||
if (projectClassPath == null || projectClassPath.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
PathEntry paths[] = projectClassPath.paths;
|
||||
String[] ret = new String[paths.length];
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
ret[i] = paths[i].toString();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static ClassInfo getClassInfoForName(String className, PCDManager pcdm) {
|
||||
ClassInfo info = classCache.get(className);
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
|
||||
byte buf[] = bootClassPath.getBytesForClass(className);
|
||||
if (buf == null) {
|
||||
buf = extClassPath.getBytesForClass(className);
|
||||
}
|
||||
if (buf == null) {
|
||||
buf = standardClassPath.getBytesForClass(className);
|
||||
}
|
||||
if (buf == null) {
|
||||
buf = projectClassPath.getBytesForClass(className);
|
||||
}
|
||||
if (buf == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
info = new ClassInfo(buf, pcdm, className);
|
||||
classCache.put(className, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
/** Returns the class loader that would load classes from the given class path. */
|
||||
public static ClassLoader getClassLoaderForPath(String classPath) throws Exception {
|
||||
boolean isWindows = System.getProperty("os.name").startsWith("Win");
|
||||
ClassPath cp = new ClassPath(classPath, false);
|
||||
PathEntry[] paths = cp.paths;
|
||||
URL[] urls = new URL[paths.length];
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
String dirOrJar = paths[i].toString();
|
||||
if (!(dirOrJar.startsWith("file://") || dirOrJar.startsWith("http://"))) {
|
||||
// On Windows, if I have path specified as "file://c:\...", (i.e. with the drive name) URLClassLoader works
|
||||
// unbelievably slow. However, if an additional slash is added, like : "file:///c:\...", the speed becomes
|
||||
// normal. To me it looks like a bug, but, anyway, I am taking measure here.
|
||||
if (isWindows && dirOrJar.charAt(1) == ':') {
|
||||
dirOrJar = "/" + dirOrJar;
|
||||
}
|
||||
dirOrJar = new File(dirOrJar).toURI().toString();
|
||||
}
|
||||
if (!(dirOrJar.endsWith(".jar") || dirOrJar.endsWith(".zip") || dirOrJar.endsWith(File.separator))) {
|
||||
dirOrJar += File.separator; // So that URLClassLoader correctly handles it as a directory
|
||||
}
|
||||
urls[i] = new URL(dirOrJar);
|
||||
}
|
||||
|
||||
return new URLClassLoader(urls);
|
||||
//} catch (java.net.MalformedURLException e) {
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------ Private implementation --------------------------------------------
|
||||
private ClassPath() {
|
||||
paths = new PathEntry[0];
|
||||
}
|
||||
|
||||
private ClassPath(String classPath, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
|
||||
if (classPath == null) {
|
||||
throw new PublicExceptions.InvalidCmdOptionException("null argument");
|
||||
}
|
||||
List<String> vec = new ArrayList<String>();
|
||||
|
||||
for (StringTokenizer tok =
|
||||
new StringTokenizer(classPath, File.pathSeparator); tok.hasMoreTokens();) {
|
||||
String path = tok.nextToken();
|
||||
vec.add(path);
|
||||
}
|
||||
init(vec, isJarOnly);
|
||||
}
|
||||
|
||||
private ClassPath(List<String> pathEntries, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
|
||||
init(pathEntries, isJarOnly);
|
||||
}
|
||||
|
||||
private void init(List<String> pathEntries, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
|
||||
if (pathEntries == null) {
|
||||
throw new PublicExceptions.InvalidCmdOptionException("null argument");
|
||||
}
|
||||
List<PathEntry> vec = new ArrayList<PathEntry>(pathEntries.size());
|
||||
for (int i = 0; i < pathEntries.size(); i++) {
|
||||
String path = pathEntries.get(i);
|
||||
if (!path.equals("")) {
|
||||
File file = new File(path);
|
||||
try {
|
||||
if (file.exists() && file.canRead()) {
|
||||
if (file.isDirectory()) {
|
||||
if (isJarOnly) {
|
||||
throw new PublicExceptions.InvalidCmdOptionException("directories are not allowed on this class path: " + path);
|
||||
}
|
||||
vec.add(new Dir(file));
|
||||
} else {
|
||||
vec.add(new Zip(new ZipFile(file)));
|
||||
}
|
||||
} else if (isJarOnly) {
|
||||
throw new IOException("file does not exist");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (isJarOnly) {
|
||||
throw new PublicExceptions.InvalidCmdOptionException("error initializing class path component " + path + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paths = new PathEntry[vec.size()];
|
||||
vec.toArray(paths);
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return paths.length == 0;
|
||||
}
|
||||
|
||||
private byte[] getBytesForClass(String className) {
|
||||
String fileName = className + ".class";
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
byte buf[] = paths[i].getBytesForClassFile(fileName);
|
||||
if (buf != null) {
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (paths == null) {
|
||||
return "NULL";
|
||||
}
|
||||
StringBuilder res = new StringBuilder();
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
res.append(paths[i].toString());
|
||||
}
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------ Private helper classes --------------------------------------------
|
||||
private static abstract class PathEntry {
|
||||
|
||||
abstract byte[] getBytesForClassFile(String fileName);
|
||||
|
||||
public abstract String toString();
|
||||
}
|
||||
|
||||
private static class Dir extends PathEntry {
|
||||
|
||||
private String dir;
|
||||
|
||||
Dir(File f) throws IOException {
|
||||
dir = f.getCanonicalPath();
|
||||
}
|
||||
|
||||
byte[] getBytesForClassFile(String fileName) {
|
||||
File file = new File(dir + File.separatorChar + fileName);
|
||||
if (file.exists()) {
|
||||
return Utils.readFileIntoBuffer(file);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Zip extends PathEntry {
|
||||
|
||||
private ZipFile zip;
|
||||
|
||||
Zip(ZipFile z) {
|
||||
zip = z;
|
||||
}
|
||||
|
||||
byte[] getBytesForClassFile(String fileName) {
|
||||
ZipEntry entry = zip.getEntry(fileName);
|
||||
if (entry != null) {
|
||||
return Utils.readZipEntryIntoBuffer(zip, entry);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return zip.getName();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,610 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class implements checking of source compatibility of classes and supporting operations
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 12 March 2004
|
||||
*/
|
||||
public class CompatibilityChecker {
|
||||
|
||||
private PCDManager pcdm;
|
||||
private RefClassFinder rf;
|
||||
ClassInfo oldClassInfo = null;
|
||||
ClassInfo newClassInfo = null;
|
||||
private boolean versionsCompatible;
|
||||
private boolean publicConstantChanged;
|
||||
|
||||
public CompatibilityChecker(PCDManager pcdm, boolean failOnDependentJar, boolean noWarnOnDependentJar) {
|
||||
this.pcdm = pcdm;
|
||||
publicConstantChanged = false;
|
||||
rf = new RefClassFinder(pcdm, failOnDependentJar, noWarnOnDependentJar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the two class versions for the given PCDEntry. Returns true if all changes are source
|
||||
* compatible, and false otherwise.
|
||||
*/
|
||||
public boolean compareClassVersions(PCDEntry entry) {
|
||||
// I once had the following optimization here with the comment "No sense to make any further checks if
|
||||
// everything is recompiled anyway", but now I believe it's wrong. For each class that was found changed
|
||||
// we need to know whether the new version is compatible with the old or not, since this may determine
|
||||
// whether the new version of this class is promoted into the pdb or not (see PCDManager.updateClassInfoInPCD()).
|
||||
// So, all changed classes should be checked just to correctly determine version compatibility.
|
||||
// if (publicConstantChanged) return false;
|
||||
|
||||
oldClassInfo = pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, entry);
|
||||
newClassInfo = pcdm.getClassInfoForPCDEntry(ClassInfo.VER_NEW, entry);
|
||||
|
||||
rf.initialize(oldClassInfo.name, entry.javaFileFullPath.endsWith(".jar"));
|
||||
versionsCompatible = true;
|
||||
|
||||
checkAccessFlags();
|
||||
checkSuperclasses();
|
||||
checkImplementedInterfaces();
|
||||
checkFields();
|
||||
checkMethodsAndConstructors();
|
||||
|
||||
return versionsCompatible;
|
||||
}
|
||||
|
||||
/** Find all dependent classes for a deleted class. */
|
||||
public void checkDeletedClass(PCDEntry entry) {
|
||||
oldClassInfo = entry.oldClassInfo;
|
||||
rf.initialize(oldClassInfo.name, entry.javaFileFullPath.endsWith(".jar"));
|
||||
rf.findReferencingClassesForDeletedClass(oldClassInfo);
|
||||
// It may happen that the only reference to deleted class X is via "X.class" construct
|
||||
String packageToLookIn =
|
||||
oldClassInfo.isPublic() ? null : oldClassInfo.packageName;
|
||||
rf.findClassesDeclaringField(("class$" + oldClassInfo.name).intern(), "java/lang/Class", true, packageToLookIn);
|
||||
checkForFinalFields();
|
||||
}
|
||||
|
||||
/** Returns the names of classes affected by source incompatible changes to the new version of the checked class. */
|
||||
public String[] getAffectedClasses() {
|
||||
return rf.getAffectedClassNames();
|
||||
}
|
||||
|
||||
/** All of the following methods return true if no source incompatible changes found, and false otherwise */
|
||||
private void checkAccessFlags() {
|
||||
char oldClassFlags = oldClassInfo.accessFlags;
|
||||
char newClassFlags = newClassInfo.accessFlags;
|
||||
if (oldClassFlags == newClassFlags) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Modifier.isFinal(oldClassFlags) && Modifier.isFinal(newClassFlags)) {
|
||||
versionsCompatible = false;
|
||||
rf.findDirectSubclasses(oldClassInfo);
|
||||
}
|
||||
|
||||
if (!Modifier.isAbstract(oldClassFlags) && Modifier.isAbstract(newClassFlags)) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClasses0(oldClassInfo);
|
||||
}
|
||||
|
||||
// Now to accessibility modifiers checking...
|
||||
if (Modifier.isPublic(newClassFlags)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Modifier.isProtected(newClassFlags)) {
|
||||
if (Modifier.isPublic(oldClassFlags)) {
|
||||
versionsCompatible = false;
|
||||
rf.findDiffPackageAndNotSubReferencingClasses1(oldClassInfo);
|
||||
}
|
||||
} else if (Modifier.isPrivate(newClassFlags)) {
|
||||
if (!Modifier.isPrivate(oldClassFlags)) {
|
||||
versionsCompatible = false;
|
||||
} else {
|
||||
return; // private -> private, nothing more to check
|
||||
}
|
||||
if (Modifier.isPublic(oldClassFlags)) {
|
||||
rf.findReferencingClasses1(oldClassInfo);
|
||||
} else if (Modifier.isProtected(oldClassFlags)) {
|
||||
rf.findThisPackageOrSubReferencingClasses1(oldClassInfo);
|
||||
} else {
|
||||
rf.findThisPackageReferencingClasses1(oldClassInfo);
|
||||
}
|
||||
} else { // newClassFlags has default access, since public has already been excluded
|
||||
if (Modifier.isPublic(oldClassFlags)) {
|
||||
versionsCompatible = false;
|
||||
rf.findDiffPackageReferencingClasses1(oldClassInfo);
|
||||
} else if (Modifier.isProtected(oldClassFlags)) {
|
||||
versionsCompatible = false;
|
||||
rf.findDiffPackageAndSubReferencingClasses1(oldClassInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSuperclasses() {
|
||||
List<String> oldSuperNames = oldClassInfo.getAllSuperclassNames();
|
||||
List<String> newSuperNames = newClassInfo.getAllSuperclassNames();
|
||||
|
||||
int oldNamesSizeMinusOne = oldSuperNames.size() - 1;
|
||||
for (int i = 0; i <= oldNamesSizeMinusOne; i++) {
|
||||
String oldSuperName = oldSuperNames.get(i);
|
||||
if (!newSuperNames.contains(oldSuperName)) {
|
||||
versionsCompatible = false;
|
||||
ClassInfo missingSuperClass =
|
||||
pcdm.getClassInfoForName(ClassInfo.VER_OLD, oldSuperName);
|
||||
if (missingSuperClass == null) { // This class is not in project
|
||||
missingSuperClass =
|
||||
ClassPath.getClassInfoForName(oldSuperName, pcdm);
|
||||
if (missingSuperClass == null) {
|
||||
missingSuperClass = new ClassInfo(oldSuperName, pcdm);
|
||||
}
|
||||
}
|
||||
rf.findReferencingClasses2(missingSuperClass, oldClassInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Now check if the class is an exception, and its kind has changed from unchecked to checked
|
||||
if (oldClassInfo.isInterface() || oldSuperNames.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (!(oldSuperNames.contains("java/lang/RuntimeException") || oldSuperNames.contains("java/lang/Error"))) {
|
||||
return;
|
||||
}
|
||||
if (!(newSuperNames.contains("java/lang/RuntimeException") || newSuperNames.contains("java/lang/Error"))) {
|
||||
if (!newSuperNames.contains("java/lang/Throwable")) {
|
||||
return;
|
||||
}
|
||||
// Ok, exception kind has changed from unchecked to checked.
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClasses0(oldClassInfo);
|
||||
rf.findRefsToMethodsThrowingException(oldClassInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkImplementedInterfaces() {
|
||||
Set<String> oldIntfNames = oldClassInfo.getAllImplementedIntfNames();
|
||||
Set<String> newIntfNames = newClassInfo.getAllImplementedIntfNames();
|
||||
|
||||
for (String oldIntfName : oldIntfNames) {
|
||||
if (!newIntfNames.contains(oldIntfName)) {
|
||||
versionsCompatible = false;
|
||||
ClassInfo missingSuperInterface =
|
||||
pcdm.getClassInfoForName(ClassInfo.VER_OLD, oldIntfName);
|
||||
if (missingSuperInterface == null) { // This class is not in project
|
||||
missingSuperInterface =
|
||||
ClassPath.getClassInfoForName(oldIntfName, pcdm);
|
||||
if (missingSuperInterface == null) {
|
||||
missingSuperInterface = new ClassInfo(oldIntfName, pcdm);
|
||||
}
|
||||
}
|
||||
rf.findReferencingClasses2(missingSuperInterface, oldClassInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the class is abstract, and an interface has been added to its list of implemented interfaces
|
||||
if (newClassInfo.isAbstract()) {
|
||||
for (String newIntfName : newIntfNames) {
|
||||
if (!oldIntfNames.contains(newIntfName)) {
|
||||
versionsCompatible = false;
|
||||
rf.findConcreteSubclasses(oldClassInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFields() {
|
||||
String oFNames[] = oldClassInfo.fieldNames;
|
||||
String oFSignatures[] = oldClassInfo.fieldSignatures;
|
||||
char oFFlags[] = oldClassInfo.fieldAccessFlags;
|
||||
String nFNames[] = newClassInfo.fieldNames;
|
||||
String nFSignatures[] = newClassInfo.fieldSignatures;
|
||||
char nFFlags[] = newClassInfo.fieldAccessFlags;
|
||||
int oFLen = oFNames != null ? oFNames.length : 0;
|
||||
int nFLen = nFNames != null ? nFNames.length : 0;
|
||||
|
||||
int oFMod, nFMod;
|
||||
String oFName, oFSig, nFName;
|
||||
int i, j, k, endIdx;
|
||||
int nonMatchingNewFields = nFLen;
|
||||
|
||||
for (i = 0; i < oFLen; i++) {
|
||||
oFMod = oFFlags[i];
|
||||
if (Modifier.isPrivate(oFMod)) {
|
||||
continue; // Changes to private fields don't affect compatibility
|
||||
}
|
||||
oFName = oFNames[i];
|
||||
oFSig = oFSignatures[i];
|
||||
boolean found = false;
|
||||
|
||||
// Look for the same field in the new version considering name and type
|
||||
endIdx = nFLen - 1;
|
||||
k = i < nFLen ? i : endIdx;
|
||||
for (j = 0; j < nFLen; j++) {
|
||||
if (oFName.equals(nFNames[k]) &&
|
||||
oFSig.equals(nFSignatures[k])) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (k < endIdx) {
|
||||
k++;
|
||||
} else {
|
||||
k = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
nonMatchingNewFields--;
|
||||
nFMod = nFFlags[k];
|
||||
checkFieldModifiers(oFMod, nFMod, i, k);
|
||||
if (publicConstantChanged) {
|
||||
return;
|
||||
}
|
||||
} else { // Matching field not found
|
||||
if (Modifier.isStatic(oFMod) && Modifier.isFinal(oFMod) &&
|
||||
oldClassInfo.primitiveConstantInitValues != null &&
|
||||
oldClassInfo.primitiveConstantInitValues[i] != null) {
|
||||
// Compile-time constant deleted
|
||||
versionsCompatible = false;
|
||||
rf.findAllProjectClasses(oldClassInfo, i);
|
||||
if (Modifier.isPublic(oFMod)) {
|
||||
publicConstantChanged = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForField(oldClassInfo, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nonMatchingNewFields > 0) { // There are some fields declared in the new version which don't exist in the old one
|
||||
// Look for fields hiding same-named fields in superclasses
|
||||
for (i = 0; i < nFLen; i++) {
|
||||
nFName = nFNames[i];
|
||||
|
||||
boolean found = false;
|
||||
for (j = 0; j < oFLen; j++) {
|
||||
if (nFName.equals(oFNames[j])) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
continue; // nFName is not an added field
|
||||
}
|
||||
String superName = oldClassInfo.superName;
|
||||
ClassInfo superInfo;
|
||||
while (superName != null) {
|
||||
superInfo =
|
||||
pcdm.getClassInfoForName(ClassInfo.VER_OLD, superName);
|
||||
if (superInfo == null) {
|
||||
break;
|
||||
}
|
||||
String[] superOFNames = superInfo.fieldNames;
|
||||
int superOFNamesLen = superOFNames != null ? superOFNames.length
|
||||
: 0;
|
||||
for (j = 0; j < superOFNamesLen; j++) {
|
||||
if (nFName == superOFNames[j]) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForField(superInfo, j);
|
||||
}
|
||||
}
|
||||
superName = superInfo.superName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** It is already known that old field is not private */
|
||||
private void checkFieldModifiers(int oFMod, int nFMod, int oldFieldIdx, int newFieldIdx) {
|
||||
if (oFMod == nFMod) {
|
||||
if (Modifier.isFinal(oFMod) &&
|
||||
(!ClassInfo.constFieldInitValuesEqual(oldClassInfo, oldFieldIdx, newClassInfo, newFieldIdx))) {
|
||||
versionsCompatible = false;
|
||||
rf.findAllProjectClasses(oldClassInfo, oldFieldIdx);
|
||||
if (Modifier.isPublic(oFMod)) {
|
||||
publicConstantChanged = true; // Means we will have to recompile ALL project classes
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// These tests are ordered such that if a previous test succeeds, there is no need to do further tests, since that
|
||||
// former test will cause more classes to be checked than any of the further tests. That is why it is possible to
|
||||
// check properties that are in fact independent (e.g. accessibility vs. static/non-static) together. But this
|
||||
// optimization only works since all kinds of tests result in the same kind of find..ReferencingClassesForField()
|
||||
// outcome. For methods this is not true, and so there we have to check independent properties separately.
|
||||
if (Modifier.isStatic(oFMod) && Modifier.isFinal(oFMod) && // oFMod is known to be non-private
|
||||
(!Modifier.isFinal(nFMod) || !ClassInfo.constFieldInitValuesEqual(oldClassInfo, oldFieldIdx, newClassInfo, newFieldIdx))) {
|
||||
versionsCompatible = false;
|
||||
rf.findAllProjectClasses(oldClassInfo, oldFieldIdx);
|
||||
if (Modifier.isPublic(oFMod)) {
|
||||
publicConstantChanged = true;
|
||||
}
|
||||
} else if (Modifier.isPrivate(nFMod) || // oFMod is known to be non-private
|
||||
(!Modifier.isFinal(oFMod) && Modifier.isFinal(nFMod)) ||
|
||||
(Modifier.isStatic(oFMod) != Modifier.isStatic(nFMod)) ||
|
||||
(Modifier.isVolatile(oFMod) != Modifier.isVolatile(nFMod))) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForField(oldClassInfo, oldFieldIdx);
|
||||
} else if (Modifier.isPublic(oFMod) && Modifier.isProtected(nFMod)) {
|
||||
versionsCompatible = false;
|
||||
rf.findDiffPackageReferencingClassesForField(oldClassInfo, oldFieldIdx);
|
||||
} else if ((Modifier.isPublic(oFMod) || Modifier.isProtected(oFMod)) &&
|
||||
(!(Modifier.isPublic(nFMod) || Modifier.isProtected(nFMod) || Modifier.isPrivate(nFMod)))) {
|
||||
versionsCompatible = false;
|
||||
if (Modifier.isPublic(oFMod)) {
|
||||
rf.findDiffPackageReferencingClassesForField(oldClassInfo, oldFieldIdx);
|
||||
} else {
|
||||
rf.findDiffPackageAndSubReferencingClassesForField(oldClassInfo, oldFieldIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForFinalFields() {
|
||||
char oFFlags[] = oldClassInfo.fieldAccessFlags;
|
||||
int oFLen = oldClassInfo.fieldNames != null ? oldClassInfo.fieldNames.length
|
||||
: 0;
|
||||
int oFMod;
|
||||
|
||||
for (int i = 0; i < oFLen; i++) {
|
||||
oFMod = oFFlags[i];
|
||||
if (Modifier.isPrivate(oFMod)) {
|
||||
continue; // Changes to private fields don't affect compatibility
|
||||
}
|
||||
if (Modifier.isStatic(oFMod) && Modifier.isFinal(oFMod)) {
|
||||
rf.findAllProjectClasses(oldClassInfo, i);
|
||||
if (Modifier.isPublic(oFMod)) {
|
||||
publicConstantChanged = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkMethodsAndConstructors() {
|
||||
String oMNames[] = oldClassInfo.methodNames;
|
||||
String oMSignatures[] = oldClassInfo.methodSignatures;
|
||||
char oMFlags[] = oldClassInfo.methodAccessFlags;
|
||||
String nMNames[] = newClassInfo.methodNames;
|
||||
String nMSignatures[] = newClassInfo.methodSignatures;
|
||||
char nMFlags[] = newClassInfo.methodAccessFlags;
|
||||
int oMLen = oMNames != null ? oMNames.length : 0;
|
||||
int nMLen = nMNames != null ? nMNames.length : 0;
|
||||
|
||||
int oMMod, nMMod;
|
||||
String oMName, oMSig, nMName, nMSig;
|
||||
int i, j, k, endIdx;
|
||||
int nonMatchingNewMethods = nMLen;
|
||||
|
||||
for (i = 0; i < oMLen; i++) {
|
||||
oMMod = oMFlags[i];
|
||||
if (Modifier.isPrivate(oMMod)) {
|
||||
continue; // Changes to private methods don't affect compatibility
|
||||
}
|
||||
oMName = oMNames[i];
|
||||
oMSig = oMSignatures[i];
|
||||
boolean found = false;
|
||||
|
||||
// Look for the same method in the new version considering name and signature
|
||||
endIdx = nMLen - 1;
|
||||
k = i < nMLen ? i : endIdx;
|
||||
for (j = 0; j < nMLen; j++) {
|
||||
if (oMName == nMNames[k] && oMSig == nMSignatures[k]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (k < endIdx) {
|
||||
k++;
|
||||
} else {
|
||||
k = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
nonMatchingNewMethods--;
|
||||
nMMod = nMFlags[k];
|
||||
if (oMMod != nMMod) {
|
||||
checkMethodModifiers(oMMod, nMMod, i);
|
||||
}
|
||||
|
||||
// Check if the new method throws more exceptions than the old one
|
||||
if (newClassInfo.checkedExceptions != null && newClassInfo.checkedExceptions[k] != null) {
|
||||
if (oldClassInfo.checkedExceptions == null) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForMethod(oldClassInfo, i);
|
||||
} else if (oldClassInfo.checkedExceptions[i] == null) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForMethod(oldClassInfo, i);
|
||||
} else {
|
||||
String oldExceptions[] =
|
||||
oldClassInfo.checkedExceptions[i];
|
||||
String newExceptions[] =
|
||||
newClassInfo.checkedExceptions[k];
|
||||
for (int ei = 0; ei < newExceptions.length; ei++) {
|
||||
String newEx = newExceptions[ei];
|
||||
found = false;
|
||||
for (int ej = 0; ej < oldExceptions.length; ej++) {
|
||||
if (newEx.equals(oldExceptions[ej])) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForMethod(oldClassInfo, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // Matching method not found
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForMethod(oldClassInfo, i);
|
||||
// Deleting a concrete method from an abstract class is a special case
|
||||
if (oldClassInfo.isAbstract() && !Modifier.isAbstract(oMMod)) {
|
||||
rf.findConcreteSubclassesNotOverridingAbstractMethod(oldClassInfo, oldClassInfo, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nonMatchingNewMethods > 0) { // There are some methods/constructors declared in the new version which don't exist in the old one
|
||||
if (!oldClassInfo.isInterface()) {
|
||||
for (i = 0; i < nMLen; i++) {
|
||||
nMMod = nMFlags[i];
|
||||
if (Modifier.isPrivate(nMMod)) {
|
||||
continue;
|
||||
}
|
||||
String newMName = nMNames[i];
|
||||
final String newMSig = nMSignatures[i];
|
||||
final boolean isStatic = Modifier.isStatic(nMMod);
|
||||
|
||||
boolean found = false;
|
||||
for (j = 0; j < oMLen; j++) {
|
||||
if (newMName.equals(oMNames[j]) &&
|
||||
newMSig.equals(oMSignatures[j])) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
continue; // nMName is not an added method
|
||||
}
|
||||
// Check if the new method is a static one that hides an inherited static method
|
||||
// Check if the new method overloads an existing (declared or inherited) method. Overloading test is rough -
|
||||
// we just check if the number of parameters is the same. Note that if a new constructor has been added, it
|
||||
// can be treated in the same way, except that we shouldn't look up "same name methods" for it in superclasses.
|
||||
oldClassInfo.findExistingSameNameMethods(newMName,
|
||||
!newMName.equals("<init>"), false,
|
||||
new ClassInfo.MethodHandler() {
|
||||
|
||||
void handleMethod(ClassInfo classInfo, int methodIdx) {
|
||||
String otherMSig =
|
||||
classInfo.methodSignatures[methodIdx];
|
||||
if ((newMSig.equals(otherMSig) && isStatic &&
|
||||
classInfo != oldClassInfo) ||
|
||||
(newMSig != otherMSig &&
|
||||
Utils.sameParamNumber(newMSig, otherMSig))) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForMethod(classInfo, methodIdx);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (Modifier.isAbstract(nMMod)) {
|
||||
// An abstract method added to the class. Find any concrete subclasses that don't override
|
||||
// or inherit a concrete implementation of this method.
|
||||
versionsCompatible = false;
|
||||
rf.findConcreteSubclassesNotOverridingAbstractMethod(oldClassInfo, newClassInfo, i);
|
||||
}
|
||||
// Check if there is a method with the same name in some subclass, such that it now overrides
|
||||
// or overloads the added method.
|
||||
if (subclassesDeclareSameNameMethod(oldClassInfo, newMName)) {
|
||||
versionsCompatible = false;
|
||||
}
|
||||
}
|
||||
} else { // We are checking an interface.
|
||||
for (i = 0; i < nMLen; i++) {
|
||||
String newMName = nMNames[i];
|
||||
final String newMSig = nMSignatures[i];
|
||||
|
||||
boolean found = false;
|
||||
for (j = 0; j < oMLen; j++) {
|
||||
if (newMName == oMNames[j] && newMSig == oMSignatures[j]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
versionsCompatible = false;
|
||||
|
||||
// Check if the new method overloads an existing (declared or inherited) method. Overloading test is rough -
|
||||
// we just check if the number of parameters is the same.
|
||||
oldClassInfo.findExistingSameNameMethods(newMName, true, true, new ClassInfo.MethodHandler() {
|
||||
|
||||
void handleMethod(ClassInfo classInfo, int methodIdx) {
|
||||
String otherMSig =
|
||||
classInfo.methodSignatures[methodIdx];
|
||||
if (newMSig != otherMSig &&
|
||||
Utils.sameParamNumber(newMSig, otherMSig)) {
|
||||
rf.findReferencingClassesForMethod(classInfo, methodIdx);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rf.findDirectlyAndOtherwiseImplementingConcreteClasses(oldClassInfo);
|
||||
rf.findAbstractSubtypesWithSameNameMethod(oldClassInfo, newMName, newMSig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkMethodModifiers(int oMMod, int nMMod, int oldMethodIdx) {
|
||||
if (Modifier.isPrivate(nMMod)) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
|
||||
} else if (Modifier.isPublic(oMMod) && Modifier.isProtected(nMMod)) {
|
||||
versionsCompatible = false;
|
||||
rf.findDiffPackageReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
|
||||
} else if ((Modifier.isPublic(oMMod) || Modifier.isProtected(oMMod)) &&
|
||||
(!(Modifier.isPublic(nMMod) || Modifier.isProtected(nMMod) || Modifier.isPrivate(nMMod)))) {
|
||||
versionsCompatible = false;
|
||||
if (Modifier.isPublic(oMMod)) {
|
||||
rf.findDiffPackageReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
|
||||
} else {
|
||||
rf.findDiffPackageAndSubReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
|
||||
}
|
||||
} else if ((Modifier.isPrivate(oMMod) && !Modifier.isPrivate(nMMod)) ||
|
||||
(Modifier.isProtected(oMMod) && Modifier.isPublic(nMMod)) ||
|
||||
(!(Modifier.isPublic(oMMod) || Modifier.isProtected(oMMod) || Modifier.isPrivate(oMMod)) &&
|
||||
(Modifier.isPublic(nMMod) || Modifier.isProtected(nMMod)))) {
|
||||
versionsCompatible = false;
|
||||
rf.findSubclassesReimplementingMethod(oldClassInfo, oldMethodIdx);
|
||||
}
|
||||
|
||||
if ((!Modifier.isAbstract(oMMod) && Modifier.isAbstract(nMMod)) ||
|
||||
(Modifier.isStatic(oMMod) != Modifier.isStatic(nMMod))) {
|
||||
versionsCompatible = false;
|
||||
rf.findReferencingClassesForMethod(oldClassInfo, oldMethodIdx);
|
||||
if (!Modifier.isAbstract(oMMod) && Modifier.isAbstract(nMMod)) {
|
||||
rf.findConcreteSubclassesNotOverridingAbstractMethod(oldClassInfo, newClassInfo, oldMethodIdx);
|
||||
}
|
||||
}
|
||||
if (!Modifier.isFinal(oMMod) && Modifier.isFinal(nMMod)) {
|
||||
versionsCompatible = false;
|
||||
rf.findSubclassesReimplementingMethod(oldClassInfo, oldMethodIdx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any subclass(es), direct or indirect, declare a method with name methodName.
|
||||
* For each such occurence, referencing classes are looked up and added to the list of affected classes.
|
||||
*/
|
||||
private boolean subclassesDeclareSameNameMethod(ClassInfo oldClassInfo, String methodName) {
|
||||
boolean res = false;
|
||||
ClassInfo[] directSubclasses = oldClassInfo.getDirectSubclasses();
|
||||
for (int i = 0; i < directSubclasses.length; i++) {
|
||||
ClassInfo subclass = directSubclasses[i];
|
||||
int methNo = subclass.declaresSameNameMethod(methodName);
|
||||
if (methNo >= 0) {
|
||||
rf.addToAffectedClassNames(subclass.name);
|
||||
rf.findReferencingClassesForMethod(subclass, methNo);
|
||||
res = true;
|
||||
}
|
||||
if (subclassesDeclareSameNameMethod(subclass, methodName)) {
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
899
third_party/jmake/src/org/pantsbuild/jmake/Main.java
vendored
899
third_party/jmake/src/org/pantsbuild/jmake/Main.java
vendored
@ -1,899 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StreamTokenizer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The main class of the <b>jmake</b> tool.<p>
|
||||
*
|
||||
* Has several entrypoints: <code>main</code>, <code>mainExternal</code>, <code>mainProgrammatic</code>,
|
||||
* <code>mainExternalControlled</code> and <code>mainProgrammaticControlled</code>.
|
||||
* The first is not intended to be used by applications other than <b>jmake</b> itself, whereas the
|
||||
* rest can be used to call <b>jmake</b> externally with various degrees of control over its behaviour.
|
||||
* See method comments for more details.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 12 October 2004
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
static final String DEFAULT_STORE_NAME = "jmake.pdb";
|
||||
static final String VERSION = "1.3.8-11";
|
||||
private String pdbFileName = null;
|
||||
private List<String> allProjectJavaFileNamesList =
|
||||
new ArrayList<String>(100);
|
||||
private String allProjectJavaFileNames[];
|
||||
private String addedJavaFileNames[], removedJavaFileNames[], updatedJavaFileNames[];
|
||||
private String destDir = "";
|
||||
private List<String> javacAddArgs = new ArrayList<String>();
|
||||
private String jcPath, jcMainClass, jcMethod;
|
||||
private String jcExecApp;
|
||||
boolean controlledExecution = false;
|
||||
Object externalApp = null;
|
||||
Method externalCompileSourceFilesMethod = null;
|
||||
private boolean failOnDependentJar = false, noWarnOnDependentJar = false;
|
||||
private boolean pdbTextFormat = false;
|
||||
private PCDManager pcdm = null;
|
||||
private String dependencyFile;
|
||||
private static final String optNames[] = {"-h", "-help", "-d", "-pdb", "-C", "-jcpath", "-jcmainclass", "-jcmethod", "-jcexec", "-Xtiming", "-version",
|
||||
"-warnlimit", "-failondependentjar", "-nowarnondependentjar", "-classpath", "-projclasspath", "-bootclasspath", "-extdirs", "-vpath", "-pdb-text-format",
|
||||
"-depfile"};
|
||||
private static final int optArgs[] = {0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1};
|
||||
private static final int OPT_H = 0;
|
||||
private static final int OPT_HELP = 1;
|
||||
private static final int OPT_D = 2;
|
||||
private static final int OPT_STORE = 3;
|
||||
private static final int OPT_JAVAC_OPT = 4;
|
||||
private static final int OPT_JCPATH = 5;
|
||||
private static final int OPT_JCMAINCLASS = 6;
|
||||
private static final int OPT_JCMETHOD = 7;
|
||||
private static final int OPT_JCEXEC = 8;
|
||||
private static final int OPT_TIMING = 9;
|
||||
private static final int OPT_VERSION = 10;
|
||||
private static final int OPT_WARNLIMIT = 11;
|
||||
private static final int OPT_FAILONDEPJAR = 12;
|
||||
private static final int OPT_NOWARNONDEPJAR = 13;
|
||||
private static final int OPT_CLASSPATH = 14;
|
||||
private static final int OPT_PROJECTCLASSPATH = 15;
|
||||
private static final int OPT_BOOTCLASSPATH = 16;
|
||||
private static final int OPT_EXTDIRS = 17;
|
||||
private static final int OPT_VPATH = 18;
|
||||
private static final int OPT_PDB_TEXT_FORMAT = 19;
|
||||
private static final int OPT_DEPENDENCY_FILE = 20;
|
||||
|
||||
/** Construct a new instance of Main. */
|
||||
public Main() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the argument is a legal jmake option. Returns its number or
|
||||
* -1 if not found.
|
||||
*/
|
||||
private static int inOptions(String option) {
|
||||
if (option.startsWith(optNames[OPT_JAVAC_OPT])) {
|
||||
return OPT_JAVAC_OPT;
|
||||
}
|
||||
for (int i = 0; i < optNames.length; i++) {
|
||||
if (option.equals(optNames[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void processCommandLine(String args[]) {
|
||||
if ((args.length == 0) || (args.length == 1 && args[0].equals(optNames[OPT_HELP]))) {
|
||||
printUsage();
|
||||
throw new PrivateException(new PublicExceptions.NoActionRequestedException());
|
||||
}
|
||||
|
||||
List<String> argsV = new ArrayList<String>();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
argsV.add(args[i]);
|
||||
}
|
||||
int argsSt = 0;
|
||||
String arg;
|
||||
|
||||
while (argsSt < argsV.size()) {
|
||||
arg = argsV.get(argsSt++);
|
||||
if (arg.charAt(0) == '-') {
|
||||
int argN = inOptions(arg);
|
||||
if (argN != -1) {
|
||||
if (argsSt + optArgs[argN] > argsV.size()) {
|
||||
optRequiresArg("\"" + optNames[argN] + "\"");
|
||||
}
|
||||
} else {
|
||||
bailOut(arg + ERR_IS_INVALID_OPTION);
|
||||
}
|
||||
|
||||
switch (argN) {
|
||||
case OPT_H:
|
||||
case OPT_HELP:
|
||||
printUsage();
|
||||
throw new PrivateException(new PublicExceptions.NoActionRequestedException());
|
||||
case OPT_D:
|
||||
destDir = argsV.get(argsSt);
|
||||
javacAddArgs.add("-d");
|
||||
javacAddArgs.add(argsV.get(argsSt));
|
||||
argsSt++;
|
||||
break;
|
||||
case OPT_CLASSPATH:
|
||||
try {
|
||||
setClassPath(argsV.get(argsSt++));
|
||||
} catch (PublicExceptions.InvalidCmdOptionException ex) {
|
||||
bailOut(ex.getMessage());
|
||||
}
|
||||
break;
|
||||
case OPT_PROJECTCLASSPATH:
|
||||
try {
|
||||
setProjectClassPath(argsV.get(argsSt++));
|
||||
} catch (PublicExceptions.InvalidCmdOptionException ex) {
|
||||
bailOut(ex.getMessage());
|
||||
}
|
||||
break;
|
||||
case OPT_STORE:
|
||||
pdbFileName = argsV.get(argsSt++);
|
||||
break;
|
||||
case OPT_JAVAC_OPT:
|
||||
String javacArg =
|
||||
(argsV.get(argsSt - 1)).substring(2);
|
||||
if (javacArg.equals("-d") ||
|
||||
javacArg.equals("-classpath") ||
|
||||
javacArg.equals("-bootclasspath") ||
|
||||
javacArg.equals("-extdirs")) {
|
||||
bailOut(javacArg + ERR_SHOULD_BE_EXPLICIT);
|
||||
}
|
||||
javacAddArgs.add(javacArg);
|
||||
break;
|
||||
case OPT_JCPATH:
|
||||
if (jcExecApp != null) {
|
||||
bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
|
||||
}
|
||||
jcPath = argsV.get(argsSt++);
|
||||
break;
|
||||
case OPT_JCMAINCLASS:
|
||||
if (jcExecApp != null) {
|
||||
bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
|
||||
}
|
||||
jcMainClass = argsV.get(argsSt++);
|
||||
break;
|
||||
case OPT_JCMETHOD:
|
||||
if (jcExecApp != null) {
|
||||
bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
|
||||
}
|
||||
jcMethod = argsV.get(argsSt++);
|
||||
break;
|
||||
case OPT_JCEXEC:
|
||||
if (jcPath != null || jcMainClass != null || jcMethod != null) {
|
||||
bailOut(ERR_NO_TWO_COMPILER_OPTIONS);
|
||||
}
|
||||
jcExecApp = argsV.get(argsSt++);
|
||||
break;
|
||||
case OPT_TIMING:
|
||||
Utils.setTimingOn();
|
||||
break;
|
||||
case OPT_VERSION:
|
||||
// Utils.printInfoMessage("jmake version " + VERSION); // Printed anyway at present...
|
||||
throw new PrivateException(new PublicExceptions.NoActionRequestedException());
|
||||
case OPT_WARNLIMIT:
|
||||
try {
|
||||
Utils.warningLimit =
|
||||
Integer.parseInt(argsV.get(argsSt++));
|
||||
} catch (NumberFormatException e) {
|
||||
Utils.ignore(e);
|
||||
bailOut(argsV.get(argsSt) + ERR_IS_INVALID_OPTION);
|
||||
}
|
||||
break;
|
||||
case OPT_FAILONDEPJAR:
|
||||
if (noWarnOnDependentJar) {
|
||||
bailOut("it is not allowed to use -nowarnondependentjar and -failondependentjar together");
|
||||
}
|
||||
failOnDependentJar = true;
|
||||
break;
|
||||
case OPT_NOWARNONDEPJAR:
|
||||
if (failOnDependentJar) {
|
||||
bailOut("it is not allowed to use -failondependentjar and -nowarnondependentjar together");
|
||||
}
|
||||
noWarnOnDependentJar = true;
|
||||
break;
|
||||
case OPT_BOOTCLASSPATH:
|
||||
try {
|
||||
setBootClassPath(argsV.get(argsSt++));
|
||||
} catch (PublicExceptions.InvalidCmdOptionException ex) {
|
||||
bailOut(ex.getMessage());
|
||||
}
|
||||
break;
|
||||
case OPT_EXTDIRS:
|
||||
try {
|
||||
setExtDirs(argsV.get(argsSt++));
|
||||
} catch (PublicExceptions.InvalidCmdOptionException ex) {
|
||||
bailOut(ex.getMessage());
|
||||
}
|
||||
break;
|
||||
case OPT_VPATH:
|
||||
try {
|
||||
setVirtualPath(argsV.get(argsSt++));
|
||||
} catch (PublicExceptions.InvalidCmdOptionException ex) {
|
||||
bailOut(ex.getMessage());
|
||||
}
|
||||
break;
|
||||
case OPT_PDB_TEXT_FORMAT:
|
||||
pdbTextFormat = true;
|
||||
break;
|
||||
case OPT_DEPENDENCY_FILE:
|
||||
dependencyFile = argsV.get(argsSt++);
|
||||
break;
|
||||
default:
|
||||
bailOut(arg + ERR_IS_INVALID_OPTION);
|
||||
}
|
||||
} else { // Not an option, at least does not start with "-". Treat it as a command line file name or source name.
|
||||
if (arg.length() > 1 && arg.charAt(0) == '@') {
|
||||
arg = arg.substring(1);
|
||||
loadCmdFile(arg, argsV);
|
||||
} else {
|
||||
if (!arg.endsWith(".java")) {
|
||||
bailOut(arg + ERR_IS_INVALID_OPTION);
|
||||
}
|
||||
allProjectJavaFileNamesList.add(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Load @-file that can contain anything that command line can contain. */
|
||||
private void loadCmdFile(String name, List<String> argsV) {
|
||||
try {
|
||||
Reader r = new BufferedReader(new FileReader(name));
|
||||
StreamTokenizer st = new StreamTokenizer(r);
|
||||
st.resetSyntax();
|
||||
st.wordChars(' ', 255);
|
||||
st.whitespaceChars(0, ' ');
|
||||
st.commentChar('#');
|
||||
st.quoteChar('"');
|
||||
st.quoteChar('\'');
|
||||
while (st.nextToken() != StreamTokenizer.TT_EOF) {
|
||||
argsV.add(st.sval);
|
||||
}
|
||||
r.close();
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(new PublicExceptions.CommandFileReadException(name + ":\n" + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entrypoint for applications that want to call <b>jmake</b> externally and are willing
|
||||
* to handle exceptions that it may throw.
|
||||
*
|
||||
* @param args command line arguments passed to <b>jmake</b>.
|
||||
*
|
||||
* @throws PublicExceptions.NoActionRequestedException if <b>jmake</b> was not requested to do any real work;
|
||||
* @throws PublicExceptions.InvalidCmdOptionException if invalid command line option was detected;
|
||||
* @throws PublicExceptions.PDBCorruptedException if project database is corrupted;
|
||||
* @throws PublicExceptions.CommandFileReadException if there was error reading a command file;
|
||||
* @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
|
||||
* or compilation errors were detected;
|
||||
* @throws PublicExceptions.ClassFileParseException if there was error parsing a class file;
|
||||
* @throws PublicExceptions.ClassNameMismatchException if there is a mismatch between the deduced and the actual class name;
|
||||
* @throws PublicExceptions.InvalidSourceFileExtensionException if a supplied source file has an invalid extension (not .java);
|
||||
* @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
|
||||
* @throws PublicExceptions.DoubleEntryException if more than one entry for the same class is found in the project
|
||||
* @throws FileNotFoundException if a <code>.java</code> or a <code>.class</code> file was not found;
|
||||
* @throws IOException if there was an I/O problem of any kind;
|
||||
* @throws PublicExceptions.InternalException if an internal problem that should never happen was detected.
|
||||
*/
|
||||
public void mainProgrammatic(String args[]) throws
|
||||
PublicExceptions.NoActionRequestedException,
|
||||
PublicExceptions.InvalidCmdOptionException,
|
||||
PublicExceptions.PDBCorruptedException,
|
||||
PublicExceptions.CommandFileReadException,
|
||||
PublicExceptions.CompilerInteractionException,
|
||||
PublicExceptions.ClassFileParseException,
|
||||
PublicExceptions.ClassNameMismatchException,
|
||||
PublicExceptions.InvalidSourceFileExtensionException,
|
||||
PublicExceptions.JarDependsOnSourceException,
|
||||
PublicExceptions.DoubleEntryException,
|
||||
FileNotFoundException,
|
||||
IOException,
|
||||
PublicExceptions.InternalException {
|
||||
try {
|
||||
Utils.printInfoMessage("Jmake version " + VERSION);
|
||||
if (!controlledExecution) {
|
||||
processCommandLine(args);
|
||||
String[] projectJars = ClassPath.getProjectJars();
|
||||
if (projectJars != null) {
|
||||
for (int i = 0; i < projectJars.length; i++) {
|
||||
allProjectJavaFileNamesList.add(projectJars[i]);
|
||||
}
|
||||
}
|
||||
allProjectJavaFileNames =
|
||||
allProjectJavaFileNamesList.toArray(new String[allProjectJavaFileNamesList.size()]);
|
||||
} else {
|
||||
String[] projectJars = ClassPath.getProjectJars();
|
||||
if (projectJars != null) {
|
||||
String newNames[] =
|
||||
new String[allProjectJavaFileNames.length + projectJars.length];
|
||||
System.arraycopy(allProjectJavaFileNames, 0, newNames, 0, allProjectJavaFileNames.length);
|
||||
System.arraycopy(projectJars, 0, newNames, allProjectJavaFileNames.length, projectJars.length);
|
||||
allProjectJavaFileNames = newNames;
|
||||
}
|
||||
}
|
||||
|
||||
Utils.startTiming(Utils.TIMING_PDBREAD);
|
||||
PCDContainer pcdc;
|
||||
pcdc = PCDContainer.load(pdbFileName, pdbTextFormat);
|
||||
Utils.stopAndPrintTiming("DB read", Utils.TIMING_PDBREAD);
|
||||
|
||||
pcdm = new PCDManager(pcdc, allProjectJavaFileNames,
|
||||
addedJavaFileNames, removedJavaFileNames, updatedJavaFileNames,
|
||||
destDir, javacAddArgs, failOnDependentJar, noWarnOnDependentJar,
|
||||
dependencyFile);
|
||||
|
||||
pcdm.initializeCompiler(jcExecApp, jcPath, jcMainClass, jcMethod, externalApp, externalCompileSourceFilesMethod);
|
||||
|
||||
pcdm.run();
|
||||
} catch (PrivateException e) {
|
||||
Throwable origException = e.getOriginalException();
|
||||
if (origException instanceof PublicExceptions.NoActionRequestedException) {
|
||||
throw (PublicExceptions.NoActionRequestedException) origException;
|
||||
} else if (origException instanceof PublicExceptions.InvalidCmdOptionException) {
|
||||
throw (PublicExceptions.InvalidCmdOptionException) origException;
|
||||
} else if (origException instanceof PublicExceptions.CommandFileReadException) {
|
||||
throw (PublicExceptions.CommandFileReadException) origException;
|
||||
} else if (origException instanceof PublicExceptions.PDBCorruptedException) {
|
||||
throw (PublicExceptions.PDBCorruptedException) origException;
|
||||
} else if (origException instanceof PublicExceptions.CompilerInteractionException) {
|
||||
throw (PublicExceptions.CompilerInteractionException) origException;
|
||||
} else if (origException instanceof PublicExceptions.ClassFileParseException) {
|
||||
throw (PublicExceptions.ClassFileParseException) origException;
|
||||
} else if (origException instanceof PublicExceptions.ClassNameMismatchException) {
|
||||
throw (PublicExceptions.ClassNameMismatchException) origException;
|
||||
} else if (origException instanceof PublicExceptions.InvalidSourceFileExtensionException) {
|
||||
throw (PublicExceptions.InvalidSourceFileExtensionException) origException;
|
||||
} else if (origException instanceof PublicExceptions.JarDependsOnSourceException) {
|
||||
throw (PublicExceptions.JarDependsOnSourceException) origException;
|
||||
} else if (origException instanceof PublicExceptions.DoubleEntryException) {
|
||||
throw (PublicExceptions.DoubleEntryException) origException;
|
||||
} else if (origException instanceof FileNotFoundException) {
|
||||
throw (FileNotFoundException) origException;
|
||||
} else if (origException instanceof IOException) {
|
||||
throw (IOException) origException;
|
||||
} else if (origException instanceof PublicExceptions.InternalException) {
|
||||
throw (PublicExceptions.InternalException) origException;
|
||||
}
|
||||
} finally {
|
||||
ClassPath.resetOnFinish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entrypoint for applications that want to call <b>jmake</b> externally and would prefer
|
||||
* receiving an error code instead of an exception in case something goes wrong.<p>
|
||||
*
|
||||
* @param args command line arguments passed to <b>jmake</b>.
|
||||
*
|
||||
* @return <dl>
|
||||
* <dt><code> 0</code> if everything was successful;
|
||||
* <dt><code> -1</code> invalid command line option detected;
|
||||
* <dt><code> -2</code> error reading command file;
|
||||
* <dt><code> -3</code> project database corrupted;
|
||||
* <dt><code> -4</code> error initializing or calling the compiler;
|
||||
* <dt><code> -5</code> compilation error;
|
||||
* <dt><code> -6</code> error parsing a class file;
|
||||
* <dt><code> -7</code> file not found;
|
||||
* <dt><code> -8</code> I/O exception;
|
||||
* <dt><code> -9</code> internal jmake exception;
|
||||
* <dt><code>-10</code> deduced and actual class name mismatch;
|
||||
* <dt><code>-11</code> invalid source file extension;
|
||||
* <dt><code>-12</code> a class in a <code>JAR</code> is found dependent on a class with the .java source;
|
||||
* <dt><code>-13</code> more than one entry for the same class is found in the project
|
||||
* <dt><code>-20</code> internal Java error (caused by <code>java.lang.InternalError</code>);
|
||||
* <dt><code>-30</code> internal Java error (caused by <code>java.lang.RuntimeException</code>).
|
||||
* </dl>
|
||||
*/
|
||||
public int mainExternal(String args[]) {
|
||||
try {
|
||||
mainProgrammatic(args);
|
||||
} catch (PublicExceptions.NoActionRequestedException e0) {
|
||||
// Nothing to do
|
||||
} catch (PublicExceptions.InvalidCmdOptionException e1) {
|
||||
Utils.printErrorMessage(e1.getMessage());
|
||||
return -1;
|
||||
} catch (PublicExceptions.CommandFileReadException e2) {
|
||||
Utils.printErrorMessage("error parsing command file:");
|
||||
Utils.printErrorMessage(e2.getMessage());
|
||||
return -2;
|
||||
} catch (PublicExceptions.PDBCorruptedException e3) {
|
||||
Utils.printErrorMessage("project database corrupted: " + e3.getMessage());
|
||||
return -3;
|
||||
} catch (PublicExceptions.CompilerInteractionException e4) {
|
||||
if (e4.getOriginalException() != null) {
|
||||
Utils.printErrorMessage("error interacting with the compiler: ");
|
||||
Utils.printErrorMessage(e4.getMessage());
|
||||
Utils.printErrorMessage("original exception:");
|
||||
Utils.printErrorMessage(e4.getOriginalException().getMessage());
|
||||
return -4;
|
||||
} else { // Otherwise there is a compilation error, and the compiler has already printed a lot...
|
||||
return -5;
|
||||
}
|
||||
} catch (PublicExceptions.ClassFileParseException e6) {
|
||||
Utils.printErrorMessage(e6.getMessage());
|
||||
return -6;
|
||||
} catch (FileNotFoundException e7) {
|
||||
Utils.printErrorMessage(e7.getMessage());
|
||||
return -7;
|
||||
} catch (IOException e8) {
|
||||
Utils.printErrorMessage(e8.getMessage());
|
||||
return -8;
|
||||
} catch (PublicExceptions.InternalException e9) {
|
||||
Utils.printErrorMessage("internal jmake exception detected:");
|
||||
Utils.printErrorMessage(e9.getMessage());
|
||||
Utils.printErrorMessage(Utils.REPORT_PROBLEM);
|
||||
Utils.printErrorMessage("the stack trace is as follows:");
|
||||
e9.printStackTrace();
|
||||
return -9;
|
||||
} catch (PublicExceptions.ClassNameMismatchException e10) {
|
||||
Utils.printErrorMessage(e10.getMessage());
|
||||
return -10;
|
||||
} catch (PublicExceptions.InvalidSourceFileExtensionException e11) {
|
||||
Utils.printErrorMessage(e11.getMessage());
|
||||
return -11;
|
||||
} catch (PublicExceptions.JarDependsOnSourceException e12) {
|
||||
Utils.printErrorMessage(e12.getMessage());
|
||||
return -12;
|
||||
} catch (PublicExceptions.DoubleEntryException e13) {
|
||||
Utils.printErrorMessage(e13.getMessage());
|
||||
return -13;
|
||||
} catch (InternalError e20) {
|
||||
Utils.printErrorMessage("internal Java error: " + e20);
|
||||
Utils.printErrorMessage("Consult the following stack trace for more info:");
|
||||
e20.printStackTrace();
|
||||
return -20;
|
||||
} catch (RuntimeException e30) {
|
||||
Utils.printErrorMessage("internal Java exception: " + e30);
|
||||
Utils.printErrorMessage("Consult the following stack trace for more info:");
|
||||
e30.printStackTrace();
|
||||
return -30;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entrypoint for applications such as Ant, that want to have full control over
|
||||
* compilations that <b>jmake</b> invokes, and are willing to handle exceptions
|
||||
* that it may throw.
|
||||
*
|
||||
* @param javaFileNames array of strings that specify <code>.java</code> file names.
|
||||
* @param destDirName name of the destination directory (<b>jmake</b> will look up binary classes
|
||||
* in there, it should be the same as the one used by the Java compiler method).
|
||||
* If <code>null</code> is passed, classes will be looked up in the same directories
|
||||
* as their sources, in agreement with the default Java compiler behaviour.
|
||||
* @param pdbFileName project database file name (if <code>null</code> is passed,
|
||||
* a file with the default name placed in the current directory will be used).
|
||||
* @param externalApp an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
|
||||
* @param externalCompileSourceFilesMethod a method of the form <code>int x(String[] args)</code>. It
|
||||
* should return <code>0</code> if compilation is successful and any non-zero value
|
||||
* otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
|
||||
* recompile in the form of canonical full path file names.
|
||||
*
|
||||
* @throws PublicExceptions.NoActionRequestedException if <b>jmake</b> was not requested to do any real work;
|
||||
* @throws PublicExceptions.InvalidCmdOptionException if invalid command line option was detected;
|
||||
* @throws PublicExceptions.PDBCorruptedException if project database is corrupted;
|
||||
* @throws PublicExceptions.CommandFileReadException if there was error reading a command file;
|
||||
* @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
|
||||
* or compilation errors were detected;
|
||||
* @throws PublicExceptions.ClassFileParseException if there was error parsing a class file;
|
||||
* @throws PublicExceptions.ClassNameMismatchException if there is a mismatch between the deduced and the actual class name;
|
||||
* @throws PublicExceptions.InvalidSourceFileExtensionException if a specified source file has an invalid extension (not .java);
|
||||
* @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
|
||||
* @throws PublicExceptions.DoubleEntryException if more than one entry for the same class is found in the project
|
||||
* @throws PublicExceptions.InternalException if an internal problem that should never happen was detected.
|
||||
* @throws FileNotFoundException if a <code>.java</code> or a <code>.class</code> file was not found;
|
||||
* @throws IOException if there was an I/O problem of any kind;
|
||||
*/
|
||||
public void mainProgrammaticControlled(String javaFileNames[], String destDirName, String pdbFileName,
|
||||
Object externalApp, Method externalCompileSourceFilesMethod) throws
|
||||
PublicExceptions.NoActionRequestedException,
|
||||
PublicExceptions.InvalidCmdOptionException,
|
||||
PublicExceptions.PDBCorruptedException,
|
||||
PublicExceptions.CommandFileReadException,
|
||||
PublicExceptions.CompilerInteractionException,
|
||||
PublicExceptions.ClassFileParseException,
|
||||
PublicExceptions.ClassNameMismatchException,
|
||||
PublicExceptions.InvalidSourceFileExtensionException,
|
||||
PublicExceptions.JarDependsOnSourceException,
|
||||
PublicExceptions.DoubleEntryException,
|
||||
PublicExceptions.InternalException,
|
||||
FileNotFoundException,
|
||||
IOException {
|
||||
|
||||
controlledExecution = true;
|
||||
this.pdbFileName = pdbFileName;
|
||||
this.destDir = destDirName;
|
||||
this.allProjectJavaFileNames = javaFileNames;
|
||||
this.externalApp = externalApp;
|
||||
this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
|
||||
|
||||
mainProgrammatic(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entrypoint for applications such as Ant, that want to have full control over
|
||||
* compilations that <b>jmake</b> invokes, and do not want to handle exceptions that it
|
||||
* may throw. Error codes returned are the same as <code>mainExternal(String[])</code> returns.
|
||||
*
|
||||
* @param javaFileNames array of strings that specify <code>.java</code> file names.
|
||||
* @param destDirName name of the destination directory (<b>jmake</b> will look up binary classes
|
||||
* in there, it should be the same as the one used by the Java compiler method).
|
||||
* If <code>null</code> is passed, classes will be looked up in the same directories
|
||||
* as their sources, in agreement with the default Java compiler behaviour.
|
||||
* @param pdbFileName project database file name (if <code>null</code> is passed,
|
||||
* a file with the default name placed in the current directory will be used).
|
||||
* @param externalApp an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
|
||||
* @param externalCompileSourceFilesMethod a method of the form <code>int x(String[] args)</code>. It
|
||||
* should return <code>0</code> if compilation is successful and any non-zero value
|
||||
* otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
|
||||
* recompile in the form of canonical full path file names.
|
||||
*
|
||||
* @see #mainExternal(String[])
|
||||
*/
|
||||
public int mainExternalControlled(String javaFileNames[], String destDirName, String pdbFileName,
|
||||
Object externalApp, Method externalCompileSourceFilesMethod) {
|
||||
controlledExecution = true;
|
||||
this.pdbFileName = pdbFileName;
|
||||
this.destDir = destDirName;
|
||||
this.allProjectJavaFileNames = javaFileNames;
|
||||
this.externalApp = externalApp;
|
||||
this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
|
||||
|
||||
return mainExternal(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entrypoint for applications such as IDEs, that themselves keep track of updated/added/removed sources,
|
||||
* want to have full control over compilations that <b>jmake</b> invokes, and are willing to handle exceptions
|
||||
* that it may throw.
|
||||
*
|
||||
* @param addedJavaFileNames names of <code>.java</code> files just added to the project
|
||||
* @param removedJavaFileNames names of <code>.java</code> files just removed from the project
|
||||
* @param updatedJavaFileNames names of updated project <code>.java</code> files
|
||||
* @param destDirName name of the destination directory (<b>jmake</b> will look up binary classes
|
||||
* in there, it should be the same as the one used by the Java compiler method).
|
||||
* If <code>null</code> is passed, classes will be looked up in the same directories
|
||||
* as their sources, in agreement with the default Java compiler behaviour.
|
||||
* @param pdbFileName project database file name (if <code>null</code> is passed,
|
||||
* a file with the default name placed in the current directory will be used).
|
||||
* @param externalApp an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
|
||||
* @param externalCompileSourceFilesMethod a method of the form <code>int x(String[] args)</code>. It
|
||||
* should return <code>0</code> if compilation is successful and any non-zero value
|
||||
* otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
|
||||
* recompile in the form of canonical full path file names.
|
||||
*
|
||||
* @throws PublicExceptions.NoActionRequestedException if <b>jmake</b> was not requested to do any real work;
|
||||
* @throws PublicExceptions.InvalidCmdOptionException if invalid command line option was detected;
|
||||
* @throws PublicExceptions.PDBCorruptedException if project database is corrupted;
|
||||
* @throws PublicExceptions.CommandFileReadException if there was error reading a command file;
|
||||
* @throws PublicExceptions.CompilerInteractionException if there was a problem initializing or calling the compiler,
|
||||
* or compilation errors were detected;
|
||||
* @throws PublicExceptions.ClassFileParseException if there was error parsing a class file;
|
||||
* @throws PublicExceptions.ClassNameMismatchException if there is a mismatch between the deduced and the actual class name;
|
||||
* @throws PublicExceptions.InvalidSourceFileExtensionException if a specified source file has an invalid extension (not .java);
|
||||
* @throws PublicExceptions.JarDependsOnSourceException if a class in a <code>JAR</code> is found dependent on a class with the .java source;
|
||||
* @throws PublicExceptions.DoubleEntryException if more than one entry for the same class is found in the project
|
||||
* @throws PublicExceptions.InternalException if an internal problem that should never happen was detected.
|
||||
* @throws FileNotFoundException if a <code>.java</code> or a <code>.class</code> file was not found;
|
||||
* @throws IOException if there was an I/O problem of any kind;
|
||||
*/
|
||||
public void mainProgrammaticControlled(String addedJavaFileNames[], String removedJavaFileNames[], String updatedJavaFileNames[],
|
||||
String destDirName, String pdbFileName,
|
||||
Object externalApp, Method externalCompileSourceFilesMethod) throws
|
||||
PublicExceptions.NoActionRequestedException,
|
||||
PublicExceptions.InvalidCmdOptionException,
|
||||
PublicExceptions.PDBCorruptedException,
|
||||
PublicExceptions.CommandFileReadException,
|
||||
PublicExceptions.CompilerInteractionException,
|
||||
PublicExceptions.ClassFileParseException,
|
||||
PublicExceptions.ClassNameMismatchException,
|
||||
PublicExceptions.InvalidSourceFileExtensionException,
|
||||
PublicExceptions.JarDependsOnSourceException,
|
||||
PublicExceptions.DoubleEntryException,
|
||||
PublicExceptions.InternalException,
|
||||
FileNotFoundException,
|
||||
IOException {
|
||||
|
||||
controlledExecution = true;
|
||||
this.pdbFileName = pdbFileName;
|
||||
this.destDir = destDirName;
|
||||
this.addedJavaFileNames = addedJavaFileNames;
|
||||
this.removedJavaFileNames = removedJavaFileNames;
|
||||
this.updatedJavaFileNames = updatedJavaFileNames;
|
||||
this.externalApp = externalApp;
|
||||
this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
|
||||
|
||||
mainProgrammatic(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entrypoint for applications such as IDEs, that themselves keep track of updated/added/removed sources,
|
||||
* want to have full control over compilations that <b>jmake</b> invokes, and do not want to handle exceptions
|
||||
* that it may throw. Error codes returned are the same as <code>mainExternal(String[])</code> returns.
|
||||
*
|
||||
* @param addedJavaFileNames names of <code>.java</code> files just added to the project
|
||||
* @param removedJavaFileNames names of <code>.java</code> files just removed from the project
|
||||
* @param updatedJavaFileNames names of updated project <code>.java</code> files
|
||||
* @param destDirName name of the destination directory (<b>jmake</b> will look up binary classes
|
||||
* in there, it should be the same as the one used by the Java compiler method).
|
||||
* If <code>null</code> is passed, classes will be looked up in the same directories
|
||||
* as their sources, in agreement with the default Java compiler behaviour.
|
||||
* @param pdbFileName project database file name (if <code>null</code> is passed,
|
||||
* a file with the default name placed in the current directory will be used).
|
||||
* @param externalApp an object on which to invoke <code>externalCompileSourceFilesMethod</code> method.
|
||||
* @param externalCompileSourceFilesMethod a method of the form <code>int x(String[] args)</code>. It
|
||||
* should return <code>0</code> if compilation is successful and any non-zero value
|
||||
* otherwise. <b>jmake</b> passes it a list of the <code>.java</code> files to
|
||||
* recompile in the form of canonical full path file names.
|
||||
*
|
||||
* @see #mainExternal(String[])
|
||||
*/
|
||||
public int mainExternalControlled(String addedJavaFileNames[], String removedJavaFileNames[], String updatedJavaFileNames[],
|
||||
String destDirName, String pdbFileName,
|
||||
Object externalApp, Method externalCompileSourceFilesMethod) {
|
||||
controlledExecution = true;
|
||||
this.pdbFileName = pdbFileName;
|
||||
this.destDir = destDirName;
|
||||
this.addedJavaFileNames = addedJavaFileNames;
|
||||
this.removedJavaFileNames = removedJavaFileNames;
|
||||
this.updatedJavaFileNames = updatedJavaFileNames;
|
||||
this.externalApp = externalApp;
|
||||
this.externalCompileSourceFilesMethod = externalCompileSourceFilesMethod;
|
||||
|
||||
return mainExternal(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entrypoint for the standalone <b>jmake</b> application. This method calls does little but calling
|
||||
* <code>mainExternal</code>, and its execution always completes with <code>System.exit(code)</code>,
|
||||
* where <code>code</code> is the value returned by <code>mainExternal</code>.
|
||||
*
|
||||
* @see #mainExternal(String[])
|
||||
* @see #mainProgrammatic(String[])
|
||||
*
|
||||
* @param args command line arguments passed to <b>jmake</b>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
Utils.startTiming(Utils.TIMING_TOTAL);
|
||||
|
||||
Main m = new Main();
|
||||
int exitCode = m.mainExternal(args);
|
||||
|
||||
Utils.stopAndPrintTiming("Total", Utils.TIMING_TOTAL);
|
||||
if ( exitCode != 0 ) {
|
||||
System.exit(exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the output of <b>jmake</b>.
|
||||
*
|
||||
* @see #setOutputStreams(PrintStream, PrintStream, PrintStream)
|
||||
*
|
||||
* @param printInfoMessages specify whether to print information messages
|
||||
* @param printWarningMessages specify whether to print warning messages
|
||||
* @param printErrorMessages specify whether to print error messages
|
||||
*/
|
||||
public static void customizeOutput(boolean printInfoMessages,
|
||||
boolean printWarningMessages,
|
||||
boolean printErrorMessages) {
|
||||
Utils.customizeOutput(printInfoMessages, printWarningMessages, printErrorMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class path to be used by the compiler, and also by the dependency checker for the purposes of
|
||||
* superclass/superinterface change tracking. For the compiler, this class path will be merged with the
|
||||
* project class path (set via setProjectClassPath(String)). Other than that, its value will be used only to
|
||||
* look up superclasses/superinterfaces of project classes. Note that non-project superclasses and
|
||||
* superinterfaces are first looked up at the boot class path, then on the extension class path, and then
|
||||
* on this class path.
|
||||
*
|
||||
* @see #setProjectClassPath(String)
|
||||
* @see #setBootClassPath(String)
|
||||
* @see #setExtDirs(String)
|
||||
*
|
||||
* @param classPath the value of the class path, in the usual format (i.e. entries that are directories
|
||||
* or JARs, separated by colon or semicolon depending on the platform).
|
||||
*
|
||||
* @throws PublicExceptions.InvalidCmdOptionException if invalid class path value is specified.
|
||||
*/
|
||||
public static void setClassPath(String classPath) throws PublicExceptions.InvalidCmdOptionException {
|
||||
ClassPath.setClassPath(classPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class path to be used by the compiler, and also by the dependency checker for the purposes of
|
||||
* superclass/superinterface change tracking and sourceless class dependency checking. For the compiler,
|
||||
* and also in order to look up superclasses/superinterfaces of project classes, this class path will be
|
||||
* merged with the standard class path (set via setClassPath(String)). But in addition, all binary classes
|
||||
* that are on this class path are stored in the project database and checked for updates every time jmake
|
||||
* is invoked. Any changes to these classes trigger the standard dependency checking procedure. However,
|
||||
* dependent classes are looked up only among the "normal" project classes, i.e. those that have sources.
|
||||
* Therefore sourceless classes are assumed to always be mutually consistent.
|
||||
*
|
||||
* Currently only JAR files can be present on this class path.
|
||||
*
|
||||
* @see #setClassPath(String)
|
||||
*
|
||||
* @param projectClassPath the value of the class path, in the usual format (i.e. entries that are directories
|
||||
* or JARs, separated by colon or semicolon depending on the platform).
|
||||
*
|
||||
* @throws PublicExceptions.InvalidCmdOptionException if invalid class path value is specified.
|
||||
*/
|
||||
public static void setProjectClassPath(String projectClassPath) throws PublicExceptions.InvalidCmdOptionException {
|
||||
ClassPath.setProjectClassPath(projectClassPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the boot class path to be used by the compiler (-bootclasspath option) and also by the dependency
|
||||
* checker (by default, the value of "sun.boot.class.path" property is used).
|
||||
*
|
||||
* @see #setClassPath(String)
|
||||
*
|
||||
* @param classPath the value of the boot class path, in the usual format (i.e. entries that are directories
|
||||
* or JARs, separated by colon or semicolon depending on the platform).
|
||||
*
|
||||
* @throws PublicExceptions.InvalidCmdOptionException if invalid class path value is specified.
|
||||
*/
|
||||
public static void setBootClassPath(String classPath) throws PublicExceptions.InvalidCmdOptionException {
|
||||
ClassPath.setBootClassPath(classPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the extensions location to be used by the compiler (-extdirs option) and also by the dependency
|
||||
* checker (by default, the value of "java.ext.dirs" property is used).
|
||||
*
|
||||
* @see #setClassPath(String)
|
||||
*
|
||||
* @param dirs the value of extension directories, in the usual format (one or more directory names
|
||||
* separated by colon or semicolon depending on the platform).
|
||||
*
|
||||
* @throws PublicExceptions.InvalidCmdOptionException if invalid class path value is specified.
|
||||
*/
|
||||
public static void setExtDirs(String dirs) throws PublicExceptions.InvalidCmdOptionException {
|
||||
ClassPath.setExtDirs(dirs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the virtual path used to find both source and class files that are part of the project
|
||||
* but are not in the local directory.
|
||||
*
|
||||
* @see #setClassPath(String)
|
||||
*
|
||||
* @param dirs the value of extension directories, in the usual format (one or more directory names
|
||||
* separated by colon or semicolon depending on the platform).
|
||||
*
|
||||
* @throws PublicExceptions.InvalidCmdOptionException if invalid path value is specified.
|
||||
*/
|
||||
public static void setVirtualPath(String dirs) throws PublicExceptions.InvalidCmdOptionException {
|
||||
ClassPath.setVirtualPath(dirs);
|
||||
}
|
||||
|
||||
/** Produce no warning or error message upon a dependent <code>JAR</code> detection. */
|
||||
public static final int DEPJAR_NOWARNORERROR = 0;
|
||||
/** Produce a warning upon a dependent <code>JAR</code> detection. */
|
||||
public static final int DEPJAR_WARNING = 1;
|
||||
/** Produce an error message (throw an exception) upon a dependent <code>JAR</code> detection. */
|
||||
public static final int DEPJAR_ERROR = 2;
|
||||
|
||||
/**
|
||||
* Set the response of <b>jmake</b> in case a dependence of a class located in a <code>JAR</code> file on a
|
||||
* class with a <code>.java</code> source is detected (such dependencies are highly discouraged, since it is not
|
||||
* possible to recompile a class in the <code>JAR</code> that has no source).
|
||||
*
|
||||
* @param code response type: DEPJAR_NOWARNORERROR, DEPJAR_WARNING (default behaviour) or DEPJAR_ERROR.
|
||||
*/
|
||||
public void setResponseOnDependentJar(int code) {
|
||||
switch (code) {
|
||||
case DEPJAR_NOWARNORERROR:
|
||||
noWarnOnDependentJar = true;
|
||||
failOnDependentJar = false;
|
||||
break;
|
||||
case DEPJAR_WARNING:
|
||||
noWarnOnDependentJar = false;
|
||||
failOnDependentJar = false;
|
||||
break;
|
||||
case DEPJAR_ERROR:
|
||||
noWarnOnDependentJar = false;
|
||||
failOnDependentJar = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the names of all classes that <b>jmake</b>, on this invocation, found updated - either because
|
||||
* <b>jmake</b> itself recompiled them or because they were updated independently (their timestamp/checksum
|
||||
* found different from those contained in the project database).
|
||||
*/
|
||||
public String[] getUpdatedClasses() {
|
||||
return pcdm.getAllUpdatedClassesAsStringArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the output print streams to be used by <b>jmake</b>.
|
||||
*
|
||||
* @see #customizeOutput(boolean, boolean, boolean)
|
||||
*
|
||||
* @param out print stream to be used for information ("logging") messages that <b>jmake</b> emits
|
||||
* @param warn print stream to be used for warning messages
|
||||
* @param err print stream to be used for error messages
|
||||
*/
|
||||
public static void setOutputStreams(PrintStream out, PrintStream warn, PrintStream err) {
|
||||
Utils.setOutputStreams(out, warn, err);
|
||||
}
|
||||
|
||||
/** Get the version of this copy of <b>jmake</b> */
|
||||
public static String getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
private static final String ERR_IS_INVALID_OPTION =
|
||||
" is an invalid option or argument.";
|
||||
private static final String ERR_NO_TWO_COMPILER_OPTIONS =
|
||||
"You may not specify both compiler class and compiler executable application";
|
||||
private static final String ERR_SHOULD_BE_EXPLICIT =
|
||||
" compiler option should be specified directly as a jmake option";
|
||||
|
||||
private static void bailOut(String s) {
|
||||
throw new PrivateException(new PublicExceptions.InvalidCmdOptionException("jmake: " + s + "\nRun \"jmake -h\" for help."));
|
||||
}
|
||||
|
||||
private static void optRequiresArg(String s) {
|
||||
bailOut("the " + s + " option requires an argument.");
|
||||
}
|
||||
|
||||
private static void printUsage() {
|
||||
Utils.printInfoMessage("Usage: jmake <options> <.java files> <@files>");
|
||||
Utils.printInfoMessage("where possible options include:");
|
||||
Utils.printInfoMessage(" -h, -help print this help message");
|
||||
Utils.printInfoMessage(" -version print the product version number");
|
||||
Utils.printInfoMessage(" -pdb <file name> specify non-default project database file");
|
||||
Utils.printInfoMessage(" -pdb-text-format if specified, pdb file is stored in text format");
|
||||
Utils.printInfoMessage(" -d <directory> specify where to place generated class files");
|
||||
Utils.printInfoMessage(" -classpath <path> specify where to find user class files");
|
||||
Utils.printInfoMessage(" -projclasspath <path> specify where to find sourceless project classes");
|
||||
Utils.printInfoMessage(" (currently only JARs are allowed on this path)");
|
||||
Utils.printInfoMessage(" -C<option> specify an option to be passed to the Java compiler");
|
||||
Utils.printInfoMessage(" (this option's arguments should also be preceded by -C)");
|
||||
Utils.printInfoMessage(" -jcpath <path> specify the class path for a non-default Java compiler");
|
||||
Utils.printInfoMessage(" (default is <JAVAHOME>/lib/tools.jar)");
|
||||
Utils.printInfoMessage(" -jcmainclass <class> specify the main class for a non-default Java compiler");
|
||||
Utils.printInfoMessage(" (default is com.sun.tools.javac.Main)");
|
||||
Utils.printInfoMessage(" -jcmethod <method> specify the method to call in the Java compiler class");
|
||||
Utils.printInfoMessage(" (default is \"compile(String args[])\")");
|
||||
Utils.printInfoMessage(" -jcexec <file name> specify a binary non-default Java compiler application");
|
||||
Utils.printInfoMessage(" -failondependentjar fail if a class on projectclasspath depends on a class");
|
||||
Utils.printInfoMessage(" with .java source (by default, a warning is issued)");
|
||||
Utils.printInfoMessage(" -nowarnondependentjar no warning or error if a class on projectclasspath");
|
||||
Utils.printInfoMessage(" depends on a class with a .java source (use with care)");
|
||||
Utils.printInfoMessage(" -warnlimit <number> specify the maximum number of warnings (20 by default)");
|
||||
Utils.printInfoMessage(" -bootclasspath <path> override location of bootstrap class files");
|
||||
Utils.printInfoMessage(" -extdirs <dirs> override location of installed extensions");
|
||||
Utils.printInfoMessage(" -vpath <dirs> a list of directories to search for Java and class files similar to GNUMake's VPATH");
|
||||
Utils.printInfoMessage(" -depfile <path> a file generated by the compiler containing additional java->class mappings");
|
||||
Utils.printInfoMessage("");
|
||||
Utils.printInfoMessage("Examples:");
|
||||
Utils.printInfoMessage(" jmake -d classes -classpath .;mylib.jar X.java Y.java Z.java");
|
||||
Utils.printInfoMessage(" jmake -pdb myproject.pdb -jcexec c:\\java\\jikes\\jikes.exe @myproject.src");
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class is a persistent container for the Project Class Directory, that can
|
||||
* read and write itself from/to disk.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 12 November 2001
|
||||
*/
|
||||
public class PCDContainer {
|
||||
|
||||
/** The data structure (currently {@link LinkedHashMap}) for PCD, that maps class name to
|
||||
record containing information about the class */
|
||||
Map<String,PCDEntry> pcd;
|
||||
String storeName;
|
||||
boolean textFormat;
|
||||
|
||||
private PCDContainer(Map<String,PCDEntry> pcd, String storeName, boolean textFormat) {
|
||||
this.storeName = storeName;
|
||||
this.pcd = pcd;
|
||||
this.textFormat = textFormat;
|
||||
}
|
||||
|
||||
public static PCDContainer load(String storeName, boolean textFormat) {
|
||||
if (storeName == null) {
|
||||
storeName = Main.DEFAULT_STORE_NAME;
|
||||
}
|
||||
File storeFile = Utils.checkFileForName(storeName);
|
||||
if (storeFile != null) {
|
||||
Utils.printInfoMessageNoEOL("Opening project database... ");
|
||||
Map<String,PCDEntry> pcd;
|
||||
if (textFormat) {
|
||||
pcd = new TextProjectDatabaseReader().readProjectDatabaseFromFile(storeFile);
|
||||
} else {
|
||||
pcd = new BinaryProjectDatabaseReader().readProjectDatabaseFromFile(storeFile);
|
||||
}
|
||||
PCDContainer pcdc = new PCDContainer(pcd, storeName, textFormat);
|
||||
Utils.printInfoMessage("Done.");
|
||||
return pcdc;
|
||||
}
|
||||
return new PCDContainer(null, storeName, textFormat);
|
||||
}
|
||||
|
||||
public void save() {
|
||||
Utils.printInfoMessageNoEOL("Writing project database... ");
|
||||
File outfile = new File(storeName);
|
||||
if (textFormat) {
|
||||
new TextProjectDatabaseWriter().writeProjectDatabaseToFile(outfile, pcd);
|
||||
} else {
|
||||
new BinaryProjectDatabaseWriter().writeProjectDatabaseToFile(outfile, pcd);
|
||||
}
|
||||
Utils.printInfoMessage("Done.");
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* An instance of this class represents an entry in the Project Class Directory.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 29 March 2002
|
||||
*/
|
||||
public class PCDEntry {
|
||||
// Class versions compare results
|
||||
|
||||
static final int CV_UNCHECKED = 0;
|
||||
static final int CV_COMPATIBLE = 1;
|
||||
static final int CV_INCOMPATIBLE = 2;
|
||||
static final int CV_DELETED = 3;
|
||||
static final int CV_NEW = 4;
|
||||
static final int CV_NEWER_FOUND_NEARER = 5;
|
||||
String className; // Dots are replaced with slashes for convenience
|
||||
transient String classFileFullPath;
|
||||
String javaFileFullPath;
|
||||
long oldClassFileLastModified;
|
||||
transient long newClassFileLastModified;
|
||||
long oldClassFileFingerprint;
|
||||
transient long newClassFileFingerprint;
|
||||
ClassInfo oldClassInfo;
|
||||
transient ClassInfo newClassInfo;
|
||||
transient int checkResult; // Reflects the result of class version comparison
|
||||
transient boolean checked; // Mark entries for classes that have been checked and found existing.
|
||||
// It helps to detect double entries for the same class in the project file list,
|
||||
// and also not to confuse them with the case when a .java source for a class is moved.
|
||||
|
||||
/** This constructor is called to initialize a record for a class that has just been added to the project. */
|
||||
public PCDEntry(String className,
|
||||
String javaFileFullPath,
|
||||
String classFileFullPath,
|
||||
long classFileLastModified,
|
||||
long classFileFingerprint,
|
||||
ClassInfo classInfo) {
|
||||
this.className = className;
|
||||
this.classFileFullPath = classFileFullPath;
|
||||
this.javaFileFullPath = javaFileFullPath;
|
||||
this.oldClassFileLastModified = this.newClassFileLastModified =
|
||||
classFileLastModified;
|
||||
this.oldClassFileFingerprint = this.newClassFileFingerprint =
|
||||
classFileFingerprint;
|
||||
this.newClassInfo = classInfo;
|
||||
checked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is called to initialize a record for a class that
|
||||
* exists at least in the previous version of the project.
|
||||
*/
|
||||
public PCDEntry(String className,
|
||||
String javaFileFullPath,
|
||||
long classFileLastModified,
|
||||
long classFileFingerprint,
|
||||
ClassInfo classInfo) {
|
||||
this.className = className;
|
||||
this.javaFileFullPath = javaFileFullPath;
|
||||
this.oldClassFileLastModified = classFileLastModified;
|
||||
this.oldClassFileFingerprint = classFileFingerprint;
|
||||
this.oldClassInfo = classInfo;
|
||||
}
|
||||
|
||||
// Debugging
|
||||
public String toString() {
|
||||
return "className = " + className +
|
||||
"; classFileFullPath = " + classFileFullPath +
|
||||
"; javaFileFullPath = " + javaFileFullPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the class that corresponds to the file name, i.e. the public class
|
||||
*/
|
||||
private String getExpectedClassName() {
|
||||
File path = new File(javaFileFullPath);
|
||||
int index = -1;
|
||||
do {
|
||||
index = className.indexOf('/', index + 1);
|
||||
path = path.getParentFile();
|
||||
} while (index != -1);
|
||||
String pathString = path.toString();
|
||||
if (!pathString.endsWith("/"))
|
||||
pathString += "/";
|
||||
// It is assumed that the javaFileFillPath ends with .java
|
||||
int javaPathWithoutSuffix = javaFileFullPath.length() - 5;
|
||||
return javaFileFullPath.substring(pathString.length(),
|
||||
javaPathWithoutSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that neither has the same name as the java file, nor an inner class, is
|
||||
* package-private.
|
||||
*/
|
||||
public boolean isPackagePrivateClass() {
|
||||
String expectedClassName = getExpectedClassName();
|
||||
|
||||
return !(className.equals(expectedClassName)
|
||||
|| (className.startsWith(expectedClassName)
|
||||
&& className.charAt(expectedClassName.length()) == '$'));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,28 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
/**
|
||||
* This class is used as a wrapper for a number of exceptions that are thrown by jmake. Its
|
||||
* only purpose is to help avoid using endless "throws" clauses in the code.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 12 November 2001
|
||||
*/
|
||||
public class PrivateException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Throwable originalException;
|
||||
|
||||
public PrivateException(Throwable e) {
|
||||
originalException = e;
|
||||
}
|
||||
|
||||
public Throwable getOriginalException() {
|
||||
return originalException;
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
/**
|
||||
* This class is a wrapper for a number of nested classes. They define exceptions that may be thrown
|
||||
* by the <b>jmake</b> code. These exceptions are caught in the <code>Main.mainExternal</code> method,
|
||||
* or they may be caught and handled by an application invoking <b>jmake</b> programmatically
|
||||
* through <code>Main.mainProgrammatic</code> method.
|
||||
*
|
||||
* @see Main#mainExternal(String[])
|
||||
* @see Main#mainProgrammatic(String[])
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 17 January 2003
|
||||
*/
|
||||
public class PublicExceptions {
|
||||
|
||||
/**
|
||||
* This exception is thrown whenever there is any problem initializing or calling the compiler, or
|
||||
* when the compiler itself reports compilation errors. Depending on the nature of the problem, an
|
||||
* instance of this class is initialized with, and then returns, an original exception signalling
|
||||
* the problem, or the compiler application exit code.
|
||||
*/
|
||||
public static class CompilerInteractionException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Exception originalException;
|
||||
private int compilerExitCode;
|
||||
|
||||
/**
|
||||
* Initialize an instance of this exception with an error message and either an original
|
||||
* exception or a compiler exit code.
|
||||
*/
|
||||
CompilerInteractionException(String msg, Exception originalException, int compilerExitCode) {
|
||||
super(msg);
|
||||
this.originalException = originalException;
|
||||
this.compilerExitCode = compilerExitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original exception that caused this exception. <code>null</code> is returned if there
|
||||
* were no original exception. In that case, there was a compilation error and the compiler has
|
||||
* returned with the exit code that can be obtained using <code>getCompilerExitCode</code> method.
|
||||
*
|
||||
* @see #getCompilerExitCode()
|
||||
*/
|
||||
public Exception getOriginalException() {
|
||||
return originalException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the compiler exit code. If <code>0</code> is returned, this indicates that an exception was
|
||||
* thrown during compiler initialization, or inside the compiler itself. Use <code>getOriginalExcepion</code>
|
||||
* method to obtain that exception.
|
||||
*
|
||||
* @see #getOriginalException
|
||||
*/
|
||||
public int getCompilerExitCode() {
|
||||
return compilerExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception signalling a problem with reading project database file. */
|
||||
public static class PDBCorruptedException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
PDBCorruptedException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception signalling a problem when parsing a class file */
|
||||
public static class ClassFileParseException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
ClassFileParseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception signalling that <b>jmake</b> was not requested to do any real work. */
|
||||
public static class NoActionRequestedException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
/** Exception signalling that an invalid command line option has been passed to jmake. */
|
||||
public static class InvalidCmdOptionException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
InvalidCmdOptionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception signalling a problem (typically an I/O exception) when parsing a command line file
|
||||
* passed to <b>jmake</b>.
|
||||
*/
|
||||
public static class CommandFileReadException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
CommandFileReadException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception signalling that for some class the deduced name (that is, name based on the source file name and
|
||||
* directory) and the real name (the one read from the class file) are different.
|
||||
*/
|
||||
public static class ClassNameMismatchException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
ClassNameMismatchException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception thrown if any specified source file has an invalid extension (not <code>.java</code> or <code>.jar</code>). */
|
||||
public static class InvalidSourceFileExtensionException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
InvalidSourceFileExtensionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception thrown if a dependence of a class in a <code>JAR</code> file on a class with a <code>.java</code> source is detected. */
|
||||
public static class JarDependsOnSourceException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
JarDependsOnSourceException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception thrown if more than one entry for the same class is detected in the project */
|
||||
public static class DoubleEntryException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
DoubleEntryException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/** Exception thrown if an internal problem that should never happen is detected. */
|
||||
public static class InternalException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
InternalException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,697 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class implements finding classes referencing other classes and members in various ways.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 12 March 2004
|
||||
*/
|
||||
public class RefClassFinder {
|
||||
|
||||
private boolean failOnDependentJar; // If true, will fail if a dependency of a sourceless class
|
||||
// (coming from a .jar) on a "normal" class is detected
|
||||
private boolean noWarnOnDependentJar; // If true, not even a warning will be issued in the above case.
|
||||
private String checkedClassName;
|
||||
private PCDManager pcdm;
|
||||
private Set<String> affectedClassNames;
|
||||
private boolean checkedClassIsFromJar;
|
||||
|
||||
/** An instance of RefClassFinder is created once per session, passing it the global options that do not change */
|
||||
public RefClassFinder(PCDManager pcdm, boolean failOnDependentJar, boolean noWarnOnDependentJar) {
|
||||
this.pcdm = pcdm;
|
||||
this.failOnDependentJar = failOnDependentJar;
|
||||
this.noWarnOnDependentJar = noWarnOnDependentJar;
|
||||
}
|
||||
|
||||
/** This method is called every time we are going to check a new class */
|
||||
public void initialize(String checkedClassName, boolean checkedClassIsFromJar) {
|
||||
this.checkedClassName = checkedClassName;
|
||||
this.checkedClassIsFromJar = checkedClassIsFromJar;
|
||||
affectedClassNames = new LinkedHashSet<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of project classes that were found potentially affec
|
||||
* by the changes to the checked class.
|
||||
*/
|
||||
public String[] getAffectedClassNames() {
|
||||
int size = affectedClassNames.size();
|
||||
if (size == 0) {
|
||||
return null;
|
||||
} else {
|
||||
String[] ret = new String[size];
|
||||
int i = 0;
|
||||
for (String className : affectedClassNames) {
|
||||
ret[i++] = className;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that can access field fieldNo of class fieldClassInfo.
|
||||
* Used if a compile-time constant is changed.
|
||||
*/
|
||||
public void findAllProjectClasses(ClassInfo fieldClassInfo, int fieldNo) {
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
if (pcde.checkResult == PCDEntry.CV_DELETED) {
|
||||
continue;
|
||||
}
|
||||
if (pcde.javaFileFullPath.endsWith(".jar")) {
|
||||
continue;
|
||||
}
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (memberAccessibleFrom(fieldClassInfo, fieldNo, clientInfo, true)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that reference class with the given name
|
||||
* (but not its array class) directly from the constantpool.
|
||||
*/
|
||||
public void findReferencingClasses0(ClassInfo classInfo) {
|
||||
findReferencingClasses(classInfo, 0, false, null);
|
||||
}
|
||||
|
||||
|
||||
/* In the following "find...ReferencingClasses1" methods, "referencing C" means
|
||||
* "referencing C or its array class directly from the constant pool, as a type of a data
|
||||
* field, as a type in a method signature or a thrown exception, as a directly implemented
|
||||
* interface or a direct superclass".
|
||||
*/
|
||||
/** Used for deleted classes. */
|
||||
public void findReferencingClassesForDeletedClass(ClassInfo classInfo) {
|
||||
String packageName = classInfo.packageName;
|
||||
boolean isPublic = classInfo.isPublic();
|
||||
boolean isInterface = classInfo.isInterface();
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (!isPublic && packageName.equals(clientInfo.packageName)) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.referencesClass(classInfo.name, isInterface, 1)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given class p.C, find each project class X referencing C, that is not a member of
|
||||
* package p and is not a direct or indirect subclass of C's directly enclosing class.
|
||||
* (public -> protected transformation)
|
||||
*/
|
||||
public void findDiffPackageAndNotSubReferencingClasses1(ClassInfo classInfo) {
|
||||
String packageName = classInfo.packageName;
|
||||
String directlyEnclosingClass = classInfo.directlyEnclosingClass;
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (packageName.equals(clientInfo.packageName) ||
|
||||
clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For class p.C, find each project class X referencing C, whose top level enclosing
|
||||
* class is different from that of C.
|
||||
* (public -> private transformation)
|
||||
*/
|
||||
public void findReferencingClasses1(ClassInfo classInfo) {
|
||||
String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (topLevelEnclosingClass.equals(clientInfo.topLevelEnclosingClass)) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For class p.C, find each project class X referencing C, whose direct or indirect superclass
|
||||
* is C's directly enclosing class, or which is a member of package p, whose top level enclosing
|
||||
* class is different from that of C.
|
||||
* (protected -> private transformation)
|
||||
*/
|
||||
public void findThisPackageOrSubReferencingClasses1(ClassInfo classInfo) {
|
||||
String directlyEnclosingClass = classInfo.directlyEnclosingClass;
|
||||
String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
|
||||
String packageName = classInfo.packageName;
|
||||
for (PCDEntry entry : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, entry);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if ((!clientInfo.packageName.equals(packageName)) &&
|
||||
!clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.topLevelEnclosingClass.equals(topLevelEnclosingClass)) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For class p.C, find each project class X referencing C, which is a member of package p and whose
|
||||
* top level enclosing class is different from that of C.
|
||||
* (default -> private transformation)
|
||||
*/
|
||||
public void findThisPackageReferencingClasses1(ClassInfo classInfo) {
|
||||
String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
|
||||
String packageName = classInfo.packageName;
|
||||
for (PCDEntry entry : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD,
|
||||
entry);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (!clientInfo.packageName.equals(packageName)) {
|
||||
continue;
|
||||
}
|
||||
if (topLevelEnclosingClass.equals(clientInfo.topLevelEnclosingClass)) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For class p.C, find each project class X referencing C, which is not a member of package p.
|
||||
* (public -> default transformation)
|
||||
*/
|
||||
public void findDiffPackageReferencingClasses1(ClassInfo classInfo) {
|
||||
String packageName = classInfo.packageName;
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (clientInfo.packageName.equals(packageName)) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For class p.C, find each project class X referencing C, which is not a member of package p and
|
||||
* whose direct or indirect superclass is C's directly enclosing class.
|
||||
* (protected -> default transformation)
|
||||
*/
|
||||
public void findDiffPackageAndSubReferencingClasses1(ClassInfo classInfo) {
|
||||
String packageName = classInfo.packageName;
|
||||
String directlyEnclosingClass = classInfo.directlyEnclosingClass;
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (clientInfo.packageName.equals(packageName)) {
|
||||
continue;
|
||||
}
|
||||
if (!clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that reference both of the classes with the
|
||||
* given names (or array classes of one or both) directly or indirectly from the
|
||||
* constantpool, as a type of a data field, as a type in a method signature or a
|
||||
* thrown exception, as a directly/indirectly implemented interface or a
|
||||
* direct/indirect superclass.
|
||||
*/
|
||||
public void findReferencingClasses2(ClassInfo classInfo1, ClassInfo classInfo2) {
|
||||
Set<String> refClazz1 = new LinkedHashSet<String>();
|
||||
findReferencingClasses(classInfo1, 2, false, refClazz1);
|
||||
Set<String> refClazz2 = new LinkedHashSet<String>();
|
||||
findReferencingClasses(classInfo2, 2, false, refClazz2);
|
||||
|
||||
for (String className1 : refClazz1) {
|
||||
if (refClazz2.contains(className1)) {
|
||||
addToAffectedClassNames(className1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Find all project classes which are direct subclasses of the given class */
|
||||
public void findDirectSubclasses(ClassInfo classInfo) {
|
||||
for (ClassInfo subclassInfo : classInfo.getDirectSubclasses()) {
|
||||
addToAffectedClassNames(subclassInfo.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all non-abstract project classes that implement the given interface or any of its
|
||||
* subclasses directly, and all non-abstract classes that are direct descendants of abstract
|
||||
* classes that implement the given interface directly or indirectly. Class C implements
|
||||
* interface I indirectly, if C or some superclass of C directly implements I or some sublcass of I.
|
||||
*/
|
||||
public void findDirectlyAndOtherwiseImplementingConcreteClasses(ClassInfo intfInfo) {
|
||||
for (PCDEntry entry : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, entry);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (clientInfo.isInterface()) {
|
||||
continue;
|
||||
}
|
||||
if (clientInfo.isAbstract()) {
|
||||
if (clientInfo.implementsInterfaceDirectlyOrIndirectly(intfInfo.name)) {
|
||||
findAllNearestConcreteSubclasses(clientInfo);
|
||||
}
|
||||
} else {
|
||||
if (clientInfo.implementsIntfOrSubintfDirectly(intfInfo.name)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void findAllNearestConcreteSubclasses(ClassInfo classInfo) {
|
||||
for (ClassInfo subclassInfo : classInfo.getDirectSubclasses()) {
|
||||
if (subclassInfo.isAbstract()) {
|
||||
findAllNearestConcreteSubclasses(subclassInfo);
|
||||
} else {
|
||||
addToAffectedClassNames(subclassInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all interfaces and abstract classes that implement the given interface and declare or inherit
|
||||
* a method with the given name. For those that overload this method, find referencing classes.
|
||||
*/
|
||||
public void findAbstractSubtypesWithSameNameMethod(ClassInfo intfInfo, String mName, final String mSig) {
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo ci =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (ci == null) {
|
||||
continue; // New class or not in project
|
||||
}
|
||||
if (!(ci.isInterface() || ci.isAbstract())) {
|
||||
continue;
|
||||
}
|
||||
if (ci.implementsInterfaceDirectlyOrIndirectly(intfInfo.name)) {
|
||||
addToAffectedClassNames(ci.name);
|
||||
// Check if the new method overloads an existing (declared or inherited) method. Overloading test is rough -
|
||||
// we just check if the number of parameters is the same.
|
||||
ci.findExistingSameNameMethods(mName, true, true, new ClassInfo.MethodHandler() {
|
||||
|
||||
void handleMethod(ClassInfo classInfo, int otherMethodIdx) {
|
||||
String otherMSig =
|
||||
classInfo.methodSignatures[otherMethodIdx];
|
||||
if ( (!mSig.equals(otherMSig)) &&
|
||||
Utils.sameParamNumber(mSig, otherMSig)) {
|
||||
findReferencingClassesForMethod(classInfo, otherMethodIdx);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Find all project classes that reference the given field. */
|
||||
public void findReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
|
||||
findReferencingClassesForMember(classInfo, fieldNo, true, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that reference the given field and which are in
|
||||
* different packages.
|
||||
*/
|
||||
public void findDiffPackageReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
|
||||
findReferencingClassesForMember(classInfo, fieldNo, true, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that reference the given field, which are in different
|
||||
* packages and are direct or indirect subclasses of the member's declaring class
|
||||
* (protected -> default transformation).
|
||||
*/
|
||||
public void findDiffPackageAndSubReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
|
||||
findReferencingClassesForMember(classInfo, fieldNo, true, true, true);
|
||||
}
|
||||
|
||||
/** Find all project classes that reference the given method. */
|
||||
public void findReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
|
||||
findReferencingClassesForMember(classInfo, methodNo, false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that reference the given method and which are in
|
||||
* different packages.
|
||||
*/
|
||||
public void findDiffPackageReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
|
||||
findReferencingClassesForMember(classInfo, methodNo, false, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that reference the given method, which are in different
|
||||
* packages and are direct or indirect subclasses of the member's declaring class
|
||||
* (protected -> default transformation)
|
||||
*/
|
||||
public void findDiffPackageAndSubReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
|
||||
findReferencingClassesForMember(classInfo, methodNo, false, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that re-implement the given method and that are
|
||||
* direct/indirect subclasses of this method's declaring class. If some subclass C
|
||||
* re-implements the given method, we don't have to search C's subclasses further.
|
||||
*/
|
||||
public void findSubclassesReimplementingMethod(ClassInfo classInfo, int methodNo) {
|
||||
findSubclassesReimplementingMethod(classInfo, classInfo, methodNo);
|
||||
}
|
||||
|
||||
private void findSubclassesReimplementingMethod(ClassInfo targetClass, ClassInfo methodDeclaringClass, int methodNo) {
|
||||
for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
|
||||
if (subclass.declaresMethod(methodDeclaringClass, methodNo)) {
|
||||
addToAffectedClassNames(subclass.name);
|
||||
} else {
|
||||
findSubclassesReimplementingMethod(subclass, methodDeclaringClass, methodNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given class C, find all concrete direct subclasses, and all direct concrente subclasses of C's direct
|
||||
* or indirect abstract subclasses.
|
||||
*/
|
||||
public void findConcreteSubclasses(ClassInfo targetClass) {
|
||||
for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
|
||||
if (subclass.isAbstract()) {
|
||||
findConcreteSubclasses(subclass);
|
||||
} else {
|
||||
addToAffectedClassNames(subclass.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find any concrete subclasses of targetClass that don't override or inherit a concrete implementation
|
||||
* of the given method.
|
||||
*/
|
||||
public void findConcreteSubclassesNotOverridingAbstractMethod(ClassInfo targetClass, ClassInfo methodDeclaringClass, int methodNo) {
|
||||
for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
|
||||
int pos =
|
||||
subclass.getDeclaredMethodPos(methodDeclaringClass, methodNo);
|
||||
if (pos == -1) { // This method is not overridden in this class
|
||||
if (!subclass.isAbstract()) {
|
||||
addToAffectedClassNames(subclass.name);
|
||||
} else {
|
||||
findConcreteSubclassesNotOverridingAbstractMethod(subclass, methodDeclaringClass, methodNo);
|
||||
}
|
||||
} else { // A chance that this method is declared abstract once again...
|
||||
if (Modifier.isAbstract(subclass.methodAccessFlags[pos])) {
|
||||
findConcreteSubclassesNotOverridingAbstractMethod(subclass, methodDeclaringClass, methodNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Find all project classes that reference any method that throws the given exception. */
|
||||
public void findRefsToMethodsThrowingException(ClassInfo excClassInfo) {
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo classInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (classInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
int methodIdx = -1;
|
||||
do {
|
||||
methodIdx =
|
||||
classInfo.hasMethodThrowingException(excClassInfo, methodIdx + 1);
|
||||
if (methodIdx != -1) {
|
||||
findReferencingClassesForMethod(classInfo, methodIdx);
|
||||
}
|
||||
} while (methodIdx != -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes declaring a static field with the given name. Currently used only to look up
|
||||
* classes referencing given class X via the "X.class" construct.
|
||||
*/
|
||||
public void findClassesDeclaringField(String name, String signature, boolean isStatic, String packageToLookIn) {
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo classInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (classInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (packageToLookIn != null &&
|
||||
!classInfo.packageName.equals(packageToLookIn)) {
|
||||
continue;
|
||||
}
|
||||
if (classInfo.declaresField(name, signature, isStatic)) {
|
||||
addToAffectedClassNames(classInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addToAffectedClassNames(String className) {
|
||||
String res = pcdm.classAlreadyRecompiledOrUncompileable(className);
|
||||
if (res == null) {
|
||||
affectedClassNames.add(className);
|
||||
} else if (!"".equals(res)) { // The dependent class comes from a .jar.
|
||||
if (checkedClassIsFromJar || noWarnOnDependentJar) {
|
||||
return;
|
||||
}
|
||||
String message = "Class " + className + " is affected by a change to " + checkedClassName + ", but can't be recompiled, " +
|
||||
"since it is located in archive " + res;
|
||||
if (failOnDependentJar) {
|
||||
throw new PrivateException(new PublicExceptions.JarDependsOnSourceException(message));
|
||||
} else {
|
||||
Utils.printWarningMessage("Warning: " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that reference the class with the given name.
|
||||
* The second parameter controls the "thoroughness degree", and its value is passed to ClassInfo.referencesClass()
|
||||
* method (see the comment to it). The fromDiffPackages parameter defines whether all such classes
|
||||
* or only classes from different packages are required.
|
||||
*/
|
||||
private void findReferencingClasses(ClassInfo classInfo,
|
||||
int thorDegree, boolean fromDiffPackages,
|
||||
Set<String> ret) {
|
||||
String packageName = classInfo.packageName;
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
if (fromDiffPackages && packageName.equals(clientInfo.packageName)) {
|
||||
continue;
|
||||
}
|
||||
// If thorDegree == 2, i.e. indirect references from the constantpool (e.g. a reference to a method which
|
||||
// has classInfo as one of its formal parameter types) are taken into account, then we should check all of
|
||||
// the classes, whether classInfo is directly accessible from them or not.
|
||||
if (thorDegree != 2 && (!classAccessibleFrom(classInfo, clientInfo))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), thorDegree)) {
|
||||
if (ret == null) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
} else {
|
||||
ret.add(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all project classes that reference the given member. If fromDiffPackages
|
||||
* is true, then only classes that do not belong to the package of the member's
|
||||
* declaring class should be returned. If onlySubclasses is true, then only
|
||||
* classes that are subclasses of member's declaring class should be returned.
|
||||
*/
|
||||
private void findReferencingClassesForMember(ClassInfo declaringClassInfo, int memberNo,
|
||||
boolean isField,
|
||||
boolean fromDiffPackages, boolean onlySubclasses) {
|
||||
String declaringClassName = declaringClassInfo.name;
|
||||
String declaringClassPackage = declaringClassInfo.packageName;
|
||||
for (PCDEntry pcde : pcdm.entries()) {
|
||||
ClassInfo clientInfo =
|
||||
pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
||||
if (clientInfo == null) {
|
||||
continue; // New class
|
||||
}
|
||||
String className = clientInfo.name;
|
||||
if (className.equals(declaringClassName)) {
|
||||
continue;
|
||||
}
|
||||
if (!memberAccessibleFrom(declaringClassInfo, memberNo, clientInfo, isField)) {
|
||||
continue;
|
||||
}
|
||||
if (fromDiffPackages &&
|
||||
declaringClassPackage.equals(clientInfo.packageName)) {
|
||||
continue;
|
||||
}
|
||||
if (onlySubclasses && !clientInfo.isSubclassOf(declaringClassName, false)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isField) {
|
||||
if (clientInfo.referencesField(declaringClassInfo, memberNo)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
} else {
|
||||
if (clientInfo.referencesMethod(declaringClassInfo, memberNo)) {
|
||||
addToAffectedClassNames(clientInfo.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if class classInfo is accessible from class clientClassInfo. */
|
||||
private boolean classAccessibleFrom(ClassInfo classInfo, ClassInfo clientClassInfo) {
|
||||
char classFlags = classInfo.accessFlags;
|
||||
String classPackage = classInfo.packageName;
|
||||
String clientClassPackage = clientClassInfo.packageName;
|
||||
|
||||
if (Modifier.isPublic(classFlags)) {
|
||||
return true;
|
||||
} else if (Modifier.isProtected(classFlags)) {
|
||||
if (classPackage.equals(clientClassPackage) ||
|
||||
clientClassInfo.isSubclassOf(classInfo.directlyEnclosingClass, false)) {
|
||||
return true;
|
||||
}
|
||||
} else if (Modifier.isPrivate(classFlags)) {
|
||||
if (classInfo.topLevelEnclosingClass.equals(clientClassInfo.topLevelEnclosingClass)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (classPackage.equals(clientClassPackage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if member memberNo (which is a field if isField == true, and method otherwise) of class memberClassInfo is
|
||||
* accessible from class clientClassInfo.
|
||||
*/
|
||||
private boolean memberAccessibleFrom(ClassInfo memberClassInfo,
|
||||
int memberNo, ClassInfo clientClassInfo, boolean isField) {
|
||||
char memberClassFlags = memberClassInfo.accessFlags;
|
||||
char memberFlags = isField ? memberClassInfo.fieldAccessFlags[memberNo]
|
||||
: memberClassInfo.methodAccessFlags[memberNo];
|
||||
String memberClassPackage = memberClassInfo.packageName;
|
||||
String clientClassPackage = clientClassInfo.packageName;
|
||||
|
||||
if (Modifier.isPublic(memberClassFlags)) {
|
||||
if (Modifier.isPublic(memberFlags)) {
|
||||
return true;
|
||||
} else if (Modifier.isProtected(memberFlags) &&
|
||||
(memberClassPackage.equals(clientClassPackage) ||
|
||||
clientClassInfo.isSubclassOf(memberClassInfo.name, false))) {
|
||||
return true;
|
||||
} else if (Modifier.isPrivate(memberFlags)) {
|
||||
if (memberClassInfo.topLevelEnclosingClass.equals(
|
||||
clientClassInfo.topLevelEnclosingClass)) {
|
||||
return true;
|
||||
}
|
||||
} else if (memberClassPackage.equals(clientClassPackage)) {
|
||||
return true;
|
||||
}
|
||||
} else if (Modifier.isProtected(memberClassFlags)) {
|
||||
if (!(memberClassPackage.equals(clientClassPackage) ||
|
||||
clientClassInfo.isSubclassOf(memberClassInfo.directlyEnclosingClass, false))) {
|
||||
return true;
|
||||
}
|
||||
if (Modifier.isPublic(memberFlags) ||
|
||||
Modifier.isProtected(memberFlags)) {
|
||||
return true;
|
||||
} else if (Modifier.isPrivate(memberFlags)) {
|
||||
if (memberClassInfo.topLevelEnclosingClass.equals(
|
||||
clientClassInfo.topLevelEnclosingClass)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (memberClassPackage.equals(clientClassPackage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (Modifier.isPrivate(memberClassFlags)) {
|
||||
if (memberClassInfo.topLevelEnclosingClass.equals(
|
||||
clientClassInfo.topLevelEnclosingClass)) {
|
||||
return true;
|
||||
}
|
||||
} else { // memberClassInfo is package-private
|
||||
if (!memberClassPackage.equals(clientClassPackage)) {
|
||||
return false;
|
||||
}
|
||||
if (Modifier.isPublic(memberFlags) || Modifier.isProtected(memberFlags)) {
|
||||
return true;
|
||||
} else if (Modifier.isPrivate(memberFlags)) {
|
||||
if (memberClassInfo.topLevelEnclosingClass.equals(
|
||||
clientClassInfo.topLevelEnclosingClass)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
/* Copyright (c) 2002-2013 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* This class creates the internal representation of the project database from a text buffer.
|
||||
*
|
||||
* The Pants build tool manipulates this data in various ways, and it's easiest for it
|
||||
* to do so by parsing text files directly. This brings JMake into line with Zinc (the
|
||||
* Scala incremental compiler) and allows Pants to handle both uniformly.
|
||||
*
|
||||
* @author Benjy Weinberger
|
||||
* 13 January 2013
|
||||
*/
|
||||
public class TextProjectDatabaseReader {
|
||||
public Map<String,PCDEntry> readProjectDatabaseFromFile(File infile) {
|
||||
try {
|
||||
BufferedReader in =
|
||||
new BufferedReader(new InputStreamReader(new FileInputStream(infile), "UTF-8"));
|
||||
try {
|
||||
return readProjectDatabase(in);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new PrivateException(e);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new PrivateException(e);
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String,PCDEntry> readProjectDatabase(BufferedReader in) {
|
||||
Map<String,PCDEntry> pcd;
|
||||
try {
|
||||
String line = in.readLine();
|
||||
if (!"pcd entries:".equals(line))
|
||||
throw error("Expected: 'pcd entries:', got: " + line);
|
||||
line = in.readLine();
|
||||
Matcher m = Pattern.compile("^(\\d+) items$").matcher(line);
|
||||
if (!m.matches())
|
||||
throw error("Expected: '<n> items', got: " + line);
|
||||
int numEntries = Integer.parseInt(m.group(1));
|
||||
pcd = new LinkedHashMap<String, PCDEntry>(numEntries);
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
line = in.readLine();
|
||||
if (line == null)
|
||||
throw error("Unexpected EOF");
|
||||
String[] parts = line.split("\t");
|
||||
if (parts.length != 5) {
|
||||
throw error("Invalid line: " + line);
|
||||
}
|
||||
String className = parts[0];
|
||||
String javaFullFilePath = parts[1];
|
||||
long oldClassFileLastModified = Long.parseLong(parts[2]);
|
||||
long oldClassFileFingerprint = Long.parseLong(parts[3]);
|
||||
ClassInfo ci = classInfoFromBase64(parts[4]);
|
||||
PCDEntry entry = new PCDEntry(className, javaFullFilePath, oldClassFileLastModified,
|
||||
oldClassFileFingerprint, ci);
|
||||
pcd.put(entry.className, entry);
|
||||
}
|
||||
// We're done: We have detailed dep information in the PCD entries, so we don't
|
||||
// need to read the dep information lines from the file.
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
return pcd;
|
||||
}
|
||||
|
||||
private PrivateException error(String msg) {
|
||||
return new PrivateException(new IllegalArgumentException(msg));
|
||||
}
|
||||
|
||||
private ClassInfo classInfoFromBase64(String s) {
|
||||
try {
|
||||
byte[] bytes = Base64.decode(s.toCharArray());
|
||||
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
|
||||
ClassInfo ret = (ClassInfo)ois.readObject();
|
||||
ret.initializeImmediateTransientFields();
|
||||
return ret;
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
/* Copyright (c) 2002-2013 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements writing a text stream representing a project database.
|
||||
*
|
||||
* @see TextProjectDatabaseReader for details.
|
||||
*
|
||||
* @author Benjy Weinberger
|
||||
* 13 January 2013
|
||||
*/
|
||||
public class TextProjectDatabaseWriter {
|
||||
private static Set<String> primitives = new LinkedHashSet<String>(
|
||||
Arrays.asList("boolean", "byte", "char", "double", "float", "int", "long", "short",
|
||||
"Z", "B", "C", "D", "F", "I", "J", "S"));
|
||||
|
||||
private ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Reusable temp buffer.
|
||||
|
||||
public void writeProjectDatabaseToFile(File outfile, Map<String, PCDEntry> pcd) {
|
||||
try {
|
||||
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outfile), "UTF-8"));
|
||||
try {
|
||||
writeProjectDatabase(out, pcd);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new PrivateException(e);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new PrivateException(e);
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeProjectDatabase(Writer out, Map<String,PCDEntry> pcd) {
|
||||
try {
|
||||
out.write("pcd entries:\n");
|
||||
out.write(Integer.toString(pcd.size()));
|
||||
out.write(" items\n");
|
||||
Map<String, Set<String>> depsBySource = new LinkedHashMap<String, Set<String>>();
|
||||
for (PCDEntry entry : pcd.values()) {
|
||||
writePCDEntry(out, entry);
|
||||
Set<String> deps = depsBySource.get(entry.javaFileFullPath);
|
||||
if (deps == null) {
|
||||
deps = new LinkedHashSet<String>();
|
||||
depsBySource.put(entry.javaFileFullPath, deps);
|
||||
}
|
||||
addDepsFromClassInfo(deps, entry.oldClassInfo);
|
||||
}
|
||||
// Write out dependency information. Note that we don't need to read this back to recreate
|
||||
// the PCD. We write it out here just as a convenience, so that external readers of the PDB
|
||||
// file don't have to grok our internal ClassInfo structures.
|
||||
out.write("dependencies:\n");
|
||||
out.write(Integer.toString(depsBySource.size()));
|
||||
out.write(" items\n");
|
||||
for (Map.Entry<String, Set<String>> item : depsBySource.entrySet()) {
|
||||
out.write(item.getKey());
|
||||
for (String s : item.getValue()) {
|
||||
out.write('\t');
|
||||
out.write(s);
|
||||
}
|
||||
out.write('\n');
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDepsFromClassInfo(Set<String> deps, ClassInfo ci) {
|
||||
for (String s : ci.cpoolRefsToClasses) {
|
||||
int i = 0;
|
||||
int j = s.length();
|
||||
|
||||
// Fix some inconsistencies in how we represent types internally:
|
||||
// Despite the comment on ci.cpoolRefsToClasses, class names may be
|
||||
// representing in it with '['s and with '@', '#' instead of 'L', ';'.
|
||||
while (s.charAt(i) == '[') i++;
|
||||
if (s.charAt(i) == '@') i++;
|
||||
if (s.endsWith("#")) j--;
|
||||
int k = s.indexOf('$');
|
||||
|
||||
// Take the outer class, on references to nested classes.
|
||||
if (k != -1) j = k;
|
||||
if (i > 0 || j < s.length())
|
||||
s = s.substring(i, j);
|
||||
|
||||
// We don't need to record deps on primitive types, or arrays of them.
|
||||
if (!primitives.contains(s))
|
||||
deps.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
private void writePCDEntry(Writer out, PCDEntry entry) {
|
||||
try {
|
||||
out.write(entry.className);
|
||||
out.write('\t');
|
||||
out.write(entry.javaFileFullPath);
|
||||
out.write('\t');
|
||||
out.write(Long.toString(entry.oldClassFileLastModified));
|
||||
out.write('\t');
|
||||
out.write(Long.toString(entry.oldClassFileFingerprint));
|
||||
out.write('\t');
|
||||
out.write(classInfoToBase64(entry.oldClassInfo));
|
||||
out.write('\n');
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private char[] classInfoToBase64(ClassInfo ci) {
|
||||
baos.reset();
|
||||
try {
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(ci);
|
||||
oos.close();
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
return Base64.encode(baos.toByteArray());
|
||||
}
|
||||
}
|
@ -1,355 +0,0 @@
|
||||
/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
||||
*
|
||||
* This program is distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
package org.pantsbuild.jmake;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Utility functions used by other classes from this package.
|
||||
*
|
||||
* @author Misha Dmitriev
|
||||
* 23 January 2003
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
static final String REPORT_PROBLEM =
|
||||
"Please report this problem to Mikhail.Dmitriev@sun.com";
|
||||
static final byte[] MAGIC = {'J', 'a', 'v', 'a', 'm', 'a', 'k', 'e', ' ', 'P', 'r', 'o', 'j', 'e', 'c', 't', ' ', 'D', 'a', 't', 'a', 'b', 'a', 's', 'e', ' ', 'F', 'i', 'l', 'e'};
|
||||
static final int magicLength = MAGIC.length;
|
||||
static final int PDB_FORMAT_CODE_OLD = 1;
|
||||
static final int PDB_FORMAT_CODE_133 = 0x01030300;
|
||||
static final int PDB_FORMAT_CODE_LATEST = PDB_FORMAT_CODE_133;
|
||||
static final int JAVAC_TARGET_RELEASE_OLDEST = 0x01040000; // 1.4 and previous versions
|
||||
static final int JAVAC_TARGET_RELEASE_15 = 0x01050000; // if class is compiled with -target 1.5
|
||||
static final int JAVAC_TARGET_RELEASE_16 = 0x01060000; // if class is compiled with -target 1.6
|
||||
static final int JAVAC_TARGET_RELEASE_17 = 0x01070000; // if class is compiled with -target 1.7
|
||||
static final int JAVAC_TARGET_RELEASE_18 = 0x01080000; // if class is compiled with -target 1.8
|
||||
static int warningLimit = 20; // Maximum number of warnings to print
|
||||
static final int TIMING_TOTAL = 0;
|
||||
static final int TIMING_PDBREAD = 1;
|
||||
static final int TIMING_SYNCHRO = 2;
|
||||
static final int TIMING_SYNCHRO_CHECK_JAVA_FILES = 3;
|
||||
static final int TIMING_FIND_UPDATED_JAVA_FILES = 4;
|
||||
static final int TIMING_CLASS_FILE_OBSOLETE_OR_DELETED = 5;
|
||||
static final int TIMING_COMPILE = 6;
|
||||
static final int TIMING_FIND_UPDATED_CLASSES = 7;
|
||||
static final int TIMING_CHECK_UPDATED_CLASSES = 8;
|
||||
static final int TIMING_PDBWRITE = 9;
|
||||
static final int TIMING_SYNCHRO_CHECK_TMP = 10;
|
||||
static final int TIMING_CLASS_FILE_OBSOLETE_TMP = 11;
|
||||
static final int TIMING_PDBUPDATE = 12;
|
||||
static final int TIMING_ARRAY_LENGTH = 13;
|
||||
private static long timings[] = new long[TIMING_ARRAY_LENGTH];
|
||||
private static boolean timingOn = false;
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Name manipulation stuff
|
||||
// -------------------------------------------------------------------------------
|
||||
/**
|
||||
* Returns package name for the given class. In case of no package, returns an
|
||||
* empty, but non-null string. Returned string is interned.
|
||||
*/
|
||||
public static String getPackageName(String clazzName) {
|
||||
int ldi = clazzName.lastIndexOf('/'); // For convenience, we use system-internal slashes, not dots
|
||||
if (ldi == -1) {
|
||||
return "";
|
||||
} else {
|
||||
return clazzName.substring(0, ldi).intern();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns directly enclosing class name for the given class. If the given class is not a
|
||||
* nested class, returns empty, but non-null string. Returned string is interned.
|
||||
* NOTE FOR JDK 1.5: this function has to work with both old (1.4 and before) and new (1.5) ways
|
||||
* of naming non-member classes. javacTargetRelease determines the javac version for this class;
|
||||
* however on rare occasions (when checking a deleted non-project class) it may be 0, denoting
|
||||
* that javac version is not known.
|
||||
* In that case, we use the old algorithm, which is error-prone due to a bug in nested class
|
||||
* naming that existed prior to JDK 1.5, where both a non-member local nested class B of A, and a
|
||||
* member nested class B of anonymous class A$1, are named A$1$B.
|
||||
*/
|
||||
public static String getDirectlyEnclosingClass(String clazzName, int javacTargetRelease) {
|
||||
int ldi = clazzName.lastIndexOf('$');
|
||||
if (ldi == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (javacTargetRelease >= JAVAC_TARGET_RELEASE_15) {
|
||||
return clazzName.substring(0, ldi).intern();
|
||||
} else { // JAVAC_TARGET_RELEASE_OLDEST or unknown
|
||||
// Take into account local classes which are named like "EncClass$1$LocalClass", where EncClass
|
||||
// is directly enclosing class.
|
||||
int lldi = clazzName.lastIndexOf('$', ldi - 1);
|
||||
if (lldi == -1 || !Character.isDigit(clazzName.charAt(lldi + 1))) {
|
||||
return clazzName.substring(0, ldi).intern();
|
||||
} else {
|
||||
return clazzName.substring(0, lldi).intern();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns top-level enclosing class name for the given class. If the given class is not a
|
||||
* nested class, returns empty, but non-null string. Returned string is interned.
|
||||
*/
|
||||
public static String getTopLevelEnclosingClass(String clazzName) {
|
||||
int fdi = clazzName.indexOf('$');
|
||||
if (fdi == -1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return clazzName.substring(0, fdi).intern();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the full path for the enclosing class file and the full name for the nested class, return the supposed
|
||||
* full path for the nested class.
|
||||
*/
|
||||
public static String getClassFileFullPathForNestedClass(String enclosingClassFileFullPath, String nestedClassFullName) {
|
||||
String enclosingClassDir = enclosingClassFileFullPath;
|
||||
int cutIndex = enclosingClassDir.lastIndexOf(File.separatorChar);
|
||||
enclosingClassDir = enclosingClassDir.substring(0, cutIndex + 1); // If slash is present, it's included, otherwise we get ""
|
||||
cutIndex = nestedClassFullName.lastIndexOf('/');
|
||||
String nestedClassLocalName;
|
||||
if (cutIndex < 0) {
|
||||
nestedClassLocalName = nestedClassFullName;
|
||||
} else {
|
||||
nestedClassLocalName = nestedClassFullName.substring(cutIndex + 1);
|
||||
}
|
||||
return enclosingClassDir + nestedClassLocalName + ".class";
|
||||
}
|
||||
|
||||
/**
|
||||
* For two strings representing signatures, check if the number of parameters in
|
||||
* both is the same.
|
||||
*/
|
||||
public static boolean sameParamNumber(String sig1, String sig2) {
|
||||
return getParamNumber(sig1) == getParamNumber(sig2);
|
||||
}
|
||||
|
||||
private static int getParamNumber(String sig) {
|
||||
char ch;
|
||||
int parNo = 0, pos = 0;
|
||||
do {
|
||||
ch = sig.charAt(++pos);
|
||||
if (ch == ')') {
|
||||
break;
|
||||
}
|
||||
while (ch == '[') {
|
||||
ch = sig.charAt(++pos);
|
||||
}
|
||||
parNo++;
|
||||
if (ch == '@') {
|
||||
// We replaced all "Lclassname;" in signatures with "@classname#"
|
||||
while (ch != '#') {
|
||||
ch = sig.charAt(++pos);
|
||||
}
|
||||
}
|
||||
} while (ch != ')');
|
||||
return parNo;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// File related stuff
|
||||
// -------------------------------------------------------------------------------
|
||||
public static File checkFileForName(String name) {
|
||||
// For each .java file, a File object is created two times when jmake executes: first when we synchronise the PCD
|
||||
// and the supplied .java file list (we make sure that the .java file exists), and second time when we check if a class
|
||||
// file was updated (we compare time stamps of the .java and the .class file). I tried to call this routine for a .java
|
||||
// class both times, and cached File objects, but it looks as if this does not bring any real speed-up (and in fact may
|
||||
// even slow down the application). Most of the time seems to go to the underlying code creating internal File
|
||||
// representation; once it is created, it takes little time to execute another "new File()" for it. Also, all operations
|
||||
// on files like getCanonicalPath() or lastModified() seem to be quite expensive, so their unnecessary repetition should
|
||||
// be avoided as much as possible.
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
File file = new File(name);
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static File checkOrCreateDirForName(String name) {
|
||||
File file = new File(name);
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
if (file.exists()) {
|
||||
if (!file.isDirectory()) {
|
||||
throw new PrivateException(new PublicExceptions.InternalException(file + " is not a directory."));
|
||||
}
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] readFileIntoBuffer(File file) {
|
||||
try {
|
||||
InputStream in = new FileInputStream(file);
|
||||
int len = (int) file.length();
|
||||
return readInputStreamIntoBuffer(in, len);
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readZipEntryIntoBuffer(ZipFile file, ZipEntry entry) {
|
||||
try {
|
||||
InputStream in = file.getInputStream(entry);
|
||||
int len = (int) entry.getSize();
|
||||
return Utils.readInputStreamIntoBuffer(in, len);
|
||||
} catch (IOException e) {
|
||||
throw new PrivateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readInputStreamIntoBuffer(InputStream in, int len) throws IOException {
|
||||
byte buf[] = new byte[len];
|
||||
int readBytes, ofs = 0, remBytes = len;
|
||||
do {
|
||||
readBytes = in.read(buf, ofs, remBytes);
|
||||
ofs += readBytes;
|
||||
remBytes -= readBytes;
|
||||
} while (ofs < len);
|
||||
in.close();
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static void readAndPrintBytesFromStream(InputStream in, OutputStream out) throws IOException {
|
||||
int avail = in.available();
|
||||
if (avail > 0) {
|
||||
byte outbytes[] = new byte[avail];
|
||||
int realOutBytes = in.read(outbytes);
|
||||
out.write(outbytes, 0, realOutBytes);
|
||||
}
|
||||
}
|
||||
|
||||
/** For a Windows path, convert the drive letter to the lower case */
|
||||
public static String convertDriveLetterToLowerCase(String path) {
|
||||
if (path.charAt(1) != ':') {
|
||||
return path;
|
||||
}
|
||||
char drive = path.charAt(0);
|
||||
if (Character.isUpperCase(drive)) {
|
||||
drive = Character.toLowerCase(drive);
|
||||
char[] chars = path.toCharArray();
|
||||
chars[0] = drive;
|
||||
path = new String(chars);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void ignore(Exception e) {
|
||||
// Ignore this exception
|
||||
}
|
||||
|
||||
/** Used when invoking a third-party executable compiler app */
|
||||
public static void delay(int ms) {
|
||||
Object o = new Object();
|
||||
synchronized (o) {
|
||||
try {
|
||||
o.wait(ms);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// -------------------------------------------------------------------------------
|
||||
// Custom printing stuff
|
||||
// -------------------------------------------------------------------------------
|
||||
private static PrintStream out = System.out;
|
||||
private static PrintStream warn = System.out;
|
||||
private static PrintStream err = System.err;
|
||||
private static boolean printInfoMessages = true;
|
||||
private static boolean printWarningMessages = true;
|
||||
private static boolean printErrorMessages = true;
|
||||
private static int warningNo;
|
||||
|
||||
public static void setOutputStreams(PrintStream out, PrintStream warn, PrintStream err) {
|
||||
Utils.out = out;
|
||||
Utils.warn = warn;
|
||||
Utils.err = err;
|
||||
}
|
||||
|
||||
public static void customizeOutput(boolean printInfoMessages, boolean printWarningMessages, boolean printErrorMessages) {
|
||||
Utils.printInfoMessages = printInfoMessages;
|
||||
Utils.printWarningMessages = printWarningMessages;
|
||||
Utils.printErrorMessages = printErrorMessages;
|
||||
}
|
||||
|
||||
public static void printInfoMessage(String message) {
|
||||
if (printInfoMessages) {
|
||||
out.println(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void printInfoMessageNoEOL(String message) {
|
||||
if (printInfoMessages) {
|
||||
out.print(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void printWarningMessage(String message) {
|
||||
if (!printWarningMessages) {
|
||||
return;
|
||||
}
|
||||
if (warningNo < warningLimit) {
|
||||
warn.println(message);
|
||||
} else if (warningNo == warningLimit) {
|
||||
warn.println("jmake: more than " + warningLimit + " warnings.");
|
||||
}
|
||||
warningNo++;
|
||||
}
|
||||
|
||||
public static void printErrorMessage(String message) {
|
||||
if (printErrorMessages) {
|
||||
err.println("jmake: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------
|
||||
// Measuring stuff
|
||||
// -------------------------------------------------------------------------------
|
||||
public static void setTimingOn() {
|
||||
timingOn = true;
|
||||
}
|
||||
|
||||
public static void startTiming(int slot) {
|
||||
timings[slot] = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static void stopAndPrintTiming(String message, int slot) {
|
||||
if (timingOn) {
|
||||
long time = System.currentTimeMillis() - timings[slot];
|
||||
printInfoMessage("========== " + message + " time = " + time);
|
||||
}
|
||||
}
|
||||
|
||||
public static void printTiming(String message, int slot) {
|
||||
if (timingOn) {
|
||||
printInfoMessage("========== " + message + " time = " + timings[slot]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopAndAddTiming(int slot1, int slot2) {
|
||||
if (timingOn) {
|
||||
long time = System.currentTimeMillis() - timings[slot1];
|
||||
timings[slot2] += time;
|
||||
}
|
||||
}
|
||||
}
|
3
third_party/robolectric/BUILD.gn
vendored
3
third_party/robolectric/BUILD.gn
vendored
@ -466,9 +466,6 @@ java_library("shadows_core_java") {
|
||||
# here which requires_android.
|
||||
bypass_platform_checks = true
|
||||
|
||||
# TODO(mikecase): Remove this once crbug.com/638875 is fixed.
|
||||
enable_incremental_javac_override = false
|
||||
|
||||
testonly = true
|
||||
processor_args_javac =
|
||||
[ "org.robolectric.annotation.processing.shadowPackage=org.robolectric" ]
|
||||
|
Reference in New Issue
Block a user