Next Article in Journal
Lightweight Strong PUF for Resource-Constrained Devices
Next Article in Special Issue
A Reinforcement Learning Approach to Guide Web Crawler to Explore Web Applications for Improving Code Coverage
Previous Article in Journal
Analysis of the Possibility of Making a Digital Twin for Devices Operating in Foundries
Previous Article in Special Issue
CMBMeTest: Generation of Test Suites Using Model-Based Testing Plus Constraint Programming and Metamorphic Testing
 
 
Font Type:
Arial Georgia Verdana
Font Size:
Aa Aa Aa
Line Spacing:
Column Width:
Background:
Article

Resolving the Java Representation Exposure Problem with an AST-Based Deep Copy and Flexible Alias Ownership System

Department of Computer Science and Information Engineering, National Central University, Taoyuan 32001, Taiwan
*
Author to whom correspondence should be addressed.
Submission received: 24 November 2023 / Revised: 8 January 2024 / Accepted: 11 January 2024 / Published: 14 January 2024
(This article belongs to the Special Issue Advances in Software Engineering and Programming Languages)

Abstract

:
Encapsulation is a critical factor in object-oriented programming languages and design patterns. Nevertheless, programs written in languages like Java may encounter broken encapsulation due to the lack of sufficient supply for ownership and immutability. As a result, this paper introduces SlimeJava, an ownership system extension based on abstract syntax trees and annotation utilization of Java that aims to help programmers prevent representation exposure. We show the features of the proposal with a motivating example using the Memento pattern. We then discuss how the utilization of annotations realizes ownership and why it is effective in avoiding representation exposure issues by comparing it with existing approaches. In the end, a quantitative performance evaluation was conducted to prove that SlimeJava does not cause a substantial overhead in execution time compared to native Java.

1. Introduction

Encapsulation is an important property of object-oriented programming and many design patterns. To efficiently protect encapsulation, the mechanisms of ownership and immutability are needed. However, when it comes to encapsulation in Java, the problem of representation exposure is difficult to avoid. The problem will cause programs written in Java to be less predictable. The term “representation exposure” was proposed by Noble et al. [1,2] in 1998. It means that the internal representation of an object within a container must be encapsulated within the object; the mutable part of the internal representation should only be accessed through the object’s interface. If the mutable part of internal representation is directly referenced outside the container and is modified afterward, representation exposure occurs. In the context of Java, the internal representation includes the fields or members of an object defined in a class.
The cause of the occurrence of dynamic or static references to the mutable internal representation outside the container is the use of aliases [3,4]. Aliases are produced when different references point to the same object or address at the same time. To better describe how aliases affect encapsulation and therefore lead to representation exposure, we classify aliases into four categories (as shown in Figure 1) based on the classification introduced by Clarke [5]. These types are as follows: internal alias A, external alias B, incoming alias C, and outgoing alias D.
  • Internal aliases (A) refer to an inside object and do not affect anything outside, as depicted in Figure 1a. For instance, Member b’ refers to Member b in Object a, creating an internal alias.
  • External aliases (B) are the ones that refer to outside objects, as demonstrated in Figure 1b. In this case, Object a’ is initialized through external source B.
  • Incoming aliases (C) are generated when a reference to an external object is passed into the object, as shown in Figure 1c. Object b is provided for the reference of Member b’ through the interface of Object a, like a setter or constructor. Member b’ is an incoming alias.
  • Outgoing aliases (D) are created when a reference to an internal object is passed to an external object, as depicted in Figure 1d. Member b provides a reference to Object b’ through the interface with Object a, such as a getter, resulting in an outgoing alias.
Figure 1. The four alias types for object passing (the symbol ’ represents an alias object).
Figure 1. The four alias types for object passing (the symbol ’ represents an alias object).
Electronics 13 00350 g001
Internal aliases and external aliases do not involve passing references through interfaces of objects like getters and setters. Therefore, they are generally considered benign [5]. On the other hand, incoming aliases and outgoing aliases are more likely to cause problems. Incoming aliases expose the internal representation of an object to the outside without the object’s permission [5], which means the object’s encapsulation is broken. Outgoing aliases offer references to the outside through the object’s interface. Whether representation exposure occurs depends on the way these references are used externally [5].
Aliases are indispensable in programming languages, although they may lead to the problem of representation exposure. Their reusability plays a crucial part in design patterns like the Observer pattern and the Flyweight pattern, as they involve the use of references to objects [6,7]. Furthermore, aliases are also used widely in data structures such as linked lists and hash tables [8]. Therefore, the goal is to find a way to keep aliases available while preventing the problem of representation exposure from happening.
Issues related to aliasing and representation exposure may also happen in other object-oriented programming languages like C++, Python, Rust, and others. Due to the different characteristics of aliases and encapsulation, each language addresses this problem differently. For instance, Python has a qualifier similar to Java’s final keyword [9]; C++ has the const keyword [10] and the standard library header “memory”, which provides smart pointers for dynamic memory management [11]; and Rust introduces features like immutable binding [12] and reference borrowing [13] mechanisms. Among the languages and features mentioned above, the C++ smart pointers lean more towards automatic memory management rather than ownership. Unique or shared pointers ensure the expected release of the pointed objects’ memory section, rather than specifying which particular “owner” is able to release or use them. In C++, due to its support for both pass by value and pass by reference functionalities [14], there are two distinct ways to create aliases. However, in a strict and precise definition, aliases in C++ are mainly generated through passing by reference [15]. Passing a unique pointer by value will result in a compiler error instead of another copy of it [16]. However, due to the inability to prevent the creation of aliases through pass by reference [17], unique pointers are unable to completely address the aliasing issues we aim to discuss in the paper. On the other hand, a shared pointer itself is a type of smart pointer designed to support the creation of aliases [18]. As for Rust, it can prevent the creation of unexpected aliases, as it has the features of immutability and ownership. Hence, the problem of representation exposure is also prevented.
To help protect encapsulation, mechanisms of immutability [19] or ownership [20] are required. Nonetheless, programmers may encounter the exposure issue using Java since it does not provide adequate mechanisms of immutability or ownership [21]. The keyword “final” in Java only ensures that reassignment to a variable declared as final after initialization is not allowed. When dealing with mutable objects, there is still a possibility of modifying their internal members [22]. Programmers can call static factory methods such as List.of() on the List, Set, and Map interfaces provided by JDK. The methods only ensure the collection itself is not modifiable. If the elements of the collection are mutable, then the contents can still be modified. Furthermore, programmers can only identify the exceptions thrown at runtime if there is any attempt to modify these non-modifiable collections [23]. Java does not support any built-in mechanism of ownership [21], which means that it relies on aliasing where references to objects are shared rather than individually owned. Only when assigning a primitive type (e.g., int) value to a variable is the value copied, rather than shared through references [24]. Moreover, Java offers syntaxes for copying objects, such as new and clone(), although copying an object is based on shallow copy [25] if programmers do not implement the deep copy method explicitly. Even though the copied objects appear as distinct entities, their internal members are shared references. This opens the door to potential representation exposure.
So far, some related research has implemented the mechanisms of immutability and ownership to solve representation exposure. At first, Noble et al. proposed an ownership type system [26] that can be applied to most object-oriented languages. It combines the characteristics of object-oriented languages with ownership types to limit the accessible scope of aliases. The approach protects encapsulation, thus preventing representation exposure from happening. Nevertheless, this approach makes the encapsulation rules too strict to use outgoing aliases due to the lack of object transitivity. In the meantime, if iterators must be used, related iterable classes like List cannot provide their internal iterators.
Boyapati [27] introduced a unified type system for improvement with SafeJava. The system achieves ownership by independently specifying owner parameters [28] and parameterizing classes [29]. The use of annotations in SafeJava also offers a flexible way to utilize aliasing [30]. Consequently, programmers can use iterators and outgoing aliases without breaking encapsulation. Since the ownership mechanism of SafeJava requires independent specifications of owner parameters and parameterized classes, it will lead to burdensome parameter annotations [27,31,32]. To achieve a more lightweight method, Potanin et al. integrate the generic mechanism of Confined FeatherWeather Java [33] with the extended language syntax from Condition Java [34] to propose Ownership Generic Java (OGJ) [35]. Y. Zibin, A. Potanin, et al. further presented Ownership Immutability Generic Java (OIGJ) [21], which dealt with ownership by generic type parameters and solved the iterator problem founded on OGJ and immutability. Nonetheless, representation exposure may still occur because immutability in OIGJ is not transitive [36]. Furthermore, OIGJ only performs copying for the immutable fields of objects [21]. Thus, programmers need to define the copying process clearly themselves.
The contributions of the research are as follows:
  • We survey the limits of protecting encapsulation in the existing related work, focusing on preventing the problem of internal representation exposure in Java.
  • In the process of researching, we find out there is room for improvement in preventing the issue resulting from mutable alias modification.
  • This study introduces SlimeJava to achieve ownership in the context of Java, a widely used programming language in industry and academia.
