Commit 2f808349 by Jamie Madill Committed by Commit Bot

Add standalone Android build.

Includes necessary DEPS and GN build changes. Some folders are copied directly from Chromium because they don't exist as source mirrors or aren't available for other reasons. Bug: angleproject:2344 Change-Id: Ibb7f8a3e2288048b7aed2b0e277ca63b5a932c57 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2826480 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org>
parent bdf5f084
...@@ -31,10 +31,15 @@ ...@@ -31,10 +31,15 @@
/src/tests/third_party/gles_conformance_tests /src/tests/third_party/gles_conformance_tests
/testing /testing
/third_party/abseil-cpp /third_party/abseil-cpp
/third_party/android_build_tools
/third_party/android_deps /third_party/android_deps
/third_party/android_ndk /third_party/android_ndk
/third_party/android_sdk
/third_party/android_system_sdk/*.jar
/third_party/bazel/desugar/*.jar
/third_party/catapult /third_party/catapult
/third_party/cherry /third_party/cherry
/third_party/colorama/src
/third_party/depot_tools /third_party/depot_tools
/third_party/fuchsia-sdk /third_party/fuchsia-sdk
/third_party/gles1_conform /third_party/gles1_conform
...@@ -42,17 +47,23 @@ ...@@ -42,17 +47,23 @@
/third_party/googletest /third_party/googletest
/third_party/jdk/current /third_party/jdk/current
/third_party/jdk/extras/java_8 /third_party/jdk/extras/java_8
/third_party/jinja2
/third_party/jsoncpp /third_party/jsoncpp
/third_party/libjpeg_turbo /third_party/libjpeg_turbo
/third_party/libpng/src /third_party/libpng/src
/third_party/llvm-build /third_party/llvm-build
/third_party/markupsafe
/third_party/nasm /third_party/nasm
/third_party/proguard/lib
/third_party/protobuf /third_party/protobuf
/third_party/Python-Markdown /third_party/Python-Markdown
/third_party/qemu-linux-x64 /third_party/qemu-linux-x64
/third_party/qemu-mac-x64 /third_party/qemu-mac-x64
/third_party/r8/lib
/third_party/rapidjson/src /third_party/rapidjson/src
/third_party/six
/third_party/SwiftShader /third_party/SwiftShader
/third_party/turbine/*.jar
/third_party/VK-GL-CTS/src /third_party/VK-GL-CTS/src
/third_party/vulkan-deps /third_party/vulkan-deps
/third_party/vulkan_memory_allocator /third_party/vulkan_memory_allocator
......
...@@ -28,5 +28,7 @@ if (host_os == "mac" && use_system_xcode == "") { ...@@ -28,5 +28,7 @@ if (host_os == "mac" && use_system_xcode == "") {
use_system_xcode = _result != 1 use_system_xcode = _result != 1
} }
# ANGLE standalone builds don't currently support java templates or APK generation enable_java_templates = true
enable_java_templates = build_with_chromium
# Build with fewer Android dependencies
limit_android_deps = true
...@@ -424,17 +424,14 @@ if (angle_standalone || build_with_chromium) { ...@@ -424,17 +424,14 @@ if (angle_standalone || build_with_chromium) {
if (is_android) { if (is_android) {
public_configs += [ "$angle_root:build_id_config" ] public_configs += [ "$angle_root:build_id_config" ]
if (build_with_chromium) {
configs -= [ "//build/config/android:hide_all_but_jni" ] configs -= [ "//build/config/android:hide_all_but_jni" ]
use_default_launcher = false use_default_launcher = false
generate_final_jni = false
android_manifest_template = "$angle_root/src/tests/test_utils/runner/android/java/AndroidManifest.xml.jinja2" android_manifest_template = "$angle_root/src/tests/test_utils/runner/android/java/AndroidManifest.xml.jinja2"
deps += [ deps += [
"$angle_root/src/tests:native_test_java", "$angle_root/src/tests:native_test_java",
"$angle_root/src/tests:native_test_support_android", "$angle_root/src/tests:native_test_support_android",
] ]
} else {
use_raw_android_executable = true
}
} }
if (is_ios) { if (is_ios) {
# We use a special main function on iOS to initialize UIKit before the normal main runs. # We use a special main function on iOS to initialize UIKit before the normal main runs.
......
...@@ -41,17 +41,34 @@ ANGLE_CHROMIUM_DEPS = [ ...@@ -41,17 +41,34 @@ ANGLE_CHROMIUM_DEPS = [
'buildtools/win', 'buildtools/win',
'testing', 'testing',
'third_party/abseil-cpp', 'third_party/abseil-cpp',
'third_party/android_build_tools',
'third_party/android_build_tools/aapt2',
'third_party/android_build_tools/art',
'third_party/android_build_tools/bundletool',
'third_party/android_deps', 'third_party/android_deps',
'third_party/android_ndk',
'third_party/android_sdk',
'third_party/android_sdk/androidx_browser/src',
'third_party/android_sdk/public',
'third_party/android_system_sdk',
'third_party/bazel',
'third_party/catapult', 'third_party/catapult',
'third_party/colorama/src',
'third_party/depot_tools', 'third_party/depot_tools',
'third_party/jdk', 'third_party/jdk',
'third_party/jdk/extras', 'third_party/jdk/extras',
'third_party/jinja2',
'third_party/libjpeg_turbo', 'third_party/libjpeg_turbo',
'third_party/markupsafe',
'third_party/nasm', 'third_party/nasm',
'third_party/proguard',
'third_party/protobuf', 'third_party/protobuf',
'third_party/Python-Markdown', 'third_party/Python-Markdown',
'third_party/qemu-linux-x64', 'third_party/qemu-linux-x64',
'third_party/qemu-mac-x64', 'third_party/qemu-mac-x64',
'third_party/r8',
'third_party/six',
'third_party/turbine',
'third_party/zlib', 'third_party/zlib',
'tools/clang', 'tools/clang',
'tools/clang/dsymutil', 'tools/clang/dsymutil',
......
...@@ -16,7 +16,7 @@ declare_args() { ...@@ -16,7 +16,7 @@ declare_args() {
is_win || is_linux || is_chromeos || is_android || is_apple || is_fuchsia is_win || is_linux || is_chromeos || is_android || is_apple || is_fuchsia
} }
if (is_android && build_with_chromium) { if (is_android) {
android_library("native_test_java") { android_library("native_test_java") {
testonly = true testonly = true
sources = [ sources = [
...@@ -414,9 +414,7 @@ if (build_angle_perftests) { ...@@ -414,9 +414,7 @@ if (build_angle_perftests) {
"${angle_root}:libGLESv2", "${angle_root}:libGLESv2",
] ]
if (build_with_chromium) {
data_deps += [ "//testing:run_perf_test" ] data_deps += [ "//testing:run_perf_test" ]
}
if (is_win || is_linux || is_chromeos) { if (is_win || is_linux || is_chromeos) {
data_deps += [ "${angle_root}/third_party/glmark2:glmark2_angle" ] data_deps += [ "${angle_root}/third_party/glmark2:glmark2_angle" ]
......
monorail {
component: "Mobile>WebView"
}
team_email: "android-webview-dev@chromium.org"
torne@chromium.org
tobiasjs@chromium.org
Name: Android System SDK
URL: https://android.googlesource.com/platform/frameworks/base
Short Name: Android System SDK
Version: 0
Revision: RPB2.200611.009
License: GPL v2
License File: LICENSE
Security Critical: No
Description:
System SDK stubs for compiling Android Webview and Monochrome targets.
Revision is the BUILD ID of the Android release build.
Because prebuilt system SDKs are not distributed publicly, Googlers will need to
follow http://go/clank-webview/rolling_the_android_sdk.md to update this.
Local Modifications:
None
# Copyright 2017 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.
# To create CIPD package run the following command.
# cipd create --pkg-def cipd.yaml -tag version:$(cat version.txt)
package: chromium/third_party/android_system_sdk
description: System SDK stubs for compile WebView and Monochrome targets
data:
- file: android-stubs-src.jar
- file: android_system.jar
monorail {
component: "Build"
}
team_email: "build@chromium.org"
agrieve@chromium.org
wnwen@chromium.org
Name: (Components of) Bazel
Short Name: (Components of) Bazel
URL: https://github.com/bazelbuild/bazel
License: Apache 2.0
License File: NOT_SHIPPED
Version: Unknown
Security Critical: no
Description:
This directory contains Bazel components used for building Android binaries.
Chromium does not use Bazel itself as the build platform. Please refer to each
sub-directory for information on each Bazel component.
# Copyright 2017 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.
package: chromium/third_party/bazel
data:
- file: desugar/Desugar.jar
- file: desugar/Desugar_runtime.jar
# Copyright 2017 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")
# Provides runtime support for desugar transformations.
java_prebuilt("desugar_runtime_java") {
supports_android = true
jar_path = "Desugar_runtime.jar"
}
Name: Desugar, transforming Java 8 bytecode to be Java 7 compatible
Short Name: desugar
URL: https://github.com/bazelbuild/bazel
Version: 1.1.0
License: Apache 2.0
Security Critical: no
Description:
Desugar is a Google-developed open-source Java library used by the build process
to transform Java 8 bytecode to Java 7. It is used to enable Java 8 features
such as lambda experssions for Chrome on Android.
Googlers: See: go/desugar
Local Modifications:
* Added BUILD.gn, proguard.flags.
* Made all lambdas be "stateful" to avoid <clinit> bloat.
* Desugar_deploy.jar split into Desugar.jar and Desugar_runtime.jar.
* Desugar.jar has been run through r8.jar to remove unused .class files.
Update instructions (requires @google.com account for uploading):
* Check out Bazel from https://github.com/bazelbuild/bazel
* Compile or install Bazel by following instructions on
https://docs.bazel.build/versions/master/install.html
* Apply stateful-lambdas.patch:
git apply $CHROMIUM_SRC/third_party/bazel/desugar/stateful-lambdas.patch
* Build Desugar_deploy.jar:
bazel build //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar_deploy.jar
* Move Desugar_deploy.jar to location within Chromium:
rm $CHROMIUM_SRC/third_party/bazel/desugar/Desugar.jar
mv bazel-bin/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar_deploy.jar $CHROMIUM_SRC/third_party/bazel/desugar/Desugar.jar
* Update Desugar_runtime.jar:
unzip Desugar.jar "com/google/devtools/build/android/desugar/runtime*"
zip -rD0 Desugar_runtime.jar com
rm -r com
* Shrink via r8 (optional - reduces size by about 66%).
java -jar ../../r8/lib/r8.jar --debug --classfile --output Desugar.new.jar --pg-conf proguard.flags --no-desugaring --lib ../../jdk/extras/java_8/jre/lib/rt.jar Desugar.jar
mv Desugar.new.jar Desugar.jar
* Perform a sanity check of chrome_public_apk:
ninja chrome_public_apk
bin/chrome_public_apk run
* Update this README.chromium (Version)
* Upload new jar to CIPD:
cd third_party/bazel
cipd create --pkg-def cipd.yaml
* Update revision //DEPS
# Copyright 2019 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.
-dontoptimize
-dontobfuscate
-keepattributes *
-dontwarn *
-keep class com.google.devtools.** {
*;
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaClassFixer.java b/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaClassFixer.java
index ff3e351996..f857e61d1e 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaClassFixer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaClassFixer.java
@@ -93,7 +93,7 @@ class LambdaClassFixer extends ClassVisitor {
checkArgument(BitFlags.noneSet(access, Opcodes.ACC_INTERFACE), "Not a class: %s", name);
checkState(this.originalInternalName == null, "not intended for reuse but reused for %s", name);
originalInternalName = name;
- hasState = false;
+ hasState = true;
hasFactory = false;
desc = null;
this.signature = null;
@@ -136,7 +136,7 @@ class LambdaClassFixer extends ClassVisitor {
} else if ("<init>".equals(name)) {
this.desc = desc;
this.signature = signature;
- if (!lambdaInfo.needFactory() && !desc.startsWith("()")) {
+ if (!lambdaInfo.needFactory()) {
access &= ~Opcodes.ACC_PRIVATE; // make constructor accessible if we'll call it directly
}
}
@@ -156,10 +156,6 @@ class LambdaClassFixer extends ClassVisitor {
@Override
public void visitEnd() {
- checkState(
- !hasState || hasFactory,
- "Expected factory method for capturing lambda %s",
- getInternalName());
if (!hasState) {
checkState(
signature == null,
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java b/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java
index 8f90d25ff5..f5ed6d524a 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java
@@ -423,8 +423,7 @@ class LambdaDesugaring extends ClassVisitor {
String lambdaClassName = internalName + "$$Lambda$" + (lambdaCount++);
Type[] capturedTypes = Type.getArgumentTypes(desc);
boolean needFactory =
- capturedTypes.length != 0
- && !attemptAllocationBeforeArgumentLoads(lambdaClassName, capturedTypes);
+ !attemptAllocationBeforeArgumentLoads(lambdaClassName, capturedTypes);
lambdas.generateLambdaClass(
internalName,
LambdaInfo.create(
@@ -435,7 +434,7 @@ class LambdaDesugaring extends ClassVisitor {
bridgeInfo.bridgeMethod()),
bsmMethod,
args);
- if (desc.startsWith("()")) {
+ if (false) {
// For stateless lambda classes we'll generate a singleton instance that we can just load
checkState(capturedTypes.length == 0);
super.visitFieldInsn(
@@ -493,7 +492,6 @@ class LambdaDesugaring extends ClassVisitor {
* @return {@code true} if we were able to insert a new/dup, {@code false} otherwise
*/
private boolean attemptAllocationBeforeArgumentLoads(String internalName, Type[] paramTypes) {
- checkArgument(paramTypes.length > 0, "Expected at least one param for %s", internalName);
// Walk backwards past loads corresponding to constructor arguments to find the instruction
// after which we need to insert our NEW/DUP pair
AbstractInsnNode insn = instructions.getLast();
monorail {
component: "Tools"
}
Copyright (c) 2010 Jonathan Hartley
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holders, nor those of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
file://build/android/gyp/OWNERS
Name: colorama
Short Name: colorama
URL: https://github.com/tartley/colorama.git
Version: 799604a104
License: BSD
License File: NOT_SHIPPED
Security Critical: no
Description:
Provides a simple cross-platform API to print colored terminal text from Python
applications.
Local modifications:
None
package(
default_visibility = [
"//src:__subpackages__",
"//third_party/ijar:__subpackages__",
],
)
licenses(["notice"]) # Apache 2.0
cc_library(
name = "zip",
srcs = [
"zip.cc",
] + select({
"//src:windows": [
"mapped_file_windows.cc",
],
"//conditions:default": [
"mapped_file_unix.cc",
],
}),
hdrs = [
"common.h",
"mapped_file.h",
"zip.h",
],
visibility = [
"//src:__subpackages__",
"//third_party/ijar:__subpackages__",
"//tools/test:__pkg__",
],
deps = [
":platform_utils",
":zlib_client",
] + select({
"//src:windows": [
"//src/main/cpp/util:errors",
"//src/main/cpp/util:filesystem",
"//src/main/cpp/util:logging",
"//src/main/cpp/util:strings",
],
"//conditions:default": [
],
}),
)
cc_library(
name = "zlib_client",
srcs = ["zlib_client.cc"],
hdrs = [
"common.h",
"zlib_client.h",
],
deps = ["//third_party/zlib"],
)
cc_library(
name = "platform_utils",
srcs = ["platform_utils.cc"],
hdrs = [
"common.h",
"platform_utils.h",
],
visibility = ["//visibility:private"],
deps = [
"//src/main/cpp/util:errors",
"//src/main/cpp/util:filesystem",
"//src/main/cpp/util:logging",
],
)
cc_binary(
name = "zipper",
srcs = ["zip_main.cc"],
visibility = ["//visibility:public"],
deps = [":zip"],
)
cc_binary(
name = "ijar",
srcs = [
"classfile.cc",
"ijar.cc",
],
visibility = ["//visibility:public"],
deps = [":zip"],
)
filegroup(
name = "srcs",
srcs = glob(["**"]) + ["//third_party/ijar/test:srcs"],
visibility = ["//third_party:__pkg__"],
)
filegroup(
name = "embedded_zipper_sources",
srcs = [
"zip.cc",
"zip.h",
"zip_main.cc",
"common.h",
"mapped_file.h",
"platform_utils.cc",
"platform_utils.h",
"zlib_client.cc",
"zlib_client.h",
"BUILD",
] + select({
"//src:windows": [
"mapped_file_windows.cc",
],
"//conditions:default": [
"mapped_file_unix.cc",
],
}),
visibility = ["//visibility:public"],
)
filegroup(
name = "transitive_sources",
srcs = [":srcs"] + ["//src/main/cpp/util:embedded_java_tools"],
visibility = ["//visibility:public"],
)
genrule(
name = "ijar_transitive_zip",
srcs = [
":ijar_srcs_zip",
"//src:zlib_zip",
"//src/main/cpp/util:cpp_util_with_deps_zip",
],
outs = ["ijar_srcs_with_deps.zip"],
cmd = "$(location //src:merge_zip_files) - $@ $(SRCS)",
tools = ["//src:merge_zip_files"],
visibility = ["//visibility:public"],
)
genrule(
name = "ijar_srcs_zip",
srcs = glob(
["**"],
exclude = ["BUILD"],
) + [
":ijar",
":zipper",
],
outs = ["ijar_srcs.zip"],
cmd = "$(location //src:zip_files) ijar $@ $(SRCS)",
tools = ["//src:zip_files"],
visibility = ["//visibility:private"],
)
# 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.
# A tool that removes all non-interface-specific parts from a .jar file.
if (is_linux || is_chromeos) {
executable("ijar") {
sources = [
"classfile.cc",
"common.h",
"ijar.cc",
"mapped_file.h",
"mapped_file_unix.cc",
"platform_utils.cc",
"platform_utils.h",
"zip.cc",
"zip.h",
"zlib_client.cc",
"zlib_client.h",
]
deps = [ "//third_party/zlib" ]
# Always build release since this is a build tool.
if (is_debug) {
configs -= [ "//build/config:debug" ]
configs += [ "//build/config:release" ]
}
}
}
monorail: {
component: "Build"
}
team_email: "build@chromium.org"
agrieve@chromium.org
dpranke@google.com
Name: ijar
URL: https://github.com/bazelbuild/bazel
Version: b786db0994a4c4c65e3f93a4f6fa268780caff9c
License: Apache 2.0
License File: NOT_SHIPPED
Security Critical: No
Description:
A tool for generating interface .jars from normal .jars.
Local Modifications:
- Removed test directory
- Removed code from platform_utils.cc that referenced "blaze_util".
- Removed mapped_file_windows.cc since it caused checkdeps to fail.
- Added BUILD.gn
ijar: A tool for generating interface .jars from normal .jars
=============================================================
Alan Donovan, 26 May 2007.
Rationale:
In order to improve the speed of compilation of Java programs in
Bazel, the output of build steps is cached.
This works very nicely for C++ compilation: a compilation unit
includes a .cc source file and typically dozens of header files.
Header files change relatively infrequently, so the need for a
rebuild is usually driven by a change in the .cc file. Even after
syncing a slightly newer version of the tree and doing a rebuild,
many hits in the cache are still observed.
In Java, by contrast, a compilation unit involves a set of .java
source files, plus a set of .jar files containing already-compiled
JVM .class files. Class files serve a dual purpose: from the JVM's
perspective, they are containers of executable code, but from the
compiler's perspective, they are interface definitions. The problem
here is that .jar files are very much more sensitive to change than
C++ header files, so even a change that is insignificant to the
compiler (such as the addition of a print statement to a method in a
prerequisite class) will cause the jar to change, and any code that
depends on this jar's interface will be recompiled unnecessarily.
The purpose of ijar is to produce, from a .jar file, a much smaller,
simpler .jar file containing only the parts that are significant for
the purposes of compilation. In other words, an interface .jar
file. By changing ones compilation dependencies to be the interface
jar files, unnecessary recompilation is avoided when upstream
changes don't affect the interface.
Details:
ijar is a tool that reads a .jar file and emits a .jar file
containing only the parts that are relevant to Java compilation.
For example, it throws away:
- Files whose name does not end in ".class".
- All executable method code.
- All private methods and fields.
- All constants and attributes except the minimal set necessary to
describe the class interface.
- All debugging information
(LineNumberTable, SourceFile, LocalVariableTables attributes).
It also sets to zero the file modification times in the index of the
.jar file.
Implementation:
ijar is implemented in C++, and runs very quickly. For example
(when optimized) it takes only 530ms to process a 42MB
.jar file containing 5878 classes, resulting in an interface .jar
file of only 11.4MB in size. For more usual .jar sizes of a few
megabytes, a runtime of 50ms is typical.
The implementation strategy is to mmap both the input jar and the
newly-created _interface.jar, and to scan through the former and
emit the latter in a single pass. There are a couple of locations
where some kind of "backpatching" is required:
- in the .zip file format, for each file, the size field precedes
the data. We emit a zero but note its location, generate and emit
the stripped classfile, then poke the correct size into the
location.
- for JVM .class files, the header (including the constant table)
precedes the body, but cannot be emitted before it because it's
not until we emit the body that we know which constants are
referenced and which are garbage. So we emit the body into a
temporary buffer, then emit the header to the output jar, followed
by the contents of the temp buffer.
Also note that the zip file format has unnecessary duplication of
the index metadata: it has header+data for each file, then another
set of (similar) headers at the end. Rather than save the metadata
explicitly in some datastructure, we just record the addresses of
the already-emitted zip metadata entries in the output file, and
then read from there as necessary.
Notes:
This code has no dependency except on the STL and on zlib.
Almost all of the getX/putX/ReadX/WriteX functions in the code
advance their first argument pointer, which is passed by reference.
It's tempting to discard package-private classes and class members.
However, this would be incorrect because they are a necessary part
of the package interface, as a Java package is often compiled in
multiple stages. For example: in Bazel, both java tests and java
code inhabit the same Java package but are compiled separately.
Assumptions:
We assume that jar files are uncompressed v1.0 zip files (created
with 'jar c0f') with a zero general_purpose_bit_flag.
We assume that javap/javac don't need the correct CRC checksums in
the .jar file.
We assume that it's better simply to abort in the face of unknown
input than to risk leaving out something important from the output
(although in the case of annotations, it should be safe to ignore
ones we don't understand).
TODO:
Maybe: ensure a canonical sort order is used for every list (jar
entries, class members, attributes, etc.) This isn't essential
because we can assume the compiler is deterministic and the order in
the source files changes little. Also, it would require two passes. :(
Maybe: delete dynamically-allocated memory.
Add (a lot) more tests. Include a test of idempotency.
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// common.h -- common definitions.
//
#ifndef INCLUDED_DEVTOOLS_IJAR_COMMON_H
#define INCLUDED_DEVTOOLS_IJAR_COMMON_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#ifdef _WIN32
# define PATH_MAX 4096
typedef int mode_t;
#endif // _WIN32
namespace devtools_ijar
{
typedef unsigned long long u8;
typedef uint32_t u4;
typedef uint16_t u2;
typedef uint8_t u1;
// be = big endian, le = little endian
inline u1 get_u1(const u1 *&p)
{
return *p++;
}
inline u2 get_u2be(const u1 *&p)
{
u4 x = (p[0] << 8) | p[1];
p += 2;
return x;
}
inline u2 get_u2le(const u1 *&p)
{
u4 x = (p[1] << 8) | p[0];
p += 2;
return x;
}
inline u4 get_u4be(const u1 *&p)
{
u4 x = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
p += 4;
return x;
}
inline u4 get_u4le(const u1 *&p)
{
u4 x = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0];
p += 4;
return x;
}
inline u8 get_u8le(const u1 *&p)
{
u4 lo = get_u4le(p);
u4 hi = get_u4le(p);
u8 x = ((u8)hi << 32) | lo;
return x;
}
inline void put_u1(u1 *&p, u1 x)
{
*p++ = x;
}
inline void put_u2be(u1 *&p, u2 x)
{
*p++ = x >> 8;
*p++ = x & 0xff;
}
inline void put_u2le(u1 *&p, u2 x)
{
*p++ = x & 0xff;
*p++ = x >> 8;
;
}
inline void put_u4be(u1 *&p, u4 x)
{
*p++ = x >> 24;
*p++ = (x >> 16) & 0xff;
*p++ = (x >> 8) & 0xff;
*p++ = x & 0xff;
}
inline void put_u4le(u1 *&p, u4 x)
{
*p++ = x & 0xff;
*p++ = (x >> 8) & 0xff;
*p++ = (x >> 16) & 0xff;
*p++ = x >> 24;
}
inline void put_u8le(u1 *&p, u8 x)
{
put_u4le(p, x & 0xffffffff);
put_u4le(p, (x >> 32) & 0xffffffff);
}
// Copy n bytes from src to p, and advance p.
inline void put_n(u1 *&p, const u1 *src, size_t n)
{
memcpy(p, src, n);
p += n;
}
extern bool verbose;
} // namespace devtools_ijar
#endif // INCLUDED_DEVTOOLS_IJAR_COMMON_H
// Copyright 2016 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include "third_party/ijar/common.h"
#include "third_party/ijar/zlib_client.h"
namespace devtools_ijar
{
u4 ComputeCrcChecksum(u1 *buf, size_t length)
{
return 0;
}
size_t TryDeflate(u1 *buf, size_t length)
{
return 0;
}
Decompressor::Decompressor() {}
Decompressor::~Decompressor() {}
DecompressedFile *Decompressor::UncompressFile(const u1 *buffer, size_t bytes_avail)
{
return NULL;
}
char *Decompressor::GetError()
{
return NULL;
}
int Decompressor::error(const char *fmt, ...)
{
return 0;
}
} // namespace devtools_ijar
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef INCLUDED_THIRD_PARTY_IJAR_MAPPED_FILE_H
#define INCLUDED_THIRD_PARTY_IJAR_MAPPED_FILE_H
#include "third_party/ijar/common.h"
namespace devtools_ijar
{
struct MappedInputFileImpl;
struct MappedOutputFileImpl;
// A memory mapped input file.
class MappedInputFile
{
private:
MappedInputFileImpl *impl_;
protected:
const char *errmsg_;
bool opened_;
u1 *buffer_;
size_t length_;
public:
MappedInputFile(const char *name);
virtual ~MappedInputFile();
// If opening the file succeeded or not.
bool Opened() const { return opened_; }
// Description of the last error that happened.
const char *Error() const { return errmsg_; }
// The mapped contents of the file.
u1 *Buffer() const { return buffer_; }
// The length of the file.
size_t Length() const { return length_; }
// Unmap a given number of bytes from the beginning of the file.
void Discard(size_t bytes);
int Close();
};
class MappedOutputFile
{
private:
MappedOutputFileImpl *impl_;
protected:
const char *errmsg_;
bool opened_;
u1 *buffer_;
size_t estimated_size_;
public:
MappedOutputFile(const char *name, size_t estimated_size);
virtual ~MappedOutputFile();
// If opening the file succeeded or not.
bool Opened() const { return opened_; }
// Description of the last error that happened.
const char *Error() const { return errmsg_; }
// The mapped contents of the file.
u1 *Buffer() const { return buffer_; }
int Close(size_t size);
};
} // namespace devtools_ijar
#endif
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <algorithm>
#include "third_party/ijar/mapped_file.h"
#define MAX_ERROR 2048
namespace devtools_ijar
{
static char errmsg[MAX_ERROR];
struct MappedInputFileImpl
{
size_t discarded_;
int fd_;
};
MappedInputFile::MappedInputFile(const char *name)
{
impl_ = NULL;
opened_ = false;
int fd = open(name, O_RDONLY);
if (fd < 0)
{
snprintf(errmsg, MAX_ERROR, "open(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
off_t length = lseek(fd, 0, SEEK_END);
if (length < 0)
{
snprintf(errmsg, MAX_ERROR, "lseek(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
void *buffer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
if (buffer == MAP_FAILED)
{
snprintf(errmsg, MAX_ERROR, "mmap(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
impl_ = new MappedInputFileImpl();
impl_->fd_ = fd;
impl_->discarded_ = 0;
buffer_ = reinterpret_cast<u1 *>(buffer);
length_ = length;
opened_ = true;
}
MappedInputFile::~MappedInputFile()
{
delete impl_;
}
void MappedInputFile::Discard(size_t bytes)
{
munmap(buffer_ + impl_->discarded_, bytes);
impl_->discarded_ += bytes;
}
int MappedInputFile::Close()
{
if (close(impl_->fd_) < 0)
{
snprintf(errmsg, MAX_ERROR, "close(): %s", strerror(errno));
errmsg_ = errmsg;
return -1;
}
return 0;
}
struct MappedOutputFileImpl
{
int fd_;
int mmap_length_;
};
MappedOutputFile::MappedOutputFile(const char *name, size_t estimated_size)
: estimated_size_(estimated_size)
{
impl_ = NULL;
opened_ = false;
int fd = open(name, O_CREAT | O_RDWR | O_TRUNC, 0644);
if (fd < 0)
{
snprintf(errmsg, MAX_ERROR, "open(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
// Create mmap-able sparse file
if (ftruncate(fd, estimated_size) < 0)
{
snprintf(errmsg, MAX_ERROR, "ftruncate(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
// Ensure that any buffer overflow in JarStripper will result in
// SIGSEGV or SIGBUS by over-allocating beyond the end of the file.
size_t mmap_length = std::min(static_cast<size_t>(estimated_size + sysconf(_SC_PAGESIZE)),
std::numeric_limits<size_t>::max());
void *mapped = mmap(NULL, mmap_length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED)
{
snprintf(errmsg, MAX_ERROR, "mmap(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
impl_ = new MappedOutputFileImpl();
impl_->fd_ = fd;
impl_->mmap_length_ = mmap_length;
buffer_ = reinterpret_cast<u1 *>(mapped);
opened_ = true;
}
MappedOutputFile::~MappedOutputFile()
{
delete impl_;
}
int MappedOutputFile::Close(size_t size)
{
if (size > estimated_size_)
{
snprintf(errmsg, MAX_ERROR, "size %zu > estimated size %zu", size, estimated_size_);
errmsg_ = errmsg;
return -1;
}
munmap(buffer_, impl_->mmap_length_);
if (ftruncate(impl_->fd_, size) < 0)
{
snprintf(errmsg, MAX_ERROR, "ftruncate(): %s", strerror(errno));
errmsg_ = errmsg;
return -1;
}
if (close(impl_->fd_) < 0)
{
snprintf(errmsg, MAX_ERROR, "close(): %s", strerror(errno));
errmsg_ = errmsg;
return -1;
}
return 0;
}
} // namespace devtools_ijar
// Copyright 2016 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "third_party/ijar/platform_utils.h"
#include <limits.h>
#include <stdio.h>
#if defined(_WIN32) || defined(__CYGWIN__)
# include <windows.h>
#else // !(defined(_WIN32) || defined(__CYGWIN__))
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
#endif // defined(_WIN32) || defined(__CYGWIN__)
#include <string>
namespace devtools_ijar
{
using std::string;
bool stat_file(const char *path, Stat *result)
{
#if defined(_WIN32) || defined(__CYGWIN__)
std::wstring wpath;
std::string error;
bool success = false;
BY_HANDLE_FILE_INFORMATION info;
HANDLE handle = ::CreateFileW(
/* lpFileName */ wpath.c_str(),
/* dwDesiredAccess */ GENERIC_READ,
/* dwShareMode */ FILE_SHARE_READ,
/* lpSecurityAttributes */ NULL,
/* dwCreationDisposition */ OPEN_EXISTING,
/* dwFlagsAndAttributes */ FILE_ATTRIBUTE_NORMAL,
/* hTemplateFile */ NULL);
if (handle == INVALID_HANDLE_VALUE)
{
// Opening it as a file failed, try opening it as a directory.
handle = ::CreateFileW(
/* lpFileName */ wpath.c_str(),
/* dwDesiredAccess */ GENERIC_READ,
/* dwShareMode */ FILE_SHARE_READ,
/* lpSecurityAttributes */ NULL,
/* dwCreationDisposition */ OPEN_EXISTING,
/* dwFlagsAndAttributes */ FILE_FLAG_BACKUP_SEMANTICS,
/* hTemplateFile */ NULL);
}
if (handle != INVALID_HANDLE_VALUE && ::GetFileInformationByHandle(handle, &info))
{
success = true;
bool is_dir = (info.dwFileAttributes != INVALID_FILE_ATTRIBUTES) &&
(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
// TODO(laszlocsomor): use info.nFileSizeHigh after we updated total_size to
// be u8 type.
result->total_size = is_dir ? 0 : info.nFileSizeLow;
// TODO(laszlocsomor): query the actual permissions and write in file_mode.
result->file_mode = 0777;
result->is_directory = is_dir;
}
::CloseHandle(handle);
return success;
#else // !(defined(_WIN32) || defined(__CYGWIN__))
struct stat statst;
if (stat(path, &statst) < 0)
{
return false;
}
result->total_size = statst.st_size;
result->file_mode = statst.st_mode;
result->is_directory = (statst.st_mode & S_IFDIR) != 0;
return true;
#endif
}
} // namespace devtools_ijar
// Copyright 2016 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_IJAR_PLATFORM_UTILS_H_
#define THIRD_PARTY_IJAR_PLATFORM_UTILS_H_
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include "third_party/ijar/common.h"
namespace devtools_ijar
{
// Platform-independent stat data.
struct Stat
{
// Total size of the file in bytes.
int total_size;
// The Unix file mode from the stat.st_mode field.
mode_t file_mode;
// True if this is a directory.
bool is_directory;
};
// Converts a Stat object to ZIP attributes.
inline u4 stat_to_zipattr(const Stat &file_stat)
{
return (((u4)file_stat.file_mode) << 16) | (file_stat.is_directory != 0 ? 0x10 : 0);
}
// Writes stat data into `result` about the file under `path`.
// Returns true if file is found and can be stat'ed.
// Returns false if the file is not found or cannot be stat'ed.
// Doesn't report any errors because it can also be used to simply check if a
// file exists.
bool stat_file(const char *path, Stat *result);
// Writes `size` bytes from `data` into file under `path`.
// The file is created or overwritten and is set to have `perm` permissions.
// Returns true upon success: file is created and all data is written.
// Returns false upon failure and reports the error to stderr.
bool write_file(const char *path, unsigned int perm, const void *data, size_t size);
// Reads at most `size` bytes into `buffer` from the file under `path`.
// Returns true upon success: file is opened and all data is read.
// Returns false upon failure and reports the error to stderr.
bool read_file(const char *path, void *buffer, size_t size);
// Returns the current working directory.
// Returns the empty string upon failure and reports the error to stderr.
std::string get_cwd();
// Do a recursive mkdir of all folders of path except the last path
// segment (if path ends with a / then the last path segment is empty).
// All folders are created using "perm" for creation mode, and are writable and
// openable by the current user.
// Returns true if all directories were created and permissions set.
// Returns false upon failure and reports the error to stderr.
bool make_dirs(const char *path, unsigned int perm);
} // namespace devtools_ijar
#endif // THIRD_PARTY_IJAR_PLATFORM_UTILS_H_
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// zip.h -- .zip (.jar) file reading/writing routines.
//
// This file specifies the interface to use the ZIP implementation of ijar.
//
#ifndef INCLUDED_THIRD_PARTY_IJAR_ZIP_H
#define INCLUDED_THIRD_PARTY_IJAR_ZIP_H
#include <sys/stat.h>
#include "third_party/ijar/common.h"
namespace devtools_ijar
{
// Tells if this is a directory entry from the mode. This method
// is safer than zipattr_to_mode(attr) & S_IFDIR because the unix
// mode might not be set in DOS zip files.
inline bool zipattr_is_dir(u4 attr)
{
return (attr & 0x10) != 0;
}
// Convert a ZIP file attribute to a Unix file permission mask.
inline mode_t zipattr_to_perm(u4 attr)
{
return ((mode_t)((attr >> 16) & 0777));
}
//
// Class interface for building ZIP files
//
class ZipBuilder
{
public:
virtual ~ZipBuilder() {}
// Returns the text for the last error, or null on no last error.
virtual const char *GetError() = 0;
// Add a new file to the ZIP, the file will have path "filename"
// and external attributes "attr". This function returns a pointer
// to a memory buffer to write the data of the file into. This buffer
// is owned by ZipBuilder and should not be free'd by the caller. The
// file length is then specified when the files is finished written
// using the FinishFile(size_t) function.
// On failure, returns NULL and GetError() will return an non-empty message.
virtual u1 *NewFile(const char *filename, const u4 attr) = 0;
// Finish writing a file and specify its length. After calling this method
// one should not reuse the pointer given by NewFile. The file can be
// compressed using the deflate algorithm by setting `compress` to true.
// By default, CRC32 are not computed as java tooling doesn't care, but
// computing it can be activated by setting `compute_crc` to true.
// On failure, returns -1 and GetError() will return an non-empty message.
virtual int FinishFile(size_t filelength, bool compress = false, bool compute_crc = false) = 0;
// Write an empty file, it is equivalent to:
// NewFile(filename, 0);
// FinishFile(0);
// On failure, returns -1 and GetError() will return an non-empty message.
virtual int WriteEmptyFile(const char *filename) = 0;
// Finish writing the ZIP file. This method can be called only once
// (subsequent calls will do nothing) and none of
// NewFile/FinishFile/WriteEmptyFile should be called after calling Finish. If
// this method was not called when the object is destroyed, it will be called.
// It is here as a convenience to get information on the final generated ZIP
// file.
// On failure, returns -1 and GetError() will return an non-empty message.
virtual int Finish() = 0;
// Get the current size of the ZIP file. This size will not be matching the
// final ZIP file until Finish() has been called because Finish() is actually
// writing the central directory of the ZIP File.
virtual size_t GetSize() = 0;
// Returns the current number of files stored in the ZIP.
virtual int GetNumberFiles() = 0;
// Create a new ZipBuilder writing the file zip_file and the size of the
// output will be at most estimated_size. Use ZipBuilder::EstimateSize() or
// ZipExtractor::CalculateOuputLength() to have an estimated_size depending on
// a list of file to store.
// On failure, returns NULL. Refer to errno for error code.
static ZipBuilder *Create(const char *zip_file, size_t estimated_size);
// Estimate the maximum size of the ZIP files containing files in the "files"
// null-terminated array.
// Returns 0 on error.
static u8 EstimateSize(char const *const *files, char const *const *zip_paths, int nb_entries);
};
//
// An abstract class to process data from a ZipExtractor.
// Derive from this class if you wish to process data from a ZipExtractor.
//
class ZipExtractorProcessor
{
public:
virtual ~ZipExtractorProcessor() {}
// Tells whether to skip or process the file "filename". "attr" is the
// external file attributes and can be converted to unix mode using the
// zipattr_to_mode() function. This method is suppoed to returns true
// if the file should be processed and false if it should be skipped.
virtual bool Accept(const char *filename, const u4 attr) = 0;
// Process a file accepted by Accept. The file "filename" has external
// attributes "attr" and length "size". The file content is accessible
// in the buffer pointed by "data".
virtual void Process(const char *filename,
const u4 attr,
const u1 *data,
const size_t size) = 0;
};
//
// Class interface for reading ZIP files
//
class ZipExtractor
{
public:
virtual ~ZipExtractor() {}
// Returns the text for the last error, or null on no last error.
virtual const char *GetError() = 0;
// Process the next files, returns false if the end of ZIP file has been
// reached. The processor provided by the Create method will be called
// if a file is encountered. If false is returned, check the return value
// of GetError() for potential errors.
virtual bool ProcessNext() = 0;
// Process the all files, returns -1 on error (GetError() will be populated
// on error).
virtual int ProcessAll();
// Reset the file pointer to the beginning.
virtual void Reset() = 0;
// Return the size of the ZIP file.
virtual size_t GetSize() = 0;
// Return the size of the resulting zip file by keeping only file
// accepted by the processor and storing them uncompressed. This
// method can be used to create a ZipBuilder for storing a subset
// of the input files.
// On error, 0 is returned and GetError() returns a non-empty message.
virtual u8 CalculateOutputLength() = 0;
// Create a ZipExtractor that extract the zip file "filename" and process
// it with "processor".
// On error, a null pointer is returned and the value of errno should be
// checked.
static ZipExtractor *Create(const char *filename, ZipExtractorProcessor *processor);
};
} // namespace devtools_ijar
#endif // INCLUDED_THIRD_PARTY_IJAR_ZIP_H
// Copyright 2016 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <algorithm>
#include <cstdio>
#include <zlib.h>
#include "third_party/ijar/common.h"
#include "third_party/ijar/zlib_client.h"
namespace devtools_ijar
{
u4 ComputeCrcChecksum(u1 *buf, size_t length)
{
return crc32(0, buf, length);
}
size_t TryDeflate(u1 *buf, size_t length)
{
u1 *outbuf = reinterpret_cast<u1 *>(malloc(length));
z_stream stream;
// Initialize the z_stream strcut for reading from buf and wrinting in outbuf.
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.total_in = length;
stream.avail_in = length;
stream.total_out = length;
stream.avail_out = length;
stream.next_in = buf;
stream.next_out = outbuf;
// deflateInit2 negative windows size prevent the zlib wrapper to be used.
if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8,
Z_DEFAULT_STRATEGY) != Z_OK)
{
// Failure to compress => return the buffer uncompressed
free(outbuf);
return length;
}
if (deflate(&stream, Z_FINISH) == Z_STREAM_END)
{
// Compression successful and fits in outbuf, let's copy the result in buf.
length = stream.total_out;
memcpy(buf, outbuf, length);
}
deflateEnd(&stream);
free(outbuf);
// Return the length of the resulting buffer
return length;
}
Decompressor::Decompressor()
{
uncompressed_data_allocated_ = INITIAL_BUFFER_SIZE;
uncompressed_data_ = reinterpret_cast<u1 *>(malloc(uncompressed_data_allocated_));
}
Decompressor::~Decompressor()
{
free(uncompressed_data_);
}
DecompressedFile *Decompressor::UncompressFile(const u1 *buffer, size_t bytes_avail)
{
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = bytes_avail;
stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(buffer));
int ret = inflateInit2(&stream, -MAX_WBITS);
if (ret != Z_OK)
{
error("inflateInit: %d\n", ret);
return NULL;
}
int uncompressed_until_now = 0;
while (true)
{
stream.avail_out = uncompressed_data_allocated_ - uncompressed_until_now;
stream.next_out = uncompressed_data_ + uncompressed_until_now;
int old_avail_out = stream.avail_out;
ret = inflate(&stream, Z_SYNC_FLUSH);
int uncompressed_now = old_avail_out - stream.avail_out;
uncompressed_until_now += uncompressed_now;
switch (ret)
{
case Z_STREAM_END:
{
struct DecompressedFile *decompressedFile =
reinterpret_cast<DecompressedFile *>(malloc(sizeof(DecompressedFile)));
// zlib said that there is no more data to decompress.
u1 *new_p = reinterpret_cast<u1 *>(stream.next_in);
decompressedFile->compressed_size = new_p - buffer;
decompressedFile->uncompressed_size = uncompressed_until_now;
decompressedFile->uncompressed_data = uncompressed_data_;
inflateEnd(&stream);
return decompressedFile;
}
case Z_OK:
{
// zlib said that there is no more room in the buffer allocated for
// the decompressed data. Enlarge that buffer and try again.
if (uncompressed_data_allocated_ == MAX_BUFFER_SIZE)
{
error(
"ijar does not support decompressing files "
"larger than %dMB.\n",
static_cast<int>((MAX_BUFFER_SIZE / (1024 * 1024))));
return NULL;
}
uncompressed_data_allocated_ *= 2;
if (uncompressed_data_allocated_ > MAX_BUFFER_SIZE)
{
uncompressed_data_allocated_ = MAX_BUFFER_SIZE;
}
uncompressed_data_ = reinterpret_cast<u1 *>(
realloc(uncompressed_data_, uncompressed_data_allocated_));
break;
}
case Z_DATA_ERROR:
case Z_BUF_ERROR:
case Z_STREAM_ERROR:
case Z_NEED_DICT:
default:
{
error("zlib returned error code %d during inflate.\n", ret);
return NULL;
}
}
}
}
char *Decompressor::GetError()
{
if (errmsg[0] == 0)
{
return NULL;
}
return errmsg;
}
int Decompressor::error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(errmsg, 4 * PATH_MAX, fmt, ap);
va_end(ap);
return -1;
}
} // namespace devtools_ijar
// Copyright 2016 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_IJAR_ZLIB_CLIENT_H_
#define THIRD_PARTY_IJAR_ZLIB_CLIENT_H_
#include <limits.h>
#include "third_party/ijar/common.h"
namespace devtools_ijar
{
// Try to compress a file entry in memory using the deflate algorithm.
// It will compress buf (of size length) unless the compressed size is bigger
// than the input size. The result will overwrite the content of buf and the
// final size is returned.
size_t TryDeflate(u1 *buf, size_t length);
u4 ComputeCrcChecksum(u1 *buf, size_t length);
struct DecompressedFile
{
u1 *uncompressed_data;
u4 uncompressed_size;
u4 compressed_size;
};
class Decompressor
{
public:
Decompressor();
~Decompressor();
DecompressedFile *UncompressFile(const u1 *buffer, size_t bytes_avail);
char *GetError();
private:
// Administration of memory reserved for decompressed data. We use the same
// buffer for each file to avoid some malloc()/free() calls and free the
// memory only in the dtor. C-style memory management is used so that we
// can call realloc.
u1 *uncompressed_data_;
size_t uncompressed_data_allocated_;
// last error
char errmsg[4 * PATH_MAX];
int error(const char *fmt, ...);
// Buffer size is initially INITIAL_BUFFER_SIZE. It doubles in size every
// time it is found too small, until it reaches MAX_BUFFER_SIZE. If that is
// not enough, we bail out. We only decompress class files, so they should
// be smaller than 64K anyway, but we give a little leeway.
// MAX_BUFFER_SIZE must be bigger than the size of the biggest file in the
// ZIP. It is set to 2GB here because no one has audited the code for 64-bit
// cleanliness.
static const size_t INITIAL_BUFFER_SIZE = 256 * 1024; // 256K
static const size_t MAX_BUFFER_SIZE = std::numeric_limits<int32_t>::max();
};
} // namespace devtools_ijar
#endif // THIRD_PARTY_IJAR_ZLIB_CLIENT_H_
monorail: {
component: "Infra"
}
team_email: "chromium-reviews@chromium.org"
agrieve@chromium.org
smaier@chromium.org
torne@chromium.org
yfriedman@chromium.org
Name: Proguard
URL: http://proguard.sourceforge.net/
Version: 6.0.3
Date: June 22, 2016
License: GPL v2
License File: NOT_SHIPPED
Security Critical: no
Description:
This directory includes proguard.jar to allow Chromium to shrink, optimize java
classes for Android, as well as retrace.jar, to allow for deobfuscation of stack
traces.
# Copyright 2018 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.
# To create CIPD package run the following command.
# cipd create --pkg-def cipd.yaml
package: chromium/third_party/proguard
data:
- file: lib/proguard603.jar
- file: lib/retrace603.jar
# Run on swarming, so symlinks don't work.
install_mode: copy
# Copyright 2021 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")
# The output jar for this target is copied and checked-in to minimize the number
# of edges in the build graph. This is a class that rarely changes.
java_library("custom_d8_java") {
sources = [ "java/src/org/chromium/build/CustomD8.java" ]
# Avoid using java_prebuilt() to ensure all uses go through the checked-in
# version.
input_jars_paths = [ "//third_party/r8/lib/r8.jar" ]
}
monorail: {
component: "Build"
}
team_email: "build@chromium.org"
Copyright (c) 2016, the R8 project authors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
smaier@chromium.org
agrieve@chromium.org
wnwen@chromium.org
Name: R8
URL: https://r8.googlesource.com/r8
Version: 3.0.33
License: BSD 3-Clause
License File: NOT_SHIPPED
Security Critical: no
Description:
lib/r8.jar
- Contains R8 (proguard replacement) as well as D8 (dexer).
3pp
- Contains code for 3pp-linux-amd64-packager to package and upload new versions of r8.
desugar_jdk_libs.json
- Configuration for Java library desugaring.
- Taken from
//third_party/android_deps/libs/com_android_tools_desugar_jdk_libs_configuration/
inside the jar file, in META-INF/desugar/d8/desugar.json. License is also BSD
3-Clause (the r8 version).
R8 is a proguard-like optimizer that also has the ability to dex.
Local Modifications:
* Refer to commit descriptions within "3pp/patches" directory.
* Added "playground" directory for quick "how does this optimize" tests.
* Removed references to ConcurrentHashMap and TimeZone#getTimeZone in desugar_jdk_libs.json.
* Added "java/src/org/chromium/build/CustomD8.java", custom_d8.jar, and BUILD.gn.
* Used in "build/android/gyp/dex.py" to enable desugar dependencies.
Update Instructions:
* For the custom d8 jar:
* This is required only when CustomD8.java changes.
* Uncomment the flag --skip-custom-d8 in internal_rules.gni to avoid depending
on the previously built custom_d8.jar.
autoninja -C out/Debug third_party/r8:custom_d8_java
cp out/Debug/obj/third_party/r8/custom_d8_java.javac.jar third_party/r8/custom_d8.jar
* For a new version of r8:
* Find the latest successful run of the build bot:
* https://ci.chromium.org/p/chromium/builders/ci/3pp-linux-amd64-packager
* The bot autoruns every 6 hours. Ping a trooper to trigger it if you need it sooner.
* https://luci-scheduler.appspot.com/jobs/chromium/3pp-linux-amd64-packager
* Go to its log page and look under:
* Expand "building chromium/third_party/r8" to see the CIPD instance ID.
* Copy this instance ID to the r8 entry in //DEPS.
* Update this file with the new version string and submit.
* If patching fails:
git clone https://r8.googlesource.com/r8
cd r8
* Find latest tag:
git fetch origin --tags
git tag -l # Often unnecessary as output from the fetch includes recent tags.
git checkout $TAG # e.g. 3.0.25-dev, whichever tag failed patching.
* Apply patches:
git checkout -b my_branch
git am $CHROMIUM_SRC/third_party/r8/patches/*.patch
* Fix patch conflicts.
* Save new patches. Change number if expecting more or fewer patches:
git format-patch -3 -o $CHROMIUM_SRC/third_party/r8/3pp/patches
* Submit these fixed patches and the bot will automatically retry.
* Local development (see above for setting up the r8 repo):
* Build
tools/gradle.py r8
* Shrink (improves r8/d8 launch time):
java -jar build/libs/r8.jar --debug --classfile --output r8.jar \
--lib $CHROMIUM_SRC/third_party/jdk/current --pg-conf src/main/keep.txt \
--no-minification --no-desugaring build/libs/r8.jar
mv $CHROMIUM_SRC/third_party/r8/lib/r8.jar{,.bak}
cp r8.jar $CHROMIUM_SRC/third_party/r8/lib/r8.jar
* Update backported methods list:
cd $CHROMIUM_SRC
java -cp third_party/r8/lib/r8.jar com.android.tools.r8.BackportedMethodList --min-api 16 > third_party/r8/backported_methods.txt
* Update instructions for desugar_jdk_libs.json:
unzip -p third_party/android_deps/libs/com_android_tools_desugar_jdk_libs_configuration/*.jar META-INF/desugar/d8/desugar.json > third_party/r8/desugar_jdk_libs.json
* Remove all mentions of ConcurrentHashMap and TimeZone#getTimeZone and remove trailing commas.
* Update //build/config/android/internal_rules.gni to point at the new .jar files:
* desugar_jdk_libs-1.1.1.jar
* desugar_jdk_libs_configuration-1.1.1.jar
How to file bugs against R8:
* Copy & paste the failing ninja command (starts with proguard.py), and add --dump-inputs.
* This also works for dex.py, it produces d8inputs.zip
* File bug at go/r8bug
* Things to include (use discretion if some are not relevant):
* src revision bug reproduces at
* Repro steps (gn gen & autoninja commands)
* Prefer enable_chrome_android_internal=false
* The r8inputs.zip from --dump-inputs
* Any relevant dexdump analysis
How to submit CLs to R8:
* Request to be added to their allowlist in order to upload CLs.
* After CLs are submitted, check the bots for build breakage.
* https://ci.chromium.org/p/r8/g/main_all/console
# Copyright 2018 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.
# To create CIPD package run the following command.
# cipd create --pkg-def cipd.yaml
package: chromium/third_party/r8
description: >
R8 is a proguard-like optimizer that also has the option to dex.
data:
- file: lib/r8.jar
// Copyright 2020 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.
/**
* Add a snippet of code here that you want to test the optimization of.
*/
public class Playground
{
public static void main(String[] args)
{
System.out.println("Your test here");
}
private static native boolean ensureSideEffects();
}
#!/bin/bash
# Copyright 2020 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.
# Usage:
# Make edits to *.java and *.pgcfg
# Then run: ./build.sh | less
# Finds any version of dexdump available
dexdumps=( ../../android_sdk/public/build-tools/*/dexdump )
DEXDUMP=${dexdumps[0]}
rm -f *.class
javac *.java && \
java -cp ../lib/r8.jar com.android.tools.r8.R8 *.class --output . --lib ../../jdk/current --no-minification --pg-conf playground.pgcfg && \
$DEXDUMP -d classes.dex > dexdump.txt
echo 'dexdump.txt updated.'
# Copyright 2020 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.
-allowaccessmodification
-keep,allowoptimization class Playground {
void main(...);
}
agrieve@chromium.org
wnwen@chromium.org
Name: Turbine
Short Name: turbine
URL: https://github.com/google/turbine
Version: 0
Revision: 3c31e67ae25b5e43713fd868e3a9b535ff6298af
License: Apache 2.0
License File: NOT_SHIPPED
Security Critical: no
Description:
Turbine is a header compiler for Java. This library does not provide versioning
or maven releases, so we need to pull it in and build it ourselves.
Since we have no working non-android java proto processor, we cannot yet build
it in GN. Thus build it manually and upload to cipd for now.
Commands to update:
- Log into cipd if necessary: `cipd auth-login`
- Update //DEPS 'src/third_party/turbine/src' commit to latest on github, see
https://github.com/google/turbine/commits/master, and `gclient sync`.
- Also update the revision in this file.
- Ensure current working directory is `src/third_party/turbine`
```
mvn package -DskipTests=true -q -f src/pom.xml
cp -f src/target/turbine-0.1-SNAPSHOT-all-deps.jar turbine.jar
cipd create --pkg-def cipd.yaml
```
- Update //DEPS with the hash id from the cipd create command output.
Local Modifications:
No modifications.
# Copyright 2020 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.
# To create CIPD package run the following command.
# cipd create --pkg-def cipd.yaml
package: chromium/third_party/turbine
description: "Turbine"
data:
- file: turbine.jar
# Copyright 2017 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("errorprone_plugin") {
# main_class and wrapper script are not actually used.
# This target is referenced directly from java_library_impl().
main_class = "<ignore>"
wrapper_script_name = "bin/helper/errorprone_plugin"
sources = [
# Turned off because of existing code which fails the check
# "src/org/chromium/tools/errorprone/plugin/NoContextGetApplicationContext.java",
"src/org/chromium/tools/errorprone/plugin/AndroidNullableCheck.java",
"src/org/chromium/tools/errorprone/plugin/NoAndroidAsyncTaskCheck.java",
"src/org/chromium/tools/errorprone/plugin/NoDynamicStringsInTraceEventCheck.java",
"src/org/chromium/tools/errorprone/plugin/NoRedundantFieldInitCheck.java",
"src/org/chromium/tools/errorprone/plugin/NoSynchronizedMethodCheck.java",
"src/org/chromium/tools/errorprone/plugin/NoSynchronizedThisCheck.java",
"src/org/chromium/tools/errorprone/plugin/TestClassNameCheck.java",
]
# Necessary to avoid dependency cycle
enable_errorprone = false
enable_bytecode_checks = false
annotation_processor_deps =
[ "//third_party/android_deps:auto_service_processor" ]
deps = [
"//third_party/android_deps:com_google_auto_service_auto_service_annotations_java",
"//third_party/android_deps:com_google_errorprone_error_prone_annotation_java",
"//third_party/android_deps:com_google_errorprone_error_prone_check_api_java",
"//third_party/android_deps:com_google_errorprone_error_prone_core_java",
"//third_party/android_deps:com_google_errorprone_javac_java",
]
}
agrieve@chromium.org
nyquist@chromium.org
wnwen@chromium.org
// Copyright 2018 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.
package org.chromium.tools.errorprone.plugin;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.sun.source.tree.AnnotationTree;
/**
* Assert androidx.annotation.Nullable is used instead of javax.annotation.Nullable.
*/
@AutoService(BugChecker.class)
@BugPattern(name = "AndroidNullableCheck",
summary = "Use androidx.annotation.Nullable instead of javax.annotation.Nullable.",
severity = BugPattern.SeverityLevel.ERROR, linkType = BugPattern.LinkType.CUSTOM,
link = "http://crbug.com/771683")
public class AndroidNullableCheck extends BugChecker implements BugChecker.AnnotationTreeMatcher
{
static final Matcher<AnnotationTree> IS_JAVAX_NULLABLE =
Matchers.anyOf(Matchers.isType("javax.annotation.Nullable"));
/**
* Match if nullable annotation is of type javax.annotation.Nullable.
*/
@Override
public Description matchAnnotation(AnnotationTree annotationTree, VisitorState visitorState)
{
if (IS_JAVAX_NULLABLE.matches(annotationTree, visitorState))
{
return describeMatch(annotationTree);
}
return Description.NO_MATCH;
}
}
// Copyright 2018 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.
package org.chromium.tools.errorprone.plugin;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MemberSelectTree;
import com.sun.tools.javac.code.Symbol;
/**
* Triggers an error for any occurrence of android.os.AsyncTask.
*/
@AutoService(BugChecker.class)
@BugPattern(name = "NoAndroidAsyncTaskCheck",
summary = "Do not use android.os.AsyncTask - use org.chromium.base.task.AsyncTask instead",
severity = BugPattern.SeverityLevel.ERROR, linkType = BugPattern.LinkType.CUSTOM,
link = "https://bugs.chromium.org/p/chromium/issues/detail?id=843745")
public class NoAndroidAsyncTaskCheck
extends BugChecker implements BugChecker.MemberSelectTreeMatcher
{
@Override
public Description matchMemberSelect(MemberSelectTree tree, VisitorState state)
{
if (tree.getIdentifier().contentEquals("AsyncTask"))
{
Symbol symbol = ASTHelpers.getSymbol(tree.getExpression());
if (symbol.getQualifiedName().contentEquals("android.os"))
{
return buildDescription(tree)
.setMessage("Do not use android.os.AsyncTask - "
+ "use org.chromium.base.task.AsyncTask instead")
.build();
}
}
return Description.NO_MATCH;
}
}
// Copyright 2018 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.
package org.chromium.tools.errorprone.plugin;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.suppliers.Supplier;
import com.google.errorprone.suppliers.Suppliers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.tools.javac.code.Type;
/**
* Checks for calls to getApplicationContext from {@link android.content.Context}.
* These calls should be replaced with the static getApplicationContext method in
* {@link org.chromium.base.ContextUtils}.
*/
@AutoService(BugChecker.class)
@BugPattern(name = "NoContextGetApplicationContext",
summary = "Do not use Context#getApplicationContext",
severity = BugPattern.SeverityLevel.ERROR, linkType = BugPattern.LinkType.CUSTOM,
link = "https://bugs.chromium.org/p/chromium/issues/detail?id=560466")
public class NoContextGetApplicationContext
extends BugChecker implements BugChecker.MethodInvocationTreeMatcher
{
private static final String CONTEXT_CLASS_NAME = "android.content.Context";
private static final String CONTEXT_UTILS_CLASS_NAME = "org.chromium.base.ContextUtils";
private static final String METHOD_NAME = "getApplicationContext";
private static final Supplier<Type> CONTEXT_UTILS_SUPPLIER =
Suppliers.typeFromString(CONTEXT_UTILS_CLASS_NAME);
private static final Matcher<ExpressionTree> CONTEXT_MATCHER =
instanceMethod()
.onDescendantOf(Suppliers.typeFromString(CONTEXT_CLASS_NAME))
.named(METHOD_NAME);
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState visitorState)
{
if (!CONTEXT_MATCHER.matches(tree, visitorState))
{
return Description.NO_MATCH;
}
// If ContextUtils can't be loaded, we are probably inside a third_party lib and shouldn't
// check for errors.
boolean canLoadContextUtils = CONTEXT_UTILS_SUPPLIER.get(visitorState) != null;
if (!canLoadContextUtils)
{
return Description.NO_MATCH;
}
return buildDescription(tree)
.setMessage("Don't use Context#getApplicationContext - "
+ "call ContextUtils.getApplicationContext instead")
.build();
}
}
// Copyright 2019 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.
package org.chromium.tools.errorprone.plugin;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.BinaryTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.SimpleTreeVisitor;
import com.sun.tools.javac.code.Symbol;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
/**
* Triggers an error for {@link org.chromium.base.TraceEvent} usages with non string literals.
*/
@AutoService(BugChecker.class)
@BugPattern(name = "NoDynamicStringsInTraceEventCheck",
summary = "Only use of string literals are allowed in trace events.",
severity = BugPattern.SeverityLevel.ERROR, linkType = BugPattern.LinkType.CUSTOM,
link = "https://crbug.com/984827")
public class NoDynamicStringsInTraceEventCheck
extends BugChecker implements BugChecker.MethodInvocationTreeMatcher
{
private static final Set<String> sTracingFunctions = new HashSet<>(Arrays.asList(
"begin", "end", "scoped", "startAsync", "finishAsync", "instant", "TraceEvent"));
private static final ParameterVisitor sVisitor = new ParameterVisitor();
@Override
public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState visitorState)
{
Symbol.MethodSymbol method = ASTHelpers.getSymbol(tree);
if (!sTracingFunctions.contains(method.name.toString())) return Description.NO_MATCH;
String className = method.enclClass().fullname.toString();
if (!"org.chromium.base.EarlyTraceEvent".equals(className)
&& !"org.chromium.base.TraceEvent".equals(className))
{
return Description.NO_MATCH;
}
// Allow the events added by tracing. Adding SuppressWarning in these files causes all
// caller warnings to be ignored.
String filename = visitorState.getPath().getCompilationUnit().getSourceFile().getName();
if (filename.endsWith("TraceEvent.java"))
{
return Description.NO_MATCH;
}
List<? extends Tree> args = tree.getArguments();
Tree eventName_expr = args.get(0);
ParameterVisitor.Result r = eventName_expr.accept(sVisitor, null);
if (r.success) return Description.NO_MATCH;
return buildDescription(tree)
.setMessage("Calling TraceEvent.begin() without a constant String object. "
+ r.errorMessage)
.build();
}
static class ParameterVisitor extends SimpleTreeVisitor<ParameterVisitor.Result, Void>
{
static class Result
{
public boolean success;
public String errorMessage;
private Result(boolean successVal, String error)
{
success = successVal;
errorMessage = error;
}
public static Result createError(String error)
{
return new Result(false, error);
}
public static Result createOk()
{
return new Result(true, null);
}
public Result append(Result other)
{
success &= other.success;
if (errorMessage == null)
{
errorMessage = other.errorMessage;
}
else if (other.errorMessage != null)
{
errorMessage += " " + other.errorMessage;
}
return this;
}
};
@Override
protected Result defaultAction(Tree tree, Void p)
{
throw new RuntimeException("Unhandled expression tree type: " + tree.getKind());
}
@Override
public Result visitBinary(BinaryTree tree, Void p)
{
return tree.getLeftOperand()
.accept(this, null)
.append(tree.getRightOperand().accept(this, null));
}
@Override
public Result visitLiteral(LiteralTree tree, Void p)
{
return Result.createOk();
}
@Override
public Result visitIdentifier(IdentifierTree node, Void p)
{
Symbol eventName = ASTHelpers.getSymbol(node);
if (eventName == null)
{
return Result.createError("Identifier was not found: " + node + '.');
}
if (eventName.getKind() == ElementKind.FIELD)
{
if (!"java.lang.String".equals(eventName.type.toString()))
{
return Result.createError("Field: " + eventName + " should be of type string.");
}
Set<Modifier> modifiers = eventName.getModifiers();
if (!modifiers.contains(Modifier.FINAL) || !modifiers.contains(Modifier.STATIC))
{
return Result.createError(
"String literal: " + eventName + " is not static final.");
}
return Result.createOk();
}
else if (eventName.getKind() == ElementKind.PARAMETER)
{
return Result.createError(
"Passing in event name as parameter: " + eventName + " is not supported.");
}
return Result.createError("Unhandled identifier kind: " + node.getKind() + '.');
}
};
}
// Copyright 2018 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.
package org.chromium.tools.errorprone.plugin;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.tools.javac.code.Symbol;
import javax.lang.model.element.Modifier;
/**
* Detects when non-final fields are explicitly initialized to default values
*/
@AutoService(BugChecker.class)
@BugPattern(name = "NoRedundantFieldInit",
summary = "Do not explicitly initialize a non-final field with a default value",
severity = BugPattern.SeverityLevel.ERROR, linkType = BugPattern.LinkType.CUSTOM,
link = "https://issuetracker.google.com/issues/37124982")
public class NoRedundantFieldInitCheck extends BugChecker implements BugChecker.VariableTreeMatcher
{
private static final Matcher<ClassTree> SUBTYPE_OF_IINTERFACE =
Matchers.isSubtypeOf("android.os.IInterface");
@Override
public Description matchVariable(VariableTree variableTree, VisitorState visitorState)
{
// Only match on fields.
if (!Matchers.isField().matches(variableTree, visitorState))
{
return Description.NO_MATCH;
}
Symbol.VarSymbol variableSymbol = ASTHelpers.getSymbol(variableTree);
Symbol.ClassSymbol enclosingClass = ASTHelpers.enclosingClass(variableSymbol);
// Temporarily turn off checks if the enclosing class is a subclass of IInterface.
if (SUBTYPE_OF_IINTERFACE.matches(
ASTHelpers.findClass(enclosingClass, visitorState), visitorState))
{
return Description.NO_MATCH;
}
// Skip fields that are final.
ModifiersTree modifiers = variableTree.getModifiers();
if (modifiers.getFlags().contains(Modifier.FINAL))
{
return Description.NO_MATCH;
}
// Skip fields in an @interface / any annotation type since these
// are implicitly final.
if (enclosingClass.isAnnotationType())
{
return Description.NO_MATCH;
}
// Fields in interfaces are also final implicitly so skip those.
if (enclosingClass.isInterface())
{
return Description.NO_MATCH;
}
// Check if field declaration is initialized to a literal default value.
if (isInitializedWithDefaultValue(variableTree))
{
// Generate fix string from original source e.g.
// public static int x = 0; --> public static int x;.
String source = visitorState.getSourceForNode(variableTree);
String suggestedSource = source.substring(0, source.indexOf('=')).trim() + ";";
return describeMatch(variableTree, SuggestedFix.replace(variableTree, suggestedSource));
}
return Description.NO_MATCH;
}
private boolean isInitializedWithDefaultValue(VariableTree variableTree)
{
ExpressionTree initializer = variableTree.getInitializer();
// Only match on literals.
if (!(initializer instanceof LiteralTree))
{
return false;
}
// Match on declarations with literal initializers that have default values.
if (variableTree.getType().getKind() == Tree.Kind.PRIMITIVE_TYPE)
{
LiteralTree literal = (LiteralTree) initializer;
if (literal.getKind() == Tree.Kind.BOOLEAN_LITERAL)
{
if (!(boolean) literal.getValue())
{
return true;
}
}
else if (literal.getKind() == Tree.Kind.LONG_LITERAL)
{
if ((long) literal.getValue() == 0L)
{
return true;
}
}
else if (literal.getKind() == Tree.Kind.CHAR_LITERAL)
{
if ((char) literal.getValue() == '\u0000')
{
return true;
}
}
else if (literal.getKind() == Tree.Kind.DOUBLE_LITERAL)
{
if ((double) literal.getValue() == 0.0d)
{
return true;
}
}
else if (literal.getKind() == Tree.Kind.FLOAT_LITERAL)
{
if ((float) literal.getValue() == 0.0f)
{
return true;
}
}
else if ((int) literal.getValue() == 0)
{
// Int/short/byte.
return true;
}
}
else if (initializer.getKind() == Tree.Kind.NULL_LITERAL)
{
// Non-primitive type default value is null.
return true;
}
return false;
}
}
// Copyright 2017 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.
package org.chromium.tools.errorprone.plugin;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.fixes.SuggestedFixes;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.MethodTree;
import com.sun.tools.javac.code.Symbol;
import javax.lang.model.element.Modifier;
/**
* Triggers an error for public methods that use "synchronized" in their signature.
*/
@AutoService(BugChecker.class)
@BugPattern(name = "NoSynchronizedMethodCheck",
summary = "Use of synchronized in public method signature disallowed.",
severity = BugPattern.SeverityLevel.ERROR, linkType = BugPattern.LinkType.CUSTOM,
link = "https://stackoverflow.com/questions/20906548/why-is-synchronized-block-better-than-synchronized-method")
public class NoSynchronizedMethodCheck extends BugChecker implements BugChecker.MethodTreeMatcher
{
@Override
public Description matchMethod(MethodTree methodTree, VisitorState visitorState)
{
Symbol.MethodSymbol method = ASTHelpers.getSymbol(methodTree);
// Skip methods that aren't synchronized and non-public methods
if (!method.getModifiers().contains(Modifier.SYNCHRONIZED)
|| !method.getModifiers().contains(Modifier.PUBLIC))
{
return Description.NO_MATCH;
}
// Skip methods that are only public due to VisibleForTesting
if (ASTHelpers.hasDirectAnnotationWithSimpleName(method, "VisibleForTesting"))
{
return Description.NO_MATCH;
}
// A Synchronized @Override methods is unavoidable if the method being overridden is
// an Android API method (Example: Exception#fillInStackTrace()).
if (ASTHelpers.hasDirectAnnotationWithSimpleName(method, "Override"))
{
return Description.NO_MATCH;
}
// Skip non-public classes
Symbol.ClassSymbol enclosingClass = ASTHelpers.enclosingClass(method);
if (!enclosingClass.getModifiers().contains(Modifier.PUBLIC))
{
return Description.NO_MATCH;
}
return buildDescription(methodTree)
.addFix(SuggestedFixes.removeModifiers(
methodTree, visitorState, Modifier.SYNCHRONIZED))
.setMessage(String.format(
"Used synchronized modifier in public method %s", method.getSimpleName()))
.build();
}
}
// Copyright 2017 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.
package org.chromium.tools.errorprone.plugin;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.SynchronizedTree;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeInfo;
import javax.lang.model.element.Modifier;
/**
* This class detects the synchronized method.
*/
@AutoService(BugChecker.class)
@BugPattern(name = "NoSynchronizedThisCheck",
summary = "Do not synchronized on 'this' in public classes",
severity = BugPattern.SeverityLevel.ERROR, linkType = BugPattern.LinkType.CUSTOM,
link = "https://stackoverflow.com/questions/442564/avoid-synchronizedthis-in-java")
public class NoSynchronizedThisCheck
extends BugChecker implements BugChecker.SynchronizedTreeMatcher
{
@Override
public Description matchSynchronized(SynchronizedTree tree, VisitorState visitorState)
{
Symbol lock = ASTHelpers.getSymbol(TreeInfo.skipParens((JCTree) tree.getExpression()));
// Skip locks that are not 'this'
if (!lock.getSimpleName().contentEquals("this"))
{
return Description.NO_MATCH;
}
// Skip non-public classes
Symbol.ClassSymbol enclosingClass = ASTHelpers.enclosingClass(lock);
if (!enclosingClass.getModifiers().contains(Modifier.PUBLIC))
{
return Description.NO_MATCH;
}
return buildDescription(tree)
.setMessage("Used instance variable as synchronization lock")
.build();
}
}
// Copyright 2019 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.
package org.chromium.tools.errorprone.plugin;
import com.google.auto.service.AutoService;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.sun.source.tree.AnnotationTree;
/**
* Assert class names of classes with Java tests in them end with "Test".
*
* Test classes are identified as those that are annotated with {@link org.junit.runner.RunWith}.
* This works for both JUnit and Instrumentation tests.
*/
@AutoService(BugChecker.class)
@BugPattern(name = "TestClassNameCheck",
summary = "Class names of classes with tests in them should end with \"Test\".",
severity = BugPattern.SeverityLevel.ERROR, linkType = BugPattern.LinkType.CUSTOM,
link = "http://crbug.com/1029370")
public class TestClassNameCheck extends BugChecker implements BugChecker.AnnotationTreeMatcher
{
static final Matcher<AnnotationTree> TEST_ANNOTATION =
Matchers.anyOf(Matchers.isType("org.junit.runner.RunWith"));
/**
* Match if nullable annotation is of type org.junit.runner.RunWith.
*/
@Override
public Description matchAnnotation(AnnotationTree annotationTree, VisitorState visitorState)
{
String filePath = visitorState.getPath().getCompilationUnit().getSourceFile().getName();
if (TEST_ANNOTATION.matches(annotationTree, visitorState)
&& !filePath.endsWith("Test.java"))
{
return describeMatch(annotationTree);
}
return Description.NO_MATCH;
}
}
# Copyright 2020 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/android_nocompile.gni")
import("nocompile_gn/nocompile_sources.gni")
android_nocompile_test_suite("errorprone_plugin_tests") {
# Depend on errorprone_plugin so that the action is re-run whenever any custom
# errorprone check is modified.
deps = [ "//tools/android/errorprone_plugin" ]
tests = [
{
target = "nocompile_gn:no_redundant_field_init_check_int_test_java"
nocompile_sources =
rebase_path(no_redundant_field_init_check_int_test_nocompile_sources,
"",
"nocompile_gn")
expected_compile_output_regex = "warning: .NoRedundantFieldInit"
},
{
target = "nocompile_gn:test_class_name_check_test_java"
nocompile_sources =
rebase_path(test_class_name_check_test_nocompile_sources,
"",
"nocompile_gn")
expected_compile_output_regex = "warning: .TestClassNameCheck"
},
]
}
# Copyright 2020 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/android_nocompile.gni")
import("//build/config/android/rules.gni")
import("nocompile_sources.gni")
empty_java = "../src/org/chromium/tools/errorprone/plugin/Empty.java"
android_library("no_redundant_field_init_check_int_test_java") {
testonly = true
enable_errorprone = true
sources = [ empty_java ]
if (enable_android_nocompile_tests) {
sources += no_redundant_field_init_check_int_test_nocompile_sources
skip_build_server = true
}
}
android_library("test_class_name_check_test_java") {
testonly = true
enable_errorprone = true
sources = [ empty_java ]
if (enable_android_nocompile_tests) {
sources += test_class_name_check_test_nocompile_sources
skip_build_server = true
}
deps = [
"//base:base_java_test_support",
"//third_party/android_support_test_runner:runner_java",
"//third_party/junit:junit",
]
}
# Copyright 2020 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.
no_redundant_field_init_check_int_test_nocompile_sources = [ "../src/org/chromium/tools/errorprone/plugin/NoRedundantFieldInitCheckIntTest.java" ]
test_class_name_check_test_nocompile_sources = [
"../src/org/chromium/tools/errorprone/plugin/TestClassNameCheckTesting.java",
]
// Copyright 2020 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.
package org.chromium.tools.errorprone.plugin;
/**
* Empty class.
*/
public class Empty
{}
// Copyright 2020 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.
package org.chromium.tools.errorprone.plugin;
/**
* |mBar| initialization should cause 'NoRedundantFieldInitCheck' errorprone warning.
*/
public class NoRedundantFieldInitCheckIntTest
{
private int mBar = 0;
public void foo()
{
System.out.println("" + mBar);
}
}
// Copyright 2020 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.
package org.chromium.tools.errorprone.plugin;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
/**
* Should cause 'TestClassNameCheck' errorprone warning.
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class TestClassNameCheckTesting
{
@Test
public void testRandom()
{}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment