list = new ArrayList<>(calledMethods);
20 | list.sort(null);
21 | try (FileWriter heapFileWritter = new FileWriter(new File("agentCoverage.csv"), true)) {
22 | for (String calledMethod : list) {
23 | heapFileWritter.append(calledMethod).append("\n").flush();
24 | }
25 | } catch (IOException e) {
26 | e.printStackTrace();
27 | }
28 | }));
29 | AgentBuilder.Transformer.ForAdvice advice = new AgentBuilder.Transformer.ForAdvice()
30 | .include(inst.getClass().getClassLoader())
31 | .advice(ElementMatchers.any(), CallAdvice.class.getCanonicalName());
32 |
33 | new AgentBuilder.Default()
34 | .ignore(ElementMatchers.nameStartsWith("net.bytebuddy.")
35 | .or(ElementMatchers.nameStartsWith("se.kth.castor.jdbl."))
36 | .or(ElementMatchers.nameStartsWith("org.jacoco."))
37 | .or(ElementMatchers.nameStartsWith("org.apache.maven."))
38 | .or(ElementMatchers.nameStartsWith("org.objectweb.asm."))
39 | .or(ElementMatchers.nameStartsWith("java."))
40 | .or(ElementMatchers.nameStartsWith("jdk."))
41 | .or(ElementMatchers.nameStartsWith("sun."))
42 | .or(ElementMatchers.nameStartsWith("com.sun.")))
43 | .type(ElementMatchers.any()).transform(advice).installOn(inst);
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/it/dummy-project/src/main/java/defaultmethods/MyTradingApi.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Gareth Jon Lynch
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | * this software and associated documentation files (the "Software"), to deal in
8 | * the Software without restriction, including without limitation the rights to
9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | * the Software, and to permit persons to whom the Software is furnished to do so,
11 | * subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | package defaultmethods;
25 |
26 |
27 | import java.util.ArrayList;
28 | import java.util.List;
29 | import java.util.UUID;
30 |
31 | import common.Order;
32 |
33 | /**
34 | * An implementation of the Trading API.
35 | *
36 | * Note: the TradingApi#getImplName has not been implemented yet.
37 | *
38 | * @author gazbert
39 | */
40 | public class MyTradingApi implements TradingApi {
41 |
42 | @Override
43 | public boolean addOrder(Order order) {
44 | System.out.println(MyTradingApi.class.getSimpleName() + " addOrder called");
45 | return false;
46 | }
47 |
48 | @Override
49 | public boolean cancelOrder(UUID orderId) {
50 | System.out.println(MyTradingApi.class.getSimpleName() + " cancelOrder called");
51 | return false;
52 | }
53 |
54 | @Override
55 | public List getOpenOrders(int marketId) {
56 | System.out.println(MyTradingApi.class.getSimpleName() + " getOpenOrders called");
57 | return new ArrayList<>();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/adapter/ClassAdapter.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.adapter;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.IOException;
6 |
7 | import org.objectweb.asm.ClassReader;
8 | import org.objectweb.asm.ClassVisitor;
9 | import org.objectweb.asm.Opcodes;
10 |
11 | public class ClassAdapter extends ClassVisitor implements Opcodes
12 | {
13 | private boolean isRemovable = false;
14 |
15 | private String baseDir;
16 |
17 | public ClassAdapter(final ClassVisitor cv, String baseDir)
18 | {
19 | super(Opcodes.ASM8, cv);
20 | this.baseDir = baseDir;
21 | }
22 |
23 | @Override
24 | public void visit(
25 | final int version,
26 | final int access,
27 | final String name,
28 | final String signature,
29 | final String superName,
30 | final String[] interfaces)
31 | {
32 | super.visit(version, access, name, signature, superName, interfaces);
33 |
34 | isRemovable = !(((access & Opcodes.ACC_INTERFACE) != 0) ||
35 | ((access & Opcodes.ACC_ENUM) != 0) ||
36 | ((access & Opcodes.ACC_ANNOTATION) != 0) ||
37 | ((access & Opcodes.ACC_FINAL) != 0) ||
38 | // (name.matches(".*[$]\\d+")) || // annonymous classes
39 | (name.contains("$")) ||
40 | extendsThrowable(superName));
41 | }
42 |
43 | private boolean extendsThrowable(String className)
44 | {
45 | ClassReader cr = null;
46 | try {
47 | if (className.startsWith("java/") || className.startsWith("sun/")) {
48 |
49 | if (className.endsWith("Exception") || className.endsWith("Error")) {
50 | return true;
51 | }
52 | return false;
53 | } else {
54 | cr = new ClassReader(new FileInputStream(new File(baseDir + "/" + className + ".class")));
55 | }
56 | } catch (IOException e) {
57 | return true;
58 | }
59 | String superName = cr.getSuperName();
60 | if (superName != null && !superName.equals("java/lang/Object")) {
61 | String superClass = superName.replace('.', '/');
62 | return extendsThrowable(superClass);
63 | }
64 | return false;
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/util/CmdExec.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.util;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.InputStreamReader;
5 | import java.util.HashSet;
6 | import java.util.Set;
7 |
8 | import org.apache.commons.lang3.ArrayUtils;
9 | import org.apache.log4j.LogManager;
10 | import org.apache.log4j.Logger;
11 |
12 | public class CmdExec
13 | {
14 | private static final Logger LOGGER = LogManager.getLogger(CmdExec.class.getName());
15 |
16 | /**
17 | * Creates a system process to execute a Java class with its parameters via command line.
18 | * E.g, java -verbose:class -jar target/clitools-1.0.0-SNAPSHOT-jar-with-dependencies.jar whoami | grep "\[Loaded " |
19 | * grep -v " from /usr/lib" | cut -d ' ' -f2 | sort > loaded-classes
20 | *
21 | * @param classPath Path to the .class file
22 | * @param clazzFullyQualifiedName Fully qualified name of the class to execute
23 | * @param parameters The parameters (i.e, -verbose:class)
24 | * @return The set of classes in the classpath that were loaded during the execution of the class
25 | */
26 | public Set execProcess(String classPath, String clazzFullyQualifiedName, String[] parameters)
27 | {
28 | Set result = new HashSet<>();
29 | try {
30 | String line;
31 | String[] cmd = {"java",
32 | "-verbose:class",
33 | "-classpath",
34 | classPath,
35 | clazzFullyQualifiedName};
36 | cmd = ArrayUtils.addAll(cmd, parameters);
37 | for (String s : cmd) {
38 | System.out.print(s + " ");
39 | }
40 | System.out.println();
41 | Process p = Runtime.getRuntime().exec(cmd);
42 | BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
43 | while ((line = input.readLine()) != null) {
44 | System.out.println(line);
45 | if (line.startsWith("[Loaded ") && line.endsWith("target/classes" + "/]")) {
46 | result.add(line.split(" ")[1]);
47 | }
48 | }
49 | input.close();
50 | } catch (Exception e) {
51 | LOGGER.error("Failed to run: " + e);
52 | }
53 | return result;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/calc/inst/report/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Coverage report
5 |
6 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/deptree/AbstractTextVisitor.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.deptree;
2 |
3 | import java.io.BufferedWriter;
4 | import java.io.IOException;
5 |
6 | import org.apache.commons.io.output.StringBuilderWriter;
7 | import org.apache.log4j.LogManager;
8 | import org.apache.log4j.Logger;
9 |
10 | public abstract class AbstractTextVisitor implements Visitor
11 | {
12 | private static final Logger LOGGER = LogManager.getLogger(AbstractTextVisitor.class.getName());
13 |
14 | private final StringBuilderWriter sbw;
15 | private final BufferedWriter bw;
16 |
17 | public AbstractTextVisitor()
18 | {
19 | sbw = new StringBuilderWriter();
20 | bw = new BufferedWriter(sbw);
21 | }
22 |
23 | public void visit(Node node)
24 | {
25 | try {
26 | writeNode(node);
27 | for (Node child : node.getChildNodes()) {
28 | visit(child);
29 | }
30 | } catch (IOException ignored) {
31 | LOGGER.error("Error visiting node: " + node.getArtifactCanonicalForm());
32 | }
33 | }
34 |
35 | private void writeNode(Node node) throws IOException
36 | {
37 | //the tree symbols
38 | writeTreeSymbols(node);
39 | //the node itself
40 | bw.write(node.getArtifactCanonicalForm());
41 | bw.newLine();
42 | }
43 |
44 | private void writeTreeSymbols(Node node) throws IOException
45 | {
46 | if (node.getParent() != null) {
47 | writeParentTreeSymbols(node.getParent());
48 | bw.write(getTreeSymbols(node));
49 | }
50 | }
51 |
52 | private void writeParentTreeSymbols(Node node) throws IOException
53 | {
54 | if (node.getParent() != null) {
55 | writeParentTreeSymbols(node.getParent());
56 | bw.write(getParentTreeSymbols(node));
57 | }
58 | }
59 |
60 | public abstract String getTreeSymbols(Node node);
61 |
62 | public abstract String getParentTreeSymbols(Node node);
63 |
64 | @Override
65 | public String toString()
66 | {
67 | try {
68 | bw.flush();
69 | sbw.flush();
70 | return sbw.toString();
71 | } catch (IOException e) {
72 | return "";
73 | } finally {
74 | try {
75 | bw.close();
76 | } catch (IOException ignored) {
77 | LOGGER.error("Error while parsing to string.");
78 | }
79 | sbw.close();
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/coverage/UsageAnalysis.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.coverage;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 | import java.util.Set;
6 |
7 | public class UsageAnalysis
8 | {
9 | Map> analysis;
10 |
11 | public UsageAnalysis()
12 | {
13 | this.analysis = new HashMap<>();
14 | }
15 |
16 | public void setAnalysis(final Map> analysis)
17 | {
18 | this.analysis = analysis;
19 | }
20 |
21 | public Set methods(String clazz)
22 | {
23 | return analysis.get(clazz);
24 | }
25 |
26 | public void addEntry(String clazz, Set methods)
27 | {
28 | analysis.put(clazz, methods);
29 | }
30 |
31 | public Map> getAnalysis()
32 | {
33 | return analysis;
34 | }
35 |
36 | public boolean containsClazz(String clazz)
37 | {
38 | return analysis.containsKey(clazz);
39 | }
40 |
41 | public void removeUncoveredClasses()
42 | {
43 | analysis.entrySet().removeIf(entry -> entry.getValue().isEmpty());
44 | }
45 |
46 | public Set classes()
47 | {
48 | return analysis.keySet();
49 | }
50 |
51 | /**
52 | * Computes the union between the current usage analysis and another usage analysis.
53 | */
54 | public UsageAnalysis mergeWith(UsageAnalysis usageAnalysis)
55 | {
56 | UsageAnalysis mergedUsageAnalysis = new UsageAnalysis();
57 | mergeClasses(usageAnalysis);
58 | mergeMethods(usageAnalysis, mergedUsageAnalysis);
59 | return mergedUsageAnalysis;
60 | }
61 |
62 | private void mergeMethods(UsageAnalysis usageAnalysis, UsageAnalysis mergedUsageAnalysis)
63 | {
64 | for (String clazz : classes()) {
65 | Set methods = methods(clazz);
66 | if (usageAnalysis.containsClazz(clazz)) {
67 | methods.addAll(usageAnalysis.methods(clazz));
68 | }
69 | mergedUsageAnalysis.addEntry(clazz, methods);
70 | }
71 | }
72 |
73 | private void mergeClasses(UsageAnalysis usageAnalysis)
74 | {
75 | for (String clazz : usageAnalysis.classes()) {
76 | if (!containsClazz(clazz)) {
77 | addEntry(clazz, usageAnalysis.methods(clazz));
78 | }
79 | }
80 | }
81 |
82 | @Override
83 | public String toString()
84 | {
85 | StringBuilder sb = new StringBuilder();
86 | analysis.forEach((key, value) -> sb.append(key + " : " + value + "\n"));
87 | return sb.toString();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/it/dummy-project/src/main/java/annotations/ObjectToJsonConverter.java:
--------------------------------------------------------------------------------
1 | package annotations;
2 |
3 | import java.lang.reflect.Field;
4 | import java.lang.reflect.InvocationTargetException;
5 | import java.lang.reflect.Method;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 | import java.util.Objects;
9 | import java.util.stream.Collectors;
10 |
11 | public class ObjectToJsonConverter
12 | {
13 | public String convertToJson(Object object) throws JsonSerializationException
14 | {
15 | try {
16 |
17 | checkIfSerializable(object);
18 | initializeObject(object);
19 | return getJsonString(object);
20 |
21 | } catch (Exception e) {
22 | throw new JsonSerializationException(e.getMessage());
23 | }
24 | }
25 |
26 | private void checkIfSerializable(Object object)
27 | {
28 | if (Objects.isNull(object)) {
29 | throw new JsonSerializationException("Can't serialize a null object");
30 | }
31 |
32 | Class> clazz = object.getClass();
33 | if (!clazz.isAnnotationPresent(JsonSerializable.class)) {
34 | throw new JsonSerializationException("The class " + clazz.getSimpleName() + " is not annotated with JsonSerializable");
35 | }
36 | }
37 |
38 | private void initializeObject(Object object) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
39 | {
40 | Class> clazz = object.getClass();
41 | for (Method method : clazz.getDeclaredMethods()) {
42 | if (method.isAnnotationPresent(Init.class)) {
43 | method.setAccessible(true);
44 | method.invoke(object);
45 | }
46 | }
47 | }
48 |
49 | private String getJsonString(Object object) throws IllegalArgumentException, IllegalAccessException
50 | {
51 | Class> clazz = object.getClass();
52 | Map jsonElementsMap = new HashMap<>();
53 | for (Field field : clazz.getDeclaredFields()) {
54 | field.setAccessible(true);
55 | if (field.isAnnotationPresent(JsonElement.class)) {
56 | jsonElementsMap.put(getKey(field), (String) field.get(object));
57 | }
58 | }
59 |
60 | String jsonString = jsonElementsMap.entrySet()
61 | .stream()
62 | .map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"")
63 | .collect(Collectors.joining(","));
64 | return "{" + jsonString + "}";
65 | }
66 |
67 | private String getKey(Field field)
68 | {
69 | String value = field.getAnnotation(JsonElement.class)
70 | .key();
71 | return value.isEmpty() ? field.getName() : value;
72 | }
73 | }
74 |
75 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | build:
13 | strategy:
14 | max-parallel: 1
15 | matrix:
16 | os: [ ubuntu-latest ]
17 | java: [ 11 ]
18 | runs-on: ${{ matrix.os }}
19 | name: Build with Java ${{ matrix.java }} on ${{ matrix.os }}
20 | steps:
21 | - name: "Checkout"
22 | uses: actions/checkout@v2
23 | with:
24 | fetch-depth: 0
25 | - name: Java ${{ matrix.java }}
26 | uses: actions/setup-java@v1
27 | with:
28 | java-version: ${{ matrix.java }}
29 | - name: "Cache Local Maven Repository"
30 | uses: actions/cache@v1
31 | with:
32 | path: ~/.m2
33 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
34 | restore-keys: ${{ runner.os }}-m2
35 | # - name: "CheckStyle"
36 | # run: mvn validate --errors
37 | - name: "Compile and Install"
38 | run: mvn clean install -DskipTests --errors
39 | - name: "Unit Tests"
40 | run: mvn test --errors --fail-at-end
41 | # - name: "Integration Tests"
42 | # run: mvn failsafe:integration-test --errors --fail-at-end
43 | # The following is only executed on Ubuntu on Java 11
44 | - name: "JaCoCo Coverage Report"
45 | if: matrix.os == 'ubuntu-latest' && matrix.java == 11 && github.repository == 'castor-software/jdbl'
46 | run: mvn jacoco:report
47 | - name: "Codecov"
48 | if: matrix.os == 'ubuntu-latest' && matrix.java == 11 && github.repository == 'castor-software/jdbl'
49 | uses: codecov/codecov-action@v1
50 | with:
51 | token: ${{ secrets.CODECOV_TOKEN }}
52 | files: ./jdbl-core/target/site/jacoco/jacoco.xml
53 | flags: unittests
54 | - name: "Cache SonarCloud"
55 | if: matrix.os == 'ubuntu-latest' && matrix.java == 11 && github.repository == 'castor-software/jdbl'
56 | uses: actions/cache@v1
57 | with:
58 | path: ~/.sonar/cache
59 | key: ${{ runner.os }}-sonar
60 | restore-keys: ${{ runner.os }}-sonar
61 | - name: "SonarCloud"
62 | if: matrix.os == 'ubuntu-latest' && matrix.java == 11 && github.repository == 'castor-software/jdbl'
63 | run: mvn sonar:sonar -Dsonar.projectKey=castor-software_jdbl -Dsonar.organization=castor-software -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=${{ secrets.SONAR_TOKEN }} -Dsonar.java.source=11 -Dsonar.java.target=11
64 | env:
65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/it/dummy-project/src/test/java/interfaces/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package interfaces;
2 |
3 | import org.junit.BeforeClass;
4 | import org.junit.Test;
5 | import static org.assertj.core.api.Assertions.*;
6 |
7 | public class ApplicationTest {
8 |
9 | private static Car car;
10 | private static Motorbike motorbike;
11 |
12 | @BeforeClass
13 | public static void setUpCarInstance() {
14 | car = new Car("BMW");
15 | }
16 |
17 | @BeforeClass
18 | public static void setUpMotorbikeInstance() {
19 | motorbike = new Motorbike("Yamaha");
20 | }
21 |
22 | @Test
23 | public void givenCarInstace_whenBrandisBMW_thenOneAssertion() {
24 | assertThat(car.getBrand()).isEqualTo("BMW");
25 | }
26 |
27 | @Test
28 | public void givenCarInstance_whenCallingSpeedUp_thenOneAssertion() {
29 | assertThat(car.speedUp()).isEqualTo("The car is speeding up.");
30 | }
31 |
32 | @Test
33 | public void givenCarInstance_whenCallingSlowDown_thenOneAssertion() {
34 | assertThat(car.slowDown()).isEqualTo("The car is slowing down.");
35 | }
36 |
37 | @Test
38 | public void givenCarInstance_whenCallingTurnAlarmOn_thenOneAssertion() {
39 | assertThat(car.turnAlarmOn()).isEqualTo("Turning the vehice alarm on.");
40 | }
41 |
42 | @Test
43 | public void givenCarInstance_whenCallingTurnAlarmOff_thenOneAssertion() {
44 | assertThat(car.turnAlarmOff()).isEqualTo("Turning the vehicle alarm off.");
45 | }
46 |
47 | @Test
48 | public void givenVehicleInterface_whenCallinggetHorsePower_thenOneAssertion() {
49 | assertThat(Vehicle.getHorsePower(2500, 480)).isEqualTo(228);
50 | }
51 |
52 | @Test
53 | public void givenMooorbikeInstace_whenBrandisYamaha_thenOneAssertion() {
54 | assertThat(motorbike.getBrand()).isEqualTo("Yamaha");
55 | }
56 |
57 | @Test
58 | public void givenMotorbikeInstance_whenCallingSpeedUp_thenOneAssertion() {
59 | assertThat(motorbike.speedUp()).isEqualTo("The motorbike is speeding up.");
60 | }
61 |
62 | @Test
63 | public void givenMotorbikeInstance_whenCallingSlowDown_thenOneAssertion() {
64 | assertThat(motorbike.slowDown()).isEqualTo("The motorbike is slowing down.");
65 | }
66 |
67 | @Test
68 | public void givenMotorbikeInstance_whenCallingTurnAlarmOn_thenOneAssertion() {
69 | assertThat(motorbike.turnAlarmOn()).isEqualTo("Turning the vehice alarm on.");
70 | }
71 |
72 | @Test
73 | public void givenMotorbikeInstance_whenCallingTurnAlarmOff_thenOneAssertion() {
74 | assertThat(motorbike.turnAlarmOff()).isEqualTo("Turning the vehicle alarm off.");
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/deptree/TextParser.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.deptree;
2 |
3 | import java.io.IOException;
4 | import java.io.Reader;
5 |
6 | public class TextParser extends AbstractLineBasedParser
7 | {
8 | public Node parse(Reader reader) throws ParseException
9 | {
10 | try {
11 | this.lines = splitLines(reader);
12 | } catch (IOException e) {
13 | throw new ParseException(e);
14 | }
15 |
16 | if (lines.isEmpty()) {
17 | return null;
18 | }
19 | return parseInternal(0);
20 | }
21 |
22 | private Node parseInternal(final int depth)
23 | {
24 | //current node
25 | final Node node = this.parseLine();
26 | this.lineIndex++;
27 |
28 | //children
29 | while (this.lineIndex < this.lines.size() && this.computeDepth(this.lines.get(this.lineIndex)) > depth) {
30 | final Node child = this.parseInternal(depth + 1);
31 | if (node != null) {
32 | node.addChildNode(child);
33 | }
34 | }
35 | return node;
36 |
37 | }
38 |
39 | private int computeDepth(final String line)
40 | {
41 | return getArtifactIndex(line) / 3;
42 | }
43 |
44 | /**
45 | * sample lineIndex structure:
46 | * | | \- org.apache.activemq:activeio-core:test-jar:tests:3.1.0:compile
47 | *
48 | * @return
49 | */
50 | private Node parseLine()
51 | {
52 | String line = this.lines.get(this.lineIndex);
53 | String artifact;
54 | if (line.contains("active project artifact:")) {
55 | artifact = extractActiveProjectArtifact();
56 | } else {
57 | artifact = extractArtifact(line);
58 | }
59 | return parseArtifactString(artifact);
60 | }
61 |
62 | private String extractArtifact(String line)
63 | {
64 | return line.substring(getArtifactIndex(line));
65 | }
66 |
67 | private int getArtifactIndex(final String line)
68 | {
69 | for (int i = 0; i < line.length(); i++) {
70 | final char c = line.charAt(i);
71 | switch (c) {
72 | case ' '://whitespace, standard and extended
73 | case '|'://standard
74 | case '+'://standard
75 | case '\\'://standard
76 | case '-'://standard
77 | case '³'://extended
78 | case 'Ã'://extended
79 | case 'Ä'://extended
80 | case 'À'://extended
81 | continue;
82 | default:
83 | return i;
84 | }
85 | }
86 | return -1;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/main/java/se/kth/castor/jdbl/plugin/AbstractDebloatMojo.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.plugin;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.io.InputStreamReader;
7 |
8 | import org.apache.maven.plugin.AbstractMojo;
9 | import org.apache.maven.plugin.MojoExecutionException;
10 | import org.apache.maven.plugin.MojoFailureException;
11 | import org.apache.maven.plugins.annotations.Parameter;
12 | import org.apache.maven.project.MavenProject;
13 |
14 | public abstract class AbstractDebloatMojo extends AbstractMojo
15 | {
16 | /**
17 | * The maven home file, assuming either an environment variable M2_HOME, or that mvn command exists in PATH.
18 | */
19 | public static File mavenHome;
20 | static {
21 | if (System.getenv().containsKey("M2_HOME")) {
22 | mavenHome = new File(System.getenv().get("M2_HOME"));
23 | } else {
24 | try {
25 | Process exec = Runtime.getRuntime().exec("mvn --version");
26 | BufferedReader stdInput = new BufferedReader(new InputStreamReader(exec.getInputStream()));
27 | exec.waitFor();
28 | mavenHome = new File(stdInput.readLine().replace("Maven home: ", ""));
29 | } catch (Exception e) {
30 | e.printStackTrace();
31 | mavenHome = new File("");
32 | }
33 | }
34 | }
35 |
36 | private static final String LINE_SEPARATOR = "------------------------------------------------------------------------";
37 |
38 |
39 | @Parameter(defaultValue = "${project}", readonly = true)
40 | private MavenProject project;
41 |
42 | /**
43 | * Skip plugin execution completely.
44 | */
45 | @Parameter(property = "skipJDBL", defaultValue = "false")
46 | private boolean skipJDBL;
47 |
48 | @Override
49 | public final void execute()
50 | throws MojoExecutionException, MojoFailureException
51 | {
52 | if (isSkipJDBL()) {
53 | getLog().info("Skipping plugin execution...");
54 | return;
55 | }
56 | this.doExecute();
57 | }
58 |
59 | protected abstract void doExecute()
60 | throws MojoExecutionException, MojoFailureException;
61 |
62 | public boolean isSkipJDBL()
63 | {
64 | return this.skipJDBL;
65 | }
66 |
67 | public MavenProject getProject()
68 | {
69 | return project;
70 | }
71 |
72 | public static File getMavenHome()
73 | {
74 | return mavenHome;
75 | }
76 |
77 | public void printCustomStringToConsole(final String s)
78 | {
79 | this.getLog().info(LINE_SEPARATOR);
80 | this.getLog().info(s);
81 | this.getLog().info(LINE_SEPARATOR);
82 | }
83 |
84 | public static String getLineSeparator()
85 | {
86 | return LINE_SEPARATOR;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/jdbl-app/src/main/java/se/kth/castor/jdbl/DebloatBuilder.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl;
2 |
3 | import java.io.File;
4 | import java.util.List;
5 |
6 | public class DebloatBuilder {
7 |
8 | //--------------------------------/
9 | //-------- CLASS FIELD/S --------/
10 | //------------------------------/
11 |
12 | private File inputFilePath;
13 | private File outputFilePath;
14 | private String entryClass;
15 | private String entryMethod;
16 | private List entryParams;
17 |
18 | //--------------------------------/
19 | //------- PUBLIC METHOD/S -------/
20 | //------------------------------/
21 |
22 | public DebloatBuilder addInputFilePath(File inputFilePath) {
23 | this.inputFilePath = inputFilePath;
24 | return this;
25 | }
26 |
27 | public DebloatBuilder addOutputFilePath(File outputFilePath) {
28 | this.outputFilePath = outputFilePath;
29 | return this;
30 | }
31 |
32 | public DebloatBuilder addEntryClass(String entryClass) {
33 | this.entryClass = entryClass;
34 | return this;
35 | }
36 |
37 | public DebloatBuilder addEntryMethod(String entryMethod) {
38 | this.entryMethod = entryMethod;
39 | return this;
40 | }
41 |
42 | public DebloatBuilder addEntryParam(List entryParams) {
43 | this.entryParams = entryParams;
44 | return this;
45 | }
46 |
47 | /**
48 | * Return the finally constructed DebloaterBuilder object.
49 | */
50 | public DebloatBuilder build() {
51 | DebloatBuilder debloatBuilder = new DebloatBuilder();
52 | debloatBuilder.inputFilePath = this.inputFilePath;
53 | debloatBuilder.outputFilePath = this.outputFilePath;
54 | debloatBuilder.entryClass = this.entryClass;
55 | debloatBuilder.entryMethod = this.entryMethod;
56 | debloatBuilder.entryParams = this.entryParams;
57 | return debloatBuilder;
58 | }
59 |
60 | //--------------------------------/
61 | //------- GETTER METHOD/S -------/
62 | //------------------------------/
63 |
64 | public File getInputFilePath() {
65 | return inputFilePath;
66 | }
67 |
68 | public File getOutputFilePath() {
69 | return outputFilePath;
70 | }
71 |
72 | public String getEntryClass() {
73 | return entryClass;
74 | }
75 |
76 | public String getEntryMethod() {
77 | return entryMethod;
78 | }
79 |
80 | public List getEntryParams() {
81 | return entryParams;
82 | }
83 |
84 | @Override
85 | public String toString() {
86 | return "inputFilePath=" + inputFilePath +
87 | ", outputFilePath=" + outputFilePath +
88 | ", entryClass='" + entryClass + '\'' +
89 | ", entryMethod='" + entryMethod + '\'' +
90 | ", entryParams='" + entryParams;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/it/dummy-project/src/test/java/defaultmethods/TestDefaultMethodUsage.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Gareth Jon Lynch
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | * this software and associated documentation files (the "Software"), to deal in
8 | * the Software without restriction, including without limitation the rights to
9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | * the Software, and to permit persons to whom the Software is furnished to do so,
11 | * subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | package defaultmethods;
25 |
26 | import static org.junit.Assert.assertEquals;
27 |
28 | import java.util.Date;
29 | import java.util.List;
30 |
31 | import org.junit.Test;
32 |
33 | import common.Order;
34 |
35 | /**
36 | * Test class for demonstrating use of default and static methods on interfaces in Java 8.
37 | *
38 | *
39 | * @author gazbert
40 | *
41 | */
42 | public class TestDefaultMethodUsage
43 | {
44 | /**
45 | * Shows use of default method.
46 | *
47 | * When you extend an interface that contains a default method, you treat it just like a superclass method:
48 | *
49 | * Not mention the default method at all - lets your extended interface inherit the default method.
50 | * Redeclare the default method - makes it abstract.
51 | * Redefine the default method - which overrides it.
52 | *
53 | */
54 | @Test
55 | public void showDefaultMethodUsage()
56 | {
57 | final TradingApi api = new MyTradingApi();
58 | final List openOrders = api.getOpenOrders(1);
59 | assertEquals(0, openOrders.size());
60 |
61 | // now invoke the default method
62 | assertEquals("Default API Impl", api.getImplName());
63 | }
64 |
65 | /**
66 | * Shows how to use static methods on Java 8 interfaces.
67 | *
68 | * Note the use of Date - another demo will cover the new Java 8 java.time API :-)
69 | */
70 | @Test
71 | public void showStaticInterfaceMethodUsage()
72 | {
73 | final Date time = TradingApi.getCurrentExchangeTime();
74 | System.out.println("Exchange clock time: " + time);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/debloat/EntryPointMethodDebloat.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.debloat;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.OutputStream;
8 | import java.util.Set;
9 |
10 | import org.apache.log4j.LogManager;
11 | import org.apache.log4j.Logger;
12 | import org.objectweb.asm.ClassReader;
13 | import org.objectweb.asm.ClassVisitor;
14 | import org.objectweb.asm.ClassWriter;
15 | import org.objectweb.asm.MethodVisitor;
16 | import org.objectweb.asm.Opcodes;
17 |
18 | import se.kth.castor.jdbl.coverage.UsageAnalysis;
19 | import se.kth.castor.jdbl.coverage.UsageStatusEnum;
20 | import se.kth.castor.jdbl.util.ClassFileType;
21 |
22 | public class EntryPointMethodDebloat extends AbstractMethodDebloat
23 | {
24 | protected static final Logger LOGGER = LogManager.getLogger(EntryPointMethodDebloat.class);
25 |
26 | public EntryPointMethodDebloat(String outputDirectory, UsageAnalysis usageAnalysis, String projectBaseDir)
27 | {
28 | super(outputDirectory, usageAnalysis, projectBaseDir);
29 | }
30 |
31 | @Override
32 | public void removeMethod(String clazz, Set usedMethods) throws IOException
33 | {
34 | FileInputStream in = new FileInputStream(new File(outputDirectory + "/" + clazz + ".class"));
35 | ClassReader cr = new ClassReader(in);
36 | ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
37 | ClassVisitor cv = new ClassVisitor(Opcodes.ASM8, cw)
38 | {
39 | @Override
40 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
41 | {
42 | MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
43 |
44 | if (usedMethods.contains(name + desc)) {
45 | System.out.println("Removed unused method: " + name + desc + " in class ==> " + clazz);
46 | // write report to file
47 | myFileWriter.writeDebloatReport(UsageStatusEnum.BLOATED_METHOD.getName(),
48 | clazz + ":" + name + desc, ClassFileType.UNKNOWN);
49 | return new MethodExceptionThrower(mv);
50 | // return null;
51 | } else {
52 | // write report to file
53 | myFileWriter.writeDebloatReport(UsageStatusEnum.USED_METHOD.getName(),
54 | clazz + ":" + name + desc, ClassFileType.UNKNOWN);
55 | }
56 | return mv;
57 | // return super.visitMethod(access, name, desc, signature, exceptions);
58 | }
59 | };
60 | cr.accept(cv, ClassReader.SKIP_DEBUG);
61 |
62 | byte[] code = cw.toByteArray();
63 | try (OutputStream fos = new FileOutputStream(outputDirectory + "/" + clazz.replace(".", "/") + ".class")) {
64 | fos.write(code);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/util/ZipUtils.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.util;
2 |
3 | //Import all needed packages
4 |
5 | import java.io.File;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.nio.file.Files;
10 | import java.nio.file.Path;
11 | import java.nio.file.Paths;
12 | import java.util.Enumeration;
13 | import java.util.jar.JarEntry;
14 | import java.util.jar.JarFile;
15 | import java.util.stream.Stream;
16 | import java.util.zip.ZipEntry;
17 | import java.util.zip.ZipOutputStream;
18 |
19 | public class ZipUtils
20 | {
21 | public static void pack(String sourceDirPath, String zipFilePath) throws IOException
22 | {
23 | Path p = Files.createFile(Paths.get(zipFilePath));
24 | Path pp = Paths.get(sourceDirPath);
25 | try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(p));
26 | Stream paths = Files.walk(pp)) {
27 | paths
28 | .filter(path -> !Files.isDirectory(path))
29 | .forEach(path -> {
30 | ZipEntry zipEntry = new ZipEntry(pp.relativize(path).toString());
31 | try {
32 | zs.putNextEntry(zipEntry);
33 | Files.copy(path, zs);
34 | zs.closeEntry();
35 | } catch (IOException e) {
36 | System.err.println(e);
37 | }
38 | });
39 | }
40 | }
41 |
42 | public static void unpack(String destinationDir, String jarPath) throws IOException
43 | {
44 | File file = new File(jarPath);
45 | JarFile jar = new JarFile(file);
46 |
47 | // fist get all directories,
48 | // then make those directory on the destination Path
49 | for (Enumeration enums = jar.entries(); enums.hasMoreElements(); ) {
50 | JarEntry entry = (JarEntry) enums.nextElement();
51 |
52 | String fileName = destinationDir + File.separator + entry.getName();
53 | File f = new File(fileName);
54 |
55 | if (fileName.endsWith("/")) {
56 | f.mkdirs();
57 | }
58 |
59 | }
60 |
61 | //now create all files
62 | for (Enumeration enums = jar.entries(); enums.hasMoreElements(); ) {
63 | JarEntry entry = (JarEntry) enums.nextElement();
64 |
65 | String fileName = destinationDir + File.separator + entry.getName();
66 | File f = new File(fileName);
67 |
68 | if (!fileName.endsWith("/")) {
69 | InputStream is = jar.getInputStream(entry);
70 | FileOutputStream fos = new FileOutputStream(f);
71 |
72 | // write contents of 'is' to 'fos'
73 | while (is.available() > 0) {
74 | fos.write(is.read());
75 | }
76 |
77 | fos.close();
78 | is.close();
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/jdbl-agent/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 | jdbl-agent
8 | 1.0.0
9 |
10 |
11 | se.kth.castor
12 | jdbl-parent-pom
13 | 1.0.0
14 | ../
15 |
16 |
17 |
18 | UTF-8
19 |
20 |
21 |
22 |
23 |
24 | org.apache.maven.plugins
25 | maven-compiler-plugin
26 | 3.8.1
27 |
28 | 11
29 | 11
30 |
31 |
32 |
33 | org.apache.maven.plugins
34 | maven-jar-plugin
35 | 2.4
36 |
37 |
38 |
39 | true
40 | true
41 | true
42 |
43 |
44 | se.kth.castor.jdbl.agent.Agent
45 | false
46 | true
47 |
48 |
49 |
50 |
51 |
52 | maven-assembly-plugin
53 |
54 |
55 | package
56 |
57 | single
58 |
59 |
60 |
61 |
62 |
63 | jar-with-dependencies
64 |
65 |
66 | src/main/resources/META-INF/MANIFEST.MF
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | net.bytebuddy
77 | byte-buddy
78 | 1.10.9
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/it/dummy-project/src/main/java/defaultmethods/TradingApi.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Gareth Jon Lynch
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | * this software and associated documentation files (the "Software"), to deal in
8 | * the Software without restriction, including without limitation the rights to
9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | * the Software, and to permit persons to whom the Software is furnished to do so,
11 | * subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | package defaultmethods;
25 |
26 |
27 | import java.util.Date;
28 | import java.util.List;
29 | import java.util.UUID;
30 |
31 | import common.Order;
32 |
33 | /**
34 | * A simple, not real-world, part-built Trading API to show use of Java 8 default methods.
35 | *
36 | * @author gazbert
37 | */
38 | public interface TradingApi {
39 | /**
40 | * Adds an order.
41 | *
42 | * @param order order to add.
43 | * @return true if order placed successfully, false otherwise.
44 | */
45 | boolean addOrder(Order order);
46 |
47 | /**
48 | * Cancels an order.
49 | *
50 | * @param orderId is of order to cancel.
51 | * @return true if order cancelled successfully, false otherwise.
52 | */
53 | boolean cancelOrder(UUID orderId);
54 |
55 | /**
56 | * Returns your open orders on the exchange.
57 | *
58 | * @param marketId id of market to fetch orders for.
59 | * @return list of open orders for given market, empty list if none found.
60 | */
61 | List getOpenOrders(int marketId);
62 |
63 | /*
64 | * Below is the default method.
65 | * This has been added but the interface is already out there and we don't want to break existing code, so we
66 | * provide a default impl.
67 | */
68 |
69 | /**
70 | * @return the API implementation name.
71 | */
72 | default String getImplName() {
73 | System.out.println(TradingApi.class.getSimpleName() + " getImplName() called");
74 | return "Default API Impl";
75 | }
76 |
77 | /*
78 | * Also new in Java 8 is ability to include staitic methods in the interface.
79 | *
80 | * Note the use of Date - another demo will cover the new Java 8 java.time API :-)
81 | */
82 | static Date getCurrentExchangeTime() {
83 | return new Date();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/java/se/kth/castor/jdbl/coverage/JCovCoverageTest.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.coverage;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 |
6 | import org.junit.After;
7 | import org.junit.Before;
8 | import org.junit.Test;
9 |
10 | import com.sun.tdk.jcov.Grabber;
11 | import com.sun.tdk.jcov.Instr;
12 | import com.sun.tdk.jcov.JCov;
13 | import com.sun.tdk.jcov.Merger;
14 | import com.sun.tdk.jcov.RepGen;
15 | import com.sun.tdk.jcov.data.Result;
16 | import se.kth.castor.jdbl.util.JarUtils;
17 | import se.kth.castor.jdbl.util.MavenUtils;
18 |
19 | public class JCovCoverageTest
20 | {
21 | private Instr instr;
22 | private RepGen repgen;
23 | private File template;
24 | private Merger merger;
25 | private File classesDir;
26 | private File instrumentedDir;
27 |
28 | @Before
29 | public void setUp() throws Exception
30 | {
31 | instr = new Instr();
32 | repgen = new RepGen();
33 | merger = new Merger();
34 | template = new File("src/test/resources/calc/template.xml");
35 | classesDir = new File("src/test/resources/calc/");
36 | instrumentedDir = new File("src/test/resources/calc/inst/");
37 | }
38 |
39 | @Test
40 | /**
41 | * java -jar jcov.jar tmplgen -t template.xml $path_to_classes
42 | * java -jar jcov.jar grabber -t template.xml -o result_jcov.xml
43 | * run the tests adding javaagent option: "-javaagent:jcov.jar=grabber"
44 | * java -jar jcov.jar grabbermanager -kill
45 | * java -jar jcov.jar repgen -o report result_jcov.xml
46 | *
47 | */
48 | public void instrument() throws IOException
49 | {
50 | // JCov jCov = new JCov();
51 | // JCov.main(new String[]{"tmplgen", "t", "template.xml", classesDir.getAbsolutePath()});
52 | // JCov.printHelp();
53 | // jCov.run(new String[]{"tmplgen", "t", "template.xml", classesDir.getAbsolutePath()});
54 | // jCov.run(new String[]{"grabber", "-t", "template.xml", "-o", "result_jcov.xml"});
55 |
56 | Grabber grabber = new Grabber();
57 | grabber.createServer();
58 | grabber.setOutputFilename("myreport.xml");
59 | grabber.startServer();
60 |
61 | grabber.stopServer(true);
62 |
63 |
64 |
65 |
66 | // template.getParentFile().mkdirs();
67 | // instr.config(true, true, true, null, null);
68 | // instr.instrumentFiles(new File[]{classesDir}, instrumentedDir, null);
69 | // instr.finishWork(template.getPath());
70 | //
71 | // MavenUtils mavenUtils = new MavenUtils(null, null);
72 | // mavenUtils.copyDependency("com.sun.tdk:jcov:1.0", instrumentedDir.getAbsolutePath());
73 | // JarUtils.decompressJars(instrumentedDir.getAbsolutePath());
74 | //
75 | //
76 | // final Result result = new Result(template.getAbsolutePath());
77 | // try {
78 | // repgen.generateReport("src/test/resources/calc/inst/jcov-reports", result);
79 | // } catch (final Exception e) {
80 | // }
81 |
82 | }
83 |
84 | @After
85 | public void tearDown() throws Exception
86 | {
87 | }
88 |
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/debloat/ConservativeMethodDebloat.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.debloat;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.OutputStream;
8 | import java.util.Set;
9 |
10 | import org.objectweb.asm.ClassReader;
11 | import org.objectweb.asm.ClassVisitor;
12 | import org.objectweb.asm.ClassWriter;
13 | import org.objectweb.asm.MethodVisitor;
14 | import org.objectweb.asm.Opcodes;
15 |
16 | import se.kth.castor.jdbl.coverage.UsageAnalysis;
17 |
18 | public class ConservativeMethodDebloat extends AbstractMethodDebloat
19 | {
20 | public ConservativeMethodDebloat(String outputDirectory, UsageAnalysis usageAnalysis, String projectBaseDir)
21 | {
22 | super(outputDirectory, usageAnalysis, projectBaseDir);
23 | }
24 |
25 | @Override
26 | public void removeMethod(String clazz, Set usedMethods) throws IOException
27 | {
28 | try {
29 | FileInputStream in = new FileInputStream(new File(outputDirectory + "/" + clazz + ".class"));
30 | ClassReader cr = new ClassReader(in);
31 | ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
32 | ClassVisitor cv = new ClassVisitor(Opcodes.ASM8, cw)
33 | {
34 | @Override
35 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
36 | {
37 | MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
38 |
39 | if (clazz.equals("calc/Calculator")) {
40 | System.out.println("method name: " + name + ", " + "desc: " + desc + ", " + "signature: " + signature +
41 | ", " + "access: " + access);
42 | System.out.println(usedMethods);
43 | }
44 |
45 | if (isContains(name, usedMethods)) {
46 | // System.out.println("Removing unused method: " + name + desc + " in class: " + clazz);
47 | return new MethodExceptionThrower(mv);
48 | // return null;
49 | }
50 | return mv;
51 | // return super.visitMethod(access, name, desc, signature, exceptions);
52 | }
53 | };
54 | cr.accept(cv, ClassReader.SKIP_DEBUG);
55 |
56 | byte[] code = cw.toByteArray();
57 | try (OutputStream fos = new FileOutputStream(outputDirectory + "/" + clazz.replace(".", "/") + ".class")) {
58 | fos.write(code);
59 | }
60 |
61 | } catch (Exception e) {
62 | } // do nothing, just continue analyzing other classes
63 | }
64 |
65 | private boolean isContains(String methodName, Set usedMethods)
66 | {
67 | if (methodName.equals("main")) {
68 | return true; // keep main classes
69 | }
70 | for (String usedMethod : usedMethods) {
71 | if (usedMethod.split("\\(")[0].equals(methodName)) {
72 | return true;
73 | }
74 | }
75 | return false;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/calc/inst/report/overview-summary.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | coverage report
5 |
6 |
7 |
8 |
9 | Coverage report
10 |
11 |
12 |
13 |
14 |
15 | #classes
16 | %method
17 | %block
18 | %branch
19 | %line
20 |
21 |
22 | Overall statistics
23 | 2
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Packages
32 |
33 |
34 | Name
35 | #classes
36 | %method
37 | %block
38 | %branch
39 | %line
40 |
41 |
42 |
43 | 2
44 |
45 |
46 | -
47 |
48 |
49 |
50 | Report generated 6/30/20 1:00 AM
51 |
52 |
53 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/test/TestRunner.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.test;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 |
7 | import org.apache.log4j.LogManager;
8 | import org.apache.log4j.Logger;
9 | import org.apache.maven.project.MavenProject;
10 |
11 | import se.kth.castor.jdbl.coverage.YajtaCoverage;
12 | import se.kth.castor.jdbl.util.MyFileWriter;
13 |
14 | public class TestRunner
15 | {
16 | private static final Logger LOGGER = LogManager.getLogger(TestRunner.class.getName());
17 |
18 | public static void runTests(MavenProject mavenProject, boolean isTestRunForCoverage) throws IOException
19 | {
20 | Runtime rt = Runtime.getRuntime();
21 | Process p;
22 |
23 | if (isTestRunForCoverage) {
24 | p = rt.exec("mvn org.apache.maven.plugins:maven-surefire-plugin:3.0.0-M5:test");
25 | } else {
26 | p = rt.exec("mvn test -Dmaven.main.skip=true -Drat.skip=true -Danimal.sniffer.skip=true -Dmaven.javadoc.skip=true -Dlicense.skip=true -Dsource.skip=true");
27 | }
28 |
29 | printProcessToStandardOutput(p);
30 |
31 | try {
32 | p.waitFor();
33 | } catch (
34 | InterruptedException e) {
35 | LOGGER.error("Re-testing process terminated unexpectedly.");
36 | Thread.currentThread().interrupt();
37 | }
38 |
39 | TestResultReader testResultReader = new TestResultReader(".");
40 | TestResult testResult = testResultReader.getResults();
41 |
42 | MyFileWriter myFileWriter = new MyFileWriter(mavenProject.getBasedir().getAbsolutePath());
43 | myFileWriter.writeTestResultsToFile(testResult);
44 |
45 | if (testResult.errorTests() != 0 || testResult.failedTests() != 0) {
46 | LOGGER.error("JDBL: THERE ARE TESTS FAILURES");
47 | }
48 | }
49 |
50 | public static void runTests2(MavenProject mavenProject, String template) throws IOException
51 | {
52 | Runtime rt = Runtime.getRuntime();
53 | Process p;
54 |
55 | p = rt.exec("mvn surefire:test " +
56 | "-DargLine=\"-Djcov.template=" + template + "\"");
57 |
58 | printProcessToStandardOutput(p);
59 |
60 | try {
61 | p.waitFor();
62 | } catch (
63 | InterruptedException e) {
64 | LOGGER.error("Re-testing process terminated unexpectedly.");
65 | Thread.currentThread().interrupt();
66 | }
67 |
68 | TestResultReader testResultReader = new TestResultReader(".");
69 | TestResult testResult = testResultReader.getResults();
70 |
71 | MyFileWriter myFileWriter = new MyFileWriter(mavenProject.getBasedir().getAbsolutePath());
72 | myFileWriter.writeTestResultsToFile(testResult);
73 |
74 | if (testResult.errorTests() != 0 || testResult.failedTests() != 0) {
75 | LOGGER.error("JDBL: THERE ARE TESTS FAILURES");
76 | }
77 |
78 | }
79 |
80 | private static void printProcessToStandardOutput(final Process p) throws IOException
81 | {
82 | String line;
83 | BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
84 | while ((line = input.readLine()) != null) {
85 | System.out.println(line);
86 | }
87 | input.close();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/calc/template.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 0=8;4=9;9=10;14=11;
39 |
40 |
41 |
42 |
43 |
44 |
45 | 0=14;
46 |
47 |
48 |
49 |
50 |
51 |
52 | 0=18;
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 0=1;
62 |
63 |
64 |
65 |
66 |
67 |
68 | 0=6;10=7;15=8;40=9;
69 |
70 |
71 |
72 |
73 |
74 |
75 | 0=13;
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/calc/inst/result.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 0=8;4=9;9=10;14=11;
39 |
40 |
41 |
42 |
43 |
44 |
45 | 0=14;
46 |
47 |
48 |
49 |
50 |
51 |
52 | 0=18;
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 0=1;
62 |
63 |
64 |
65 |
66 |
67 |
68 | 0=6;10=7;15=8;40=9;
69 |
70 |
71 |
72 |
73 |
74 |
75 | 0=13;
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/calc/inst/template.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 0=8;4=9;9=10;14=11;
39 |
40 |
41 |
42 |
43 |
44 |
45 | 0=14;
46 |
47 |
48 |
49 |
50 |
51 |
52 | 0=18;
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 0=1;
62 |
63 |
64 |
65 |
66 |
67 |
68 | 0=6;10=7;15=8;40=9;
69 |
70 |
71 |
72 |
73 |
74 |
75 | 0=13;
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/coverage/result_jcov.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 0=8;4=9;9=10;14=11;
39 |
40 |
41 |
42 |
43 |
44 |
45 | 0=14;
46 |
47 |
48 |
49 |
50 |
51 |
52 | 0=18;
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 0=1;
62 |
63 |
64 |
65 |
66 |
67 |
68 | 0=6;10=7;15=8;40=9;
69 |
70 |
71 |
72 |
73 |
74 |
75 | 0=13;
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/main/java/se/kth/castor/jdbl/plugin/ConservativeDebloatMojo.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.plugin;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.HashSet;
6 | import java.util.Objects;
7 | import java.util.Set;
8 |
9 | import org.apache.maven.plugins.annotations.LifecyclePhase;
10 | import org.apache.maven.plugins.annotations.Mojo;
11 |
12 | import se.kth.castor.jdbl.callgraph.JCallGraphModified;
13 | import se.kth.castor.jdbl.coverage.UsageAnalysis;
14 | import se.kth.castor.jdbl.debloat.AbstractMethodDebloat;
15 | import se.kth.castor.jdbl.debloat.ConservativeMethodDebloat;
16 | import se.kth.castor.jdbl.util.MyFileUtils;
17 | import se.kth.castor.jdbl.util.JarUtils;
18 | import se.kth.castor.jdbl.util.MavenUtils;
19 |
20 | /**
21 | * This Maven mojo statically instruments the project and its dependencies in order to remove unused API members.
22 | */
23 | @Mojo(name = "conservative-debloat", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, threadSafe = true)
24 | public class ConservativeDebloatMojo extends AbstractDebloatMojo
25 | {
26 | @Override
27 | public void doExecute()
28 | {
29 | printCustomStringToConsole("S T A R T I N G C O N S E R V A T I V E D E B L O A T");
30 |
31 | String outputDirectory = getProject().getBuild().getOutputDirectory();
32 | File baseDir = getProject().getBasedir();
33 |
34 | MavenUtils mavenUtils = new MavenUtils(getMavenHome(), baseDir);
35 |
36 | // copy the dependencies
37 | mavenUtils.copyRuntimeDependencies(outputDirectory);
38 |
39 | // copy the resources
40 | mavenUtils.copyResources(outputDirectory);
41 |
42 | // decompress the copied dependencies
43 | JarUtils.decompressJars(outputDirectory);
44 |
45 | JCallGraphModified jCallGraphModified = new JCallGraphModified();
46 |
47 | // run de static usage analysis
48 | UsageAnalysis usageAnalysis = jCallGraphModified
49 | .runUsageAnalysis(getProject().getBuild().getOutputDirectory());
50 |
51 | Set classesUsed = usageAnalysis.getAnalysis().keySet();
52 |
53 | this.getLog().info(String.format("#Total classes: %d",
54 | (long) usageAnalysis.getAnalysis().entrySet().size()));
55 | this.getLog().info(String.format("#Unused classes: %d",
56 | usageAnalysis.getAnalysis().entrySet().stream().filter(e -> e.getValue() == null).count()));
57 | this.getLog().info(String.format("#Unused methods: %d",
58 | usageAnalysis.getAnalysis().values().stream().filter(Objects::nonNull).mapToInt(Set::size).sum()));
59 |
60 | // delete unused classes
61 | MyFileUtils MyFileUtils = new MyFileUtils(outputDirectory,
62 | new HashSet<>(),
63 | classesUsed,
64 | getProject().getBasedir().getAbsolutePath(), null);
65 | try {
66 | MyFileUtils.deleteUnusedClasses(outputDirectory);
67 | } catch (IOException e) {
68 | this.getLog().error(String.format("Error deleting unused classes: %s", e));
69 | }
70 |
71 | // delete unused methods
72 | AbstractMethodDebloat conservativeMethodDebloat = new ConservativeMethodDebloat(outputDirectory,
73 | usageAnalysis,
74 | getProject().getBasedir().getAbsolutePath() + "/.jdbl/debloat-report.csv");
75 | try {
76 | conservativeMethodDebloat.removeUnusedMethods();
77 | } catch (IOException e) {
78 | this.getLog().error(String.format("Error: %s", e));
79 | }
80 |
81 | printCustomStringToConsole("C O N S E R V A T I V E D E B L O A T F I N I S H E D");
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/calc/inst/report/package-summary.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | coverage report
5 |
6 |
7 |
8 |
9 | Coverage report
10 |
11 |
12 |
13 |
14 |
15 | #classes
16 | %method
17 | %block
18 | %branch
19 | %line
20 |
21 |
22 |
23 | 2
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Classes
32 |
33 |
34 | Name
35 | %method
36 | %block
37 | %branch
38 | %line
39 |
40 |
41 | Calc
42 |
43 |
44 | -
45 |
46 |
47 |
48 | Main
49 |
50 |
51 | -
52 |
53 |
54 |
55 | Report generated 6/30/20 1:00 AM
56 |
57 |
58 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/callgraph/ClassVisitor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011 - Georgios Gousios
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions are
6 | * met:
7 | *
8 | * * Redistributions of source code must retain the above copyright
9 | * notice, this list of conditions and the following disclaimer.
10 | *
11 | * * Redistributions in binary form must reproduce the above
12 | * copyright notice, this list of conditions and the following
13 | * disclaimer in the documentation and/or other materials provided
14 | * with the distribution.
15 | *
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 | */
28 |
29 | package se.kth.castor.jdbl.callgraph;
30 |
31 | import java.util.LinkedList;
32 | import java.util.List;
33 |
34 | import org.apache.bcel.classfile.Constant;
35 | import org.apache.bcel.classfile.ConstantPool;
36 | import org.apache.bcel.classfile.EmptyVisitor;
37 | import org.apache.bcel.classfile.JavaClass;
38 | import org.apache.bcel.classfile.Method;
39 | import org.apache.bcel.generic.ConstantPoolGen;
40 | import org.apache.bcel.generic.MethodGen;
41 |
42 | /**
43 | * The simplest of class visitors, invokes the method visitor class for each
44 | * method found.
45 | */
46 | public class ClassVisitor extends EmptyVisitor
47 | {
48 | private JavaClass clazz;
49 | private ConstantPoolGen constants;
50 | private String classReferenceFormat;
51 | private final DynamicCallManager dynamicCallManager = new DynamicCallManager();
52 | private List methodCalls = new LinkedList<>();
53 |
54 | public ClassVisitor(JavaClass jc)
55 | {
56 | clazz = jc;
57 | constants = new ConstantPoolGen(clazz.getConstantPool());
58 | classReferenceFormat = "C:" + clazz.getClassName() + " %s";
59 | }
60 |
61 | @Override
62 | public void visitJavaClass(JavaClass jc)
63 | {
64 | jc.getConstantPool().accept(this);
65 | Method[] methods = jc.getMethods();
66 | for (Method method : methods) {
67 | dynamicCallManager.retrieveCalls(method, jc);
68 | dynamicCallManager.linkCalls(method);
69 | method.accept(this);
70 | }
71 | }
72 |
73 | @Override
74 | public void visitConstantPool(ConstantPool constantPool)
75 | {
76 | for (int i = 0; i < constantPool.getLength(); i++) {
77 | Constant constant = constantPool.getConstant(i);
78 | if (constant == null) {
79 | continue;
80 | }
81 | if (constant.getTag() == 7) {
82 | String referencedClass = constantPool.constantToString(constant);
83 | // System.out.println(String.format(classReferenceFormat, referencedClass));
84 | }
85 | }
86 | }
87 |
88 | @Override
89 | public void visitMethod(Method method)
90 | {
91 | MethodGen mg = new MethodGen(method, clazz.getClassName(), constants);
92 | MethodVisitor visitor = new MethodVisitor(mg, clazz);
93 | methodCalls.addAll(visitor.start());
94 | }
95 |
96 | public ClassVisitor start()
97 | {
98 | visitJavaClass(clazz);
99 | return this;
100 | }
101 |
102 | public List methodCalls()
103 | {
104 | return this.methodCalls;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/coverage/JVMClassCoverage.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.coverage;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.IOException;
6 | import java.io.InputStreamReader;
7 | import java.util.ArrayList;
8 | import java.util.Arrays;
9 | import java.util.HashSet;
10 | import java.util.List;
11 | import java.util.Map;
12 | import java.util.Set;
13 |
14 | import org.apache.log4j.LogManager;
15 | import org.apache.maven.project.MavenProject;
16 |
17 | import se.kth.castor.jdbl.debloat.DebloatTypeEnum;
18 |
19 | public class JVMClassCoverage extends AbstractCoverage
20 | {
21 | public JVMClassCoverage(MavenProject mavenProject, File mavenHome, DebloatTypeEnum debloatTypeEnum)
22 | {
23 | super(mavenProject, mavenHome, debloatTypeEnum);
24 | LOGGER = LogManager.getLogger(JVMClassCoverage.class.getName());
25 | }
26 |
27 | protected UsageAnalysis executeTestBasedAnalysis()
28 | {
29 | try {
30 | writeCoverage();
31 | } catch (IOException e) {
32 | LOGGER.info("Error writing JVM usage analysis.");
33 | }
34 | UsageAnalysis usageAnalysis = new UsageAnalysis();
35 | Set classesLoaded = JVMClassesCoveredSingleton.INSTANCE.getClassesLoaded();
36 | for (String classLoaded : classesLoaded) {
37 | usageAnalysis.addEntry(classLoaded, new HashSet<>(Arrays.asList("UNKNOWN")));
38 | }
39 |
40 | return usageAnalysis;
41 | }
42 |
43 | public void writeCoverage() throws IOException
44 | {
45 | LOGGER.info("Starting executing tests in verbose mode to get JVM class loader report.");
46 | Set classesLoadedTestDebloat = new HashSet<>();
47 | List args = new ArrayList<>();
48 | args.add("mvn");
49 | args.add("surefire:test");
50 | args.add("-X");
51 | args.add("-DargLine=@{argLine}");
52 | args.add("-DargLine=-verbose:class");
53 | ProcessBuilder processBuilder = new ProcessBuilder(args);
54 | Map environment = processBuilder.environment();
55 | environment.put("JAVA_HOME", System.getenv().get("JAVA_HOME"));
56 | Process p = processBuilder.start();
57 | new Thread(() -> {
58 | BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
59 | String line;
60 | try {
61 | while ((line = input.readLine()) != null) {
62 | if ((line.contains("class,load") && line.endsWith("target/classes/")) ||
63 | (line.contains("[Loaded") && line.endsWith("target/classes/]"))) {
64 | classesLoadedTestDebloat.add(line.split(" ")[1]);
65 | }
66 | }
67 | } catch (IOException e) {
68 | LOGGER.error("Error parsing line.");
69 | }
70 | input = new BufferedReader(new InputStreamReader(p.getErrorStream()));
71 | try {
72 | while ((line = input.readLine()) != null) {
73 | LOGGER.info(line);
74 | }
75 | } catch (IOException e) {
76 | LOGGER.error("Error reading line.");
77 | }
78 | }).start();
79 |
80 | try {
81 | p.waitFor();
82 | } catch (InterruptedException e) {
83 | Thread.currentThread().interrupt();
84 | }
85 |
86 | // print info about the number of classes loaded
87 | JVMClassesCoveredSingleton.INSTANCE.setClassesLoaded(classesLoadedTestDebloat);
88 | }
89 |
90 | protected UsageAnalysis executeConservativeAnalysis()
91 | {
92 | // TODO implement the conservative approach
93 | return null;
94 | }
95 |
96 | protected UsageAnalysis executeEntryPointAnalysis()
97 | {
98 | // TODO implement the entry point approach
99 | LOGGER.info("Output directory: " + mavenProject.getBuild().getOutputDirectory());
100 | LOGGER.info("entryClass: " + entryClass);
101 | LOGGER.info("entryMethod: " + entryMethod);
102 | LOGGER.info("entryParameters: " + entryParameters);
103 | return null;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/jdbl-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 | 4.0.0
6 |
7 | se.kth.castor
8 | jdbl-core
9 | 1.0.0
10 |
11 |
12 | se.kth.castor
13 | jdbl-parent-pom
14 | 1.0.0
15 | ../
16 |
17 |
18 | jdbl-core
19 | https://github.com/castor-software/royal-debloat/jdbl/jdbl-maven-plugin
20 |
21 |
22 | UTF-8
23 | 11
24 | 11
25 |
26 |
27 |
28 |
29 |
30 | org.apache.maven
31 | maven-core
32 | 3.6.0
33 |
34 |
35 | org.apache.maven
36 | maven-plugin-api
37 | 3.6.0
38 |
39 |
40 | org.apache.maven.shared
41 | maven-invoker
42 | 3.0.1
43 |
44 |
45 |
46 |
47 | org.ow2.asm
48 | asm
49 | 9.3
50 |
51 |
52 | org.ow2.asm
53 | asm-util
54 | 9.3
55 |
56 |
57 | org.apache.bcel
58 | bcel
59 | 6.3.1
60 | compile
61 |
62 |
63 |
64 |
65 | org.apache.logging.log4j
66 | log4j-api
67 | 2.13.1
68 |
69 |
70 | org.apache.logging.log4j
71 | log4j-core
72 | 2.13.2
73 |
74 |
75 | log4j
76 | log4j
77 | 1.2.17
78 |
79 |
80 |
81 |
82 | com.fasterxml.jackson.core
83 | jackson-databind
84 | 2.10.0.pr1
85 |
86 |
87 | se.kth.castor
88 | yajta-lean
89 | 2.0.2
90 |
91 |
92 |
93 |
94 | org.jacoco
95 | org.jacoco.core
96 | 0.8.5
97 |
98 |
99 | org.jacoco
100 | org.jacoco.report
101 | 0.8.5
102 |
103 |
104 |
105 |
106 | com.sun.tdk
107 | jcov
108 | 1.0
109 |
110 |
111 |
112 |
113 |
114 |
115 | org.junit.jupiter
116 | junit-jupiter-engine
117 | 5.6.1
118 | test
119 |
120 |
121 | org.junit.platform
122 | junit-platform-runner
123 | 1.6.1
124 | test
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/main/java/se/kth/castor/jdbl/plugin/EntryPointDebloatMojo.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.plugin;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.util.HashSet;
6 | import java.util.Objects;
7 | import java.util.Set;
8 |
9 | import org.apache.maven.plugins.annotations.LifecyclePhase;
10 | import org.apache.maven.plugins.annotations.Mojo;
11 | import org.apache.maven.plugins.annotations.Parameter;
12 |
13 | import se.kth.castor.jdbl.coverage.AbstractCoverage;
14 | import se.kth.castor.jdbl.coverage.JVMClassesCoveredSingleton;
15 | import se.kth.castor.jdbl.coverage.JacocoCoverage;
16 | import se.kth.castor.jdbl.coverage.UsageAnalysis;
17 | import se.kth.castor.jdbl.debloat.AbstractMethodDebloat;
18 | import se.kth.castor.jdbl.debloat.DebloatTypeEnum;
19 | import se.kth.castor.jdbl.debloat.EntryPointMethodDebloat;
20 | import se.kth.castor.jdbl.util.JarUtils;
21 | import se.kth.castor.jdbl.util.MavenUtils;
22 | import se.kth.castor.jdbl.util.MyFileUtils;
23 |
24 | /**
25 | * This Maven mojo instruments the project according to an entry point provided as parameters in Maven configuration.
26 | * Probes are inserted in order to keep track of the classes and methods used.
27 | * Non covered elements are removed from the final bundled jar file.
28 | */
29 | @Mojo(name = "entry-point-debloat", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, threadSafe = true)
30 | public class EntryPointDebloatMojo extends AbstractDebloatMojo
31 | {
32 | @Parameter(property = "entry.class", name = "entryClass", required = true)
33 | private String entryClass = "";
34 |
35 | @Parameter(property = "entry.method", name = "entryMethod", required = true)
36 | private String entryMethod = "";
37 |
38 | @Parameter(property = "entry.parameters", name = "entryParameters", defaultValue = " ")
39 | private String entryParameters = null;
40 |
41 | @Override
42 | public void doExecute()
43 | {
44 | printCustomStringToConsole("S T A R T I N G E N T R Y P O I N T D E B L O A T");
45 |
46 | String outputDirectory = getProject().getBuild().getOutputDirectory();
47 | File baseDir = getProject().getBasedir();
48 |
49 | MavenUtils mavenUtils = new MavenUtils(getMavenHome(), baseDir);
50 |
51 | // copy the dependencies
52 | mavenUtils.copyRuntimeDependencies(outputDirectory);
53 |
54 | // copy the resources
55 | mavenUtils.copyResources(outputDirectory);
56 |
57 | // decompress the copied dependencies
58 | JarUtils.decompressJars(outputDirectory);
59 |
60 | // getting the used methods
61 | AbstractCoverage jacocoCoverage = new JacocoCoverage(
62 | getProject(),
63 | mavenHome,
64 | DebloatTypeEnum.ENTRY_POINT_DEBLOAT,
65 | this.entryClass,
66 | this.entryMethod,
67 | this.entryParameters);
68 |
69 | UsageAnalysis usageAnalysis = null;
70 |
71 | // run the usage analysis
72 | usageAnalysis = jacocoCoverage.analyzeUsages();
73 | // print some results
74 | this.getLog().info(String.format("#Unused classes: %d",
75 | usageAnalysis.getAnalysis().entrySet().stream().filter(e -> e.getValue() == null).count()));
76 | this.getLog().info(String.format("#Unused methods: %d",
77 | usageAnalysis.getAnalysis().values().stream().filter(Objects::nonNull).mapToInt(Set::size).sum()));
78 |
79 | // remove unused classes
80 | MyFileUtils myFileUtils = new MyFileUtils(outputDirectory, new HashSet<>(),
81 | JVMClassesCoveredSingleton.INSTANCE.getClassesLoaded(),
82 | getProject().getBasedir().getAbsolutePath(), null);
83 | try {
84 | myFileUtils.deleteUnusedClasses(outputDirectory);
85 | } catch (IOException e) {
86 | this.getLog().error(String.format("Error deleting unused classes: %s", e));
87 | }
88 |
89 | // remove unused methods
90 | AbstractMethodDebloat entryPointMethodDebloat = new EntryPointMethodDebloat(outputDirectory,
91 | usageAnalysis,
92 | getProject().getBasedir().getAbsolutePath() + "/.jdbl/debloat-report.csv");
93 | try {
94 | entryPointMethodDebloat.removeUnusedMethods();
95 | } catch (IOException e) {
96 | this.getLog().error(String.format("Error: %s", e));
97 | }
98 | printCustomStringToConsole("E N T R Y P O I N T D E B L O A T F I N I S H E D");
99 | }
100 | }
101 |
102 |
103 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/it/dummy-project/src/main/java/common/Order.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Gareth Jon Lynch
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | * this software and associated documentation files (the "Software"), to deal in
8 | * the Software without restriction, including without limitation the rights to
9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | * the Software, and to permit persons to whom the Software is furnished to do so,
11 | * subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | package common;
25 |
26 | import java.math.BigDecimal;
27 | import java.util.UUID;
28 |
29 | /**
30 | * Models an order for placing on an exchange.
31 | *
32 | * @author gazbert
33 | */
34 | public class Order {
35 |
36 | /**
37 | * Defines the martket ids.
38 | *
39 | * @author gazbert
40 | */
41 | public enum Market {
42 | /**
43 | * Dollar
44 | */
45 | USD,
46 |
47 | /**
48 | * Yuan
49 | */
50 | CNY,
51 |
52 | /**
53 | * Euro
54 | */
55 | EUR
56 | }
57 |
58 | /**
59 | * Defines the types of order.
60 | *
61 | * @author gazbert
62 | */
63 | public enum Type {
64 | /**
65 | * BUY order
66 | */
67 | BUY,
68 |
69 | /**
70 | * SELL order
71 | */
72 | SELL
73 | }
74 |
75 | /**
76 | * Id
77 | */
78 | private UUID id;
79 |
80 | /**
81 | * Market id
82 | */
83 | private Market marketId;
84 |
85 | /**
86 | * Type of order BUY|SELL
87 | */
88 | private Type type;
89 |
90 | /**
91 | * Amount of units in the order
92 | */
93 | private BigDecimal amount;
94 |
95 | /**
96 | * Price per unit of order
97 | */
98 | private BigDecimal price;
99 |
100 | /**
101 | * Fee
102 | */
103 | private BigDecimal fee;
104 |
105 | /**
106 | * Number of trades it took to fill the order
107 | */
108 | private int tradeCountToFill;
109 |
110 |
111 | /**
112 | * Constructor builds an order.
113 | *
114 | * @param marketId the market id.
115 | * @param type the order type
116 | * @param amount the order amount.
117 | * @param price the order price.
118 | * @param fee the order fee.
119 | */
120 | public Order(Market marketId, Type type, BigDecimal amount, BigDecimal price, BigDecimal fee) {
121 |
122 | this.marketId = marketId;
123 | this.type = type;
124 | this.amount = amount;
125 | this.price = price;
126 | this.fee = fee;
127 |
128 | id = UUID.randomUUID();
129 | }
130 |
131 | public UUID getId() {
132 | return id;
133 | }
134 |
135 | public Market getMarketId() {
136 | return marketId;
137 | }
138 |
139 | public Type getType() {
140 | return type;
141 | }
142 |
143 | public BigDecimal getAmount() {
144 | return amount;
145 | }
146 |
147 | public BigDecimal getPrice() {
148 | return price;
149 | }
150 |
151 | public BigDecimal getFee() {
152 | return fee;
153 | }
154 |
155 | /**
156 | * Returns the order details required for auditing.
157 | *
158 | * @return audit details.
159 | */
160 | public String provideAuditDetails() {
161 | // keep it simple for the demo.
162 | return "OrderId: " + id + " Market: " + marketId + " Amount: " + amount + " Price: " + price;
163 | }
164 |
165 | public int getTradeCountToFill() {
166 | return tradeCountToFill;
167 | }
168 |
169 | public void setTradeCountToFill(int tradeCountToFill) {
170 | this.tradeCountToFill = tradeCountToFill;
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/jcov/report.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 0=8;4=9;9=10;14=11;
39 |
40 |
41 |
42 |
43 |
44 |
45 | 0=15;
46 |
47 |
48 |
49 |
50 |
51 |
52 | 0=20;
53 |
54 |
55 |
56 |
57 |
58 |
59 | 0=25;
60 |
61 |
62 |
63 |
64 |
65 |
66 | 0=29;
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | 0=3;
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | 0=5;5=6;
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | 0=3;
94 |
95 |
96 |
97 |
98 |
99 |
100 | 0=6;20=7;50=8;
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/deptree/AbstractParser.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.deptree;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public abstract class AbstractParser implements Parser
7 | {
8 | /**
9 | * Parses a string representing a Maven artifact in standard notation.
10 | *
11 | * @param artifact the artifact
12 | * @return an instance of {@link Node} representing the artifact.
13 | */
14 | protected Node parseArtifactString(final String artifact)
15 | {
16 | final List tokens = new ArrayList<>(7);
17 | int tokenStart = 0;
18 | boolean tokenStarted = false;
19 | boolean hasDescription = false;
20 | boolean omitted = false;
21 | int tokenEnd = 0;
22 | for (; tokenEnd < artifact.length(); tokenEnd++) {
23 | final char c = artifact.charAt(tokenEnd);
24 | switch (c) {
25 | case ' ': // in descriptions only
26 | if (tokenStarted && !hasDescription) {
27 | tokens.add(artifact.substring(tokenStart, tokenEnd));
28 | tokenStarted = false;
29 | hasDescription = true;
30 | }
31 | continue;
32 |
33 | case ':':
34 | case ')': //end of descriptions and omitted artifacts
35 | tokens.add(artifact.substring(tokenStart, tokenEnd));
36 | tokenStarted = false;
37 | continue;
38 |
39 | case '-': // in omitted artifacts descriptions
40 | continue;
41 |
42 | case '(': // in omitted artifacts
43 | if (tokenEnd == 0) {
44 | omitted = true;
45 | }
46 | continue;
47 |
48 | default:
49 | if (!tokenStarted) {
50 | tokenStart = tokenEnd;
51 | tokenStarted = true;
52 | }
53 | continue;
54 | }
55 | }
56 |
57 | //last token
58 | if (tokenStarted) {
59 | tokens.add(artifact.substring(tokenStart, tokenEnd));
60 | }
61 |
62 | String groupId;
63 | String artifactId;
64 | String packaging;
65 | String classifier;
66 | String version;
67 | String scope;
68 | String description;
69 |
70 | if (tokens.size() == 3) {
71 | groupId = tokens.get(0);
72 | artifactId = tokens.get(1);
73 | version = tokens.get(2);
74 | packaging = null;
75 | scope = null;
76 | description = null;
77 | classifier = null;
78 |
79 | } else if (tokens.size() == 4) {
80 |
81 | groupId = tokens.get(0);
82 | artifactId = tokens.get(1);
83 | packaging = tokens.get(2);
84 | version = tokens.get(3);
85 | scope = null;
86 | description = null;
87 | classifier = null;
88 |
89 | } else if (tokens.size() == 5) {
90 |
91 | groupId = tokens.get(0);
92 | artifactId = tokens.get(1);
93 | packaging = tokens.get(2);
94 | version = tokens.get(3);
95 | scope = tokens.get(4);
96 | description = null;
97 | classifier = null;
98 |
99 | } else if (tokens.size() == 6) {
100 |
101 | if (hasDescription) {
102 | groupId = tokens.get(0);
103 | artifactId = tokens.get(1);
104 | packaging = tokens.get(2);
105 | version = tokens.get(3);
106 | scope = tokens.get(4);
107 | description = tokens.get(5);
108 | classifier = null;
109 | } else {
110 | groupId = tokens.get(0);
111 | artifactId = tokens.get(1);
112 | packaging = tokens.get(2);
113 | classifier = tokens.get(3);
114 | version = tokens.get(4);
115 | scope = tokens.get(5);
116 | description = null;
117 | }
118 |
119 | } else if (tokens.size() == 7) {
120 | groupId = tokens.get(0);
121 | artifactId = tokens.get(1);
122 | packaging = tokens.get(2);
123 | classifier = tokens.get(3);
124 | version = tokens.get(4);
125 | scope = tokens.get(5);
126 | description = tokens.get(6);
127 |
128 | } else {
129 | throw new IllegalStateException("Wrong number of tokens: " + tokens.size() + " for artifact: " + artifact);
130 | }
131 |
132 | return new Node(groupId, artifactId, packaging, classifier, version, scope, description, omitted);
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/calc/inst/report/Calc.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tests coverage
5 |
6 |
7 |
52 |
53 |
54 |
62 |
63 |
64 |
65 |
66 | %method
67 | %block
68 | %branch
69 | %line
70 |
71 |
72 | Calc
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | hit count
84 | method name
85 | method modifiers
86 | method signature
87 |
88 |
89 | 1
90 | <init>
91 | [public]
92 | void <init>(int,int)
93 |
94 |
95 | 0
96 | rest
97 | [public]
98 | int rest()
99 |
100 |
101 | 1
102 | sum
103 | [public]
104 | int sum()
105 |
106 |
107 |
108 |
109 |
Report generated 6/30/20 1:00 AM
110 |
111 |
112 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/calc/inst/report/Main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | tests coverage
5 |
6 |
7 |
52 |
53 |
54 |
62 |
63 |
64 |
65 |
66 | %method
67 | %block
68 | %branch
69 | %line
70 |
71 |
72 | Main
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | hit count
84 | method name
85 | method modifiers
86 | method signature
87 |
88 |
89 | 0
90 | <init>
91 | [public]
92 | void <init>()
93 |
94 |
95 | 0
96 | anotherSum
97 | [private, static]
98 | int anotherSum(int,int)
99 |
100 |
101 | 1
102 | main
103 | [public, static]
104 | void main(java.lang.String[])
105 |
106 |
107 |
108 |
109 |
Report generated 6/30/20 1:00 AM
110 |
111 |
112 |
--------------------------------------------------------------------------------
/jdbl-maven-plugin/src/it/dummy-project/src/main/java/lambda/LambdaScoping.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2015 Gareth Jon Lynch
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of
7 | * this software and associated documentation files (the "Software"), to deal in
8 | * the Software without restriction, including without limitation the rights to
9 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
10 | * the Software, and to permit persons to whom the Software is furnished to do so,
11 | * subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all
14 | * copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 |
24 | package lambda;
25 |
26 | import java.util.function.Consumer;
27 |
28 | /**
29 | * Demonstrates lambda scoping. Based on the example in the Java 8 tutorial.
30 | *
31 | * Lambdas capture variables like local and anonymous classes. In other words, they have the same access to
32 | * local variables of the enclosing scope.
33 | *
34 | * One key difference though, is that lambdas do not suffer from shadowing issues, e.g. where a local class
35 | * has the same variable and that is used in place of/hides the variable in the outer scope.
36 | *
37 | * Lambda expressions are lexically scoped: they do not inherit any names from a supertype or introduce a new level of
38 | * scoping.
39 | *
40 | * See the example below.
41 | *
42 | * @author gazbert
43 | */
44 | public class LambdaScoping
45 | {
46 |
47 | /**
48 | * Outermost level scoped x arg
49 | */
50 | public int x = 1; // 3 // change this to 3 and LambdaScoping.this.x sysout will print 3
51 |
52 | /**
53 | * First level nested class.
54 | *
55 | * @author gazbert
56 | */
57 | class FirstLevel
58 | {
59 | /**
60 | * First level scoped x arg
61 | */
62 | public int x = 2; // 5 // change this to 5 and this.x sysout will print 5
63 |
64 | /**
65 | * x arg is passed in by the caller.
66 | *
67 | * @param x
68 | */
69 | void methodInFirstLevel(int x)
70 | {
71 | /*
72 | * The following statement causes the compiler to generate
73 | * the error "Local variable x defined in an enclosing scope must be final or effectively final"
74 | *
75 | * Like local and anonymous classes, a lambda expression can only access local variables and parameters of
76 | * the enclosing block that are final or effectively final.
77 | */
78 | //x = 99;
79 |
80 | /*
81 | * Our lambda uses JDK standard Consumer functional interface.
82 | * More details on standard functions here:
83 | * http://download.java.net/lambda/b81/docs/api/java/util/function/package-summary.html
84 | *
85 | * If you change y arg to be x arg, compiler will error "Lambda expression's parameter x cannot redeclare
86 | * another local variable defined in an enclosing scope."
87 | */
88 | Consumer myConsumer = (y) -> {
89 |
90 | // x is the arg value passed into the first level methodInFirstLevel().
91 | System.out.println("x = " + x); // prints 10
92 |
93 | // y is the value passed into the accept() method called by the methodInFirstLevel() method.
94 | System.out.println("y = " + y); // prints 10
95 |
96 | // Lambda can access the x instance variable in first level nested class using 'this'
97 | System.out.println("this.x = " + this.x); // prints 2
98 |
99 | // Lambda can access the x intance variable in the outermost scope
100 | System.out.println("LambdaScoping.this.x = " + LambdaScoping.this.x); // prints 1
101 | };
102 |
103 | // Call the lambda expression. The x arg beomes the the y arg in the lambda
104 | myConsumer.accept(x); // 15 // set this to 15 and y sysout will print 15
105 | }
106 | }
107 |
108 | /**
109 | * Runs the scoping test.
110 | *
111 | * @param args
112 | */
113 | public static void main(String... args)
114 | {
115 | final LambdaScoping outerClass = new LambdaScoping();
116 | final LambdaScoping.FirstLevel firstLevelClass = outerClass.new FirstLevel();
117 | firstLevelClass.methodInFirstLevel(10);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/jdbl-core/src/test/resources/coverage/report_jacoco.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/jdbl-core/src/main/java/se/kth/castor/jdbl/coverage/JCovReportReader.java:
--------------------------------------------------------------------------------
1 | package se.kth.castor.jdbl.coverage;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.io.StringReader;
6 | import java.util.HashSet;
7 |
8 | import org.w3c.dom.Document;
9 | import org.w3c.dom.Node;
10 | import org.w3c.dom.NodeList;
11 | import org.xml.sax.InputSource;
12 | import org.xml.sax.SAXException;
13 |
14 | import javax.xml.parsers.DocumentBuilder;
15 | import javax.xml.parsers.DocumentBuilderFactory;
16 | import javax.xml.parsers.ParserConfigurationException;
17 |
18 | public class JCovReportReader
19 | {
20 | private UsageAnalysis usageAnalysis;
21 | private final DocumentBuilder dBuilder;
22 |
23 | public JCovReportReader() throws ParserConfigurationException
24 | {
25 | DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
26 |
27 | dBuilder = dbFactory.newDocumentBuilder();
28 | // Ignore the lack of DTD
29 | dBuilder.setEntityResolver((publicId, systemId) -> {
30 | if (systemId.contains(".dtd")) {
31 | return new InputSource(new StringReader(""));
32 | } else {
33 | return null;
34 | }
35 | });
36 | }
37 |
38 | /**
39 | * Return a collection of used classes and used methods organized as following:
40 | * Map: class fullyQualifiedName -> Set< used method qualifier > if the class contains covered methods
41 | * method qualifier = methodSimpleName + descriptor
42 | * descriptor = (paramTypes;*)returnType
43 | * ex: method "contains(Ljava/lang/Object;)Z" is named contains and take an object as parameter and return a boolean
44 | */
45 | public UsageAnalysis getUsedClassesAndMethods(File jcovXMLReport) throws IOException, SAXException
46 | {
47 | usageAnalysis = new UsageAnalysis();
48 | Document doc = dBuilder.parse(jcovXMLReport);
49 | doc.getDocumentElement().normalize();
50 |
51 | NodeList packages = doc.getElementsByTagName("package");
52 | for (int i = 0; i < packages.getLength(); i++) {
53 | visitPackage(packages.item(i));
54 | }
55 |
56 | // Remove all classes that do not contain any covered method
57 | usageAnalysis.removeUncoveredClasses();
58 |
59 | return usageAnalysis;
60 | }
61 |
62 | private void visitPackage(Node p)
63 | {
64 | String packageName = p.getAttributes().getNamedItem("name").getNodeValue().replace(".", "/");
65 | NodeList classes = p.getChildNodes();
66 | for (int i = 0; i < classes.getLength(); i++) {
67 | Node n = classes.item(i);
68 | if (n.getNodeName().equals("class")) {
69 | visitClass(n, packageName);
70 | }
71 | }
72 | }
73 |
74 | private void visitClass(Node c, String packageName)
75 | {
76 | NodeList methods = c.getChildNodes();
77 |
78 | String className = c.getAttributes().getNamedItem("name").getNodeValue();
79 | usageAnalysis.addEntry(packageName + "/" + className, new HashSet<>());
80 | for (int i = 0; i < methods.getLength(); i++) {
81 | Node n = methods.item(i);
82 | if (!n.getNodeName().equals("meth")) {
83 | continue;
84 | }
85 | visitMethod(n, packageName);
86 | }
87 | }
88 |
89 | private void visitMethod(Node m, String packageName)
90 | {
91 | if (!isCovered(m, "methenter")) {
92 | return;
93 | }
94 | String desc = m.getAttributes().getNamedItem("name").getNodeValue() +
95 | m.getAttributes().getNamedItem("vmsig").getNodeValue();
96 |
97 | // we add the method only if it is covered
98 | String className = m.getParentNode().getAttributes().getNamedItem("name").getNodeValue();
99 | usageAnalysis.methods(packageName + "/" + className).add(desc);
100 | }
101 |
102 | private boolean isCovered(Node m, String entity)
103 | {
104 | NodeList counters = m.getChildNodes();
105 |
106 | if (counters.getLength() == 0) {
107 | Node nodeCounter = null;
108 | nodeCounter = m.getAttributes().getNamedItem("count");
109 | if (nodeCounter == null) {
110 | return true;
111 | } else {
112 | return !nodeCounter.getNodeValue().equals("0");
113 | }
114 | } else {
115 |
116 | for (int i = 0; i < counters.getLength(); i++) {
117 | Node n = counters.item(i);
118 | if (!n.getNodeName().equals("bl")) {
119 | continue;
120 | }
121 |
122 | NodeList blList = n.getChildNodes();
123 | Node nodeCounter = null;
124 | for (int j = 0; j < blList.getLength(); ++j) {
125 | Node node = blList.item(j);
126 | if (node.getNodeName().equals(entity)) {
127 | nodeCounter = node.getAttributes().getNamedItem("count");
128 | break;
129 | }
130 | }
131 | if (nodeCounter == null) {
132 | continue;
133 | } else {
134 | return !nodeCounter.getNodeValue().equals("0");
135 | }
136 | }
137 | }
138 | return true;
139 | }
140 | }
141 |
--------------------------------------------------------------------------------