0
$\begingroup$

We can execute a c/cpp file by creating an object file through compilatio.But why do we need to pass java bytecodes through an interpreter to execute them?Or why we can execute python/bash files by directly interpreting them? What's the differentiating feature that makes interpreter necessary to execute java bytecodes/python files/bash files etc?

$\endgroup$
1
  • $\begingroup$ Who says it’s necessary? There are design decisions that lead to different solutions for different problems. $\endgroup$
    – gnasher729
    Sep 6 at 7:02
2
$\begingroup$

Interpretation and compilation are simply two different ways of implementing a programming language. They are not a property of the programming language.

Every programming language can be implemented by an interpreter and every programming language can be implemented by a compiler.

Case in point: there are interpreters for C and there are native code compilers for Java and for JVM byte code.

However, it is important to understand that all a compiler does is translate a program from one language to another language. That's it. That's all it does.

Only an interpreter can actually execute code.

So, your statement

We can execute a c/cpp file by creating an object file through compilatio.

is false. You cannot execute a C program by compiling it. You can only execute something through interpretation. If you compile a C file into, say, an x86 native object code executable file, you still haven't executed the program. You still need an interpreter to execute the program. This interpreter could be a physical CPU, but it could also be something like JPC, which is a PC emulator written in Java, running on a Java Virtual Machine, or JSLinux, which is a PC emulator written in ECMAScript, running in the browser.

But why do we need to pass java bytecodes through an interpreter to execute them?

There are two possible answers to this, depending on what, exactly you mean by that statement.

The first possible answer is: because, as I explained above, everything needs to be interpreted in order to execute it. If you want to execute JVM bytecode, you need an interpreter for JVM bytecode. If you want to execute x86 native machine code, you need an interpreter for x86 native machine code.

Note that this interpreter can be a piece of software or a piece of hardware. As mentioned above, there are software interpreters for x86 native machine code (JPC, JSLinux, QEmu, Bochs). There are also CPUs which execute JVM bytecode directly (picoJava, Jazelle DBX).

The second possible answer is: you don't have to!

You don't have to execute JVM bytecode at all. You can compile it to some other language. For example, there are compilers that can compile JVM bytecode to CIL bytecode, or to x86 native machine code (Excelsior JET, GCJ).

Furthermore, many modern JVMs (including Oracle's HotSpot and Eclipse/IBM's J9) include a native code JIT compiler, so the JVM bytecode is not necessarily interpreted. Both HotSpot and J9 with default settings will interpret the same piece of code only a limited number of times, and when that threshold is reached, instead compile it to native machine code.

Or why we can execute python/bash files by directly interpreting them?

We can execute Bash files by directly interpreting them, because someone wrote an interpreter that interprets Bash files. Nobody has written a compiler for Bash yet, but that doesn't mean it is impossible. It just means that nobody has done it yet.

Note that your characterization of Python is not actually correct. There is no Python interpreter in existence at the moment. All widely-used mainstream Python implementations have compilers:

  • CPython compiles Python to CPython byte code (which it then interprets),
  • Jython compiles Python to JVM byte code,
  • IronPython compiles Python to DLR Trees,
  • GraalPython is a strange one, it parses Python into Truffle ASTs, and then interprets the Truffle ASTs with a specializing interpreter, which is essentially equivalent to compiling the Truffle ASTs (it's related to Partial Evaluation and Abstract Interpretation (aka Supercompilation)),
  • PyPy is another strange one, it compiles Python to PyPy byte code, interprets that PyPy byte code, and uses a tracing compiler to compile a specialized version of the interpreter which can only interpret that one program, which is essentially equivalent to compiling the PyPy byte code (it's related to Partial Evaluation and Abstract Interpretation (aka Supercompilation)).

What's the differentiating feature that makes interpreter necessary to execute java bytecodes/python files/bash files etc?

You need an interpreter to execute something. Period. There is no way around that. Even if you just read the source code and figure out the result by hand, you are interpreting the code in your head: you are the interpreter. For native machine code, the CPU is the interpreter.

You cannot execute code without interpreting it.

If you have a program written in some language X, and you want to know what the result of that program is, there are only two things you can do:

  • Interpret the program with an interpreter for language X, or
  • Compile the program with a compiler from X to Y to some other language Y, for which you have an interpreter.

Note that you can construct a longer chain if necessary: If you have a program written in X, and you have a compiler from X to Y, but no interpreter for Y, but instead you have an interpreter for Z and a compiler from Y to Z, you can

  1. Compile the program from X to Y, then
  2. Compile the program from Y to Z, then
  3. Interpret the program using your Z interpreter.

This is actually fairly common. Many new language implementations start out with a compiler from that new language to (a subset of) C, then compile C to native machine code using one of the many existing C compilers, and then execute the native machine code on the CPU.

In fact, YARV's (the most widely-used implementation of Ruby) JIT compiler is based on generating C source code, so when you "interpret" Ruby using YARV, what is actually happening is that YARV compiles Ruby to YARV bytecode, then interprets that bytecode (not Ruby). In parallel, it also keeps a counter, and when it has interpreted the same bytecode a certain number of times, it will pass the bytecode to the JIT compiler, which first compiles the bytecode into its own internal representation, then compiles that internal representation into C source code, then calls GCC, which in turn compiles that C source code into assembly code (going through a couple of additional steps actually, it first compiles C to GENERIC, then GENERIC to GIMPLE, then GIMPLE to RTL, and then RTL to assembly), then calls GAS, which compiles that assembly code into native machine code. All of that is happening when you do what you call in your question "directly interpreting code".

Whether to implement a language with a compiler, an interpreter, both, or a combination is simply a question of preference. Sometimes, the author is experienced in writing compilers but not interpreters, or the other way around.

I wrote some more about this stuff on SoftwareEngineering.SE, if you are interested: Understanding the differences: traditional interpreter, JIT compiler, JIT interpreter and AOT compiler.

$\endgroup$
2
$\begingroup$

gcj compiles Java straight to machine code. PyPy compiles Python to machine code.

I think that a compiler for Bash would be pointless, because it's a terrible language for large-scale programming, and any Bash script that reaches the point of needing a compiler should be rewritten in some other language for reliability and maintainability as well as performance reasons. But pointless isn't the same as impossible.

There are C and C++ interpreters, but I have no experience with them.

The reason Java has a standard bytecode format is not that the language can't work without bytecode. It's that it was designed to support running untrusted code delivered over the Internet, and a standard, manifestly type-safe bytecode was an efficient way to do that. (JavaScript, a totally unrelated language, never had a standard bytecode format, and it's delivered as source code instead, which adds some compilation overhead on the client side.)

Some languages are easier to compile than others. Python is pretty bad for this, because so many things are changeable at run time in ways that can't always be detected at compile time. PyPy proves properties of programs when it can; when it can't prove something, it guesses, and inserts code to check at runtime if the guess is wrong, and fall back on slower generic code. This is very complicated and makes writing a compiler for Python a daunting project. But PyPy does exist and it works. JavaScript is hard to compile for similar reasons, but good JavaScript compilers exist because there's an economic incentive for companies like Google to make them.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.