We sequentially present the following content in this paper: Section 2 explains our research motivation. Section 3 describes our proposal and implementation; the methods of this paper are presented in the section. Section 4 evaluates the proposal and describes the results and discussion. Section 5 discusses the related work. Section 6 concludes our research.

2. Motivation

Building on the discussion in Section 1, if we can incorporate the flexible aliasing from SafeJava while maintaining encapsulation, along with features akin to the ones of generic ownership presented in OIGJ, then issues of iterators, representation exposure due to immutability, and shallow copy can be resolved. We aim to employ these strategies to help programmers write programs with less ambiguity and mitigate the problem arising from aliases.
In this section, we illustrate the issue of representation exposure with a concrete example written in Java. Subsequently, we propose an ownership system extended from Java to address the issue. It aids programmers in safeguarding encapsulation to avoid representation exposure by establishing annotations [31,37] and source-to-source transformations. We present the details of the methods in Section 3 and then show the experimental results and discussion in Section 4 to demonstrate the effectiveness of our proposal. During the testing presented in Section 4.3, we found that Slime does not cause significant overhead on execution time in comparison to the performance of Java.
To emphasize the importance of representation exposure, it is essential to give examples that are closely related to encapsulation. An instance of a design pattern that utilizes encapsulation is the Memento pattern [7]. The Memento pattern mainly consists of three roles, which are:
  • The originator is the object that has an internal state to be saved by the memento object. It can restore the original state from the memento later if needed.
  • The caretaker stores all the memento objects, allowing the originator to restore its internal state before some operations.
  • The memento object serves as the core component of the design pattern. It stores the state provided by the originator.

2.1. The Memento Pattern Example

We provide an implementation example using a referenced tweet mechanism [38]. A programmer would like to employ the Memento pattern to implement a tweet mechanism for storing the state of sent tweets. The state of a tweet includes the tweet’s content, timestamp, and comments. In the main program, the programmer will record the changes in tweet comments for a specific tweet over the development of an hour and then store the tweet state in the caretaker. The implementation process is as follows:
  • Implement the Tweet class, which includes the tweet’s content (Text), timestamp (Timestamp), and a list of comments (CommentList). Use the private keyword to maintain the encapsulation of all internal fields. Electronics 13 00350 i001
  • The Originator is responsible for describing objects whose internal state needs to be saved. Here, the tweet is viewed as an object whose internal state requires saving. As a result, we implement the Originator interface with the Tweet class as follows.Electronics 13 00350 i002
  • The Memento holds the target object state. The Originator can store and restore the object state through the Memento. In our example, the tweet’s Text, Timestamp, and CommentList states are considered the entities to be saved. The tweet state can be stored or restored when necessary.Electronics 13 00350 i003
  • Lastly, the Caretaker protects the stored Mementos containing the states of target objects. All Mementos related to the tweet are saved in the Caretaker, consequently completing the implementation of the Memento pattern.Electronics 13 00350 i004
However, there is a possibility of encountering representation exposure when using the above implementation in the client-side application. For example, a programmer intends to store tweets that include those from one hour ago along with the current tweet states utilizing the implemented tweet mechanism. The implementation process is as follows:
  • Declare the necessary variables in advance, such as ONE_HOUR to represent the constant value of one hour, careTaker for safekeeping the mementos, timestamp to record the current time, and commentList to store the list of comments.Electronics 13 00350 i005
  • Create a new tweet with the content “My first tweet”, a timestamp from one hour ago, and a comment “A”. Then, store the tweet in the Caretaker.Electronics 13 00350 i006
  • Create another new tweet with the content “My second tweet”, a timestamp from the current time, and a new comment “B”. Store the tweet in the Caretaker.Electronics 13 00350 i007
The programmer expects the saved tweet states (i.e., saveStateList) in the Caretaker to match the content in Table 1.
Nevertheless, the actual result corresponds to the content in Table 2.
The first and second tweets’ commentList both have “A” and “B”, which is unexpected. The main program encounters the error when running due to the lack of ownership in Java. The error results from the programmer’s failure to consider the aliasing issue, and it is not noticeable to the programmer at the time of coding. In addition, programmers may unintentionally alter commentLists’ value when calling add() on commentLists due to the absence of immutability in Java.
The Rust programming language with ownership and immutability [12,39] can detect this problem and prevent unexpected results. Rust has the feature of immutable binding. It ensures that when performing operations equivalent to “push()” (like “add()” in Java List) on commentList [40], these operations cannot be performed if the target object is immutable. Thus, it prevents representation exposure and protects the data of the mementos stored in saveStateList from unintended external modifications. Finally, programmers can complete the saving of the second commentList through copying (as shown in the following code listing).Electronics 13 00350 i008

2.2. The Benefits of Designing an Ownership System to Resolve Representation Exposure

The approach in the above main program in Rust involves binding objects that need immutability to prevent modifications. Additionally, Rust incorporates the borrowing mechanism [13] to allow objects to be borrowed by other non-owning references. Nonetheless, these references cannot access the object outside the scope of their influence. It means that objects must be returned to their owners because they are not owned by the borrowing references. The mechanism realizes the concept of ownership.
By implementing reference borrowing from Rust, we can safely manage aliases and prevent them from unintentionally modifying internal representations. The next benefit is effectively handling the problems resulting from incoming aliases. As ownership systems must address representation exposure caused by incoming aliases, Potanin et al. [41] discovered that the use of an ownership system had a very small impact on testing performance. Moreover, programmers do not need to spend extra time debugging bugs originating from incoming aliases. We can ensure that encapsulation remains unharmed by unexpected modifications with aliases through the ownership system [26]. If we can guarantee the encapsulation of objects is not broken, the correctness of program logic and object behavior can be enhanced [28].

2.3. The Difficulty of Designing an Ownership System to Resolve Representation Exposure

To implement ownership mechanisms in Java, additional design and implementation efforts are required. Currently, many ownership systems designed to address representation exposure primarily rely on rewriting code structures with patterns [5,26,27,33]. Nonetheless, practical application in Java still has shortcomings that developers must deal with themselves. As mentioned in the introduction, issues resulting from shallow copying arise because OIGJ does not provide mutable object copying capabilities. Additionally, while OIGJ implements immutability to address iterator initialization problems, issues related to non-transitivity may lead to representation exposure [36].
As a result, we will use the example of accessing iterator operations in iterable classes and separate them as mutable and immutable situations, as shown below.
  • Mutable iterable class: If we request a mutable iterable class MutableList to provide an immutable iterator, denoted as ImmutableIter, we expect that ImmutableIter can be used in a read-only way by external code. However, we can indirectly modify the contents of MutableList through ImmutableIter in OIGJ. Electronics 13 00350 i009
  • Immutable iterable class: In the case of an immutable iterable class like ImmutableList, when we request it to provide an iterator, denoted as Iter, we expect that ImmutableList will contain mutable objects in a read-only state. However, under the OIGJ mechanism, even though Iter itself is read-only, it can allow external modifications to the mutable objects contained in ImmutableList.Electronics 13 00350 i010

3. Proposal and Implementation

