Understanding and Analyzing Java Reflection YUE LI',UNSW Sydney,Australia TIAN TAN',UNSW Sydney,Australia JINGLING XUE,UNSW Sydney,Australia Java reflection has been widely used in a variety of applications and frameworks.It allows a software system to inspect and change the behaviour of its classes,interfaces,methods and fields at runtime,enabling the software to adapt to dynamically changing runtime environments.However,this dynamic language feature imposes significant challenges to static analysis,because the behaviour of reflection-rich software is logically complex and statically hard to predict.As a result,existing static analysis tools either ignore reflection or handle it partially,resulting in missed,important behaviours,i.e.,unsound results.Therefore,improving or even achieving soundness in(static)reflection analysis-an analysis that infers statically the behaviour of reflective code-will provide significant benefits to many analysis clients,such as bug detectors,security analyzers and program verifiers. In this paper,we provide a comprehensive understanding of Java reflection through examining its underlying concept,API and real-world usage,and,building on this,we introduce a new static approach to resolving Java reflection effectively in practice.We have implemented our reflection analysis in an open-source tool,called SoLAR,and evaluated its effectiveness extensively with large Java programs and libraries.Our experimental results demonstrate that SoLAR is able to(1)resolve reflection more soundly than the state-of-the-art reflection analyses:(2)automatically and accurately identify the parts of the program where reflection is resolved unsoundly or imprecisely:and(3)guide users to iteratively refine the analysis results by using lightweight annotations until their specific requirements are satisfied. CCS Concepts:·Theory of computation→Program analysis;:·Software and its engineering→ Object oriented languages. Additional Key Words and Phrases:Java reflection,static analysis,points-to analysis ACM Reference Format: Yue Li,Tian Tan,and Jingling Xue.2019.Understanding and Analyzing Java Reflection.ACM Trans.Softw. Eng.Methodol.28,2,Article 7(February 2019),51 pages.https://doi.org/10.1145/nnnnnnn.nnnnnnn 1 INTRODUCTION Java reflection allows a software system to inspect and change the behaviour of its classes,interfaces, methods and fields at runtime,enabling the software to adapt to dynamically changing runtime environments.This dynamic language feature eases the development and maintenance of Java programs in many programming tasks by,for example,facilitating their flexible integration with the third-party code and their main behaviours to be configured according to a deployed runtime "This work was mostly done while these authors were at UNSW Sydney.Both authors are now affiliated with Aarhus University,Denmark. Authors'addresses:Yue Li,Aarhus University,Denmark,yueli@cs.au.dk;Tian Tan,Aarhus University,Denmark, tiantan@cs.au.dk;Jingling Xue,UNSW Sydney,Australia,jingling@cs.au.dk. Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page.Copyrights for components of this work owned by others than ACM must be honored. Abstracting with credit is permitted.To copy otherwise,or republish,to post on servers or to redistribute to lists,requires prior specific permission and/or a fee.Request permissions from permissions@acm.org. 2019 Association for Computing Machinery. 1049-331X/2019/02-ART7$15.00 https://doi.org/10.1145/nnnnnnn.nnnnnnn ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
7 Understanding and Analyzing Java Reflection YUE LI∗ , UNSW Sydney, Australia TIAN TAN∗ , UNSW Sydney, Australia JINGLING XUE, UNSW Sydney, Australia Java reflection has been widely used in a variety of applications and frameworks. It allows a software system to inspect and change the behaviour of its classes, interfaces, methods and fields at runtime, enabling the software to adapt to dynamically changing runtime environments. However, this dynamic language feature imposes significant challenges to static analysis, because the behaviour of reflection-rich software is logically complex and statically hard to predict. As a result, existing static analysis tools either ignore reflection or handle it partially, resulting in missed, important behaviours, i.e., unsound results. Therefore, improving or even achieving soundness in (static) reflection analysis—an analysis that infers statically the behaviour of reflective code—will provide significant benefits to many analysis clients, such as bug detectors, security analyzers and program verifiers. In this paper, we provide a comprehensive understanding of Java reflection through examining its underlying concept, API and real-world usage, and, building on this, we introduce a new static approach to resolving Java reflection effectively in practice. We have implemented our reflection analysis in an open-source tool, called Solar, and evaluated its effectiveness extensively with large Java programs and libraries. Our experimental results demonstrate that Solar is able to (1) resolve reflection more soundly than the state-of-the-art reflection analyses; (2) automatically and accurately identify the parts of the program where reflection is resolved unsoundly or imprecisely; and (3) guide users to iteratively refine the analysis results by using lightweight annotations until their specific requirements are satisfied. CCS Concepts: • Theory of computation → Program analysis; • Software and its engineering → Object oriented languages. Additional Key Words and Phrases: Java reflection, static analysis, points-to analysis ACM Reference Format: Yue Li, Tian Tan, and Jingling Xue. 2019. Understanding and Analyzing Java Reflection. ACM Trans. Softw. Eng. Methodol. 28, 2, Article 7 (February 2019), 51 pages. https://doi.org/10.1145/nnnnnnn.nnnnnnn 1 INTRODUCTION Java reflection allows a software system to inspect and change the behaviour of its classes, interfaces, methods and fields at runtime, enabling the software to adapt to dynamically changing runtime environments. This dynamic language feature eases the development and maintenance of Java programs in many programming tasks by, for example, facilitating their flexible integration with the third-party code and their main behaviours to be configured according to a deployed runtime ∗This work was mostly done while these authors were at UNSW Sydney. Both authors are now affiliated with Aarhus University, Denmark. Authors’ addresses: Yue Li, Aarhus University, Denmark, yueli@cs.au.dk; Tian Tan, Aarhus University, Denmark, tiantan@cs.au.dk; Jingling Xue, UNSW Sydney, Australia, jingling@cs.au.dk. Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights for components of this work owned by others than ACM must be honored. Abstracting with credit is permitted. To copy otherwise, or republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Request permissions from permissions@acm.org. © 2019 Association for Computing Machinery. 1049-331X/2019/02-ART7 $15.00 https://doi.org/10.1145/nnnnnnn.nnnnnnn ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
7:2 Yue Li,Tian Tan,and Jingling Xue environment in a decoupled way.Due to such advantages,reflection has been widely used in a variety of Java applications and frameworks [32,69]. Static analysis is widely recognized as a fundamental tool for bug detection [17,43],security vulnerability analysis [1,36],compiler optimization [14,59],program verification [4,12],and program debugging and understanding [34,58].However,when applying static analysis to Java programs,reflection poses a major obstacle [32,33,38,51].If the behavior of reflective code is not resolved well,much of the codebase will be rendered invisible for static analysis,resulting in missed,important behaviours,i.e.,unsound analysis results [37].Therefore,improving or even achieving soundness in (static)reflection analysis-an analysis that infers statically the behavior of reflective code-will provide significant benefits to all the client analyses as just mentioned above. 1.1 Challenges Developing effective reflection analysis for real-world programs remains a hard problem,widely acknowledged by the static analysis community [37]: "Reflection usage and the size of libraries/frameworks make it very difficult to scale points-to analysis to modern Java programs."[64]; "Reflection makes it difficult to analyze statically."[48]; "In our experience [18],the largest challenge to analyzing Android apps is their use of reflection.”[2]: "Static analysis of object-oriented code is an exciting,ongoing and challenging research area,made especially challenging by dynamic language features,a.k.a.reflection."[26]. There are three reasons on why it is hard to untangle this knotty problem: The Java reflection API is large and its common uses in Java programs are complex.It remains unclear how an analysis should focus on its effort on analyzing which of its reflection methods in order to achieve some analysis results as desired. The dynamic behaviours of reflective calls are mainly specified by their string arguments, which are usually unknown statically(e.g.,with some string values being encrypted,read from configuration files,or retrieved from the Internet). The reflective code in a Java program cannot be analyzed alone in isolation.To resolve reflective calls adequately,a reflection analysis often works inter-dependently with a pointer analysis [32,33,38,50,51],with each being both the producer and consumer of the other. When some reflective calls are not yet resolved,the pointer information that is currently available can be over-or under-approximate.Care must be taken to ensure that the reflection analysis helps increase soundness(coverage)while still maintaining sufficient precision for the pointer analysis.Otherwise,the combined analysis would be unscalable for large programs. As a result,most of the papers on static analysis for object-oriented languages,like Java,treat reflection orthogonally (often without even mentioning its existence).Existing static analysis tools either ignore reflection or handle it partially and ineffectively. 1.2 Previous Approaches Initially,reflection analysis mainly relies on string analysis,especially when the string arguments to reflective calls are string constants,to resolve reflective targets,i.e.,methods or fields reflectively accessed.Currently,this mainstream approach is still adopted by many static analysis tools for Java, such as SooT,WALA,CHORD and DooP.However,as described in Section 1.1,string analysis will fail in many situations where string arguments are unknown(Figure 5),resulting in limited soundness ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
7:2 Yue Li, Tian Tan, and Jingling Xue environment in a decoupled way. Due to such advantages, reflection has been widely used in a variety of Java applications and frameworks [32, 69]. Static analysis is widely recognized as a fundamental tool for bug detection [17, 43], security vulnerability analysis [1, 36], compiler optimization [14, 59], program verification [4, 12], and program debugging and understanding [34, 58]. However, when applying static analysis to Java programs, reflection poses a major obstacle [32, 33, 38, 51]. If the behavior of reflective code is not resolved well, much of the codebase will be rendered invisible for static analysis, resulting in missed, important behaviours, i.e., unsound analysis results [37]. Therefore, improving or even achieving soundness in (static) reflection analysis—an analysis that infers statically the behavior of reflective code—will provide significant benefits to all the client analyses as just mentioned above. 1.1 Challenges Developing effective reflection analysis for real-world programs remains a hard problem, widely acknowledged by the static analysis community [37]: “Reflection usage and the size of libraries/frameworks make it very difficult to scale points-to analysis to modern Java programs.” [64]; “Reflection makes it difficult to analyze statically.” [48]; “In our experience [18], the largest challenge to analyzing Android apps is their use of reflection ...” [2]; “Static analysis of object-oriented code is an exciting, ongoing and challenging research area, made especially challenging by dynamic language features, a.k.a. reflection.” [26]. There are three reasons on why it is hard to untangle this knotty problem: • The Java reflection API is large and its common uses in Java programs are complex. It remains unclear how an analysis should focus on its effort on analyzing which of its reflection methods in order to achieve some analysis results as desired. • The dynamic behaviours of reflective calls are mainly specified by their string arguments, which are usually unknown statically (e.g., with some string values being encrypted, read from configuration files, or retrieved from the Internet). • The reflective code in a Java program cannot be analyzed alone in isolation. To resolve reflective calls adequately, a reflection analysis often works inter-dependently with a pointer analysis [32, 33, 38, 50, 51], with each being both the producer and consumer of the other. When some reflective calls are not yet resolved, the pointer information that is currently available can be over- or under-approximate. Care must be taken to ensure that the reflection analysis helps increase soundness (coverage) while still maintaining sufficient precision for the pointer analysis. Otherwise, the combined analysis would be unscalable for large programs. As a result, most of the papers on static analysis for object-oriented languages, like Java, treat reflection orthogonally (often without even mentioning its existence). Existing static analysis tools either ignore reflection or handle it partially and ineffectively. 1.2 Previous Approaches Initially, reflection analysis mainly relies on string analysis, especially when the string arguments to reflective calls are string constants, to resolve reflective targets, i.e., methods or fields reflectively accessed. Currently, this mainstream approach is still adopted by many static analysis tools for Java, such as Soot, Wala, Chord and Doop. However, as described in Section 1.1, string analysis will fail in many situations where string arguments are unknown (Figure 5), resulting in limited soundness ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
Understanding and Analyzing Java Reflection 7:3 and precision.As a static analysis,a(more)sound reflection analysis is one that allows (more) true reflective targets(i.e.,targets that are actually accessed at runtime)to be resolved statically. In practice,any reflection analysis must inevitably make a trade-off among soundness,precision, scalability,and(sometimes)automation. In addition,existing reflection analyses [2,8,20,22,28,32,35,38,51,68]cannot answer two critical questions that are raised naturally,in practice:Q(1)how sound is a given reflection analysis and Q(2)which reflective calls are resolved unsoundly or imprecisely?We argue for their importance as follows: If Q(1)is unanswered,users would be unsure (or lose confidence)about the effectiveness of the analysis results produced.For example,a bug detector that reports no bugs may actually miss many bugs if some reflective calls are resolved unsoundly. If Q(2)is unanswered,users would not have an opportunity to contribute in improving the precision and soundness of the analysis results,e.g.,by providing some user annotations.For some client analyses (e.g.,verification),soundness is required. 1.3 Contributions In this paper,we attempt to uncover the mysterious veil of Java reflection and change the informed opinion in the program analysis community about static reflection analysis:"Java reflection is a dynamic feature which is nearly impossible to handle effectively in static analysis".Specifically,we make the following contributions: We provide a comprehensive understanding of Java reflection through examining its under- lying concept (what it is),interface (how its API is designed),and real-world usage (how it is used in practice).As a result,we will provide the answers to several critical questions,which are somewhat related,including: What is reflection,why is it introduced in programming languages,and how is Java reflection derived from the basic reflection concept? Which methods of the Java reflection API should be analyzed carefully and how are they related,as the API is large and complex(with about 200 methods)? How is reflection used in real-world Java programs and what can we learn from its common uses?We have conducted a comprehensive study about reflection usage in a set of 16 representative Java programs by examining their 1,423 reflective call sites.We report 7 useful findings to enable the development of improved practical reflection analysis techniques and tools in future research. We introduce a new static analysis approach,called SoLAR (soundness-guided reflection analy- sis),to resolve Java reflection effectively in practice.As shown in Figure 1,SoLAR has three unique advantages compared with previous work: SoLAR is able to yield significantly more sound results than the state-of-the-art reflec- tion analysis.In addition,SoLAR allows its soundness to be reasoned about when some reasonable assumptions are met. SoLAR is able to accurately identify the parts of the program where reflection is analyzed unsoundly or imprecisely,making it possible for users to be aware of the effectiveness of their analysis results(as discussed in Section 1.2). -SoLAR provides a mechanism to guide users to iteratively refine the analysis results by adding lightweight annotations until their specific requirements are satisfied,enabling reflection to be analyzed in a controlled manner. ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
Understanding and Analyzing Java Reflection 7:3 and precision. As a static analysis, a (more) sound reflection analysis is one that allows (more) true reflective targets (i.e., targets that are actually accessed at runtime) to be resolved statically. In practice, any reflection analysis must inevitably make a trade-off among soundness, precision, scalability, and (sometimes) automation. In addition, existing reflection analyses [2, 8, 20, 22, 28, 32, 35, 38, 51, 68] cannot answer two critical questions that are raised naturally, in practice: Q(1) how sound is a given reflection analysis and Q(2) which reflective calls are resolved unsoundly or imprecisely? We argue for their importance as follows: • If Q(1) is unanswered, users would be unsure (or lose confidence) about the effectiveness of the analysis results produced. For example, a bug detector that reports no bugs may actually miss many bugs if some reflective calls are resolved unsoundly. • If Q(2) is unanswered, users would not have an opportunity to contribute in improving the precision and soundness of the analysis results, e.g., by providing some user annotations. For some client analyses (e.g., verification), soundness is required. 1.3 Contributions In this paper, we attempt to uncover the mysterious veil of Java reflection and change the informed opinion in the program analysis community about static reflection analysis: “Java reflection is a dynamic feature which is nearly impossible to handle effectively in static analysis”. Specifically, we make the following contributions: • We provide a comprehensive understanding of Java reflection through examining its underlying concept (what it is), interface (how its API is designed), and real-world usage (how it is used in practice). As a result, we will provide the answers to several critical questions, which are somewhat related, including: – What is reflection, why is it introduced in programming languages, and how is Java reflection derived from the basic reflection concept? – Which methods of the Java reflection API should be analyzed carefully and how are they related, as the API is large and complex (with about 200 methods)? – How is reflection used in real-world Java programs and what can we learn from its common uses? We have conducted a comprehensive study about reflection usage in a set of 16 representative Java programs by examining their 1,423 reflective call sites. We report 7 useful findings to enable the development of improved practical reflection analysis techniques and tools in future research. • We introduce a new static analysis approach, called Solar (soundness-guided reflection analysis), to resolve Java reflection effectively in practice. As shown in Figure 1, Solar has three unique advantages compared with previous work: – Solar is able to yield significantly more sound results than the state-of-the-art reflection analysis. In addition, Solar allows its soundness to be reasoned about when some reasonable assumptions are met. – Solar is able to accurately identify the parts of the program where reflection is analyzed unsoundly or imprecisely, making it possible for users to be aware of the effectiveness of their analysis results (as discussed in Section 1.2). – Solar provides a mechanism to guide users to iteratively refine the analysis results by adding lightweight annotations until their specific requirements are satisfied, enabling reflection to be analyzed in a controlled manner. ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
7:4 Yue Li,Tian Tan,and Jingling Xue ---2-.Unknown Soundness Boundary Achieved Soundness Known Soundness Boundary Identified Unsoundness (a)Previous Reflection Analysis (b)SoLAR:Soundness-Guided Reflection Analysis Fig.1.Reflection analysis:prior work vs.SoLAR. We have implemented SoLAR in Doop [8](a state-of-the-art pointer analysis tool for Java) and released it as an open-source tool.In particular,SoLAR can output its reflection analysis results with the format that is supported by Soor [63](a popular framework for analyzing Java and Android applications),allowing Soor's clients to use SoLAR's results directly. We conduct extensive experiments on evaluating SoLAR's effectiveness with large Java applications and libraries.Our experimental results provide convincing evidence on the ability of SoLAR in analyzing Java reflection effectively,in practice. 1.4 Organization The rest of this paper is organized as follows.We will start by providing a comprehensive under- standing of Java reflection in Section 2.Building on this understanding,we give an overview of SoLAR in Section 3 and introduce its underlying methodology in Section 4.Then,we formalize SoLAR in Section 5,describe its implementation in Section 6,and evaluate its effectiveness in Section 7.Finally,we discuss the related work in Section 8 and conclude in Section 9. 2 UNDERSTANDING JAVA REFLECTION Java reflection is a useful but complex language feature.To gain a deep understanding about Java reflection,we examine it in three steps.First,we describe what Java reflection is,why we need it,and how it is proposed(Section 2.1).Second,we explain how Java reflection is designed to be used,i.e,its API(Section 2.2).Finally,we investigate comprehensively how it has been used in real-world Java applications(Section 2.3).After reading this section,the readers are expected to develop a whole picture about the basic mechanism behind Java reflection,understand its core API design,and capture the key insights needed for developing practical reflection analysis tools. 2.1 Concept Reflection,which has long been studied in philosophy,represents one kind of human abilities for introspecting and learning their nature.Accordingly,a(non-human)object can also be endowed with the capability of such self-awareness.This arises naturally in artificial intelligence:"Here I am walking into a dark room.Since I cannot see anything,I should turn on the light".As explained in [54],"such thought fragment reveals a self-awareness of behaviour and state,one that leads to a ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
7:4 Yue Li, Tian Tan, and Jingling Xue Previous Reflection Analysis ? (a) (b) SOLAR: Soundness-Guided Reflection Analysis Achieved Soundness Identified Unsoundness ? Unknown Soundness Boundary Known Soundness Boundary Fig. 1. Reflection analysis: prior work vs. Solar. • We have implemented Solar in Doop [8] (a state-of-the-art pointer analysis tool for Java) and released it as an open-source tool. In particular, Solar can output its reflection analysis results with the format that is supported by Soot [63] (a popular framework for analyzing Java and Android applications), allowing Soot’s clients to use Solar’s results directly. • We conduct extensive experiments on evaluating Solar’s effectiveness with large Java applications and libraries. Our experimental results provide convincing evidence on the ability of Solar in analyzing Java reflection effectively, in practice. 1.4 Organization The rest of this paper is organized as follows. We will start by providing a comprehensive understanding of Java reflection in Section 2. Building on this understanding, we give an overview of Solar in Section 3 and introduce its underlying methodology in Section 4. Then, we formalize Solar in Section 5, describe its implementation in Section 6, and evaluate its effectiveness in Section 7. Finally, we discuss the related work in Section 8 and conclude in Section 9. 2 UNDERSTANDING JAVA REFLECTION Java reflection is a useful but complex language feature. To gain a deep understanding about Java reflection, we examine it in three steps. First, we describe what Java reflection is, why we need it, and how it is proposed (Section 2.1). Second, we explain how Java reflection is designed to be used, i.e., its API (Section 2.2). Finally, we investigate comprehensively how it has been used in real-world Java applications (Section 2.3). After reading this section, the readers are expected to develop a whole picture about the basic mechanism behind Java reflection, understand its core API design, and capture the key insights needed for developing practical reflection analysis tools. 2.1 Concept Reflection, which has long been studied in philosophy, represents one kind of human abilities for introspecting and learning their nature. Accordingly, a (non-human) object can also be endowed with the capability of such self-awareness. This arises naturally in artificial intelligence: “Here I am walking into a dark room. Since I cannot see anything, I should turn on the light". As explained in [54], “such thought fragment reveals a self-awareness of behaviour and state, one that leads to a ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
Understanding and Analyzing Java Reflection 7:5 Reify Metasystem ofS Reffect Computational The Domain Computational The Domain System S of S System S of S Answer Questions abou Answer Questions about andor and/or Support Actions in Support Actions in the Domain the Domain and S itself (a)Computational System and its Domain (b)Reflective System and its Domain Fig.2.Computational vs.reflective computational systems. change in that selfsame behaviour and state",which allows an object to examine itself and leverage the meta-level information to figure out what to do next. Similarly,when we enable programs to avail themselves of such reflective capabilities,reflective programs will also allow the programs to observe and modify properties of their own behaviour. Thus,let a program be self-aware-this is the basic motivation of the so-called computational reflection,which is also considered as the reflection used in the area of programming languages [15]. In the rest of this section,we will introduce what computational reflection is(Section 2.1.1),what reflective abilities it supports(Section 2.1.2),and how Java reflection is derived from it(Section 2.1.3). 2.1.1 Computational Reflection.Reflection,as a concept for computational systems,dates from Brian Smith's doctoral dissertation [53].Generally,as shown in Figure 2(a),a computational system is related to a domain and it answers questions about and/or support actions in the domain [39]. As described in [39],a computational system "incorporates internal structures representing the domain.These structures include data representing entities and relations in the domain and a program describing how these data may be manipulated". A computational system S is said to be also a reflective system,as shown in Figure 2(b),if the following two conditions are satisfied: First,the system S has its own representation,known as its self-representation or metasystem, in its domain as a kind of data to be examined and manipulated. Second,the system S and its representation are causally connected:a change to the repre- sentation implies a change to the system,and vice versa. The base system S should be reified into its representation before its metasystem can operate. Then the metasystem examines and manipulates its behaviour using the reified representation.If any changes are made by the metasystem,then the effects will also be reflected in the behavior of the corresponding base system. 2.1.2 Reflective Abilities.Generally,(computational)reflection is the ability of a program to examine and modify the structure and behavior of a program at runtime [23,40].Thus,it endows the program the capabilities of self-awareness and self-adapting.These two reflective abilities are known as introspection and intercession,respectively,and both require a reification mechanism to encode a program's execution state as data first [15]. Introspection:the ability of a program to observe,and consequently,reason about its own execution state. ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
Understanding and Analyzing Java Reflection 7:5 Metasystem of S The Domain of S Computational System S Reify Reflect and/or Support Actions in Answer Questions about the Domain and S itself The Domain of S Computational System S and/or Support Actions in Answer Questions about the Domain (a) Computational System and its Domain (b) Reflective System and its Domain Fig. 2. Computational vs. reflective computational systems. change in that selfsame behaviour and state”, which allows an object to examine itself and leverage the meta-level information to figure out what to do next. Similarly, when we enable programs to avail themselves of such reflective capabilities, reflective programs will also allow the programs to observe and modify properties of their own behaviour. Thus, let a program be self-aware — this is the basic motivation of the so-called computational reflection, which is also considered as the reflection used in the area of programming languages [15]. In the rest of this section, we will introduce what computational reflection is (Section 2.1.1), what reflective abilities it supports (Section 2.1.2), and how Java reflection is derived from it (Section 2.1.3). 2.1.1 Computational Reflection. Reflection, as a concept for computational systems, dates from Brian Smith’s doctoral dissertation [53]. Generally, as shown in Figure 2(a), a computational system is related to a domain and it answers questions about and/or support actions in the domain [39]. As described in [39], a computational system “incorporates internal structures representing the domain. These structures include data representing entities and relations in the domain and a program describing how these data may be manipulated”. A computational system S is said to be also a reflective system, as shown in Figure 2(b), if the following two conditions are satisfied: • First, the system S has its own representation, known as its self-representation or metasystem, in its domain as a kind of data to be examined and manipulated. • Second, the system S and its representation are causally connected: a change to the representation implies a change to the system, and vice versa. The base system S should be reified into its representation before its metasystem can operate. Then the metasystem examines and manipulates its behaviour using the reified representation. If any changes are made by the metasystem, then the effects will also be reflected in the behavior of the corresponding base system. 2.1.2 Reflective Abilities. Generally, (computational) reflection is the ability of a program to examine and modify the structure and behavior of a program at runtime [23, 40]. Thus, it endows the program the capabilities of self-awareness and self-adapting. These two reflective abilities are known as introspection and intercession, respectively, and both require a reification mechanism to encode a program’s execution state as data first [15]. • Introspection: the ability of a program to observe, and consequently, reason about its own execution state. ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
7:6 Yue Li,Tian Tan,and Jingling Xue Intercession:the ability of a program to modify its own execution state or alter its own interpretation or meaning. Providing full reflective abilities as shown above is challenging in practice,as this will introduce both implementation complexities and performance problems [10].Thus,in modern programming languages like Java,reflective abilities are only partially supported [6,19]. 2.1.3 Java Reflection.Java reflection supports introspection and very limited intercession;in particular,an introspection step is usually followed by behaviour changes such as object creation, method invocation and attribute manipulation [9,19].Note that some other researchers hold a different view that Java reflection does not support intercession [6,16],as they adopt a more strict definition of intercession,which implies the ability to modify the self-representation of a program. Despite its limited reflective abilities,Java reflection is able to allow programmers to break the constraints of staticity and encapsulation,enabling the program to adapt to dynamically changing runtime environments.As a result,Java reflection has been widely used in real-world Java applications to facilitate flexibly different programming tasks,such as reasoning about control (i.e.,about which computations to pursue next)[19],interfacing (e.g.,interaction with GUIs or database systems)[21,46],and self-activation (e.g.,through monitors)[13]. Java reflection does not have a reify operation as described in Section 2.1.1(Figure 2(b))to turn the basic(running)system(including stack frames)into a representation(data structure)that is passed to a metasystem.Instead,a kind of metarepresentation,based on metaobjects,exists when the system starts running and persists throughout the execution of the system[19]. A metaobject is like the reflection in a mirror:one can adjust one's smile(behaviour changes)by looking at oneself in a mirror(introspection).In Section 2.2,we will look at how Java reflection uses metaobjects and its API to facilitate reflective programming. 1 Aa new A(); 2 String cName,mName,fName =... 3 class clz Class.forName(cName); 4 Object obj clz.newInstance(); 5 Method mtd clz.getDeclaredMethod(mName,A.class); 6 Object 1 mtd.invoke(obj,a); 7 Field fld clz.getField(fName); 8 x r (X)fld.get(a); 9 fld.set(null,a); Fig.3.An example of reflection usage in Java. 2.2 Interface We first use a toy example to illustrate some common uses of the Java reflection API(Section 2.2.1). We then delve into the details of its core methods,which are relevant to (and thus should be handled by)any reflection analysis (Section 2.2.2). 2.2.1 An Example.There are two kinds of metaobjects:Class objects and member objects.In Java reflection,one always starts with a Class object and then obtain its member objects(e.g., Method and Field objects)from the Class object by calling its corresponding accessor methods (e.g.,getMethod()and getField()). ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
7:6 Yue Li, Tian Tan, and Jingling Xue • Intercession: the ability of a program to modify its own execution state or alter its own interpretation or meaning. Providing full reflective abilities as shown above is challenging in practice, as this will introduce both implementation complexities and performance problems [10]. Thus, in modern programming languages like Java, reflective abilities are only partially supported [6, 19]. 2.1.3 Java Reflection. Java reflection supports introspection and very limited intercession; in particular, an introspection step is usually followed by behaviour changes such as object creation, method invocation and attribute manipulation [9, 19]. Note that some other researchers hold a different view that Java reflection does not support intercession [6, 16], as they adopt a more strict definition of intercession, which implies the ability to modify the self-representation of a program. Despite its limited reflective abilities, Java reflection is able to allow programmers to break the constraints of staticity and encapsulation, enabling the program to adapt to dynamically changing runtime environments. As a result, Java reflection has been widely used in real-world Java applications to facilitate flexibly different programming tasks, such as reasoning about control (i.e., about which computations to pursue next) [19], interfacing (e.g., interaction with GUIs or database systems) [21, 46], and self-activation (e.g., through monitors) [13]. Java reflection does not have a reify operation as described in Section 2.1.1 (Figure 2(b)) to turn the basic (running) system (including stack frames) into a representation (data structure) that is passed to a metasystem. Instead, a kind of metarepresentation, based on metaobjects, exists when the system starts running and persists throughout the execution of the system [19]. A metaobject is like the reflection in a mirror: one can adjust one’s smile (behaviour changes) by looking at oneself in a mirror (introspection). In Section 2.2, we will look at how Java reflection uses metaobjects and its API to facilitate reflective programming. 1 A a = new A(); 2 String cName, mName, fName = ...; 3 Class clz = Class.forName(cName); 4 Object obj = clz.newInstance(); 5 Method mtd = clz.getDeclaredMethod(mName, A.class); 6 Object l = mtd.invoke(obj, a); 7 Field fld = clz.getField(fName); 8 X r = (X)fld.get(a); 9 fld.set(null, a); Fig. 3. An example of reflection usage in Java. 2.2 Interface We first use a toy example to illustrate some common uses of the Java reflection API (Section 2.2.1). We then delve into the details of its core methods, which are relevant to (and thus should be handled by) any reflection analysis (Section 2.2.2). 2.2.1 An Example. There are two kinds of metaobjects: Class objects and member objects. In Java reflection, one always starts with a Class object and then obtain its member objects (e.g., Method and Field objects) from the Class object by calling its corresponding accessor methods (e.g., getMethod() and getField()). ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
Understanding and Analyzing Java Reflection 7:7 Class::newInstance Class::getDeclaredConstructor Constructor object Class::getConstructor Constructor::newInstance Class::getDeclaredConstructors Class object class:getConstructors Class:forName Class:getDeclaredMethod Method object ClassLoader::loadClass Class:getMethod Object::getclass Class::getDeclaredMethods Method::invoke Others .class Class::getMethods Field object Class:getDeclaredField Class::getPield Field::get Class:getDeclaredFields Field::set Class::getFields Proxy:newProxyInstance Array::newInstance Class object flows to (as a receiver object) Class-Retrieving Methods Array object Member object flows to(as a receiver object)Member-Retrieving Methods Class/Array object flows to (as an argument)Reflective-Action Methods Array::get Array::set Fig.4.Overview of core Java reflection APl. In Figure 3,the metaobjects clz,mtd and fld are instances of the metaobject classes Class, Method and Field,respectively.Constructor can be seen as Method except that the method name ""is implicit.Class allows an object to be created reflectively by calling newInstance(). As shown in line 4,the dynamic type of obj is the class(type)represented by clz(specified by cName).In addition,Class provides accessor methods such as getDeclaredMethod()in line 5 and getField()in line 7 to allow the member metaobjects (e.g.,of Method and Field)related to a Class object to be introspected.With dynamic invocation,a Method object can be commanded to invoke the method that it represents(line 6).Similarly,a Field object can be commanded to access or modify the field that it represents(lines 8 and 9). 2.2.2 Core Java Reflection APl.In reflection analysis,we are concerned with reasoning about how reflection affects the control and data flow information in the program.For example,if a target method(say m)that is reflectively invoked in line 6 in Figure 3 cannot be resolved statically,the call graph edge from this call site to method m(control flow)and the values passed interprocedurally from obj and a to this and the parameter of m(data flow),respectively,will be missing.Therefore,we should focus on the part of the Java reflection API that affects a pointer analysis,a fundamental analysis that statically resolves the control and data flow information in a Pr0gram[27,30-32,38,41,42,51,52,60-62]. It is thus sufficient to consider only the pointer-affecting methods in the Java reflection API.We can divide such reflective methods into three categories(Figure 4): We summarize and explain the core reflection API(25 methods)that is critical to static analysis.A more complete reflection API list(181 methods)is given in [26]without explanations though. ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
Understanding and Analyzing Java Reflection 7:7 Class::forName Class object flows to (as a receiver object) Class-Retrieving Methods Member-Retrieving Methods Reflective-Action Methods .class Object::getClass ClassLoader::loadClass Class::getMethod Class::getDeclaredMethods Class::getMethods Class::getField Class::getDeclaredField Class::getDeclaredFields Class::getFields Class::getConstructor Class::getDeclaredConstructor Class::getDeclaredConstructors Class::getConstructors Constructor::newInstance Method::invoke Field::get Field::set Class::newInstance Proxy::newProxyInstance Array::newInstance Array::get Array::set Class Object Constructor Object Method Object Field Object Array Object Member object flows to (as a receiver object) Class/Array object flows to (as an argument) Others Class::getDeclaredMethod Fig. 4. Overview of core Java reflection API.1 In Figure 3, the metaobjects clz, mtd and fld are instances of the metaobject classes Class, Method and Field, respectively. Constructor can be seen as Method except that the method name “” is implicit. Class allows an object to be created reflectively by calling newInstance(). As shown in line 4, the dynamic type of obj is the class (type) represented by clz (specified by cName). In addition, Class provides accessor methods such as getDeclaredMethod() in line 5 and getField() in line 7 to allow the member metaobjects (e.g., of Method and Field) related to a Class object to be introspected. With dynamic invocation, a Method object can be commanded to invoke the method that it represents (line 6). Similarly, a Field object can be commanded to access or modify the field that it represents (lines 8 and 9). 2.2.2 Core Java Reflection API. In reflection analysis, we are concerned with reasoning about how reflection affects the control and data flow information in the program. For example, if a target method (say m) that is reflectively invoked in line 6 in Figure 3 cannot be resolved statically, the call graph edge from this call site to method m (control flow) and the values passed interprocedurally from obj and a to this and the parameter of m (data flow), respectively, will be missing. Therefore, we should focus on the part of the Java reflection API that affects a pointer analysis, a fundamental analysis that statically resolves the control and data flow information in a program [27, 30–32, 38, 41, 42, 51, 52, 60–62]. It is thus sufficient to consider only the pointer-affecting methods in the Java reflection API. We can divide such reflective methods into three categories (Figure 4): 1We summarize and explain the core reflection API (25 methods) that is critical to static analysis. A more complete reflection API list (181 methods) is given in [26] without explanations though. ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
7:8 Yue Li,Tian Tan,and Jingling Xue class-retrieving methods,which create Class objects,e.g.,forName()in line 3 in Figure 3. member-retrieving methods,which introspect and retrieve member metaobjects,i.e.,Method (Constructor)and Field objects from a Class object,e.g.,getDeclaredMethod()in line 5 and getField()in line 7 in Figure 3. reflective-action methods,which affect the pointer information in the program reflectively, e.g.,newInstance(),invoke(),get()and set()in lines 4,6,8 and 9 in Figure 3 for creating an object,invoking a method,accessing and modifying a field,respectively. Class-Retrieving Methods.Everything in Java reflection begins with Class objects and they are returned by calling class-retrieving methods.There are many class-retrieving methods in the Java reflection API.In Figure 4,only the four most widely used ones are listed explicitly Note that forName()(loadclass())returns a Class object representing a class that is specified by the value of its string argument.The class object returned by o.getclass()and A.class represents the dynamic type(class)of o and A,respectively. Member-Retrieving Methods.Class provides a number of accessor methods for retrieving its member metaobjects,i.e.,the Method(Constructor)and Field objects.In addition,these member metaobjects can be used to introspect the methods,constructors and fields in their target class. Formally,these accessor methods are referred to here as the member-retrieving methods. As shown in Figure 4,for each kind of member metaobjects,there are four member-retrieving methods.We take a Method object as an example to illustrate these methods,whose receiver objects are the Class objects returned by the class-retrieving methods. getDeclaredMethod(String,Class[])returns a Method object that represents a declared method of the target Class object with the name(formal parameter types)specified by the first (second)parameter (line 5 in Figure 3). getMethod(String,Class[])is similar to getDeclaredMethod(String,Class[])ex- cept that the returned Method object is public(either declared or inherited).If the target Class does not have a matching method,then its superclasses are searched first recursively (bottom-up)before its interfaces(implemented). getDeclaredMethods()returns an array of Method objects representing all the methods declared in the target Class object. getMethods()is similar to getDeclaredMethods()except that all the public methods(either declared or inherited)in the target Class object are returned. Reflective-Action Methods.As shown in Figure 4,a total of nine reflective-action methods that can possibly modify or use(as their side effects)the pointer information in a program are listed. Accordingly,Table 1 explains how these methods affect the pointer information by giving their side effects on the pointer analysis. In Figure 4,the first five reflective-action methods use four kinds of metaobjects as their receiver objects while the last four methods use Class or Array objects as their arguments.Below we briefly examine them in the order given in Table 1. The side effect of newInstance()is allocating an object with the type specified by its metaobject clz or ctor(say A)and initializing it via a constructor of A,which is the default constructor in the case of Class:newInstance()and the constructor specified explicitly in the case of Constructor:newInstance(). The side effect of invoke()is a virtual call when the first argument of invoke(),say o,is not null.The receiver object is o as shown in the "Side Effect"column in Table 1.When o is null,invoke()should be a static call. ACM Trans.Softw.Eng.Methodol,Vol.28,No.2,Article 7.Publication date:February 2019
7:8 Yue Li, Tian Tan, and Jingling Xue • class-retrieving methods, which create Class objects, e.g., forName() in line 3 in Figure 3. • member-retrieving methods, which introspect and retrieve member metaobjects, i.e., Method (Constructor) and Field objects from a Class object, e.g., getDeclaredMethod() in line 5 and getField() in line 7 in Figure 3. • reflective-action methods, which affect the pointer information in the program reflectively, e.g., newInstance(), invoke(), get() and set() in lines 4, 6, 8 and 9 in Figure 3 for creating an object, invoking a method, accessing and modifying a field, respectively. Class-Retrieving Methods. Everything in Java reflection begins with Class objects and they are returned by calling class-retrieving methods. There are many class-retrieving methods in the Java reflection API. In Figure 4, only the four most widely used ones are listed explicitly. Note that forName() (loadClass()) returns a Class object representing a class that is specified by the value of its string argument. The Class object returned by o.getClass() and A.class represents the dynamic type (class) of o and A, respectively. Member-Retrieving Methods. Class provides a number of accessor methods for retrieving its member metaobjects, i.e., the Method (Constructor) and Field objects. In addition, these member metaobjects can be used to introspect the methods, constructors and fields in their target class. Formally, these accessor methods are referred to here as the member-retrieving methods. As shown in Figure 4, for each kind of member metaobjects, there are four member-retrieving methods. We take a Method object as an example to illustrate these methods, whose receiver objects are the Class objects returned by the class-retrieving methods. • getDeclaredMethod(String, Class[]) returns a Method object that represents a declared method of the target Class object with the name (formal parameter types) specified by the first (second) parameter (line 5 in Figure 3). • getMethod(String, Class[]) is similar to getDeclaredMethod(String, Class[]) except that the returned Method object is public (either declared or inherited). If the target Class does not have a matching method, then its superclasses are searched first recursively (bottom-up) before its interfaces (implemented). • getDeclaredMethods() returns an array of Method objects representing all the methods declared in the target Class object. • getMethods() is similar to getDeclaredMethods() except that all the public methods (either declared or inherited) in the target Class object are returned. Reflective-Action Methods. As shown in Figure 4, a total of nine reflective-action methods that can possibly modify or use (as their side effects) the pointer information in a program are listed. Accordingly, Table 1 explains how these methods affect the pointer information by giving their side effects on the pointer analysis. In Figure 4, the first five reflective-action methods use four kinds of metaobjects as their receiver objects while the last four methods use Class or Array objects as their arguments. Below we briefly examine them in the order given in Table 1. • The side effect of newInstance() is allocating an object with the type specified by its metaobject clz or ctor (say A) and initializing it via a constructor of A, which is the default constructor in the case of Class::newInstance() and the constructor specified explicitly in the case of Constructor::newInstance(). • The side effect of invoke() is a virtual call when the first argument of invoke(), say o, is not null. The receiver object is o as shown in the “Side Effect” column in Table 1. When o is null, invoke() should be a static call. ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
Understanding and Analyzing Java Reflection 7:9 Table 1.Nine reflective-action methods and their side effects on the pointer analysis,assuming that the target class of clz and ctor is A,the target method of mtd is m and the target field of fld is f. Simplified Method Calling Scenario Side Effect Class::newInstance o=clz.newInstance( o=new A() Constructor::newInstance o ctor.newInstance(farg1,.) o new A(arg1,...) Method::invoke a mtdinvoke(o,farg1,...) a=o.m(arg1,.) Field:get a fldget(o) a=o.f Field::set fld.set(o,a) o.f=a Proxy:newProxyInstance o Proxy.newProxyInstance(...) o=new Proxy$*(.) Array::newInstance o Array.newInstance(clz,size) o=new A[size] Array::get a=Array.get(o,i) a=o[i] Array::set Array.set(o,i,a) o[)=a The side effects of get()and set()are retrieving (loading)and modifying (storing)the value of a instance field,respectively,when their first argument,say o,is not null;otherwise, they are operating on a static field. The side effect of newProxyInstance()is creating an object of a proxy class Proxy$*,and this proxy class is generated dynamically according to its arguments(containing a Class object).Proxy.newProxyInstance()can be analyzed according to its semantics.A call to this method returns a Proxy object,which has an associated invocation handler object that implements the InvocationHandler interface.A method invocation on a Proxy object through one of its Proxy interfaces will be dispatched to the invoke()method of the object's invocation handler. The side effect of Array.newInstance()is creating an array (object)with the compo- nent type represented by the Class object(e.g.,clz in Table 1)used as its first argument. Array.get()and Array.set()are retrieving and modifying an index element in the array object specified as their first argument,respectively. 2.3 Reflection Usage The Java reflection API is rich and complex.We have conducted an empirical study to understand reflection usage in practice in order to guide the design and implementation of a sophisticated reflection analysis described in this paper.In this section,we first list the focus questions in Section 2.3.1,then describe the experimental setup in Section 2.3.2,and finally,present the study results in Section 2.3.3. 2.3.1 Focus Questions.We address the following seven focus questions in order to understand how Java reflection is used in the real world. .Q1.Existing reflection analyses resolve reflection by analyzing statically the string arguments of class-retrieving and member-retrieving method calls.How often are these strings constants ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
Understanding and Analyzing Java Reflection 7:9 Table 1. Nine reflective-action methods and their side effects on the pointer analysis, assuming that the target class of clz and ctor is A, the target method of mtd is m and the target field of fld is f. Simplified Method Calling Scenario Side Effect Class::newInstance o = clz.newInstance() o = new A() Constructor::newInstance o = ctor.newInstance({arg1, ...}) o = new A(arg1, ...) Method::invoke a = mtd.invoke(o, {arg1, ...}) a = o.m(arg1, ...) Field::get a = fld.get(o) a = o.f Field::set fld.set(o, a) o.f = a Proxy::newProxyInstance o = Proxy.newProxyInstance(...) o = new Proxy$*(...) Array::newInstance o = Array.newInstance(clz, size) o = new A[size] Array::get a = Array.get(o, i) a = o[i] Array::set Array.set(o, i, a) o[i] = a • The side effects of get() and set() are retrieving (loading) and modifying (storing) the value of a instance field, respectively, when their first argument, say o, is not null; otherwise, they are operating on a static field. • The side effect of newProxyInstance() is creating an object of a proxy class Proxy$*, and this proxy class is generated dynamically according to its arguments (containing a Class object). Proxy.newProxyInstance() can be analyzed according to its semantics. A call to this method returns a Proxy object, which has an associated invocation handler object that implements the InvocationHandler interface. A method invocation on a Proxy object through one of its Proxy interfaces will be dispatched to the invoke() method of the object’s invocation handler. • The side effect of Array.newInstance() is creating an array (object) with the component type represented by the Class object (e.g., clz in Table 1) used as its first argument. Array.get() and Array.set() are retrieving and modifying an index element in the array object specified as their first argument, respectively. 2.3 Reflection Usage The Java reflection API is rich and complex. We have conducted an empirical study to understand reflection usage in practice in order to guide the design and implementation of a sophisticated reflection analysis described in this paper. In this section, we first list the focus questions in Section 2.3.1, then describe the experimental setup in Section 2.3.2, and finally, present the study results in Section 2.3.3. 2.3.1 Focus Questions. We address the following seven focus questions in order to understand how Java reflection is used in the real world. • Q1. Existing reflection analyses resolve reflection by analyzing statically the string arguments of class-retrieving and member-retrieving method calls. How often are these strings constants ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019
7:10 Yue Li,Tian Tan,and Jingling Xue and how often can non-constant strings be resolved by a simple string analysis that models string operations such as“+”and append()? Q2.Existing reflection analyses ignore the member-retrieving methods that return an array of member metaobjects.Is it necessary to handle such methods? .Q3.Existing reflection analyses usually treat reflective method calls and field accesses as being non-static.Does this treatment work well in real-world programs?Specifically,how often are static reflective targets used in reflective code? .Q4.In [38],intraprocedural post-dominating cast operations are leveraged to resolve newInstance() when its class type is unknown.This approach is still adopted by many reflection analysis tools.Does it generally work in practice? .Q5.The Java reflection API contains many class-retrieving methods for returning Class objects.Which ones should be focused on by an effective reflection analysis? .Q6.The core part of reflection analysis is to resolve all the nine reflective-action methods (Table 1)effectively.What are the reflective-action methods that are most widely used and how are the remaining ones used in terms of their relative frequencies? Q7.What are new insights on handling Java reflection(from this paper)? 2.3.2 Experimental Setup.We have selected a set of 16 representative Java programs,including three popular desktop applications,javac-1.7.0,jEdit-5.1.0 and Eclipse-4.2.2(denoted Eclipse4),two popular server applications,Jetty-9.0.5 and Tomcat-7.0.42,and all eleven DaCapo benchmarks(2006-10-MR2)[3].Note that the DaCapo benchmark suite includes an older version of Eclipse(version 3.1.2).We exclude its bloat benchmark since its application code is reflection-free.We consider lucene instead of luindex and lusearch separately since these two benchmarks are derived from lucene with the same reflection usage. We consider a total of 191 methods in the Java reflection API(version 1.6),including the ones mainly from package java.lang.reflect and class java.lang.Class. We use Soor [63]to pinpoint the calls to reflection methods in the bytecode of a program. To understand the common reflection usage,we consider only the reflective calls found in the application classes and their dependent libraries but exclude the standard Java libraries.To in- crease the code coverage for the five applications considered,we include the jar files whose names contain the names of these applications(e.g.,*jetty*.jar for Jetty)and make them avail- able under the process-dir option supported by Soor.For Eclipse4,we use org.eclipse.core. runtime.adaptor.EclipseStarter to let Soor locate all the other jar files used. We manually inspect the reflection usage in a program in a demand-driven manner,starting from its reflective-action methods,assisted by Open Call Hierarchy in Eclipse,by following their backward slices.For a total of 609 reflective-action call sites examined,510 call sites for calling class-retrieving methods and 304 call sites for calling member-retrieving methods are tracked and studied.As a result,a total of 1,423 reflective call sites,together with some nearby statements,are examined in our study. 2.3.3 Results.Below we describe our seven findings on reflection usage as our answers to the seven focus questions listed in Section 2.3.1,respectively.We summarize our findings as individual remarks,which are expected to be helpful in guiding the development of practical reflection analysis techniques and tools in future research. O1.String Constants and String Manipulations.In class-retrieving methods,Class.forName( and loadclass()each have a String parameter to specify the target class.In member-retrieving methods,getDeclaredMethod(String,...and getMethod(String,...)each return a Method ACM Trans.Softw.Eng.Methodol.,Vol.28,No.2,Article 7.Publication date:February 2019
7:10 Yue Li, Tian Tan, and Jingling Xue and how often can non-constant strings be resolved by a simple string analysis that models string operations such as “+” and append()? • Q2. Existing reflection analyses ignore the member-retrieving methods that return an array of member metaobjects. Is it necessary to handle such methods? • Q3. Existing reflection analyses usually treat reflective method calls and field accesses as being non-static. Does this treatment work well in real-world programs? Specifically, how often are static reflective targets used in reflective code? • Q4. In [38], intraprocedural post-dominating cast operations are leveraged to resolve newInstance() when its class type is unknown. This approach is still adopted by many reflection analysis tools. Does it generally work in practice? • Q5. The Java reflection API contains many class-retrieving methods for returning Class objects. Which ones should be focused on by an effective reflection analysis? • Q6. The core part of reflection analysis is to resolve all the nine reflective-action methods (Table 1) effectively. What are the reflective-action methods that are most widely used and how are the remaining ones used in terms of their relative frequencies? • Q7. What are new insights on handling Java reflection (from this paper)? 2.3.2 Experimental Setup. We have selected a set of 16 representative Java programs, including three popular desktop applications, javac-1.7.0, jEdit-5.1.0 and Eclipse-4.2.2 (denoted Eclipse4), two popular server applications, Jetty-9.0.5 and Tomcat-7.0.42, and all eleven DaCapo benchmarks (2006-10-MR2) [3]. Note that the DaCapo benchmark suite includes an older version of Eclipse (version 3.1.2). We exclude its bloat benchmark since its application code is reflection-free. We consider lucene instead of luindex and lusearch separately since these two benchmarks are derived from lucene with the same reflection usage. We consider a total of 191 methods in the Java reflection API (version 1.6), including the ones mainly from package java.lang.reflect and class java.lang.Class. We use Soot [63] to pinpoint the calls to reflection methods in the bytecode of a program. To understand the common reflection usage, we consider only the reflective calls found in the application classes and their dependent libraries but exclude the standard Java libraries. To increase the code coverage for the five applications considered, we include the jar files whose names contain the names of these applications (e.g., *jetty*.jar for Jetty) and make them available under the process-dir option supported by Soot. For Eclipse4, we use org.eclipse.core. runtime.adaptor.EclipseStarter to let Soot locate all the other jar files used. We manually inspect the reflection usage in a program in a demand-driven manner, starting from its reflective-action methods, assisted by Open Call Hierarchy in Eclipse, by following their backward slices. For a total of 609 reflective-action call sites examined, 510 call sites for calling class-retrieving methods and 304 call sites for calling member-retrieving methods are tracked and studied. As a result, a total of 1,423 reflective call sites, together with some nearby statements, are examined in our study. 2.3.3 Results. Below we describe our seven findings on reflection usage as our answers to the seven focus questions listed in Section 2.3.1, respectively. We summarize our findings as individual remarks, which are expected to be helpful in guiding the development of practical reflection analysis techniques and tools in future research. Q1. String Constants and String Manipulations. In class-retrieving methods, Class.forName() and loadClass() each have a String parameter to specify the target class. In member-retrieving methods, getDeclaredMethod(String,...) and getMethod(String,...) each return a Method ACM Trans. Softw. Eng. Methodol., Vol. 28, No. 2, Article 7. Publication date: February 2019