You noticed right that the term “normally”, as well as the absence of any byte code description in the JLS is intended to define the Java Programming Language as independent from the execution environment as possible. Still, it’s not that easy:
As noted above, this specification often refers to classes of the Java SE platform API. In particular, some classes have a special relationship with the Java programming language. Examples include classes such as Object
, Class
, ClassLoader
, String
, Thread
, and the classes and interfaces in package java.lang.reflect
, among others. This specification constrains the behavior of such classes and interfaces, but does not provide a complete specification for them. The reader is referred to the Java SE platform API documentation.
Consequently, this specification does not describe reflection in any detail. Many linguistic constructs have analogs in the Core Reflection API (java.lang.reflect
) and the Language Model API (javax.lang.model
), but these are generally not discussed here. For example, when we list the ways in which an object can be created, we generally do not include the ways in which the Core Reflection API can accomplish this. Readers should be aware of these additional mechanisms even though they are not mentioned in the text.
So the Java Programming Language is more than the JLS, it’s also the Java SE platform API. And there, we have the defineClass
methods of the mentioned ClassLoader
class, accepting an input in the class file format. So even if we use other means of deployment than class files in the bytecode format, a fully compliant environment would have to support that format at this place. Note that Java 9 introduced another method accepting input in the class file format that doesn’t even require Reflection or implementing custom class loaders.
This rules out JavaME, which does not have these API artifacts mentioned by the JLS, otherwise, we already had an example of a Java environment not supporting bytecode manipulation.
But this still doesn’t fully answer the question whether bytecode manipulation is off-language, speaking of JavaSE or EE. Even if support for the bytecode format is provided by the standard API, bytecode manipulation depends on implementation details, either the Instrumentation API whose support is not mandatory or by processing compiled class files in their deployed form, as file hierarchy, jar files, or module files, neither being guaranteed to be the deployed form of the application (as said at the beginning). So it’s indeed impossible to implement a bytecode manipulation tool that is guaranteed to work with every possible Java environment, though you would have to go great lengths to create an environment that is fully compliant, but not working with these tools…