We aim to address representation exposure by introducing an annotation system for Java extension, known as SlimeJava, based on the abstract syntax tree. This system enables source-to-source transformations, facilitating the establishment of ownership for programmers. Our ultimate goal is to avoid representation exposure, eliminating unintended mutable alias modifications. To achieve this, we draw inspiration from SafeJava’s flexible alias specifications [27], OIGJ’s generic ownership features [21], and Rust’s borrowing mechanism for references [13]. Additionally, we utilize abstract syntax trees to generate deep copies, resolving the issue of shallow copying. Thereby, the proposal obeys the ownership mechanisms we have designed.
As a result, we introduce three annotations: SlimeOwned, SlimeBorrow, and SlimeCopy. SlimeOwned represents the declaration of ownership, indicating that the object is possessed by the respective class. In developing this annotation, we took inspiration from SafeJava’s ownership domains [30] and complied with the typing rules in OIGJ. Additionally, we optimized it for the management of incoming aliases to grant control to the programmer. Next, we took Rust’s borrowing mechanism as a prototype and proposed the SlimeBorrow annotation, which constrains aliases to the scope of their use and forces their return to the owner once their usage is completed. Lastly, we offer the SlimeCopy annotation to address the problem of representation exposure arising from shallow copying. By employing abstract syntax trees to generate deep copies, we duplicate objects not owned by any class. This approach relieves programmers from the burden of repeatedly implementing deep copying.
We dive into the details of the annotations used and the contexts in which they are applied in Section 3.1, Section 3.2 and Section 3.3. Using some code examples, we present the functionality of the annotations. Following this, we provide a concise overview of the features of SlimeJava’s proposal. To validate the feasibility and usability of our proposal, we explore the implementation details in Section 3.5. This section explains how we use abstract syntax trees and the tools employed in this implementation. Finally, in Section 3.6, we transform the motivating example into another one featuring SlimeJava functionality and analyze how they work.

3.1. SlimeOwned

SlimeOwned signifies the ownership of a Java object In SlimeJava. It indicates that the class possesses the object and that, simultaneously, no external references are made to any values in that object. SlimeOwned applies only to all internal members within the class, including public, protected, and private members, and to method return types (excluding void). For example, in the Java class Owner, we declare ownership of its internal member mObj and set up the setter and getter methods to illustrate the working process.
  • Ownership Declaration: As the Owner class owns its internal member mObj, we can declare SlimeOwned on mObj, showing that the Owner class holds ownership of mObj. Electronics 13 00350 i011
  • Setter: When we need to assign newObj to mObj, it is represented as follows: Electronics 13 00350 i012
  • Since mObj has been declared as SlimeOwned, indicating a deep copy transformation is required to eliminate the potential of representation exposure resulting from external references, any assignment to mObj will be converted into a deep copy operation automatically to establish ownership. The code transformed by SlimeJava will be as follows: Electronics 13 00350 i013
  • Getter: When we need to provide mObj for external use, we must mark the data as SlimeOwned for identification. Electronics 13 00350 i014
  • On the other hand, if an internal member related to SlimeOwned is not annotated and is returned, then a transformation error will occur before the process of source-to-source transformation begins. Electronics 13 00350 i015
When external code interacts with objects related to SlimeOwned, our research anticipates that direct access through the owning class itself is the intended approach. For example, when using mObj in the Owner class, we should only access it by calling Owner.getObj(). However, due to Java’s inherent aliasing features, disabling aliases would disable functionalities like iterators and parameter passing. Therefore, we regulate aliases to allow mutable aliases according to our expectations. In SlimeJava, we offer two alias management methods for SlimeOwned objects, which are detailed in Section 3.2 and Section 3.3. We explain how to employ these methods through examples. Assume the goal is to use a List<String> object belonging to the Owner class in a for loop to print the result "Hello World". We demonstrate correct and incorrect borrowing in sequence.

3.2. SlimeBorrow

The SlimeBorrow annotation for temporary borrowing of ownership objects is inspired by Rust’s borrowing mechanism [13]. Due to the lack of immutability in Java, we apply the mutable borrowing mechanism to ensure alias control. SlimeBorrow can be applied to the passing of parameters, modifications, and deep copying within the context of other SlimeBorrow annotations. However, SlimeBorrow cannot provide references, assignments, or returns to arbitrary objects since ownership does not belong to the borrower, and such a return would result in dangling references [13,42]. Therefore, we restrict SlimeOwned objects to be borrowed only once within the same scope, and SlimeBorrow annotations must be used in the order of borrowing.
  • Borrowing from SlimeOwned: When using a foreach loop with Owner.getList(), we need to borrow the List<String> from Owner to operate its values. Therefore, we use SlimeBorrow to obtain its values as str. Electronics 13 00350 i016
  • Borrowing from SlimeBorrow: You can borrow str within the for loop. Electronics 13 00350 i017
  • Sequential Usage of Borrowing: If you attempt to use the str that has already been borrowed in borrowAgainStr, an error will occur. Electronics 13 00350 i018
  • Inability to Provide Unexpected Aliases: If you attempt to assign to a non-SlimeBorrow alias, then an assignment error will occur. Electronics 13 00350 i019

3.3. SlimeCopy

The annotation for deep copying of ownership objects, SlimeCopy, generates an entirely new object that does not have the property of SlimeOwned or SlimeBorrow. It is treated as a new Java object. SlimeCopy can only be applied to objects that are annotated as SlimeOwned or SlimeBorrow to represent the copying behavior.
  • Copying the Entire List: When we want to print all the strings in a List using the already written printListAllString method, we may require the use of copying. Electronics 13 00350 i020
At this point, we can use SlimeCopy on Owner.getList() to perform a deep copy.    
Electronics 13 00350 i021
  • Cannot Be Used on Ordinary Objects: When you attempt to use SlimeCopy on ordinary objects that are not annotated as SlimeOwned or SlimeBorrow, an error will occur. Electronics 13 00350 i022

3.4. Summary of the Proposal

In summary, we can achieve the following functionalities through SlimeJava:
  • Design an ownership system that offers SlimeOwned, SlimeBorrow, and SlimeCopy annotations to help programmers address representation exposure and write programs with less ambiguity.
  • Avoid unintended modifications of the Tweet state values stored in the caretaker’s Memento through CommentList or Timestamp in the motivating example, thus protecting the encapsulation in this case.
  • Implement flexible aliasing, as referenced in SafeJava [27]. It allows the use of aliases without breaking encapsulation and enables iterators to be used with iterable classes. We also take inspiration from Rust’s borrowing mechanism [13].
  • In contrast to OIGJ [21], which cannot handle the copying of mutable internal members, we provide a method for deep copying to prevent the creation of unexpected aliases.

3.5. Implementation Detail

