Sharing components between programming languages by use of polymorphic proxy6901588
Abstract
A method and system for representing and implementing a concept between two functional domains (e.g., programming languages) by using a proxy component in a first domain to wrap a component of a second domain, where the proxy component has a semantic usability in the first domain closely corresponding to the semantic usability of the underlying component from the second domain. Further, provided is a method and system for automatically generating such a proxy component. Such proxy components may be used to gradually transform a digital entity (e.g., a software application) from a first digital domain to a second digital domain. Further, such proxy components may be generated using models that transform a component of a first domain to a component (e.g., a proxy component) of a second domain.
Claims
1. A computer program product for representing and implementing a concept from a first functional domain within a second functional domain, wherein the first functional domain includes a first component representing the concept in the first functional domain, the first component having a type and a first semantic usability in the first functional domain, and wherein the computer program product comprises:
a computer-readable medium having computer-readable signals stored thereon,
wherein the signals define a proxy component representing the concept in the second functional domain, the proxy component defined to cause execution of the first component in the first functional domain, and
wherein the proxy component has a second semantic usability in the second functional domain and the second semantic usability closely corresponds to the first semantic usability,
the proxy having been generated by analyzing the first component to determine its type.
2. The computer program product of claim 1, wherein the proxy component was generated by a transformation performed on the first component.
3. The computer program product of claim 1, wherein the first semantic usability of the first component is defined at least by a first mutability, and
wherein the second semantic usability of the proxy component is defined at least by a second mutability of the proxy component determined from at least the first mutability of the first component.
4. The computer program product of claim 1, wherein the first semantic usability of the first component is defined at least by a first accessibility, and
wherein the second semantic usability of the proxy component is defined at least by a second accessibility of the proxy component determined from at least the first accessibility of the first component.
5. The computer program product of claim 1, wherein the first semantic usability of the first component is defined at least by a first instantiability, and
wherein the second semantic usability of the proxy component is defined at least by a second instantiability of the proxy component determined from at least the first instantiability of the first component.
6. The computer program product of claim 1, wherein the first semantic usability of the first component is defined at least by a first inheritability, and
wherein the second semantic usability of the proxy component is defined at least by a second inheritability of the proxy component determined from at least the first inheritability of the first component.
7. The computer program product of claim 1, wherein the first semantic usability of the first component is defined at least by a first context usability, and
wherein the second semantic usability of the proxy component is defined at least by a context usability of the proxy component determined from at least the first context usability of the first component.
8. The computer program product of claim 1, wherein the first semantic usability of the first component is defined at least by a first polymorphicability, and
wherein the second semantic usability of the proxy component is defined at least by a polymorphicability of the proxy component determined from at least the first polymorphicability of the first component.
9. The computer program product of claim 1, wherein the first functional domain is the Java programming language and the second functional domain is the C++ programming language.
10. The computer program product of claim 9, wherein the first component is a first Java component and the proxy component is a C++ proxy component, and
wherein the C++ proxy component is aware of a context in which it is defined.
11. The computer program product of claim 9, wherein the first component is a first Java component and the proxy component is a C++ proxy component, and
wherein the C++ proxy component includes one or more proxy support elements.
12. The computer program product of claim 11, wherein the C++ proxy component is a C++ proxy class.
13. The computer program product of claim 12, wherein one or more proxy support elements of the C++ proxy class allow an instance of the C++ proxy class to be context-aware.
14. The computer program product of claim 13, wherein one or more of the proxy support elements that allow an instance of the C++ proxy class to be context-aware are context constructors.
15. The computer program product of claim 13, wherein one of the proxy support elements allows an instance of the C++ proxy class to be aware of being a C++ proxy instance field.
16. The computer program product of claim 13, wherein one of the proxy support elements allows an instance of the C++ proxy class to be aware of being a C++ proxy static field.
17. The computer program product of claim 13, wherein one of the proxy support elements allows an instance of the C++ proxy class to be aware of being a C++ proxy array element.
18. The computer program product of claim 13, wherein one of the proxy support elements allows an instance of the C++ proxy class to be aware of being a C++ proxy stand-alone object.
19. The computer program product of claim 13, wherein the C++ proxy class includes a proxy layer, and awareness of the contexts is required by the proxy layer.
20. The computer program product of claim 19, wherein the proxy layer is coded using the Java Native Interface.
21. The computer program product of claim 12, wherein at least one of the proxy support elements of the C++ proxy class allows usage of null in C++ corresponding to usage of a null Java object reference in Java.
22. The computer program product of claim 21, wherein the C++ proxy class includes a conversion constructor to create a stand-alone proxy instance of the C++ proxy class initialized to not refer to any Java instance.
23. The computer program product of claim 12, wherein at least one of the proxy support elements provides a semantic usability to the C++ proxy class that closely corresponds to the semantic usability of a Java cast expression corresponding to the Java component.
24. The computer program product of claim 23, wherein the C++ proxy class includes a static class method that provides the semantic usability to the C++ proxy class that closely corresponds to the semantic usability of a Java cast expression corresponding to the Java component.
25. The computer program product of claim 12, wherein at least one of the proxy support elements provides an ability to map an instance of the C++ proxy class to an object that represents the Java component.
26. The computer program product of claim 25, wherein the C++ proxy class includes a framework support method that provides the ability to map an instance of the C++ proxy class to an object that represents the Java component.
27. The computer program product of claim 9, wherein the first component is a Java package and the proxy component is a C++ namespace.
28. The computer program product of claim 9, wherein the first component is a Java class and the proxy component is either a C++ proxy class or a C++ proxy struct.
29. The computer program product of claim 28, wherein the proxy component includes one or more proxy support elements.
30. The computer program product of claim 29, wherein one or more of the proxy support elements allow an instance of the C++ proxy class to be context-aware.
31. The computer program product of claim 28, wherein the Java class is declared abstract, and the C++ proxy component is instantiable.
32. The computer program product of claim 9, wherein the first component is a Java array type and the proxy component is either a C++ proxy class or a C++ proxy struct.
33. The computer program product of claim 32, wherein the proxy component includes one or more proxy support elements.
34. The computer program product of claim 33, wherein one or more of the proxy support elements allow an instance of the proxy component to be context-aware.
35. The computer program product of claim 32, wherein the proxy component includes a length field corresponding to a length attribute of the Java array.
36. The computer program product of claim 32, wherein the Java array has an element type and the proxy component has an element type corresponding to the Java array's element type, and wherein the proxy component includes one or more array subscript operators that return a context-aware instance of the proxy component's type.
37. The computer program product of claim 32, wherein the Java array has a primitive element type, and the proxy component has a primitive element type corresponding to the lava array's element type and includes one or more array subscript operators that return a primitive value.
38. The computer program product of claim 9, wherein the first component is a Java interface and the proxy component is either a C++ proxy class or a C++ proxy struct.
39. The computer program product of claim 38, wherein the first component includes one or more proxy support elements.
40. The computer program product of claim 39, wherein one or more of the proxy support elements allow an instance of the proxy component to be context-aware.
41. The computer program product of claim 38, wherein the proxy component is instantiable.
42. The computer program product of claim 9, wherein the first component is a Java method and the proxy component is a C++ proxy method.
43. The computer program product of claim 42, wherein the C++ proxy method has a constness determined from a mutability of the Java method.
44. The computer program product of claim 42, wherein the C++ proxy method declares a return type that has a mutability determined from a mutability of a return type declared by the Java method.
45. The computer program product of claim 42, wherein the C++ proxy method passes one or more arguments, each argument having a mutability determined from a mutability of a corresponding argument passed by the Java method.
46. The computer program product of claim 42, wherein the C++ proxy method is defined to throw an exception based on the Java method being defined to throw an exception.
47. The computer program product of claim 42, wherein the C++ proxy method is defined not to throw an exception based on the Java method being defined to throw an exception.
48. The computer program product of claim 42, wherein whether the C++ proxy method is declared virtual or not declared virtual is determined from one or more of the following aspects of the Java method: polymorphicability, mutability, and instantiability.
49. The computer program product of claim 9, wherein the first component is a Java field and the proxy component is a C++ proxy field.
50. The computer program product of claim 49, wherein the C++ proxy field is declared to be of type C++ proxy class, the C++ proxy class including one or more proxy support elements that allow an instance of the C++ proxy class to be context-aware, such that an instance of the C++ proxy field is context-aware.
51. The computer program product of claim 49, wherein the Java field is of primitive type and the C++ proxy field is declared to be of type C++ primitive proxy class.
52. The computer program product of claim 51, wherein the C++ primitive proxy class includes one or more proxy support elements that allow an instance of the C++primitive proxy class to be context-aware, such that an instance of the C++ proxy field is context-aware.
53. The computer program product of claim 45, wherein one or more proxy support elements of the C++ primitive proxy class is an overloaded operator that permits instances the C++ primitive proxy class 52 to be used on the left-hand side of one or more syntactical productions.
54. The computer program product of claim 43, wherein the C++ proxy field has a mutability determined from a mutability of the Java field.
55. A system including instructions stored on a computer readable medium for modeling a first component of a first functional domain, wherein the first component defines a first concept and includes one or more subcomponents, the system comprising:
means for receiving the first component; and
means for generating a first model of the first component, including:
means for generating, for each subcomponent of the first component, a discrete element of the first model to represent the subcomponent; and
means for providing the first model with a property of relationship awareness such that, if a first discrete element or attribute of the first model is changed, the first model is operative to:
determine if the first discrete element or attribute has one or more elements and attributes related to the first discrete element or attribute,
if the first discrete element or attribute has one or more related elements and attributes, determine whether to change the one or more related elements, and
if it is determined to change one or more related elements and attributes, change such one or more elements and attributes in accordance with the changed first discrete element or attribute.
56. A system for modeling including instructions stored on a computer readable medium a first component of a first functional domain, wherein the first component defines a first concept and includes one or more subcomponents, the system comprising:
a model generator to receive the first component, to generate a first model including:
for each subcomponent of the first component, a discrete element of the first model representing the subcomponent; and
one or more model concepts that provide the first model with a property of relationship awareness such that, if a first discrete element or attribute of the first model is changed, the first model is operative to:
determine if the first discrete element or attribute has one or more elements and
attributes related to the first discrete element or attribute,
if the first discrete element or attribute has one or more related elements and
attributes, determine whether to change the one or more related elements, and
if it is determined to change one or more related elements and attributes, change such one or more elements and attributes in accordance with the changed first discrete element or attribute.
57. A computer program product for modeling a first component of a first functional domain, wherein the first component defines a first concept and includes one or more subcomponents, the system comprising:
a computer-readable medium having computer-readable signals stored thereon,
wherein the signals define a model generator to receive the first component, to generate a first model including:
for each subcomponent of the first component, a discrete element of the first model representing the subcomponent; and
one or more model concepts that provide the first model with a property of relationship awareness such that, if a first discrete element or attribute of the first model is changed, the first model is operative to:
determine if the first discrete element or attribute has one or more elements and attributes related to the first discrete element or attribute,
if the first discrete element or attribute has one or more related elements and attributes, determine whether to change the one or more related elements, and
if it is determined to change one or more related elements and attributes, change such one or more elements and attributes in accordance with the changed first discrete element or attribute.
58. A method executed from instructions stored on a computer readable medium of transforming a first component of a first domain to a proxy component of a second domain, wherein the first component has a type and defines a first concept, the method comprising acts of:
(a) analyzing the first component to determine its type; and
(b) transforming the first component into the proxy component in accordance with the determined type, wherein the proxy component defines at least the concept defined by the first component.
59. The method of claim 58, wherein the first component is in parsed form.
60. The method of claim 58, wherein the first component is a source component.
61. The method of claim 58, wherein the first component is a compiled component.
62. The method of claim 58, wherein act (b) includes:
(i) generating a model from the first component; and
(ii) generating the proxy component from the model.
63. The method of claim 58, wherein the proxy component has a semantic usability in the second domain closely corresponding to the semantic usability of the first component in the first domain.
64. The method of claim 63, wherein the proxy component includes one or more proxy support elements to allow an instance of the proxy component to be context-aware.
65. The method of claim 58, wherein the first domain is a first programming language.
66. The method of claim 65, wherein the first programming language is a high-level programming language.
67. The method of claim 66, wherein the first programming language is Java.
68. The method of claim 67, wherein the second domain is C++.
69. The method of claim 68, wherein:
act (a) includes determining that the type of the first component is a Java class, and
act (b) includes transforming the Java class into a C++ proxy class.
70. The method of claim 69, wherein act (b) includes: generating, within the C++ proxy class, one or more proxy support elements.
71. The method of claim 70, wherein one or more of the proxy support elements allow an instance of the C++ proxy class to be context-aware.
72. The method of claim 69, wherein the Java class is declared abstract, and act (b) includes:
defining the C++ proxy component to be instantiable.
73. The method of claim 68, wherein:
act (a) includes determining that the type of the first component is a Java array, and
act (b) includes transforming the Java array into a C++ proxy class.
74. The method of claim 73, wherein act (b) includes:
generating, within the C++ proxy class, one or more proxy support elements.
75. The method of claim 74, wherein one or more of the proxy support elements allow an instance of the proxy component to be context-aware.
76. The method of claim 73, wherein the Java array has a length attribute, and act (b) includes:
defining the proxy class to include a length field corresponding to the length attribute of the Java array.
77. The method of claim 73, wherein the Java array has an element type, and act (b) includes:
defining the C++ proxy class to have an element type corresponding to the element type of the Java array; and
generating, within the C++ proxy class, one or more array subscript operators that return a context-aware instance of the proxy class's type.
78. The method of claim 73, wherein the Java array has a primitive element type, and act (b) includes:
defining the C++ proxy class to have a primitive element type corresponding to the Java array primitive element type; and
generating one or more array subscript operators that return a primitive value.
79. The method of claim 68, wherein:
act (a) includes determining that the type of the first component is a Java method, and
act (b) includes transforming the Java method into a C++ proxy method.
80. The method of claim 74, wherein act (b) includes:
defining the C++ proxy method to have a constness based on a mutability of the Java method.
81. The method of claim 79, wherein act (b) includes:
defining the C++ proxy method to declare a return type that has a mutability based on a mutability of a return type declared by the Java method.
82. The method of claim 79, wherein act (b) includes:
defining the C++ proxy method to pass one or more arguments; and
defining each argument to have a mutability based on a mutability of a corresponding argument passed by the Java method.
83. The method of claim 79, wherein act (b) includes:
defining the C++ proxy method to throw an exception based on the Java method being defined to throw an exception.
84. The method of claim 79, wherein act (b) includes:
defining the C++ proxy method not to throw an exception based on the Java method being defined to throw an exception.
85. The method of claim 79, wherein act (b) includes:
determining whether the C++ proxy method is declared virtual or not declared virtual from one or more of the following aspects of the Java method: polymorphicability, mutability, and instantiability; and
defining the C++ proxy method to be declared virtual or not declared virtual based on the determination.
86. The method of claim 68, wherein:
act (a) includes determining that the type of the first component is a Java field, and
act (b) includes transforming the Java field into a C++ proxy field.
87. The method of claim 86, wherein act (b) includes:
declaring the C++ proxy field to be of type C++ proxy class; and
generating, within the C++ proxy class, one or more proxy support elements that allow an instance of the C++ proxy class to be context-aware, such that an instance of the C++ proxy field is context-aware.
88. The method of claim 86, wherein the Java field is of primitive type, act (b) including:
defining the C++ proxy field to be declared of type C++ primitive proxy class.
89. The method of claim 88, wherein act (b) further includes:
declaring the C++ proxy field to be of type C++ proxy class; and
generating, within the C++ proxy class, one or more proxy support elements that allow an instance of the C++ proxy class to be context-aware, such that an instance of the C++ proxy field is context-aware.
90. The method of claim 88, wherein one or more of the proxy support elements of the C++ primitive proxy class is an overloaded operator that permits instances the C++ primitive proxy class 52 to be used on the left-hand side of one or more syntactical productions.
91. The method of claim 86, wherein act (b) includes:
determining a mutability of the Java field; and
defining the C++ proxy field to have a mutability determined from the mutability of the Java field.
92. The method of claim 68, wherein act (b) includes:
(i) transforming the Java component into a C++ proxy class that includes one or more proxy support elements.
93. The method of claim 92, wherein one or more proxy support elements of the C++ proxy class allow an instance of the C++ proxy class to be context-aware.
94. The method of claim 93, wherein one of the proxy support elements allows an instance of the C++ proxy class to be aware of being a C++ proxy instance field.
95. The method of claim 93, wherein one of the proxy support elements allows an instance of the C++ proxy class to be aware of being a C++ proxy static field.
96. The method of claim 93, wherein one of the proxy support elements allows an instance of the C++ proxy class to be aware of being a C++ proxy array element.
97. The method of claim 93, wherein one of the proxy support elements allows an instance of the C++ proxy class to be aware of being a C++ proxy stand-alone object.
98. The method of claim 93, wherein act (b)(i) includes:
generating a proxy layer, and wherein awareness of the contexts is required by the proxy layer.
99. The method claim 98, wherein act (b)(i) includes:
coding the proxy layer using the Java Native Interface.
100. The method of claim 92, wherein at least one of the proxy support elements of the C++proxy class allows usage of null in C++ corresponding to usage of a null Java object reference in Java.
101. The method of claim 100, wherein act (b)(i) includes:
generating a conversion constructor within the C++ proxy class, the conversion constructor for creating a stand-alone proxy instance of the C++ proxy class initialized to not refer to any Java instance.
102. The method of claim 92, wherein at least one of the proxy support elements provides a semantic usability to the C++ proxy class that closely corresponds to the semantic usability of a Java cast expression corresponding to the Java component.
103. The method of claim 102, wherein act (b)(i) includes:
generating a static class method within the C++ proxy class, the static class method providing the semantic usability to the C++ proxy class that closely corresponds to the semantic usability of a Java cast expression corresponding to the Java component.
104. The method of claim 92, wherein at least one of the proxy support elements provides an ability to map an instance of the C++ proxy class to an object that represents the Java component.
105. The method of claim 104, wherein act (b)(i) includes:
generating a framework support method within the C++ proxy class, the framework support method providing the ability to map an instance of the C++ proxy class to an object that represents the Java component.
106. The method of claim 69, wherein:
act (a) includes determining that the type of the first component is a Java interface, and
act (b) includes transforming the Java interface into a C++ proxy class.
107. The method of claim 106, wherein act (b) includes:
generating, within the C++ proxy class, one or more proxy support elements.
108. The method of claim 107, wherein one or more of the proxy support elements allow an instance of the C++ proxy class to be context-aware.
109. The method of claim 106, wherein act (b) includes:
defining the C++ proxy class to be instantiable.
110. The method of claim 58, wherein the first semantic usability of the first component is defined at least by a first mutability, and act (b) includes:
(i) determining a second mutability of the proxy component from at least the first mutability of the first component, wherein the second semantic usability of the proxy component is defined at least by the second mutability.
111. The method of claim 58, wherein the first semantic usability of the first component is defined at least by a first accessibility, and act (b) includes:
(i) determining a second accessibility of the proxy component from at least the first accessibility of the first component, wherein the second semantic usability of the proxy component is defined at least by the second accessibility.
112. The method of claim 58, wherein the first semantic usability of the first component is defined at least by a first instantiability, and act (b) includes:
(i) determining a second instantiability of the proxy component from at least the first instantiability of the first component, wherein the second semantic usability of the proxy component is defined at least by the second instantiability.
113. The method of claim 58, wherein the first semantic usability of the first component is defined at least by a first inheritability, and act (b) includes:
(i) determining a second inheritability of the proxy component from at least the first inheritability of the first component, wherein the second semantic usability of the proxy component is defined at least by the second instantiability.
114. The method of claim 58, wherein the first semantic usability of the first component is defined at least by a first context usability, and act (b) includes:
(i) determining a second context usability of the proxy component from at least the first context usability of the first component, wherein the second semantic usability of the proxy component is defined at least by the second context usability.
115. The method of claim 58, wherein the first semantic usability of the first component is defined at least by a first polymorphicability, and act (b) includes:
(i) determining a second polymorphicability of the proxy component from at least the first polymorphicability of the first component, wherein the second semantic usability of the proxy component is defined at least by the second polymorphicability.
116. A system including instructions stored on a computer readable medium for transforming a first component of a first domain to a proxy component of a second domain, wherein the first component has a type and defines a first concept, the system comprising:
means for analyzing the first component to determine its type; and
means for transforming the first component into the proxy component in accordance with the determined type, wherein the proxy component defines at least the concept defined by the first component.
117. A system including instructions stored on a computer readable medium for transforming a first component of a first domain to a proxy component of a second domain, wherein the first component has a type and defines a first concept, the system comprising:
a component transformer to receive as input the first component, to analyze the first component to determine its type, to transform the first component into the proxy component in accordance with the determined type, and to output the proxy component,
wherein the proxy component defines at least the concept defined by the first component.
118. A computer program product for transforming a first component of a first domain to a proxy component of a second domain, wherein the first component has a type and defines a first concept, the computer program product comprising:
a computer-readable medium having computer-readable signals thereon,
wherein the signals define a component transformer to receive as input the first component, to analyze the first component to determine its type, and to transform the first component into the proxy component in accordance with the determined type,
wherein the proxy component defines at least the concept defined by the first component.
119. A method executing on a computer comprising,
for a program component expressed in a first domain, the component being of any arbitrary type belonging to a set of different types of program components expressed in the first domain, the type having first relationships to other types in the set,
using a predefined mapping of relationships between types in the set to relationships between types in a second domain to transform the first component to a second component in the second domain, the second component being of a type belonging to a set of different types of program components in the second domain, the type having relationships to components of other types in the second domain that closely correspond to the first relationships.
Description
BACKGROUND
A common problem in software engineering is sharing or making available a software component written in one language to software written in another language. A common solution to sharing a software component with another language is to "wrap" the component with an adapter layer or proxy layer of code. This proxy layer allows software written in another language to interface with the wrapped component. Depending on the software involved, different wrapping technologies may be used. For example, to make a Java™ component available to non-Java software such as a C program, the Java Native Interface (JNI) may be used to develop a proxy layer for the component.
Sharing a component of a first language with other languages is more complex than merely sharing or providing access to data such as, for example, by sharing a database. Typically, programming languages have semantics to be accounted for when developing a proxy layer for a component. This difficulty of developing a proxy layer for a component increases with increased complexity of the semantics of the component.
The Common Object Request Broker Architecture (CORBA) is a technology often used to wrap a component. CORBA, however, is a technology geared more for creation of distributed software systems than for wrapping software components. Accordingly, CORBA includes coding restraints necessary for its other uses that are unnecessary for developing a proxy layer for a component. Further CORBA imposes its own semantics. Therefore, CORBA adds even more complexity to developing a proxy layer for a component.
Another common problem in software engineering is transferring (i.e., porting) a program, or part of a program, written in a first programming language (e.g., C++) to a second programming language (e.g., Java). An ideal solution to this problem would be an automatic translation tool that could automatically translate source code from a first language into source code of a second language that is maintainable by humans. The applicants, however, are not aware of such a translation tool. A typical solution to porting a program is to create manually (i.e., write the code for) the program in the second programming language. Such manual porting, however, involves a risk that the program will not work properly, or at least not have the same behavior as the legacy program. This risk, as well as the difficulty in scheduling and managing the porting process, increases with increased size and complexity of the application. For example, for a program with over a million lines of code, the risk, management, and scheduling of a manual conversion may make manual conversion an unrealistic option.
SUMMARY
The semantics of a programming language affect the usability of a component in the domain of the programming language. If a component of a first language has a usability in accordance with the semantics of the first language (i.e., semantic usability), it is difficult to wrap this component such that the wrapper or proxy component has a corresponding usability in a second language. Specifically, it is difficult to develop such a proxy component that has such a corresponding usability in the second language in accordance with the different semantics of the second language.
If the semantic usability of a proxy component does not closely correspond to the semantic usability of an underlying component of another domain, the proxy component may have restricted usability as a specific solution to a specific problem under limited circumstances. Such a restricted proxy component is not generally useful in the programming language for which it is written.
Accordingly, provided is a method and apparatus for representing and implementing a concept between two functional domains (e.g., programming languages) by using a proxy component in a first domain to wrap a component of a second domain, where the proxy component has a semantic usability in the first domain closely corresponding to the semantic usability of the underlying component from the second domain. Further, provided is a method and apparatus for automatically generating such a proxy component. Such proxy components may be used to gradually transform a digital entity (e.g., a software application) from a first domain to a second domain. Further, such proxy components may be generated using robust models that transform a component of a first domain to a component (e.g., a proxy component) of a second domain.
BRIEF DESCRIPTION OF THE DRAWINGS
In the drawings
FIG. 1 is a block diagram illustrating an example proxy component;
FIG. 2 is a block diagram illustrating an example proxy component and related components of a functional domain;
FIG. 3 is a Java code fragment illustrating an example of Java components;
FIG. 4 is a C++ code fragment illustrating an example of C++ proxy components wrapping the Java components of FIG. 3, but not having semantic usabilities closely corresponding to the semantic usabilities of the Java component of FIG. 3;
FIG. 5 is a C++ code fragment illustrating an example of a C++ proxy components wrapping the Java components of FIG. 3, and having semantic usabilities closely corresponding to the semantic usabilities of the Java component of FIG. 3;
FIG. 6 is a C++ code fragment illustrating an example of C++ context constructors;
FIG. 7 is a C++ code fragment illustrating an example of a C++ proxy class;
FIG. 8 is a C++ code fragment illustrating an example of a C++ primitive proxy class;
FIG. 9 a Java code fragment illustrating an example of a Java components and Java assignment statements in several contexts, where the context usability of the Java components each have a context usability permitting usage in each of several contexts;
FIG. 10 is a C++ code fragment illustrating an example of a C++ proxy components wrapping the Java components of FIG. 8, and C++ assignment statements in several contexts and corresponding to the Java assignment statements of FIG. 8, where the C++ proxy components do not have a context usability permitting usage in each of the several contexts;
FIG. 11 a C++ code fragment illustrating example C++ proxy components wrapping the Java components of FIG. 8, and C++ assignment statements in several contexts and corresponding to the Java assignment statements of FIG. 8, where the C++ proxy components have a context usability permitting usage in each of the several contexts;
FIG. 12 is a flowchart illustrating an example method of determining a mutability of a Java method argument;
FIG. 13 is a block diagram illustrating an example of a relationship between a C++ proxy method argument, Java method argument, and underlying Java object;
FIG. 14 is a block diagram illustrating an example of an Observer pattern;
FIG. 15 is a block diagram illustrating an example of a Template Method pattern;
FIG. 16 is a block diagram illustrating an example of an incorrect C++ proxy implementation of a Java callback pattern;
FIG. 17 is a block diagram illustrating an example of a correct C++ proxy implementation of a Java callback pattern;
FIG. 18 is a data flow diagram illustrating an example of a system for compiling, linking, and running a C++ application having C++ proxy components;
FIG. 19 is a data flow diagram illustrating an example of a system for transforming a component from a first digital domain to a second digital domain;
FIG. 20 is a flowchart illustrating an example of a method of transforming a component from a first domain to a second domain;
FIG. 21 is a flowchart illustrating an example of a method of analyzing a component being transformed from a first domain to a second domain;
FIG. 22 is a flowchart illustrating an example of a method of generating a C++/Java cross-domain callback pattern from a Java class or interface;
FIG. 23 is a data flow diagram illustrating an exemplary aspect of the system of FIG. 19;
FIG. 24 is a Java code fragment illustrating an example of Java class;
FIG. 25 is a screen shot illustrating an example of a digital representation of a robust Java model;
FIG. 26 is a screen shot illustrating an example embodiment of a digital representation of a robust C++ model; and
FIGS. 27a-27f are block diagrams illustrating an example of a bottom-up port-by-proxy process.
TERMINOLOGY
Software engineering employs a complex lexicon of terminology, wherein a single term may have several meanings, depending on the context and the individual using the term. Accordingly, to clarify uses of terminology in the present application, the following terms are defined as follows.
A "digital domain" as used herein is a digitally-based technology capable of digitally representing a concept. A concept may be anything from a simple concept such as, for example, a number, ranging to a complex concept such as, for example, a control system for a spacecraft or even a person. A concept has state (e.g., on, off) and may have behavior (i.e., the ability to change state—e.g., turn on, turn off). A dynamic concept is a concept having state and behavior such as, for example, a person, whereas a static concept is a concept having only state such as, for example, a number.
A "functional domain" as used herein is a digital domain that provides a framework for digitally representing a dynamic concept. Thus, functional domains have the ability to describe the state of a thing and the behavior of the thing, whereas other digital domains (i.e., non-functional domains) have the ability to describe only the state of a things. Functional domains include, but are not limited to, programming languages and object architectures such as, for example, CORBA and the Component Object Model (COM). Non-functional domains include, but are not limited to: document formats such as, for example, the Hyper-Text Markup Language (HTML) and the extensible Markup Language (XML); and data format standards such as, for example, MPEG for video images, and NTSC (National Television Standards Committee) for video signals.
Both functional and non-functional domains provide the ability to define data structures to organize information representing state. Functional domains, however, also provide operators and the ability to define higher-level functional abstractions (e.g., functions, procedures, methods) to represent behavior of the information. Functional abstractions may define and modify the state of the information.
A "digital entity" as used herein is a digital representation of a dynamic concept defined by a combination of inter-related components (or components of components, etc.). Typically, the dynamic concept represented by the digital entity is more complex than the concepts represented by the individual components of the digital entity. These individual components may be static or dynamic. If a functional domain is a programming language, a digital entity may be, for example, a program.
A functional domain may provide both a set of lexical productions and a method for representing concepts (both static and dynamic). The lexical productions use a specific alphabet of symbols as building blocks (i.e., terminal symbols) to form lexical tokens. Lexical tokens may be combined to assemble syntactical productions. A syntactical production may be considered a higher level abstraction than a lexical token. A syntactical production such as, for example, an expression (e.g. "A+B"), may be combined with one or more other syntactical productions to form another syntactical production such as, for example, a function.
For example, if the functional domain is the Java programming language, then the alphabet is the Unicode character set. Java's set of lexical productions uses the Unicode character set to create the lexical elements, including white spaces, comments, and lexical tokens. The lexical tokens of Java include identifiers (e.g., "A"), keywords (e.g., "public"), literals (e.g., "tes"), 1.2, ‘a’, -5), separators (e.g., ";"), and operators (e.g., "+"). These lexical tokens may be combined to form a syntactical production such as, for example, a type, a method, a field, a class, a compilation unit, etc.
A "component" as used herein is any element defined in a digital domain at a level of abstraction higher than lexical elements or any element derived from such elements. Thus, a component may be, for example, an expression, a variable, a line of code, a component, a procedure, a method, a function, an object, a program, etc.
A "source component" as used herein is a component in source-code form, i.e., written in source code. The use of the term "source" in this context ("source component") is unrelated to the use of the term "source" in the context of a transformation from a "source" domain to a "target domain" discussed in more detail below.
A "compiled component" as used herein is a compiled source component, i.e., a L component in machine-code form or in any form that is more suitable for machine interpretation than a source file. For example, a Java class file is more suitable for machine interpretation than a Java source file.
"Syntax" as used herein is the set of syntactical productions from which a source component of a functional domain can be composed. Syntax serves as a guide for a compiler, or equivalent program, that parses a source component and translates the source component into a compiled component.
A "high-level component" as used herein is a component that uses other components to represent a discrete concept In programming languages, high-level components may include high-level data components, high-level functional components, and objects (objects are defined below). Examples of high-level data components may include, but are not limited to, records and arrays, but would not include lower-level data components such as, for example, characters and integers. High-level functional components may include, but are not limited to, functions, procedures, and methods, but would not include lower-level functional components such as, for example, operators, which do not include other components, and fragments of code that do not represent a discrete concept such as, for example, a line of code of a multi-line procedure.
A "sub-component" as used herein is a component contained within a high-level component. For example, in the Pascal programming language, an expression may be a sub-component of a procedure. Further, in an object-oriented programming language (OOPL), discussed below in more detail, a method (which is a high-level component itself) may be a sub-component of a class.
A "high-level sub-component" as used herein is a high-level component contained within a high-level component. For example, in an OOPL, discussed below in more detail, a method may be considered a high-level sub-component of a class.
The term "proxy component" as used herein is a component defined in a first functional domain such that execution of the proxy component at runtime in the first functional domain results in execution of one or more components in the second functional domain. A proxy component may also be referred to herein as a wrapper. For example, if the first domain is the C++ programming language, and the second domain is the Java programming language, a C++ proxy class is a C++ class that delegates execution of methods and access of fields to a corresponding Java class.
The ability of a component to represent properly a concept in a functional domain depends on a number of factors. One factor is adherence to the syntax of the functional domain. Proper syntax is a minimum criteria that must be satisfied in order for successful compilation of a component.
The term "semantics" as used herein refers to other factors (besides syntax) that affect the ability of a component to represent properly a concept in a functional domain. Semantics may be considered as the meaning of a component in the context of a functional domain, as opposed to the structure of the component, i.e., the syntax.
Semantics as used herein may include run-time behavior such as, for example allocation and deallocation of memory, garbage collection, thread management, loading components into memory, and linking components (which also may occur at compile-time). Semantics of a digital domain may also include the usability of a component. For example, typical semantics of a programming language may require that the type of a variable be defined-before the variable is assigned a value. Another related usage rule for programming languages is that a variable may only be assigned a value of its defined type. Consequently, an attempt to assign a value to a variable of undefined type or to assign a value of a type different from that defined for a variable may result in a compile-time or run-time error. Depending on the particular functional domain, the semantics may be more complex. For example, depending on the semantics of a programming language, the usability of a component may be defined by at least the component's: accessibility, mutability, inheritability, instantiability, ability to be polymorphic, context-usability, or any combination thereof.
As used herein, "semantic usability" refers to the usability of a component in accordance with the semantics of the component's functional domain.
As used herein, "accessibility" refers to the ability of a component to be accessed by other components of a digital domain. For example, in the programming language C++, declaring a field as private limits the accessibility of the field to the declaring class.
As used herein, "mutability" refers to the ability of the components to be changed or to cause change. For example, in some programming languages, a component may be declared as "constant," and therefore not changeable (i.e., not mutable).
As used herein, "inheritability" refers to the ability of a component to have any of its properties inherited by other components. For example, in the Java programming language, a class declared with the keyword final cannot have any subclasses. In another example, in the C++ programming language, the virtual keyword being or not being used in method declarations has a big impact on the suitability of a class as a superclass.
As used herein in the context of an OOPL, "instantiate" means to create an instance (of an object). For example, if a method may be declared to return an object, then the implementation of the method may instantiate an object to return as the result.
As used herein, "instantiability" refers to the ability to create an instance of a component. For example, in the Java programming language, a Java interface cannot be instantiated, i.e., it is not instantiable.
As used herein, "polymorphicability" refers to the ability of a component to be polymorphic, i.e. the ability to exhibit the property of polymorphism. Polymorphism is typically, although not necessarily exclusively, a feature of an object-oriented programming language that permits an instance of a type (e.g., a class) to be referred to as an instance of a related type (e.g., superclass). Typically, although not necessarily, the relationship is an inheritance relationship. For example, an instance of a Java class may be referred to as an instance of a superclass extended by the Java class.
As used herein, "context-usability" refers to the ability of a component to be used in one or more contexts. For example, depending on the programming language, a component may be used in the context of: an instance field, a class field, an array element, a stand-alone object, a method argument and a return value. The semantics of a programming language control the use of a component in each of these contexts.
Other terms that are used frequently herein include: implement, invoke, abstract, concrete, and object. To "implement" as used herein means to provide code that performs a task associated with (or that is to be associated with) a declaration. In other words, to implement means to provide code that honors the contract made through (or that is to be made through) a declaration.
As used herein, "invoke" means to call or execute. For example, a proxy component may invoke a JNI function to access a Java component.
As used herein, an "abstract" component is a component defined such that it cannot be instantiated and a "concrete" component is a component defined to be instantiated.
Although the term "object" has several meanings in the field of software engineering, as used herein, an "object" is a component defining state and behavior, and is not limited to the strict definition of an object of an OOPL. An object is capable of representing a dynamic concept by itself. For example, in a procedural programming language, an object may be a component of a program that includes variables to define state and a functional abstraction such as, for example, a function, procedure or method, to define behavior and change the state of the variables.
In an OOPL, objects may have members such as, for example, methods that define a behavior of the object and fields that define the state of the containing object. A field may be of type object or type primitive. A primitive type is a fundamental data type provided by a language. Primitive types are specific for each programming language, but typically include characters, booleans and numbers such as, for example, integer.
For example, an object representing a column of a spreadsheet may contain: a method for calculating the sum of the values of all the cells in the column, a field of primitive type for representing the column number, and field of object type representing a cell of the column.
DETAILED DESCRIPTION
The following detailed description should be read in conjunction with the attached drawing in which similar reference numbers indicate similar structures. All references cited herein are hereby expressly incorporated by reference.
A proxy component has a semantic usability in a first domain that closely corresponds to the semantic usability of a shared component in a second domain when the proxy component is generally useful in the first domain, rather than restricted in use as a specific solution to a specific problem. To create a proxy component that has a semantic usability in a first domain that closely corresponds to the semantic usability of a shared component of a second domain, rules and heuristics based on knowledge of the semantics of the first and second domains may be applied to determine default component mappings between the two domains.
Close correspondence of semantic usability may be achieved from the application of these rules and heuristics. A default mapping is a mapping that may be applied generally for a particular type of component, but that may be overridden (i.e., customized) for a specific component if desired.
Different rules and heuristics may be applied depending on the domains involved and the components being mapped. The close correspondence of semantic usability between two components of different domains often is limited by the dissimilarities between the semantics of the two domains, as opposed to being limited by lack of knowledge from which the rules and heuristics are developed. For example, Pascal provides neither classes nor modifiers that enforce inheritability semantics. Consequently, the inheritability of a Pascal proxy component may not correspond too closely to the inheritability of a Java class.
FIG. 1 is a block diagram illustrating an example embodiment of sharing a component with other functional domains. First component 2 of first functional domain 6 has a first semantic usability 4 in the functional domain 6.
Proxy component 8 of second functional domain 16 has a semantic usability 10 in the second functional domain closely corresponding with semantic usability 4. Proxy component 8 includes a proxy layer 14 that wraps first component 2 such that first component 2 may be accessed by the second domain 16. Proxy component 8 may also include a utility layer 12 to facilitate access and use of the proxy component 8 by other components of the second functional domain 16. The semantic usability 10 may be implemented by utility layer 12, or proxy layer 14, or any combination of the two.
As described above, depending on the functional domain of the first component 2, the semantic usability of a first component 2 may be defined by at least the first component's: accessibility, mutability, inheritability, instantiability, polymorphicability, context usability, or any combination thereof. Accordingly, in various embodiments of a proxy component having a semantic usability closely corresponding to the semantic usability of the component that it wraps, the proxy component's accessibility, mutability, inheritability, instantiability, polymorphicability, context usability, or any combination thereof may be determined from the accessibility, mutability, inheritability, instantiability, polymorphicability, context usability, or any combination thereof of the wrapped component.
FIG. 2 is a block diagram illustrating example possible uses of proxy component 8 in functional domain 16 that may be impacted by the semantic usability of proxy component 8. For example, depending on the functional domain, proxy component 8 may inherit from components 18 and 20. For example, if the proxy component 8 were a C++ proxy component, the C++ proxy component 8 may inherit from C++ superclasses 18 and 20.
The proxy component 8 also may be inherited by another component 22 of functional domain 16. For example, if functional domain 16 is the C++ programming language, then C++ proxy component 8 may be a C++ proxy class serving as a super class for a C++ class 22.
Proxy component 8 may also be instantiated by a subcomponent 25 of component 24. For example, a C++ proxy class 8 may be instantiated by a C++ method 25 of a C++ class 24. The C++ proxy class 8 may be defined such that it may not be instantiated such as, for example, if C++ proxy class 8 includes pure virtual methods. Pure virtual methods are described below in more detail.
Proxy component 8 may be used as a field 28 of another component 26 of the second domain 16. For example, a C++ proxy class 8 may be used as an instance field 28 of a C++ class instance 26. Such use may depend on whether C++ proxy class 8 is accessible to C++ class instance 26 and whether C++ proxy class 8 may be used in this context (i.e., as an instance field).
Each of components 18-28 may be a proxy component itself, which adds another level of complexity to providing proxy component 8 that has a semantic usability in the second functional domain 16 closely corresponding to the semantic usability of first component 2 in the first domain.
If a component is shared between two domains, a C++ proxy component representing a first concept has a semantic usability closely corresponding to the semantic usability of a Java component representing the first concept. In one implementation, the Java Native Interface (JNI), a Java Application Programming Interface (API), may be used to code the proxy layer of the C++ proxy component. In another implementation, other interfaces such as, for example, Microsoft's Raw Native Interface (RNI) or Netscape's Java Runtime Interface (JRI), may be used to code the proxy layer of the C++ proxy component. In either implementation, the interface being used is supported by the targeted Java Virtual Machine (JVM).
Several example embodiments of components, semantics, usability, rules, heuristics and default mappings are described below in connection with Java, C++, and JNI. These embodiments are merely illustrative and not limiting, and may be applied to other functional domains.
Further, although each embodiment is discussed separately below, various aspects of each embodiment may overlap. Thus, the following separate descriptions are not meant to be exclusive to a particular embodiment, but each description may have application to other embodiments described below.
JNI
JNI is a Java API that may be used to develop a proxy layer between Java and C. A C program may call and be called by many other languages. Accordingly, a JNI proxy layer may be used as part of a proxy layer between Java and components of one of these many other languages. Thus, a developer who knows Java, JNI and C can use JNI to call Java components from C and vice versa.
If a developer also knows C++, the developer can use JNI to develop a proxy layer to call Java components from C++ or vice versa For a more detailed description of Java, see The Java Language Specification, 1996, by James Gosling et al (Gosling). For a more detailed description of C++, see The C++ Programming Language, Third Edition, 1997, by Bjarne Stroustrup (Stroustrup). For a more detailed description of JNI, see The Java Native Interface Programmer's Guide and Specification, 1999, by Sheng Liang (Liang).
Applicants have discovered, however, that JNI is a complex interface for which it is difficult to write proxy layers that execute properly even for developers that know both C and Java. This difficulty arises because JNI is a low-level API, almost at a level equivalent to assembly language. Consequently, a developer is forced to leave an object-oriented level of abstraction to utilize JNI. Further, JNI is not strongly type-checked, resulting in more bugs that typically are hard to find. For these and possibly other reasons, JNI currently is underutilized as an interface.
Although JNI addresses the requirements for low-level conversions, JNI does not address C++ specifically, beyond providing a C++ binding for JNI functions. Further, a developer must not only have knowledge of both C++ and Java, but know how to adapt C++ language components into C components that can invoke JNI functions that access Java components. Even if a developer has such knowledge, developing (i.e., writing, testing, and modifying) JNI code for a C++-to-Java interface adds more time and cost to the development of a program.
Another drawback of using JNI is that JNI source code is often difficult for a C or C++ developer to understand (except perhaps for the developer who wrote it). Further, a program using JNI often has excess code caused by several components of the application including JNI code (often redundant JNI code) to proxy to Java components. Such excess code is more likely if a developer is inexperienced in coding with JNI.
Three particular aspects of JNI that impact generating C++ proxy components are: JNI thread management; JNI object-reference types; and JNI context-dependency. JNI thread management imposes a requirement that a thread originally created in a C or C++ program must explicitly be attached to a JVM before other JNI calls can be made on this processing thread. Most JNI function invocations take a JNIEnv pointer as an argument. The JNIEnv pointer is specific to a thread, i.e., attaching the thread to the JVM returns a JNIEnv pointer that must not be used from other threads.
JNI has three object reference types, called local, global and weak global. Local references are only valid within the thread that created them, whereas global and weak-global references are valid among all threads that are attached to the JVM.
JNI provides different JNI functions for different JNI-object reference types. To create a new object reference for an existing object, a developer must choose between JNI functions: NewLocalRef; NewGlobalRef; or NewWeakGlobalRef. Accordingly, in JNI, one of the following JNI functions must be used to notify the JVM of the release of an object: DeleteLocalRef; DeleteGlobalRef; or DeleteWeakGlobalRef, depending on the object-reference type. See Liang for a more detailed description of the different types of JNI references.
JNI context-dependency means that JNI function invocation is context-dependent. In other words, JNI provides different functions depending on the context of the object invoking the function. JNI object contexts include: stand-alone object; class (i.e., static) variables; instance variables; and array elements. In contrast, in both C++ and Java, the use of an object is not context-dependent, i.e., to use an object, the developer does not have to pay attention to the context in which the object resides. As used herein, the "JNI contexts" refer to these four contexts.
For example, in C++ and Java, the developer does not have to differentiate between a string used as an array element, a string used as a stand-alone object or a string used as an instance- or class-field. The usage of the string in all cases is identical.
JNI provides context-dependent functions to retrieve (i.e., return) the value of an object reference. To retrieve the value of an instance object field, for example, JNI provides the GetObjectFieldi function. To retrieve the value of a static object field, the GetStaticObjectField function is used. To retrieve an element of an array, the GetObjectArrayElement function is used. JNI does not provide a specific function to retrieve stand-alone objects.
The impact of these aspects of JNI on creating C++ proxy components that have semantic usability closely corresponding to the Java components that they wrap is explained in more detail below.
Java Package→C++ Namespace
A Java package may be mapped by default to a C++ namespace. Accordingly, in an embodiment of sharing a Java component, a C++ namespace representing a first concept provides a proxy layer to a Java package representing the first concept, and has a semantic usability closely corresponding to the semantic usability of a Java package.
The Java component package naturally maps to the C++ component namespace. Both elements are abstraction mechanisms for organizing and modularizing components of a program. Although C++ uses namespaces only for managing the visibility of declarations, Java has accessibility rules associated with the membership of a class in a package. This accessibility aspect of a Java package is not readily adaptable to C++. Although the remaining aspects of a package map to a namespace relatively straightforward, there is a complication in relation with the default package in Java.
The Java default package is the package in which classes reside that have not had a package specified. As such, the Java default package may be considered analogous to the C++ unnamed namespace or C++ namespace without a name. C++ has two namespaces that do not have a name, the global namespace and an explicitly declared namespace without a name. The latter may be referred to herein as the unnamed namespace.
Any declarations made in the global namespace are visible from every other namespace. In contrast, the unnamed namespace typically is used to make declarations local to a compilation unit. Neither the global namespace nor the unnamed namespace represent all aspects of the Java default package. The compilation-unit-local property of the C++ unnamed namespace renders the C++ unnamed namespace unsuitable as the C++ component corresponding to the Java default package. A namespace named "default" (or some such name) may be used to represent the default package, but such a namespace may clash with a namespace named "default".
Accordingly, in an aspect of mapping a Java package to a C++ namespace, Java packages are mapped to C++ namespaces of the same name with the C++ namespace hierarchy mirroring the Java package hierarchy. Optionally, the Java default package may map to the global namespace.
Even though namespaces are a part of the C++ standard, C++ compilers that do not support namespaces or have bugs associated with the use of namespaces may still be in use. To support the use of C++ proxy component on such compilers, in another aspect of mapping a Java package to a C++ proxy component, the package name may be made part of a C++ proxy class name, i.e. a fully qualified name may be generated. For example, the String class of the java.lang package may be wrapped by a C++ class named java_lang_String).
Java Interface→Instantiable C++ Proxy Class
A Java interface is a Java reference type whose members are abstract methods and public static final fields representing constant values or constant references. The Java interface is used to establish a "calling contract" that can be implemented by concrete classes. For example, a Java "factory" method declared to return an interface guarantees (i.e., contracts) to other Java components that it will return an instance of a concrete class that implements the Java interface. In other words, the factory method guarantees that it will return an object that meets minimum criteria defined by the interface.
Java interface in this context is a Java data abstraction similar to a Java class. Thus, the use of the term "Java interface" in this context should not be confused with any use of the term "interface" to describe a C++/Java proxy layer.
Another possible use for a Java interface is as an empty interface (i.e., so-called "marker interface") which does not declare any methods. Yet another use for a Java interface is as a container for constant values.
Java semantics do not allow Java interfaces to be instantiated, but allow concrete classes to implement these interfaces. Also, Java semantics allow a concrete class to implement multiple interfaces, which can result in a concrete class implementing the same interface along different paths. For example, a Java class that implements an interface may also extend a class that implements the same interface.
FIG. 3 is a Java code fragment illustrating an example embodiment of Java components, including a Java interface Foo 202, a concrete Java class FooImpl 204, and a Java class FooUser 206. The interface 202 includes a method fooMethod that by virtue of being declared in a Java interface is an abstract method. The concrete class 204 is defined to implement the interface 202. The class 206 includes a factory method get_a_Foo 208 that is declared to return an instance of a concrete class that implements Java interface 202. The body 209 of factory method 208 satisfies this calling contract by creating and returning an instance of concrete class 204. In another example, Java component 200 may include several concrete classes that implement Java interface 202. In this case, the factory method body 209 may include logic that defines which concrete class will be instantiated depending upon an object state at the time at which the factory method 208 is invoked.
In C++, a class having no instance data and only pure virtual methods correlates loosely to a Java interface. As used herein, a "C++ interface"is a C++ class including no instance data and only pure virtual methods.
A concrete C++ class may define an interface to be one of the concrete class' superclasses. In such a situation, each pure virtual method of the C++ interface has an entry in the concrete class's virtual function table and can be invoked polymorphically. Thus, the concrete C++ class provides an implementation for each pure virtual method of a C++ interface. C++ semantics, however, prohibit the instantiation of a C++ interface. If a concrete C++ class implements a C++ interface, to refer to the concrete C++ class as the C++ interface, the C++ class must be referred to through a pointer or a reference to the C++ interface. A pointer is an indirection mechanism used in C and C++ that does not require the pointed-to instance to be of the exact type of the declared pointer. Thus a pointer can be used to refer to a C++ instance as a C++ interface instance even though C++ interfaces cannot be instantiated.
Consequently, if a C++ interface needs to be used, for example as a field or as a method argument or as a method return value, C++ semantics enforce the use of pointers or references.
It may be desired to wrap a Java interface with a C++ proxy interface so that 1) C++ components may access and use the Java interface itself (discussed immediately below), and 2) to implement the abstract methods of the Java interface with concrete C++ methods(discussed below in connection with cross-domain callback patterns and FIGS. 14-17).
Although a Java interface appears to transform relatively simply to a C++ proxy interface, it does not.
FIG. 4 is a C++ code fragment illustrating an example embodiment of C++ proxy components 210 wrapping Java components 200 of FIG. 3, but not having semantic usabilities closely corresponding to the semantic usabilities of the Java component of FIG. 3. C++ proxy components 210 result from applying an incorrect mapping to Java components 200. C++ components 210 includes a C++ interface Foo 212, a concrete class FooImpl 214, and a class FooUser 218. The concrete C++ class 214 implements the C++ interface 212 by declaring the C++ interface 212 as its superclass. Class 216 contains a method get_a_Foo 218 that guarantees to return a pointer to an instance of a concrete class that implements interface 212. The body 219 of factory method 218 satisfies the guarantee by creating and returning an instance of concrete class 214. If the C++ components 210 of FIG. 4 were non-proxy components, they would have semantic usability closely corresponding to the Java components 200 of FIG. 3. In contrast, if C++ components 210 are proxy components, they do not implement properly the dynamic concept represented by the Java components 200 for the following reasons.
If the C++ components 210 are proxy components, the C++ proxy method 218 can only guarantee that it returns a C++ proxy object which implements C++ interface 212. Because C++ interface 212 cannot be instantiated, an instance of a concrete C++ class needs to be returned as the result. Java method 208 only contracts to return an instance implementing Java interface 202, so in general the concrete type of the instance that is being returned is unknown. Although source code inspection of Java method 208 would yield the concrete type, it is impossible to rely on the presence of source code under all circumstances. This problem becomes more apparent if frequently-implemented Java interfaces like Serializable or Clonable are considered.
For example, a Java method declared to return a Serializable instance would be impossible to map to a C++ proxy method in this fashion. Such a C++ proxy method could not possibly be implemented to anticipate all classes that implement the Serializable proxy interface and return an instance of the appropriate concrete proxy class. Another consequence of this design is that the addition of a new Java class implementing Serializable would necessitate the modification of aforementioned C++proxy method to account for a new concrete Java class.
A solution to this problem is to have the C++ proxy method 218 declared to return an instance of the actual C++ proxy interface 212 itself. This declaration allows all callers of the proxy method 218 to use the method result in accordance with the contract established by interface 212. To be able to return an instance of the C++ proxy interface 212, it must be instantiable.
Therefore, a Java interface may be mapped by default to a to an instantiable C++ proxy class. Accordingly, an instantiable C++ proxy class representing a first concept may wrap a Java interface representing the first concept, and may have a semantic usability closely corresponding to the semantic usability of the Java interface.
For the C++ proxy interface 212 to be instantiable, the methods contained in the C++ proxy interface 212 must not be declared pure virtual. Generating instantiable C++ proxy interfaces and the methods that they contain has an impact on the declaration of C++ proxy methods, as will be discussed in more detail below.
Thus, by making instantiable a C++ proxy interface that wraps a Java interface, C++ proxy components can properly access and execute the Java interface through the C++ proxy interface, and properly access and execute Java components that use the Java interface, such that the concepts represented by the Java interface and Java components are implemented properly.
FIG. 5 is a C++ code fragment illustrating an example embodiment of C++ proxy components 220, including C++ proxy interface 222 and C++ proxy class 226 that may wrap Java interface Foo 202, a concrete Java class FooImpl 204, and a Java class FooUser 206, respectively. In this example, proxy class jcpp_ref 230 represents a proxy support base class. Proxy support classes are described in more detail below. Proxy constructor 223 initializes instances of C++ proxy class 222. Proxy constructors also are discussed below in more detail. C++ proxy interface method 224 uses the virtual JNI invocation technique (also discussed below in more detail) to call a correct Java implementation of this C++ proxy method.
C++ proxy method 228 may return a pointer to an instance of a concrete C++ proxy class that implements the C++ proxy interface 222, or may return a pointer for other reasons. Alternatively, the C++ proxy method may return an instance of the C++ proxy interface 222 itself, as indicated in the method declaration and the body 229 of the method 228.
By obviating the need for use of pointers, the C++ proxy classes also become more usable for the following reasons. Often, results returned by methods are not required for further processing and, consequently, developers choose to ignore the return value of a method. If a pointer to a newly allocated object is returned, however, that object is typically allocated on the program's heap. Consequently, to prevent resource leaks from occurring, the instance referred to by the result pointer is freed explicitly by the caller. Thus, the caller maintains the result pointer until explicitly freeing it. This maintenance clashes with the developer's desire to simply ignore the result, which is something that is possible in Java with Java method 208. Therefore, by obviating the need for using a pointer, callers of the C++ proxy classes may ignore the result, and need not maintain and explicitly free a result pointer.
C++ proxy interface 222 may have data that maintains the JNI object reference (not shown in FIG. 4). Because the C++ proxy interface 222 may possess instance data, concrete C++ classes or proxy classes inherit virtually from this C++ proxy interface to prevent multiple inclusion of instance data in the C++ class derived from the interface. Virtual inheritance prevents any derived C++ classes (including derived proxy classes) from maintaining multiple memory locations for the same C++ proxy interface 222 and all the problems associated with this situation. Virtual inheritance is discussed in more detail in Stroustrup.
Java Class→C++ Proxy Class
A Java class may be mapped by default to a to a C++ proxy class. Accordingly, in an embodiment of sharing a Java component, a C++ proxy class representing a first concept provides a proxy layer to a Java class representing the first concept, and has a semantic usability closely corresponding to the semantic usability of the Java class. Optionally, the accessibility of each member of the C++ proxy class is determined from one or more properties such as, for example, accesibility, of a corresponding member of the Java class.
Private members of the Java class are by definition only accessible to the declaring Java class itself so there is no need to make them accessible to callers in another domain. Accordingly, in an aspect of mapping a Java class to a C++ proxy class, private members of the Java class need not be mapped at all, but may be omitted from the C++ proxy class or struct.
For illustration purposes, in the description below, it will be assumed that C++ proxy classes, as opposed to C++ structs (or C++F unions), wrap Java classes, although the same general considerations are applicable if a C++ proxy struct wraps a Java class. Although a union may be able to represent a C++ proxy type, its usage may introduce complications such that's its use may be of no additional value.
Because the Java classes have declaration modifiers, a default mapping that captures the transformation of a Java class to a C++ proxy class is complex. For example, a Java class can be declared abstract. A Java class must be declared abstract if: at least one of its declared methods is abstract; if it does not implement a method that is declared in a Java interface that the Java class implements; or the Java class does not implement a method that was declared abstract in the class's superclass. Further, even if the Java rules do not dictate that a Java class must be declared abstract, a Java class may be declared abstract to prevent it from being instantiated.
An abstract Java class may have constructors intended for use by subclasses. In an aspect of creating a C++ proxy class with semantic usability closely corresponding to an abstract Java class, such constructors may be transformed to be protected if they were public in the underlying Java class. The context-sensitive constructors described below allow instantiation of a C++ abstract proxy class for pre-existing Java instances of classes extending the abstract Java class.
An abstract Java class is similar to a Java interface. Thus, for the same reasons as discussed above in connection to Java interfaces, a Java class with abstract methods may be mapped by default to a to an instantiable C++ proxy class. Accordingly, in an embodiment of sharing a Java component, an instantiable C++ proxy class representing a first concept provides a proxy layer to a Java class having abstract methods and representing the first component, and has a semantic usability closely corresponding to the semantic usability of the Java class.
To properly transform method members of an abstract Java class to a C++ class, further considerations are discussed in more detail below in connection to methods.
Java permits Java classes to be defined final (as Java also permits for methods, which will be discussed in more detail below). The Java semantics dictate that a Java class defined to be final is not allowed to be a superclass for other classes. In other words, the Java class is not inheritable. C++ does not have a corresponding concept to this final definition that restricts inheritability of C++ classes. This absence of the final concept in C++ does not have adverse effects on the transformation per se. For example, a C++ proxy class may include a comment reflecting the intended inheritability of the C++ proxy class. Although this comment can not be enforced by the C++ compiler, it may inform a C++ developer of the intended non-inheritability.
The Java keyword final is also used to modify a method declaration, and should be taken into account by a C++ proxy class. The implications of the Java keyword final in creating C++ proxy classes are discussed in more detail below in connection to proxy methods.
In one implementation of a C++ proxy class wrapping a Java class, the C++ proxy class may be used in each context that the Java class may be used in the Java domain. Further, when JNI provides the proxy layer of the C++ proxy class, the C++ proxy class may be usable in each JNI context. Thus, in an aspect of a C++ proxy class having a semantic usability closely corresponding to the semantic usability of the Java class, the C++ proxy class may be used in one or more of the following contexts: - 1) as an instance field in a C++ class;
- 2) as a class (static) field in a C++ class;
- 3) as an array element; and
- 4) as a stand alone object (not a field of a class, not a field of a class instance and not an element of an array).
As discussed above, JNI functions are context-dependent. To access Java fields from a C++ proxy field (discussed below), it may be desirable to have a C++ proxy class that is usable in all JNI contexts, to invoke the appropriate JNI function depending on the context of the Java instance. If a C/C++ program only needs to access one field from the Java functional domain, the context-dependency of JNI is not too difficult to incorporate into the C/C++ code. If, on the other hand, a C/C++ program requires access to multiple Java components, or requires use of multiple Java components, or requires use of a higher-level Java component, then incorporating into the C/C++ code the proper JNI code to accommodate the JNI context-dependencies becomes a more difficult task.
A C++ proxy class may be used to represent all JNI contexts by providing different constructors, one for each context to be represented. Such constructors may be referred to herein as context constructors. Each context constructor differs in the arguments that it requires, in correspondence to the context that it represents. Each constructor is appropriate for only one context and should only be used in that one context. The use of context constructors is only one example technique of providing information about the context to a proxy instance. For example, other techniques may rely on the explicit specification of the context through a separate method declared by the proxy class.
FIG. 6 illustrates an embodiment of four example context constructors: stand-alone object constructor 30, instance field constructor 36, static field constructor 42, and array element constructor 48. In FIG. 6, proxy_name is the proxy class to which the constructors belong, jobject is the JNI object reference type, proxy_ref is the base class for all proxy objects, proxy_class is the base class for all proxy classes. Proxy-ref is derived from proxy-base, the base class for all proxies, including proxy objects and proxies for primitives. Proxy_class is derived from proxy_ref. The proxy_array class is derived from the proxy_ref class and represents generic array objects.
The stand-alone object constructor 30 may be used to construct a C++ proxy instance for an already existing Java object represented by its JNI jobject value. This C++ proxy instance may maintain its jobject value, for example, by storing its value in an internal field. The stand-alone object constructor 30 may be considered a direct proxy constructor because it leads to the creation of a C++ proxy instance that directly contains the jobject reference.
The instance field constructor 36 may be used to construct a proxy instance for an instance field of another proxy. In this example, the first argument, ref 38, is a pointer to the proxy instance representing the owner of the field, and the second argument, _fieldName 40, is the field identifier. The field identifier 40 needs to be able to uniquely identify the represented field in the scope of the owner. Thus, the field identifier 40 may be the field name, the JNI jfieldID, both the field name and the JNI jfieldID, or a pointer or a reference to an object maintaining this information. The instance field constructor 36 may be considered an indirect proxy constructor because it refers to another proxy component that represents the instance to which the field represented by the field identifier belongs.
The static field constructor 42 may be used to construct a proxy instance for a static field. The static field constructor 42 takes a first argument, _clazz 44, that represents the proxy_class instance representing the owner of the field and takes a second argument, _fieldName 46, that identifies the field. In this example, the first argument _clazz 44 is a pointer to the proxy_class instance representing the owner of the field. The field identifier_fieldName 46 could be just the field name or the JNI jfield ID or both or a pointer or a reference to an object maintaining this information. The static field constructor 42 may be considered an indirect proxy constructor because it refers to another proxy instance that represents the class to which the represented field belongs.
In an aspect of C++ proxy class having a class field constructor, the C++ proxy class also has a virtual get class 0 method and a static get_static class ( ) method, either of which can be used to ask a proxy instance for its class at run time.
The array element constructor 48 may be used to construct a proxy instance for an element of an array. The first argument, _array 50, represents the array owning the element and the second argument, _index 52, represents the index of the element in the array. In this example, the first argument _array 50 is a pointer to the proxy representing the array. The array element constructor 48 may be considered an indirect proxy constructor because it refers to another proxy that represents the array to which the represented element belongs. An array proxy class may possess an overloaded subscript operator that may return a proxy instance that has been constructed using the array element constructor (described below in more detail in connection to Array Support).
Proxy Support Elements
A C++ context constructor is a type of C++ proxy support element. In an embodiment of wrapping a Java class or a Java interface with a C++ proxy class, or wrapping a Java primitive with a C++ proxy class (discussed in detail below in connection to FIG. 8), the C++ proxy class may include one or more C++ proxy support elements.
FIG. 7 illustrates an example embodiment of declarations of proxy support elements for a C++ proxy class 108, including: typedef 110; conversion constructor 112; context constructors 114; copy constructor 116; destructor 118; assignment operator 120; comparison operators 122; static framework support method 126; instance framework support method 128; and dynamic casting method 130. A concrete C++ proxy support element may be declared in the declaration in the declaration context of a C++ proxy component, typically in a class header (.h) file, and the implementation of the C++ proxy support element may appear in an implementation file (.cpp) of a C++ proxy class.
Each of the proxy support elements of FIG. 7 will now be described.
Support for Null
In Java, null is a distinctly typed object reference representing that the object reference does not refer to an object instance. Java defines several semantic rules involving null, for example, null can be used in any context in which an object reference is allowed. Thus, null may, for example, be used as an operand in object identity comparisons or as a method argument, if the method argument is of a non-primitive type.
A C++ proxy component may have to support a concept like null in order to provide a semantic usability closely corresponding to the semantic usability of an underlying Java component. In C++, there is no such distinctly typed null object. In C++, the most closely corresponding concept is NULL. NULL is simply a #define for "0" or "(void*)0." NULL often causes confusion if C++ interprets it as an integer rather than as a pointer. Further, a NULL value can only be used in relation with pointer types and not with objects or object references. Thus, the use of NULL forces the programmer to use pointer types. Although this forced usage may simply be undesirable in the case of method arguments, it is harmful in the case of object identity comparisons. In the case of C++ proxy instances, object identity comparisons need to inspect the object identity of the underlying Java instances. If pointers and NULL are used, overloaded comparison operators cannot be used easily. Consequently, in the following use-case:
FOO * fool = new Foo( ); if ( fool == NULL ) ;// take some action the C++ pointers are compared for identity, rather than comparing the underlying Java object references. While this comparison may yield the correct result in some cases, it generally does not yield the correct result.
In an aspect of generating a C++ proxy component that has a semantic usability closely corresponding to its underlying Java component, the C++ proxy component may contain proxy support elements that allow usage of null in a manner that corresponds with usage of null in Java.
To wrap a Java null argument and provide such a semantic usability in C++, a C++ proxy class may include a Tnull conversion constructor that utilizes a helper class, named, for example, Tnull, and a file-scoped Tnull helper class instance named null.
Such a Tnull helper class may have an empty default constructor and a number of overloaded comparison operators. These comparison operators provide semantic usability for cases where null is used as the first operand in the comparison. Inclusion of the header file in which Tnull is defined may cause such a null instance to be available to all code in the compilation unit.
Accordingly, a Tnull conversion constructor, i.e., a constructor for a proxy instance that takes a Tnull instance as an argument, may be provided such as, for example, Tnull conversion constructor 112 of FIG. 7. As long as Tnull conversion constructor 112 is not declared explicit, C++ automatically invokes the Tnull conversion constructor to convert a Tnull instance into a temporary proxy object if the semantic context requires a C++ proxy instance and a Tnull instance is available. Specifically, if the file-scoped null instance is used in such a context, such a conversion constructor is invoked.
Tnull conversion constructor 112 creates a stand-alone proxy instance (described above in connection to FIG. 6) initialized to not refer to any Java instance.
Comparison Operators
In Java, the use of the operators == or !=on two object references compares the two object references for object identity, not for object equality. In an effort to provide closely corresponding semantics for C++ proxy classes, it may be desirable to provide a comparison operator that compares the underlying Java instances for object identity. Because more than one JNI object reference may actually refer to a single object, a simple value-based comparison is not sufficient, rather, a JNI function has to be executed. Further, if a comparison is to be made, it may be desired for completeness sake to have both a comparison for equality and a comparison for inequality, such as, for example:
bool Foo::operator == (const Foo & _rhs ) const; bool Foo::operator != (const Foo & _rsh ) const;
Accordingly, in an embodiment of wrapping a Java class or interface with a C++ proxy class, the C++ proxy class may include inequality and equality operator declarations 122 of FIG. 7 and the above inequality and equality operator implementations. These declarations and operators may be implemented in terms of a C++ standard support proxy method. A C++ proxy class that includes equality and inequality operators implemented in terms of a C++ standard support proxy method, and the Tnull conversion constructor described above, may handle all comparisons for object identity.
Copy Semantics & Destructor Semantics (Lifecycle Management)
In an embodiment of wrapping a Java class, a Java interface or a Java primitive component with a C ++ proxy class, a C++ proxy class may include a copy constructor and an assignment operator to help map the copy semantics of a Java component to a C++ proxy component. The copy constructor and assignment operator provide two actions for mapping copy semantics of a Java component, depending on the reference context of the Java component. In a context of being a field reference or a context of being an array element reference, a C++ proxy component has a value set into it if an assignment or a copy operation occurs. In a stand-alone reference context, a C++ proxy component has a source reference duplicated and maintained by the C++ proxy component.
In Java, an assignment involving an object causes another reference to this object to be held in the JVM. It does not cause a copy of the object's state to be made. In contrast, in C++, if a copy constructor or assignment operator is not provided explicitly, the copy constructor and assignment operator generated by the C++ compiler will copy the object's state. In the case of C++ proxy objects, this default behavior would create a duplicate of a Java object reference held in the JVM without informing the JVM of this fact. In general, creating such a duplicate without informing the JVM would cause erratic execution due to resulting problems in the management of the underlying Java object lifecycle. Similarly, the destructor of a C++ proxy class needs to release the reference to the underlying Java object. Correct lifecycle management for underlying Java objects requires both correct copy semantics and destructor semantics.
In an aspect of context-awareness, different actions may need to be taken depending on the assignment-target's context and the technology providing domain connectivity (for example JNI). For example, if the target of an assignment is an array element and the connecting technology is JNI, the assignment operator will have to call the JNI method SetObjectArrayElement( ).
Accordingly, a C++ proxy component, to provide copy semantics in the C++ domain closely corresponding to the copy semantics of the wrapped Java component, may include proxy support element declarations 116 and 120.
To provide lifecycle management for wrapped Java components, a C++ proxy component may provide proxy support element declaration 118 in conjunction with declarations 116 and 120.
Casting
In Java, all objects are used through references. Java is strongly typed and has a cast expression that will throw an exception if an attempted cast is not legal (in accordance with Java semantics). In C++, the most closely-related feature is the dynamic_cast operator that operates on pointers or references. In the case of pointers, it returns NULL if the attempted cast is not a compatible cast. In the case of references, it throws a bad_cast exception if the attempted cast is not a compatible cast. Java casting cannot be directly mapped to the C++ dynamic cast operator because the C++ dynamic cast operator limits use to only pointers and references and because the type of the C++ proxy instance does not necessarily correspond directly to the type of the underlying Java instance.
Accordingly, in an embodiment of wrapping Java classes, interfaces or primitives with C++ classes, each C++ proxy class may include a static class method dyna_cast 130 of FIG. 7. Method 130 of FIG. 6 may provide a semantic usability to a C++ proxy component closely corresponding to the semantic usability of a cast expression involving the wrapped Java component.
A dyna_cast method may act as a "factory" for proxy instances of the cast target-type and may take a generic jcpp_ref reference as input (jcpp_ref being a baseclass for all C++ proxy classes corresponding to Java classes or interfaces). A dyna_cast method may have the following implementation signature: - static Foo Foo::dyna_cast(const jcpp_ref & src);
The dyna_cast method may internally use a C++ proxy support method to determine whether the C++ proxy instance represented by _src is compatible with the target type. Further, the dyna_cast method may give a copy of the reference maintained by _src to the newly created target type instance if the two types are compatible. A configuration parameter may govern the behavior of the dyna_cast method if an incompatible cast is attempted. Such behavior may include throwing an exception, which is a behavior shared by Java and current-standard C++, or returning a reference that refers to null, which is a behavior common to old-standard C++.
Array Support
In Java, arrays are objects. Every array instance knows the maximum number of elements it can hold. This number is accessible to a Java programmer through a read-only field named length (the actual implementation of this feature in the JVM may be different). Multi-dimensional arrays are arrays of objects of an array-type.
In contrast, in C++, an array (single- or multi-dimensional is a contiguous memory area that is of a size that is sufficient to hold the declared number of instances. C++ arrays, like the C arrays that they are based on, do not maintain their declared dimension or order. Consequently, array-bound overwrites are a frequent source of error in C++ programs.
A Java array type may be mapped by default to a C++ proxy class. Optionally, such a C++ proxy class may have a semantic usability closely corresponding to the Java array type that it wraps. Such a C++ proxy class may declare a read-only length field that can be used in any semantic context in which an integer can be used and which corresponds with the number of elements in the underlying array.
In another aspect of a C++ proxy class having closely corresponding semantic usability to a Java array type that it wraps, the C++ proxy class may declare a const (not allowed to modify the state of the array) and a non-const (allowed to modify the state of the array) subscript operator with appropriate return-types. For example, in a C++ proxy array for a primitive type, the return type for the const version of the subscript operator may be the primitive type, whereas the return type for the non-const version of the subscript operator may be another proxy type that can be used on the left hand side of assignment statements. The returned C++ proxy instance may have been constructed using the array context constructor mentioned above.
In an embodiment of mapping a Java array type to a C++ proxy class, a template class may be provided to declare the previously mentioned length field and subscript operators. It may be desirable to provide a template class because, although the mechanism for accessing all object array types is identical, the return types for the subscript operators depend on the array element type. Such a template class may expect its template argument to be a class that provides a static method to allow the template class to discover the jcpp_class of its elements. To provide such a static method, in an embodiment of wrapping a Java class, interface, or primitive with a C++ proxy class, the C++ proxy class may include a static framework support method 126. Other known techniques may be used to provide such a static method.
Although a template class simplifies implementation, it also makes the use of the resulting C++ proxy array types a cumbersome, as illustrated by the following example of two array instance declarations:
jccp_object_y<jcpp_object_array<Foo> > a2(5); jccp_object_array<Foo> a1_1 = a2[0];
Typedefs for concrete C++ array proxy types may make the use of these types less cumbersome while improving readability of the using code.
Therefore, in an aspect of wrapping Java arrays, a C++ proxy class may have a typedef that provides uniform access to the C++ array proxy class such as, for example, typedef 10 of FIG. 7.
A typedef such as typedef 110 enables the use of typedef synonyms in calling code, for example:
Foo::array2D a2(5); and Foo::array1D a1_1 = a2[0].
This example is equivalent to the other example described above, but it is much more readable.
Optionally, if it is known that a wrapped Java class is never used as an element class of an array, then the Java proxy class that wraps the Java array need not include the array context constructor the get_static_class( ) method and the typedef d described above.
Framework Support Methods
Although each of the previously described proxy support elements may be necessary to enable a certain, specific use-case in C++, there are certain services that every C++ proxy class may provide. One service that may need to be provided for most if not all use-cases is the ability to map an instance of a C++ proxy class to an object that represents the underlying Java class (e.g. ajcpp_class instance). Framework support methods are just one possible embodiment of making generally useful information available to a potentially large number of C++ proxy classes; other mechanisms are a possible.
Accordingly, in an aspect of wrapping Java components with C++ proxy classes, a C++ proxy class may include an instance framework support method, which is a virtual method that enables a C++ proxy instance to be queried for its underlying jcpp_class, where a jcpp_class represents the JNI type of the proxy instance and can be regarded as a proxy instance for a Java class. Such an instance framework support method may have the following signature: - const jcpp_class * Foo::get_class( ) const;
The benefits of including context constructors in C++ proxy classes is described below with respect to C++ proxy fields in connection to FIGS. 7-10.
Java Field→C++ Proxy Field
A Java field may be mapped by default to a to a C++ proxy field. Accordingly, in an embodiment of sharing a Java component, a C++ proxy field representing a first concept provides a proxy layer to a Java field representing the first concept, and has a semantic usability closely corresponding to the semantic usability of the Java field.
Both Java classes and Java interfaces may include fields. In Java, the Java syntax limits a field declared by a Java interface to being a public, static and final (i.e., constant) field. In contrast, the Java syntax permits a field declared by a Java class to be either a class field or an instance field and to have any accessibility and mutability defined by Java.
Although the declaration syntax for Java fields is similar to the declaration syntax for C++ fields, Java permits a field to be initialized in a declaration, whereas C++ does not allow a field to be initialized in a declaration. In contrast, C++ initializes an instance field in a constructor of a containing instance of the field, and initializes a class-field in the class-field's definition.
If a C++ proxy class has a C++ proxy field, to truly serve as a proxy layer to the underlying Java field, the proxy field should not be initialized to the value of the underlying Java field. Preferably, a C++ proxy field should be initialized with information that uniquely identifies the proxy field's underlying Java field so that, when the C++ proxy field is used, the correct proxy-layer action can be taken to access the underlying Java field.
In Java, each declaration (for a field or a method) has its own accessibility modifier (e.g. public, private). In contrast, in C++, accessibility is a separate declarative component that applies to all the declarations that fall within the scope of the declarative component. Further, Java has accessibility modifiers that are different from C++ accessibility modifiers.
In C++, the absence of an accessibility modifier indicates that the default accessibility of the containing element should be used (specifically, public for a struct and private for a class). In Java, on the other hand, the absence of an accessibility modifier indicates default accessibility, a type of accessibility that is not known in C++. The protected accessibility in Java has a different meaning from the protected accessibility in C++. As a consequence, accessibility modifiers of C++ proxy components may be adjusted to be less restrictive to allow semantic usability in C++ that closely corresponds to the semantic usability of the underlying Java component.
Accordingly, both default and protected Java accessibility modifiers may be mapped by default to a less-restrictive public accessibility in C++ such that the C++ field (or method) may have a usability in the C++ domain that closely corresponds with the intended usability of the underlying Java field (or method).
A Java field may be made immutable by declaring the field final. A Java field not declared final is mutable. The Java final modifier has a slightly different impact on a Java field depending on whether the field is of primitive type or of reference type. If a field of primitive type is declared final, its state cannot change, but if a field of reference type (i.e., an object field) is declared final, only the reference maintained by the field cannot change. Consequently, in Java, a final modifier that modifies an object field does not prevent changes to the referred to object, but merely prevents changes to the reference.
A C++ field may be considered immutable if it is declared const. As used herein, "constness" refers to the property of a C++ component as being declared const or not being declared const.
To map a Java field to a C++ proxy field, the mutability (i.e., constness) of the C++ proxy field may be determined from properties of the Java field and its type, for example, from their mutability. Accordingly, a C++ proxy field may have a mutability determined from the mutability of the Java field that it wraps. For example, a primitive Java field declared final may be mapped to a C++ proxy field declared const, and a primitive Java field not declared final may be mapped to a C++ proxy field not declared const. Heuristics may be applied to a Java field and related Java components to determine the mutability (i.e., constness) of a C++ proxy field. A number of rules, together constituting an example of such heuristics, is provided in Table 1.
In the description related to proxy methods below, the mutability of Java and C++ classes are described. One factor that may be used to determine a class' mutability is the ability of a user of the class to change the values of fields declared by the class. Further, in analyzing fields to determine a mutability of the field's declaring class, factors other than the field's mutability may be considered. For example, although a field may be considered mutable if it is not declared final, the field may also be declared private, effectively not allowing a field to modify a state of an instance to which the field belongs. Accordingly, such a field may be regarded as "conceptually not-mutating" (see description of methods below).
Table 1 introduces rules that may be used to determine a mutability attribute for a Java field and, consequently, the constness of a C++ proxy field that wraps the Java field.
TABLE 1 Field Mutability Rules Field Field Type Acces- Constness (Instance sibil- Field Mutability Of Proxy or Class) ity Final Type Attribute Field 1 Don't Pri- Don't Don't Conceptually N/A care vate care care Not Mutating 2 Static Non- Yes Primitive Conceptually Const (Class) private Not Mutating 3 Static Non- Yes Reference Conceptually const if (Class) private Not reference Mutating type is immutable 4 Static Non- No Don't Concep- Not const (Class) private care tually Not Mutating 5 Non-static Non- Yes Primitive Assumed const (Instance) private Non- mutating 6 Non-static Non- Yes Reference Mutability const if (Instance) private Attribute of reference reference type is type immutable 7 Non-static Non- No Don't Assumed Not const (Instance) private care Mutating
Table 1. Field Mutability Rules
Row 1 of Table 1 indicates that a private field cannot be used to modify its owner's state (as described above), and thus may be determined to have a mutability attribute of "conceptually not-mutating". A private field need not be proxied and, consequently, determining the constness of a C++ proxy for such a private field may not be necessary.
Rows 2, 3 and 4 express that a static, non-private Java field does not affect a Java instance's state, and thus may be determined to have a mutability attribute of "conceptually not mutating". The constness of such a Java field's corresponding C++ proxy field, however, may not be directly related to the value of the Java field's mutability attribute. Specifically, Row 2 indicates that a C++ proxy field for a final, static, non-private and primitive Java field may be declared const. Row 3 indicates that the constness of a static final C++ proxy field of a reference type (i.e., an object field) may be dependent on the mutability of the wrapped Java field's reference type, for similar reasons as described below in connection to Row 6. Row 4 indicates that a non-final, static, non-private Java static field may map to a C++ proxy field that is not declared const.
Row 5 indicates that non-static, non-private Java instance field of primitive type and declared final cannot be used to modify its owning instance's state, and, therefore, the Java instance field may be determined to have a mutability attribute of "assumed not-mutating". Such a field may map to a C++ proxy field that is declared const.
Row 6 indicates that a non-static, non-private Java instance field of reference type (i.e., an object field) that is declared final may be determined to have a mutability attribute corresponding to the mutability of its reference type. As described above, the Java final field declaration modifier applies to a reference to an object, not to the object itself. Thus, an object referred to through a Java final field may potentially be changed, thereby also potentially changing the state of the owning Java instance. If the Java field's type is "immutable," however, such state changes are not possible (although it is theoretically possible that a subclass of an immutable class is mutable (i.e., not immutable), it is highly unlikely). Thus, the mutability attribute of a final Java reference type instance field may be set to the mutability of its type. Further, the constness of the C++ proxy field of such a Java field may be directly derived from the Java field's mutability attribute.
Row 7 indicates that all other fields not addressed by Rows 1-6 may be regarded as "assumed mutating", i.e., as having the ability to be used to modify their owning instance's state. Such a field may map to a proxy field that is not declared as const.
As described above, a C++ proxy field may hold a unique field identifier instead of a value of an underlying Java field. Consequently, if the underlying Java field is declared final, the constness of the field may be expressed through a C++ proxy class that does not permit value changes. Also, such a class may query only a value of an underlying Java field a first time the field is accessed and cache the value for later use because the value is guaranteed not to change. Such a proxy class may be used in addition to or as an alternative to declaring a field const.
In an aspect of mapping Java fields to C++ proxy fields, a Java static (i.e., class) field of type class_a maps by default to a C++ proxy static field of type C++ proxy class_a, where the C++ proxy class_a includes proxy support elements that enable the use of class_a as a static field. These proxy support elements may be provided in the form of context constructors that are described above with respect to C++ proxy classes and FIG. 6.
In an another aspect of mapping Java fields to C++ proxy fields, a Java instance field of type class_a maps by default to a C++ proxy instance field of type C++ proxy class_a, where the C++ proxy class_a includes proxy support elements that enable the use of class_a as an instance field.
In another aspect of mapping Java fields to C++ proxy fields, a Java static field of primitive type (e.g., integer) maps by default to a C++ proxy static field of type C++ primitive proxy class_a, where the C++ primitive proxy class_a includes proxy support elements that enable the use of class_a as a static field, as well as other proxy support elements described above in connection to FIGS. 6 and 7. An example C++ primitive proxy class is described below in connection to FIG. 8.
In yet another aspect of mapping Java fields to C++ proxy fields, a Java instance field of primitive type (e.g., integer) maps by default to a C++ proxy instance field of type C++ primitive proxy class_a, where the C++ primitive proxy class_a includes proxy support elements that enable the use of class_a as an instance field.
In each of the aspects described immediately above, each C++ proxy instance may maintain its context (i.e., instance field, or class (static) field) after being constructed by the proper C++ constructor of C++ proxy class_a. Such an instance may maintain (i.e., know) its context internally by storing it in a context field. A C++ proxy instance that knows its context is referred to herein as a "context-aware" instance. A context-aware C++ proxy instance that is maintained by a field is referred to herein as a "context-aware" field.
FIG. 8 illustrates an embodiment of a C++ primitive proxy class jcpp_int 52. FIG. 8 has many of the same proxy support elements as FIG. 7. Primitive proxy class 52 may be the primitive proxy class for the primitive type integer. Primitive proxy class 52 may inherit from a primitive proxy base class jcpp_base 54, which may be the base class for all C++ proxies.
Primitive proxy class 52 may include any combination of the following C++ proxy support elements: typedefs 56 (similar to typedef 110); context constructors 59 (similar to context constructors 36, 42, 48 and 114); copy constructor 65 (similar to copy constructor 116); destructor 66 (similar to destructor 118); special methods 62; conversion operator 64; mathematical operators 66; and frame work support method 68 (similar to framework support method 128).
Typedefs 56 allow the use of type-safe arrays in a standardized way. For example, an array of integers may be referred to through an array helper class jcpp_int_array 57 or through the typedefjcpp_int::array1D 58.
Context constructors 59 may include instance field constructor 60, static field constructor 61, and array element constructor 62, which are examples of context constructors 36, 42 and 48, respectively, of FIG. 6. In a primitive proxy class 52, a stand-alone context constructor similar to primitive proxy constructor 30 is not necessary because a stand-alone primitive type may be represented by a JNI native type. For example, Java primitive type int may be represented by the JNI type jint.
Special methods 62 may permit the C++ proxy class 52 to reside in a DLL for all platforms, and conversion operator 64 may convert an instance to a jint. Conversion operator 64 allows an instance of a jcpp_int to be used on the right-hand side of a syntactical production such as, for example, the expression: jint i=instance_name.x, where instance_name.x is a jcpp_int instance field. In this example, conversion operator 64 enables instance_name.x to be converted to a jint.
Mathematical operators 66 permit instances of primitive proxy class 52 to be used on the left-hand side of certain syntactical productions, thereby increasing the semantic usability of such instances. For example, mathematical operators 66 permit primitive proxy class 52 to be used in the following example: instance_name.x=20, where instance_name.x is a jcpp_int instance field. Another example expression is: instance_namne.x+=4. In either example, the assignment and additive operators ("=" and "+=", respectively) are overloaded so that a compiler would know how to assign a jint to a jcpp_int object. Without overloading the operators, the C++ compiler would not know how to evaluate C++ statements involving the invocation of such an operator on a jcpp_int as the receiving object.
Framework support method 68 may be used by other classes to find out the JNI type of an instance. Framework support method 68 may be declared virtual using C++ keyword virtual in a baseclass (e.g. jcpp_ref) from which the primitive proxy class 52 inherits.
Primitive proxy classes, particularly those that include primitive proxy support elements, for example, primitive proxy class 52, may simplify the code needed to use a Java field in a C++ application. For example, consider the following C++ operation performed on a C++ array proxy:
In the absence of a C++ primitive proxy class, the developer may have to explicitly perform the following steps, an equivalent of which the proxy classes perform automatically: - 1) invoke JNI function GetFieldId to retrieve the identifier of the instance field instance_name.x;
- 2) invoke JNI function GetObjectField to retrieve the jobject reference for the wrapped Java array;
- 3) invoke JNI function GetIntArrayRegion to retrieve the integer value of the Java array element;
- 4) invoke JNI function SetIntArrrayRegion to set the incremented value into the wrapped Java array; and
- 5) invoke JNI function DeleteLocalRef to clean up the retrieved reference to the wrapped Java array.
This list of JNI function calls does not include JNI calls that should be issued after many of the above calls in order to check for possible error conditions that might cause abnormal program termination if not handled correctly.
The previous example illustrates how a simple C++ task may translate into a complex sequence of JNI API calls. Therefore, providing primitive proxy classes like primitive proxy class 52 that construct context-aware field instances, allows the developer to use the C++ proxy field without ever having to write JNI code. A C++ developer using the field may not even have to be aware of the fact that the underlying implementation is in Java.
FIGS. 9-11 illustrate the benefits of using C++ primitive proxy classes that include context constructors. FIG. 10 is a Java component 230. The Java component 230 includes a Java interface Foo 232, a Java class Bar 234, and various assignment statements 236, 238, 240, 242, 244, and 246. Assignment statements 236-246 may be included in any of a variety of Java components and are presented in list form in FIG. 10 for illustrative purposes only.
Java interface 232 includes a primitive field X 233 defined to be a public static final field and initialized to a value of twenty-five. Java class 234 includes a primitive static (class) field X 235 initialized to a value of twenty-five and a primitive instance field ch 237 initialized to a value ‘a’.
FIG. 10 is a C++ code fragment illustrating an example embodiment of C++ proxy components that may result by applying an incorrect mapping to the primitive Java fields 232, 235 and 237. The C++ components 250 include class Foo 252, class Bar 254 and several C++ assignment statements in the form of assignment statements 256, 258, 260, 262, 264, and 266. Assignment statements 256-266 may be included in any of a variety of C++ components and are presented in list form in FIG. 8 for illustrative purposes only.
C++ class 252 includes C++ primitive proxy field X 253, defined to be of JNI primitive type jint. Class 254 includes primitive proxy static field X 255 and primitive proxy instance field ch 257. Primitive proxy field 255 is declared to be of JNI primitive type jint, and primitive proxy field 257 is declared to be of JNI primitive character type jchar.
Each of C++ proxy fields 253, 255, and 257 are of primitive JNI type, not a proxy class type that would enable the use of these proxy fields in each JNI context. Consequently, to enable proper use of each of these proxy fields in any of the assignment statements 256-266, a developer would need to write explicit JNI code for each assignment statement such that an appropriate instance is constructed in accordance with the context of the assignment statement.
For example, assignment statement 256 sets C++ field i equal to the value of primitive proxy interface field 253. The issue is how to initialize primitive proxy field 253 such that assignment statement 256 executes properly. Because JNI primitive types have well-defined semantics and primitive field 253 is defined to be of JNI primitive type jint, assignment statement 256 would cause i to be set equal to the bytes stored at the location of the static jint field X. This result is not desired. What is desired is that field 253 serve as a proxy to Java primitive field 233, and that assignment statement 256 set the value of i equal to the value of Java primitive field 233. For assignment statement 256 to set the value of i properly, a JNI call has to be made to retrieve the value of primitive Java field 233.
Assignment statement 256, however, does not invoke a JNI call. Therefore, the assignment made by assignment statement 256 is incorrect. Consequently, the value of C++ field 253 does not correspond with the value of Java field 233 and would require an external mechanism to initialize or refresh it with the value of Java field 233. Assignment statements 258-266 are generally incorrect for similar reasons.
FIG. 11 is a C++ code fragment illustrating am embodiment of C++ components 270, including a C++ class Foo 272, a C++ class Bar 274, and the same C++ assignment statement |