SlimeJava is a source-to-source translator (https://github.com/ncu-psl/slime-java, accessed on 10 January 2024) developed using the Kotlin [43] programming language in the environment of Windows 10 64-bit with 32 GB RAM and Java 17. Developed in IntelliJ IDEA [44], we use the Gradle [45] build automation tool to create a typical Kotlin development project. Kotlin offers greater flexibility compared to Java, as it comes with features like null safety, smart cast, and co-routines [46]. Furthermore, Kotlin is compatible with Java. Meanwhile, we implemented it using the annotation system already present in Java to ensure that developers can compile and run code even when SlimeJava is removed. This approach reduces developers’ dependency on a single implementation.
We introduce the stages that SlimeJava goes through. First, we parse Java code using JavaParser. Then, we create a symbol table to check if the syntax adheres to our expected standards. Finally, we hand it over to the Java compiler for further processing after generating the deep copy methods. Due to the nature of generating deep copies, there are some unsupported cases in Java, which we will discuss after all of the SlimeJava stages.
We use the JavaParser [47] library to parse the code. Listing 1 shows a snippet of code from the Tweet class with SlimeOwned added. We pass the code to JavaParser to generate an abstract syntax tree, whose structure is represented in a YAML [48] file as shown in Figure 2. We traverse all the SlimeOwned, SlimeBorrow, and SlimeCopy annotations in our classes. To effectively utilize the parsed code, we build a symbol table during the code traversal process. We use the VoidVisitorAdapter provided by JavaParser to facilitate code traversal.
Listing 1: A code snippet of Tweet class with SlimeOwned added.
Electronics 13 00350 i023
Next, we build the symbol table and utilize it to help us check for annotation validity. For the SlimeOwned annotation, it checks whether the annotation is on a field. When assignments occur for incoming aliases and method returns, the subsequent behavior is tracked. If there is any scope in which this method is called, we further check whether any variable with assignment statements generates unexpected aliases to ensure the ownership system works correctly.
For variables marked with SlimeBorrow, we verify if they are parameters. Additionally, it confirms whether they correspond to other targets marked as SlimeOwned or SlimeBorrow and judges whether subsequent SlimeBorrow annotations use the borrowed reference in the same scope. Regarding variables with SlimeCopy annotations, we need to ensure they are used on the right-hand side of assignments [49]. Additionally, they should correspond to variables annotated with SlimeOwned and SlimeCopy.
Figure 2. The AST structure of the code snippet of the Tweet class in the YAML file format.
Figure 2. The AST structure of the code snippet of the Tweet class in the YAML file format.
Electronics 13 00350 g002
The process of checking valid annotations is followed by the stage of transformation. The transformation process involves performing deep copies based on the functionality of SlimeCopy. For internal members marked with SlimeOwned, if the member is not a primitive type, the system will traverse the member’s internal components and automatically generate a deepCopy() method within the member’s class. Subsequently, in the original variables annotated with SlimeCopy, the deepCopy() method will also be automatically applied to complete the transformation process. The process of SlimeJava translation is represented in pseudo-code Algorithm 1.
Algorithm 1 Procedure of SlimeJava Translation
1:
procedure  SlimeJavaTranslate(sourceCode)         ▹ using com.github.javaparser [47]
2:
     A S T p a r s e ( S o u r c e C o d e )
3:
     c h e c k A n n o t a t i o n s V a l i d i t y ( s y m b o l T a b l e )
4:
     T r a n s l a t e ( s y m b o l T a b l e )
5:
procedure  checkAnnotationsValidity(symbolTable)
6:
     s y m b o l T a b l e e m p t y t a b l e
7:
    for all nodes in AST do
8:
        if the annotation is not a slime annotation then
9:
            continue
10:
        if the annotation has no parent node then
11:
           continue
12:
        if the annotation is not a marker annotation then
13:
           continue
14:
        if the annotation is not a valid declaration then
15:
           continue
16:
        if the annotation is duplicated then
17:
           continue
18:
         a d d S y m b o l F r o m N o d e ( s y m b o l T a b l e )
19:
procedure  Translate(symbolTable)
20:
    for all members marked with @SlimeOwned do
21:
        if the member is not of primitive type then
22:
            g e n e r a t e D e e p C o p y M e t h o d ( s y m b o l T a b l e )
23:
    for all variables marked with @SlimeCopy do
24:
         g e n e r a t e D e e p C o p y M e t h o d ( s y m b o l T a b l e )
Because our deep copy generation requires interaction with Java libraries, it encounters resource allocation errors when used with classes that conform to the Singleton pattern, meaning only one instance is permitted in the JVM [50]. For example, consider the following classes:
  • GUI (Graphical User Interface) libraries like AWT or Swing [51].
  • Thread-related classes, including ThreadPoolExecutor libraries [52].
  • Database SQL (Structured Query Language) library drivers [53].

3.6. Rewriting the Motivating Example for Analysis

We use the example of the Memento design pattern mentioned in Section 2.1 to demonstrate how a programmer can design a tweet mechanism using SlimeJava. We explain the process step by step, including creating SlimeOwned internal members, preventing issues related to incoming aliases, creating a memento using SlimeCopy, and storing it in a caretaker. Finally, we explain what the client-side instructions are like, what the result is, and any differences from the original example in Section 2.1.
  • Creating SlimeOwned internal members: Programmers can explicitly use SlimeOwned provided by SlimeJava to annotate the internal members they want to protect against issues related to representation exposure in the Tweet class. By adding SlimeOwned to mCommentList, the result is as follows: Electronics 13 00350 i024
  • Preventing representation exposure resulting from incoming aliases: In the case of the client-side main program, if the client similarly uses the commentLists of tweets from an hour ago and the present, they will encounter the issue of reusing commentList. Since commentList will be passed as an incoming alias to the Tweet class, mCommentList, which has been declared as SlimeOwned, will be automatically transformed into a deep copy through the source-to-source transformation by SlimeJava instead of direct assignment to mCommentList when obtaining an external incoming alias (commentList). The process avoids issues related to the representation exposure originating from incoming aliases. Electronics 13 00350 i025
  • Establishing the memento with SlimeCopy and storing it in the Caretaker: Next, the client uses the careTaker to manage the currently stored Tweet states. By calling Tweet.saveToMemento(), a memento of the current state is created. At this point, we encounter the need to store mText, mTimestamp, and mCommentList in a Memento class that is not SlimeOwned. With the aid of SlimeJava, we can use SlimeCopy to create a complete copy of the data and establish a new Memento object. Electronics 13 00350 i026
  • SlimeJava will automatically transform the portion of the code that uses SlimeCopy into a deep copy operation, resulting in the following code: Electronics 13 00350 i027
  • Now, the Memento contains new data for mCommentListState and immutable, primitive data types mTextState and mTimestamp. There will not be any issues related to exposing references. We can store the current state of the tweet in the caretaker by using the newly created memento. Electronics 13 00350 i028
4.
Changes in the subsequent instructions: Now, developers need to change the timestamp to the current time and add a new comment “B” to the commentList so they can call add() to add “B” to the commentList. Nevertheless, the call of add() may not change the original mCommentList that was saved when the previous tweet was created. SlimeJava has already handled the issue of exposing representation through incoming aliases. Therefore, developers can pass commentList to the current tweet and save its state in the caretaker. Electronics 13 00350 i029
  • In the end, the Caretaker is responsible for keeping the data inside the savedStateList. The data in this list will correspond to our expectations (as shown in Table 1).

4. Evaluation

When considering the relationship between encapsulation and representation exposure issues, key aspects include cloning [1], iterators [54], and object encapsulation [5]. To ensure a fair comparison with other relevant research, we use representative operations in design patterns to evaluate this research [7]:
  • Cloning in the Prototype pattern;
  • Iterator-related operations in the Iterator Pattern;
  • Object encapsulation in the Memento pattern.
We assess the practicality of SlimeJava in terms of its ability to block issues related to representation exposure. Following this, we analyze the SlimeJava implementation by examining how it assists programmers in preventing problems associated with representation exposure in the context of the motivating example. To demonstrate that our approach both promotes clear coding intention and does not cause an unacceptable execution burden when running the generated code, we employ quantitative evaluations to compare the performance differences between using SlimeJava and not using SlimeJava.

4.1. Design Pattern Operations for Evaluation

We have gathered the common operations of the three design patterns in Table 3. Based on the four alias types proposed by Clarke [5], as mentioned in the introduction, we categorize the situations leading to the potential creation of mutable aliases into four types (Figure 1). We evaluate whether the ownership system can block unexpected mutable aliases, which is critical in assessing the occurrence of representation exposure. These situations include:
  • Internal Aliases (IL) (Figure 1a): While they may not directly result in representation exposure in the ownership system, they could cause indirect exposure problems when combined with other aliasing mechanisms.
  • External Aliases (EL) (Figure 1b): These aliases are created when external objects directly initialize an object as an alias. Representation exposure is blocked based on whether the operations on this object align with the expected mutable aliasing of the ownership system.
  • Incoming Aliases (IG) (Figure 1c): This type of aliasing generates unexpected mutable aliases so it leads to representation exposure in the ownership system. This occurs when two or more objects hold the same internal member through aliases.
  • Outgoing Aliases (OG) (Figure 1d): While direct exposure problems may not occur, initializing external objects as aliases through internal members can indirectly cause unexpected mutable aliases outside. This can result in potential representation exposure.
Table 3. Representative operations in design patterns.
Table 3. Representative operations in design patterns.
Design Pattern NameOperationsExplanationExample
PrototypeP_INITInitialize obj into a Prototype objectnew Prototype(obj)
P_CLONEClone a new object based on prototypeprototype.clone()
IteratorI_INITInitialize a new Container through collectionnew Container(collection)
I_CREATECreate a new Iterator that belongs to Collection through containercontainer.createIterator()
I_FIRSTShow the first object in iteratoriterator.first()
I_CURRENTShow the object pointed to by iteratoriterator.current()
I_NEXTMove iterator to the next objectiterator.next()
I_DONEShow if iterator has reached the last objectiterator.isDone()
MementoM_INITInitialize a new CareTakernew CareTaker(savedStates)
Initialize a new Originatornew Originator(args)
M_SAVESave originator’s current state in Mementooriginator.saveToMemento()
M_RESTORERestore originator’s state from mementooriginator.restoreFromMemento(memento)
In Table 3, “Prototype” represents the cloning operation process, “Iterator” represents the iteration process, and “Memento” represents the object encapsulation process. Each of these design patterns, Prototype, Iterator, and Memento, involves an initialization (INIT) operation. Following that, we provide explanations of the representative operations for each design pattern, with examples to illustrate how these operations are performed.
We have further broken down the content of Table 3 and organized it into Table 4 to evaluate the functionality of SlimeJava. In Table 4, the variables starting with “m” are all internal members.
In Table 4, we can observe that using SlimeOwned allows developers to achieve expected control over most internal aliases (IL), external aliases (EL), and outgoing aliases (OG). For cloning functionality (CLONE), SlimeCopy is provided to fulfill it. As for incoming aliases (IG) related functionality, it is implemented using SlimeBorrow. However, in the I_NEXT section of Iterator, the ownership system does not apply to the null type (the ownership system does not support objects of null type, which would lead to dangling references [13,42]), so it only supports objects of non-null type.

4.2. Comparison with Existing Research Approaches

We now compare the functionality provided by SlimeJava from the operations presented in Table 3 and Table 4 with that provided by other proposals. We also include a comparison between the operations of Rust, a language with immutable binding and reference borrowing, and SlimeJava. Additionally, we evaluate how each approach helps prevent representation exposure. This comparison is summarized in Table 5.
When observing Table 5 from left to right, we can make the following observations. Due to the lack of immutability and ownership in Java (Version 17), it cannot effectively prevent representation exposure in the above operations. Noble et al.’s ownership types only support iterator operations without incoming and outgoing aliases because of the strict encapsulation. It does not work for I_CREATE since it lacks support for incoming and outgoing aliases. OIGJ proposed by Potanin et al. offers immutability, but it lacks transitivity. This limitation leads to representation exposure during iterator operations. SafeJava, introduced by Boyapati, is more flexible concerning aliasing, which allows it to support a wider range of design pattern operations compared to ownership types and OIGJ. However, it still cannot address the cloning issue encountered in the Prototype operation (P_CLONE).
In the context of Rust, we can observe the comparison of P_CLONE and I_NEXT operations. P_CLONE operation still faces mutable object issues in Rust and cannot prevent representation exposure. I_NEXT operation is fully supported in Rust because it does not have null types like Java. Instead, Rust uses the Option type with None to represent the absence of a value [55]. In contrast, Java is a language that supports null values, even though Java does provide the Optional library [56]. Java developers from Oracle announced the library is primarily designed to improve API clarity, not intended for variable declarations or internal members [57]. Nevertheless, there is no syntax limitation in Java for using Optional. We discuss this topic more in Section 4.4.
Furthermore, in the case of the P_CLONE operation, our implementation of the deep copy mechanism is better than other proposals. It can effectively address unexpected mutable alias issues.

4.3. Quantitative Performance Evaluation

To prove that our SlimeJava provides ownership features and clear usage intention and prevents representation exposure without affecting the performance of the original program, we conduct runtime timing comparisons by comparing the runtime timing between code with the added SlimeJava annotations to the code without any SlimeJava annotations. We introduce the test code we use and explain how the testing is conducted as well as the objectives of the tests. Subsequently, we present the test results graphically to compare the differences between SlimeJava and standard Java.
We have used the code for the tweet mechanism mentioned in Section 2.1 and Section 3.6, incorporating SlimeOwned, SlimeBorrow, and SlimeCopy. We have also rewritten the Tweet class and main program as follows:
Electronics 13 00350 i030
In this code, we used the commentList as a passed parameter and utilized SlimeCopy to assign commentListCopy to the internal member mCommentList. The key difference from the code mentioned earlier is that we generated 100 tweets using a loop to test if our annotations would have any impact on the performance. On the other hand, for the code that does not use SlimeJava, we simply removed the annotations, as SlimeJava is a detachable proposal.
Then, we calculate the time spent in milliseconds to create and save the state to the Memento using Java’s built-in currentTimeMillis method.    
Electronics 13 00350 i031
After running 100,000 tests (as shown in Figure 3), we found that Java’s execution time ranged from approximately 9 to 17 milliseconds. In comparison, SlimeJava showed execution time ranging from about 13 to 21 milliseconds because of the added annotation system. In the entire testing, there was no significant difference in execution time such as thousands of milliseconds between the two. The additional overhead was not substantial.

4.4. Discussion

Compared with existing research based on Java in Table 5, SlimeJava can better prevent representation exposure in the operations specified in Table 3. As for Rust, it is effective in protecting encapsulation, but it cannot avoid unexpected modifications when it comes to the P_CLONE operation.
Regarding the issue of the Optional library, as mentioned in Section 4.2, there is no limitation on syntax in Java. Java programmers need to consider the following scenarios:
  • Issues in syntax: The objects declared as Optional can still be assigned with null.
  • Rewriting code: To integrate a no-value mechanism to fulfill the ownership system, programmers need to add “Optional” in every place where null type values might occur, including every parameter declaration, variable declaration, and return value.
  • Functionalities overlapping with specific classes: In Java, there are classes that can implement the concept of no value, for instance, declaring an empty List using Collections.emptyList(). Adding Optional in such cases might be redundant.
Due to the above considerations, the current use of Java’s Optional might not be the best choice. Apparently, SlimeJava has the same limitations as Java. This is the reason why it cannot fully support the I_NEXT operation (as shown in Table 4). As we discussed in Section 4.2 regarding the null type issue, there are currently various research proposals for null safety checkers [58,59]. By adopting Java’s built-in Optional library, one can prevent potential null issues and establish an ownership system. Nevertheless, the original intention of Java Optional [56] was to deal with specific use cases in functional programming in Java, rather than focusing on an ownership system in object-oriented programming. Hence, it may not be a suitable choice for an ownership system. The Checker Framework [60,61] provides the Nullness checker that can check the programs at compile time. If the checker does not issue any warning, then the checked programs will never throw a null pointer exception when running. Assigning a @Nullable object to a @NonNull object will result in a warning that the @NonNull object may become null. Thus, integration with the Nullness checker into SlimeJava is a potential way forward for future work.
At the end of the section, we discuss other future work of the paper. The ownership system of SlimeJava proposed in this paper is implemented through annotations. Similar to what Abi-Antoun et al. [62] mentioned regarding the visual static ownership domain annotations, we can subsequently integrate it into a Linter [63], a static analysis tool provided by the development environment, to narrow the gap between programmers’ expectations and the actual results. This enables programmers to visually reduce potential spelling or syntax errors. Through the approach presented in this paper, even though we can utilize Linter, it still relies on programmers to manually avoid unexpected behavior by adding some annotations in their code. If we could automatically analyze potential exposure points, it would significantly assist programmers in identifying and resolving problems more quickly with clearer coding intention. The aforementioned Checker Framework may help us on this subject, as it can check Java programs at compile time to detect errors. For instance, we might define some must-call obligations related to ownership by adding annotations of @MustCall [64] and @EnsuresCalledMethods [65] on an object in order to detect behaviors that would violate our ownership system. Then, programmers can fix them by applying the approach proposed in the paper.
Another subject we can work on in the future is to survey the feasibility of supporting multiple or/and shared ownership, as well as the features associated with inheritance and classes in SlimeJava. Among the features, interfaces and enums may not need the alias management of SlimeJava since interfaces have no internal members [66] and all members in enums are unchangeable constants [67]. As for abstract classes and inner classes, we think it is possible to support them with SlimeJava. However, implementing these features requires extra effort in the source-to-source translation process and more research on issues related to inheritance, which we have not explored. In this paper, we also employed quantitative performance evaluation for the proposed approach with the code mentioned in Section 2.1, Section 3.6 and Section 4.3. To better evaluate the overhead created by SlimeJava’s translation process in terms of scalability, future research could gather some widely adopted open-source projects and run them with SlimeJava.

5. Related Work

In developing our research, we sought to address the issue of representation exposure with ownership systems, aiming to refine and augment existing models for enhanced practicality. In this section, we present the survey of related work to provide the background and the evolution of ownership types and domains. The selection of related work is also guided by the need to identify the limits in existing research and room for us to improve.

5.1. Ownership Types

The concept of flexible alias protection was initially introduced by Noble et al. [1]. Building on this foundation, they established it into an ownership type system [2]. It suggests that, when accessing any internal member of an object, it must be explicitly requested from the owner object. However, due to the limitation that this system could not use the “this” pointer in ownership types, it led to potential issues in its ownership system. Subsequently, Clarke [5] made modifications and created a topological property called “owners-as-dominators”, providing a robust encapsulation concept.
Nonetheless, the excessively strict encapsulation in such systems resulted in problems related to iterators and the inability to use outgoing aliases, as mentioned in the introduction. The reason is iterators inherently require a reference to the iterable object [27], which would violate the topological property of owners-as-dominators. Consequently, SafeJava addressed the iterator problem and introduced ownership types. To tackle the stack-based variables problem, a relaxed mechanism was introduced, known as stack-based relaxations of the owners-as-dominators principle in the topological property [32]. This topological property allows for a relaxed owners-as-dominators approach, ensuring that iterators can access private internal members in a class without compromising encapsulation. Both OIGJ and the proposal in this paper draw inspiration from the owners-as-dominators with stack-based relaxations principle to establish ownership systems.
Systems like Joe, proposed by Clarke [68], also employ the topological property based on owners-as-dominators with stack-based relaxations. They achieve their ownership system through a read-only variable, “myList”, mapped to the “this” pointer of the class. “myList” serves as a reserved keyword, accessed using dot notation [69] to locate the corresponding class pointer along a path. Distinct from SafeJava, Joe resolves the iterator issue by storing iterators only on the stack, as local objects. This means it can only be used with classes that have “this” pointer, and errors may occur when trying to use them in classes that lack this pointer, like static objects or Singleton pattern classes [7].

5.2. Ownership Domains

Aldrich et al. [30] introduced a model known as ownership domains to allow control of aliases based on the ownership of objects. This model provides flexibility for developers to adjust the objects requiring ownership through public or private domains. Objects can be associated with one or multiple public or private domains, and the model defines how objects from these different domains and non-annotated objects should interact. SafeJava’s annotations [27] are integrated with the usage of this ownership domain model. Nevertheless, it is important to have a clear understanding of when to use annotations in this model, as the more types of annotations attached to a single object, the higher the associated learning cost [32].
Subsequently, Schäfer et al. [31] introduced the concept of simple loose ownership domains based on ownership domains. In contrast to ownership domains, this model allows for associating a single object with only one public or private domain. This simplification reduces syntax overhead but comes at the cost of limited fine-grained control over structural details across different domains. In this model, every owner of an object has a boundary domain, which must have access to any public or private domain of the object being held. Unlike the topological property of ownership types’ owners-as-dominators principle, this model adheres to the boundary-as-dominators principle. The annotation system in the SlimeJava proposal combines aspects of simple loose ownership domains.
Nonetheless, the issues regarding the timing of annotations and whether the correct annotations are used still remain. To address these challenges, Abi-Antoun et al. [62] introduced SCHOLIA. SCHOLIA provides visualizations of static annotations of ownership domains and predicts how they will work during runtime. These visual representations assist programmers in reducing differences between the expected and actual outcomes, ultimately enhancing the precision of annotations.

6. Conclusions

In this research, we addressed the shortcomings in Java related to ownership and immutability, which leads to representation exposure when using encapsulation. We explained that the main problem arises from unexpected mutable aliases, causing internal members to be modified from the outside. Subsequently, we discussed and analyzed existing studies related to representation exposure and proposed a viable method for improvement. We have developed an ownership system called SlimeJava based on abstract syntax trees and source-to-source transformation. This system ensures encapsulation, preventing representation exposure. It includes annotations such as SlimeOwned for assigning ownership to objects and SlimeBorrow and SlimeCopy to handle mutable aliases in a way that is predictable and does not compromise functionality. This maintains code flexibility while effectively tackling representation exposure.
In the implementation, we have verified the feasibility and usability of our proposal by implementing SlimeJava. Our process begins by checking the correctness of the syntax for SlimeOwned, SlimeBorrow, and SlimeCopy. We then perform a source-to-source transformation to generate a deep copying function if needed. For the tools, we adopt the Kotlin language to parse abstract syntax trees using JavaParser, which helps us build our symbol table. Subsequently, we use JavaParser’s VoidVisitorAdapter to traverse the required nodes and add found names to the symbol table. Finally, we use the symbol table to determine the correctness based on the conditions outlined in the proposal. In our evaluation, we choose some commonly used operations from the design patterns associated with copying, iterator-related operations, and encapsulation. We then assess whether our proposal provides sufficient functionality. Additionally, we compare SlimeJava to Rust and other existing research to demonstrate that SlimeJava offers advantages in terms of functionality. We show that our research can better prevent representation exposure, except for a certain operation associated with some limitations in native Java; however, it does not cause an unacceptable burden in execution time.

Author Contributions

Conceptualization, Y.-Y.Z.; methodology, Y.-Y.Z. and W.K.; software, W.K.; validation, S.-C.T.; investigation, W.K. and S.-C.T.; writing—original draft preparation, S.-C.T.; writing—review and editing, Y.-Y.Z.; supervision, Y.-Y.Z. All authors have read and agreed to the published version of the manuscript.

Funding

This research was funded by National Science and Technology Council grant number MOST 110-2221-E-008-020.

Data Availability Statement

Source code for SlimeJava: https://github.com/ncu-psl/slime-java (SlimeJava is licensed under the MIT License). Source of all code listings and tables: contribution of the authors.

Conflicts of Interest

The authors declare no conflicts of interest.

References

  1. Noble, J.; Vitek, J.; Potter, J. Flexible alias protection. In Proceedings of the ECOOP’98—Object-Oriented Programming: 12th European Conference, Brussels, Belgium, 20–24 July 1998; Proceedings 12. Springer: Berlin/Heidelberg, Germany, 1998; pp. 158–185. [Google Scholar]
  2. Clarke, D.G.; Potter, J.M.; Noble, J. Ownership types for flexible alias protection. In Proceedings of the 13th ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, New York, NY, USA, 18–22 October 1998; pp. 48–64. [Google Scholar]
  3. Detlefs, D.L.; Leino, K.R.M.; Nelson, G. Wrestling with Rep Exposure; Digital, Systems Research Center: Palo Alto, CA, USA, 1998; Volume 156. [Google Scholar]
  4. Clarke, D.G.; Noble, J.; Potter, J. Overcoming representation exposure. In Proceedings of the Workshop on Object-Oriented Technology, Lisbon, Portugal, 14–18 June 1999; pp. 149–151. [Google Scholar]
  5. Clarke, D.G. Object Ownership & Containment; University of New South Wales: Kensington, Australia, 2002. [Google Scholar]
  6. Rayside, D.; Mendel, L.; Seater, R.; Jackson, D. An analysis and visualization for revealing object sharing. In Proceedings of the 2005 OOPSLA Workshop on Eclipse Technology eXchange, San Diego, CA, USA, 16–17 October 2005; pp. 11–15. [Google Scholar]
  7. Gamma, E.; Helm, R.; Johnson, R.; Johnson, R.E.; Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software; Pearson Deutschland GmbH: Hoboken, NJ, USA, 1995. [Google Scholar]
  8. Wrigstad, T. Ownership-Based Alias Managemant. Ph.D. Thesis, KTH Royal Institute of Technology, Stockholm, Sweden, 2006. [Google Scholar]
  9. Proposals, P.E. PEP 591—Adding a Final Qualifier to Typing. Available online: https://peps.python.org/pep-0591 (accessed on 6 June 2023).
  10. C++ Keyword: Const. Available online: https://en.cppreference.com/w/cpp/keyword/const (accessed on 6 June 2023).
  11. Standard Library Header <Memory>. Available online: https://en.cppreference.com/w/cpp/header/memory (accessed on 31 December 2023).
  12. Variables and Mutability. Available online: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html (accessed on 15 May 2023).
  13. References and Borrowing. Available online: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html (accessed on 15 May 2023).
  14. VasuDev4. C++ Functions—Pass by Reference. Available online: https://www.geeksforgeeks.org/cpp-functions-pass-by-reference/ (accessed on 31 December 2023).
  15. Horgan, P. Understanding C/C++ Strict Aliasing. Available online: https://web.archive.org/web/20230424173236/http://dbp-consulting.com/tutorials/StrictAliasing.html (accessed on 31 December 2023).
  16. Std::Unique_ptr. Available online: https://en.cppreference.com/w/cpp/memory/unique_ptr (accessed on 31 December 2023).
  17. Sutter, H. Back to Basics! Essentials of Modern C++ Style. Available online: https://github.com/CppCon/CppCon2014/blob/master/Presentations/Back%20to%20the%20Basics!%20Essentials%20of%20Modern%20C%2B%2B%20Style/Back%20to%20the%20Basics!%20Essentials%20of%20Modern%20C%2B%2B%20Style%20-%20Herb%20Sutter%20-%20CppCon%202014.pdf (accessed on 31 December 2023).
  18. Std::Shared_ptr. Available online: https://en.cppreference.com/w/cpp/memory/shared_ptr (accessed on 31 December 2023).
  19. Haack, C.; Poll, E. Type-Based Object Immutability with Flexible Initialization. In Proceedings of the ECOOP: European Conference on Object-Oriented Programming, Genoa, Italy, 6–10 July 2009; Springer: Berlin/Heidelberg, Germany, 2009; Volume 5653, pp. 520–545. [Google Scholar]
  20. Banerjee, A.; Naumann, D.A. State based ownership, reentrance, and encapsulation. In Proceedings of the ECOOP 2005-Object-Oriented Programming: 19th European Conference, Glasgow, UK, 25–29 July 2005; Proceedings 19; Springer: Berlin/Heidelberg, Germany, 2005; pp. 387–411. [Google Scholar]
  21. Zibin, Y.; Potanin, A.; Li, P.; Ali, M.; Ernst, M.D. Ownership and immutability in generic Java. ACM Sigplan Not. 2010, 45, 598–617. [Google Scholar] [CrossRef]
  22. Final vs. Immutability in Java. Linked Lists—Advanced Operations. Available online: https://www.geeksforgeeks.org/final-vs-immutability-java (accessed on 6 June 2023).
  23. Oracle. Creating Immutable Lists, Sets, and Maps. Available online: https://docs.oracle.com/javase/9/core/creating-immutable-lists-sets-and-maps.htm (accessed on 7 May 2023).
  24. Rishabhsingh054. Primitive Data Type vs. Object Data Type in Java with Examples. Available online: https://www.geeksforgeeks.org/primitive-data-type-vs-object-data-type-in-java-with-examples (accessed on 11 May 2023).
  25. Javapoint. Shallow Copy Java. Available online: https://www.javatpoint.com/shallow-copy-java (accessed on 11 May 2023).
  26. Noble, J.; Clarke, D.; Potter, J. Object ownership for dynamic alias protection. In Proceedings of the Technology of Object-Oriented Languages and Systems, TOOLS 32, Melbourne, VIC, Australia, 22–25 November 1999; IEEE: Piscataway, NJ, USA, 1999; pp. 176–187. [Google Scholar]
  27. Boyapati, C. SafeJava: A Unified Type System for Safe Programming. Ph.D. Thesis, Massachusetts Institute of Technology, Cambridge, MA, USA, 2003. [Google Scholar]
  28. Boyapati, C.; Lee, R.; Rinard, M. Ownership types for safe programming: Preventing data races and deadlocks. In Proceedings of the 17th ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, New York, NY, USA, 4–8 November 2002; pp. 211–230. [Google Scholar]
  29. Boyapati, C.; Rinard, M. A parameterized type system for race-free Java programs. In Proceedings of the 16th ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, New York, NY, USA, 14–18 October 2001; pp. 56–69. [Google Scholar]
  30. Aldrich, J.; Chambers, C. Ownership domains: Separating aliasing policy from mechanism. In Proceedings of the ECOOP 2004–Object-Oriented Programming: 18th European Conference, Oslo, Norway, 14–18 June 2004; Proceedings 18. Springer: Berlin/Heidelberg, Germany, 2004; pp. 1–25. [Google Scholar]
  31. Schäfer, J.; Poetzsch-Heffter, A. A Parameterized Type System for Simple Loose Ownership Domains. J. Object Technol. 2007, 6, 71–100. [Google Scholar] [CrossRef]
  32. Clarke, D.; Östlund, J.; Sergey, I.; Wrigstad, T. Ownership types: A survey. In Aliasing in Object-Oriented Programming. Types, Analysis and Verification; Springer: Berlin/Heidelberg, Germany, 2013; pp. 15–58. [Google Scholar]
  33. Zhao, T.; Palsberg, J.; Vitek, J. Lightweight confinement for featherweight java. ACM Sigplan Not. 2003, 38, 135–148. [Google Scholar] [CrossRef]
  34. Huang, S.S.; Zook, D.; Smaragdakis, Y. cJ: Enhancing Java with safe type conditions. In Proceedings of the 6th International Conference on Aspect-Oriented Software Development, New York, NY, USA, 12–16 March 2007; pp. 185–198. [Google Scholar]
  35. Potanin, A.; Noble, J.; Clarke, D.; Biddle, R. Generic ownership for generic Java. ACM Sigplan Not. 2006, 41, 311–324. [Google Scholar] [CrossRef]
  36. Gordon, C.S.; Parkinson, M.J.; Parsons, J.; Bromfield, A.; Duffy, J. Uniqueness and reference immutability for safe parallelism. ACM Sigplan Not. 2012, 47, 21–40. [Google Scholar] [CrossRef]
  37. Oracle. Java Annotation. Available online: https://docs.oracle.com/javase/8/docs/technotes/guides/language/annotations.html (accessed on 6 June 2023).
  38. Abstraction Functions & Rep Invariants. Available online: https://web.mit.edu/6.005/www/fa15/classes/13-abstraction-functions-rep-invariants (accessed on 7 May 2023).
  39. Language, T.R.P. What Is Ownership? Available online: https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html (accessed on 15 May 2023).
  40. Language, T.R.P. Vectors. Available online: https://doc.rust-lang.org/book/ch08-01-vectors.html (accessed on 15 May 2023).
  41. Potanin, A.; Damitio, M.; Noble, J. Are your incoming aliases really necessary? Counting the cost of object ownership. In Proceedings of the 2013 35th International Conference on Software Engineering (ICSE), San Francisco, CA, USA, 18–26 May 2013; IEEE: Piscataway, NJ, USA, 2013; pp. 742–751. [Google Scholar]
  42. Dashora, S. Dangling References in Rust—Should You Be Worried? Available online: https://progressivecoder.com/dangling-references-in-rust (accessed on 24 May 2023).
  43. Marat Akhin, M.B. Kotlin Language Specification. Available online: https://kotlinlang.org/spec/introduction.html (accessed on 6 June 2023).
  44. Jetbrain. What is IntelliJ IDEA? Available online: https://www.jetbrains.com/idea/features (accessed on 6 June 2023).
  45. What Is Gradle? Available online: https://docs.gradle.org/current/userguide/what_is_gradle.html (accessed on 6 June 2023).
  46. Bansal, A. Java vs. Kotlin. Available online: https://www.baeldung.com/kotlin/java-vs-kotlin (accessed on 6 June 2023).
  47. JavaParser Repository. Available online: https://github.com/javaparser/javaparser (accessed on 6 June 2023).
  48. Ben-Kiki, O.; Evans, C.; döt Net, I. YAML. Available online: https://yaml.org/spec (accessed on 6 June 2023).
  49. Mitchell, J.C. Foundations for Programming Languages; MIT Press: Cambridge, CA, USA, 1996; Volume 1. [Google Scholar]
  50. Pankaj. Java Singleton Design Pattern Best Practices with Examples. Available online: https://www.digitalocean.com/community/tutorials/java-singleton-design-pattern-best-practices-examples#singleton-pattern-principles (accessed on 6 June 2023).
  51. Javapoint. Java Swing Tutorial. Available online: https://www.javatpoint.com/java-swing (accessed on 6 June 2023).
  52. Orcale. ThreadPoolExecutor. Available online: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html (accessed on 6 June 2023).
  53. Orcale. Driver. Available online: https://docs.oracle.com/javase/8/docs/api/java/sql/Driver.html (accessed on 6 June 2023).
  54. Noble, J. Iterators and encapsulation. In Proceedings of the 33rd International Conference on Technology of Object-Oriented Languages and Systems TOOLS 33, Mont-Saint-Michel, France, 5–8 June 2000; IEEE: Piscataway, NJ, USA, 2000; pp. 431–442. [Google Scholar]
  55. Language, T.R.P. Option Library. Available online: https://doc.rust-lang.org/core/option/enum.Option.html (accessed on 6 June 2023).
  56. Oracle. Java Optional. Available online: https://www.oracle.com/technical-resources/articles/java/java8-optional.html (accessed on 6 June 2023).
  57. Daniel. Java 8 Optional Best Practices and Wrong Usage. Available online: http://dolszewski.com/java/java-8-optional-use-cases (accessed on 6 June 2023).
  58. Banerjee, S.; Clapp, L.; Sridharan, M. Nullaway: Practical type-based null safety for java. In Proceedings of the 2019 27th ACM Joint Meeting on European Software Engineering Conference and Symposium on the Foundations of Software Engineering, New York, NY, USA, 26–30 August 2019; pp. 740–750. [Google Scholar]
  59. Brotherston, D.; Dietl, W.; Lhoták, O. Granullar: Gradual nullable types for java. In Proceedings of the 26th International Conference on Compiler Construction, New York, NY, USA, 5–6 February 2017; pp. 87–97. [Google Scholar]
  60. Papi, M.M.; Ali, M.; Correa, T.L.; Perkins, J.H.; Ernst, M.D. Practical Pluggable Types for Java. In Proceedings of the 2008 International Symposium on Software Testing and Analysis, New York, NY, USA, 20–24 July 2008; ISSTA ’08. pp. 201–212. [Google Scholar]
  61. Framework, C. The Checker Framework: Pluggable Type-checking for Java. Available online: https://github.com/typetools/checker-framework (accessed on 26 December 2023).
  62. Abi-Antoun, M.; Aldrich, J. Static extraction and conformance analysis of hierarchical runtime architectural structure using annotations. In Proceedings of the 24th ACM SIGPLAN Conference on Object Oriented Programming Systems Languages and Applications, New York, NY, USA, 25–29 October 2009; pp. 321–340. [Google Scholar]
  63. Andrew. Linter. Available online: https://github.com/mcandre/linters#what-is-a-linter (accessed on 29 May 2023).
  64. Framework, C. MustCall (Checker-Framework 3.42.0 API). Available online: https://checkerframework.org/api/org/checkerframework/checker/mustcall/qual/MustCall.html (accessed on 5 January 2024).
  65. Framework, C. EnsuresCalledMethods (Checker-Framework 3.42.0 API). Available online: https://checkerframework.org/api/org/checkerframework/checker/calledmethods/qual/EnsuresCalledMethods.html (accessed on 5 January 2024).
  66. Oracle. What Is an Interface? Available online: https://docs.oracle.com/javase/tutorial/java/concepts/interface.html (accessed on 5 January 2024).
  67. W3Schools. Java Enums. Available online: https://www.w3schools.com/java/java_enums.asp (accessed on 5 January 2023).
  68. Clarke, D.; Drossopoulou, S. Ownership, encapsulation and the disjointness of type and effect. In Proceedings of the 17th ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications, New York, NY, USA, 4–8 November 2002; pp. 292–310. [Google Scholar]
  69. Cardelli, L.; Leroy, X. Abstract types and the dot notation. In Proceedings of the IFIP TC2 Working Conference on Programming Concepts and Methods, Tiberias, Israel, 2–5 April 1990; pp. 479–504. [Google Scholar]
Figure 3. Box plot describing the execution time of Java and SlimeJava in milliseconds.
Figure 3. Box plot describing the execution time of Java and SlimeJava in milliseconds.
Electronics 13 00350 g003
Table 1. Expected saved tweet states.
Table 1. Expected saved tweet states.
ListIndexTextTimestampCommentList
0My first tweetOne hour beforeA
1My second tweetNowA, B
Table 2. Actual saved tweet states.
Table 2. Actual saved tweet states.
ListIndexTextTimestampCommentList
0My first tweetOne hour beforeA, B
1My second tweetNowA, B
Table 4. Evaluation of SlimeJava’s functionality.
Table 4. Evaluation of SlimeJava’s functionality.
OperationsAlias TypeExampleSlimeJava
P_INITIGnew Prototype(obj)
P_CLONEOGreturn mObj.clone()
ELObject obj = prototype.clone()
I_INITEL, IGCollection collection = new Collection(objects)
IGnew Container(collection)
I_CREATEIG, OGreturn new Iterator(mCollection)
ELIterator iterator = container.createIterator()
I_FIRSTOGreturn mCollection.get(0)
ELObject obj = iterator.first()
I_CURRENTOGreturn mCollection.get(mCurrentIndex)
ELObject obj = iterator.current()
I_NEXTILObject nextObj = collection.getOrNull(++mCurrentIndex) 1
OGreturn nextObj
ELObject obj = iterator.next()
I_DONEOGreturn mCurrentIndex > collection ? true : false
ELboolean isDone = iterator.isDone()
M_INITEL, IGCareTaker careTaker = new CareTaker(savedStates)
EL, IGOriginator originator = new Originator(args)
M_SAVEIG, OGreturn new Memento(mState)
ELMemento memento = originator.saveToMemento()
IGcareTaker.savedStates.add(memento)
M_RESTOREELMemento memento = careTaker.savedStates.get(0)
IGoriginator.restoreFromMemento(memento)
(◯ implemented with blocking functionality; ∆ supported partially or in specific cases for blocking functionality. 1 The ownership system does not support objects of null type, which would lead to dangling references [13,42].
Table 5. Comparison between SlimeJava and other existing research approaches.
Table 5. Comparison between SlimeJava and other existing research approaches.
OperationsJava (Ver. 17)Noble et al. Ownership Type [26]Potanin et al. OIGJ [21]Boyapati SafeJava [27]RustSlimeJava
P_INITXX
P_CLONEXXXX 1
I_INITXXX 2
I_CREATEXX 3 X 2
I_FIRSTX 3 X 2
I_CURRENTX 3 X 2
I_NEXTX 3 2
I_DONEX 4 3 X 2
M_INITXX
M_SAVEXX
M_RESTOREXX
(◯ implemented with blocking functionality; ∆ supported partially or in specific cases for blocking functionality; X not supported for blocking or unable to support this operation). 1 While immutable binding in Rust is able to prevent representation exposure related to copying immutable objects, it cannot address the issue when it comes to mutable objects. 2 As mentioned in the introduction, the lack of transitivity of immutability may result in representation exposure. 3 Because objects can not be initialized through external references, functionalities associated with iterators will have some limitations. 4 Unexpected modifications by mutable aliases will be prevented only when the return type is the primitive type boolean.
Disclaimer/Publisher’s Note: The statements, opinions and data contained in all publications are solely those of the individual author(s) and contributor(s) and not of MDPI and/or the editor(s). MDPI and/or the editor(s) disclaim responsibility for any injury to people or property resulting from any ideas, methods, instructions or products referred to in the content.

Share and Cite

MDPI and ACS Style

Zhuang, Y.-Y.; Kuo, W.; Tseng, S.-C. Resolving the Java Representation Exposure Problem with an AST-Based Deep Copy and Flexible Alias Ownership System. Electronics 2024, 13, 350. https://0-doi-org.brum.beds.ac.uk/10.3390/electronics13020350

AMA Style

Zhuang Y-Y, Kuo W, Tseng S-C. Resolving the Java Representation Exposure Problem with an AST-Based Deep Copy and Flexible Alias Ownership System. Electronics. 2024; 13(2):350. https://0-doi-org.brum.beds.ac.uk/10.3390/electronics13020350

Chicago/Turabian Style

Zhuang, Yung-Yu, Wei Kuo, and Shang-Chun Tseng. 2024. "Resolving the Java Representation Exposure Problem with an AST-Based Deep Copy and Flexible Alias Ownership System" Electronics 13, no. 2: 350. https://0-doi-org.brum.beds.ac.uk/10.3390/electronics13020350

Note that from the first issue of 2016, this journal uses article numbers instead of page numbers. See further details here.

Article Metrics

Back to TopTop