Why Java Can Be a Scripting Language

Java is in many ways similar to a scripting language. The Java language defines the constructs and syntax rules, and the JVM can execute Java bytecodes. JNI allows Java to invoke native methods and native functions to execute Java code.

Productivity

As with typical scripting languages, Java offers a fast edit-test cycle, which is important. Unlike C/C++, Java has a substantially faster compile time, mostly because compiles are incremental and no linking is performed at compile time.

Ease of Learning

The simplicity and power of a language tend to work against each other. Java has the power of a complete object-oriented language but tries to do without unnecessary things. For example, multiple inheritance is possible but limited so that it does not get out of hand. Some may argue that an object-oriented language is overkill for a scripting language. If you are sure that you do not need an object-oriented language, then you should look into other scripting languages. However, keep in mind that objects are an intuitive way of grouping data and functionality. Many languages that did not originally support objects now support them.

Keep in mind that familiar languages are easier to learn. Developers who are familiar with an existing scripting language such as Macromedia Flash script (ActionScript) or Maya script (MELScript) can learn Java’s syntax without any difficulty. This is in part because many languages including Java have C-like syntax.

Strongly Typed versus Loosely Typed Languages

To make a language easier, some languages use runtime or loosely typed variables. In other words, they do not map a variable to a specific type. Each variable is instead a reference to a structure that contains a field indicating its current type. By doing so, the type of the variable can be changed when necessary. For example, if an integer is passed to a function that takes a string as its parameter, the integer value can be converted to the equivalent string that contains the number.

Some languages do not require a variable to be declared before the language is first used. Instead, when the language comes across a name that has not been seen before, it assumes that the variable is new and therefore registers it. Can you imagine the bugs that can show up if the name of a variable is misspelled?

There is no question that such features make writing scripts more convenient and, as a result, reduce the learning curve. However, they are open invitations for bugs. It is important to know that the mistakes made in such languages do not show up until the interpreter actually tries to execute them. Such features make it more convenient not only to write scripts but also to overlook mistakes and write bugs. This is not to say that such languages are fundamentally flawed. If there is the need to write a few short and isolated scripts, using such languages can be extremely helpful, in part because a small and isolated script can be tested quickly. On the other hand, as the length of a program grows, the amount of effort required to test and debug it tends to grow exponentially.

Another important point is if you have to rely on the interpreter to find mistakes, you must test every single control path to make sure the script is sound. Can you imagine testing every possible control path, just to find out if you have misspelled the name of a variable?

If your team writes scripts that are bug prone, they will spend a lot of time trying to find the bugs. Do not forget that debugging skills are not easy to learn and can take a while to hone. Having to spend more time to learn a language that makes it harder to write bugs is generally an investment worth making.

Continuation Support

A potential disadvantage of using Java as a scripting language is the fact that there is no support for continuations, which are supported by some scripting languages such as Python, Scheme, and Lua. Continuations can make scripts much easier to read and write. Coroutines, generators, and microthreads are different approaches to continuations. A language (or VM) that supports continuation is one that allows a function to call another function without requiring the second function to return execution to the caller. In other words, control can be forwarded to any frame in the call stack, not only to the top frame, and a function’s frame can be saved and restored at a later time so that it continues where it left off. This is in some sense like using the devious goto statement, but before leaving a function, the state of the function is stored and the state of the other function can be restored before entering it. As messy as this sounds, it can make implementation of many systems significantly simpler. Take, for example, the task of traversing a tree recursively. If there were only one thread in the game, it would not be possible to perform a task recursively unless it guarantees not to take a significant amount of time. The recursion would have to be unrolled and emulated to allow the thread to leave in the middle of recursing to allow the game to perform other things. With the use of continuations, you could simply yield to the game even if you were 20 plies deep, and pick up right at the 20th ply later. A more game-related example would be the implementation of a state machine. A state machine written in a language that supports continuations does not need a variable to keep track of the current state. Instead, the program counter keeps track of the current state, which makes the code much easier to read and write.

You might have realized that some of the previously mentioned tasks can be emulated by the use of threads. It is correct to say that continuations can be adequately emulated using threads in Java. One of the disadvantages of this approach would be sizable overhead of threads if there were many NPCs. Because microthreads use assembly code to store the execution state, there can be thousands micro-threads with an extremely small amount of overhead.

Tools and Documentation

Java has many development tools that can make life easier. Features such as syntax highlighting and pop-up helpers can be useful, especially for new developers. Project files can also be beneficial for grouping script files and providing easy navigation between them when working on larger projects. Besides the basic development tools, debugging and profiling tools are also important. Even though it is easier to write high-level scripts, debugging and profiling them is not as easy. In fact, using a basic scripting language can become worrisome if a project requires numerous intricate scripts. Many scripting languages try to provide profiling and debugging support, but it is safe to say that achieving the completeness of tools available for a language such as Java is not trivial.

Many Java books are available, as well as many online tutorials that can help your team learn Java. Many in the user community already know Java or can easily learn it because of the tremendous amount of books and tutorials, and will, therefore, be able to modify the game. In addition, most colleges and universities now teach Java. Some have even adopted it as their main programming language.

Efficiency

Even though Sun Microsystems has defined the language, many different vendors besides Sun have implemented JVMs. If you read the Java language specifications, you will not find many details about how the VM should do things. This lack of information is so the implementers of a VM do not have to face unnecessary restrictions and make decisions that are more appropriate for their target platform. In this book we focus on Sun VMs. The Sun VM has gone through a significant amount of change since its first version. Sun has focused on improving memory management and performance in different releases.

Performance

As far as performance goes, Java is not what it used to be. The 1.0 VM acted as a typical and basic interpreter. It simply interpreted each bytecode. In the following releases, Sun added JIT compilers that converted Java bytecodes to native instruction corresponding to the CPU of the host machine. Therefore, after the VM ran for a little while, the VM would not have much interpreting left to do. This, of course, resulted in a tremendous amount of speed increase. One of the disadvantages of the JIT VM, however, was that it eagerly loaded class files and tried to compile them, which resulted in substantial consumption of memory. In the later releases, the HotSpot VMs were introduced. Instead of compiling every possible bytecode to native CPU instructions, they tried to convert only the most important parts of the code to native instructions. The HotSpot VMs rely on the 10-90 rule, which states that only 10 percent of the code runs 90 percent of the time. In fact, when the hot spot of the code is found, the VM tries to do many other optimizations before it converts the code to native instructions. For example when possible it converts virtual methods to final, eliminates null checks and bounds checks, and inlines methods. In addition, because HotSpot is aware of the host CPU on which it is running, it can generate native instructions specific to the design of the CPU, including hardware-specific optimizations such as taking advantage of special registers and instructions. Keep in mind that a C/C++ program compiled to take advantage of hardware-specific features cannot run on a machine that does not have the features. It is, therefore, common for developers to compile their code for an older machine.

Having mentioned this, have no doubt in your mind that Java is one of the absolute fastest scripting languages you can get your hands on. Any documentation that you find that states languages such as Python, Perl, or Lua run faster than Java are almost certainly comparisons performed with older versions of the VM. It is also important to note that it is likely for Java to run more slowly than other interpreted languages if it is run in interpreted mode. When run in interpreted mode, the performance is almost like that of the 1.0 VM, which was anything but fast.

You may wonder, if running in interpreted mode can be slow, why would anyone run in the interpreted mode. Even though Java bytecode is platform independent, as with other scripting languages, the VMs are highly platform dependent. If you want to run Java on a platform for which a VM is not available, you must compile the VM for the specific platform, or write your own VM. Then, if you have some extra resources to spare, you can write compilers to convert bytecode to the corresponding native code during runtime.

Memory

The smarter a VM, the more memory it is likely to use to perform certain optimizations. For example, hot spot detection consumes some resources. In addition, when the code must be compiled, the VM has to keep track of the bytecodes as well as the native assembly code. The memory footprint of Sun’s VMs are definitely not as small as the footprint of the interpreter of languages such as Lua. Lua’s binaries are the absolute minimum amount of code that does only the basic tasks.

The VM does a superb job at memory management. Sun’s VM has been used by many developers and has had many years to mature. The VM comes with multiple garbage collection algorithms to deal with programs that have different behavior. The garbage collector is also highly tunable. You can use parameters that give you total control over the size of the heap and how often collections occur. Chapter 6, “Performance and the Java Virtual Machine,” provides detailed information about the memory management and the runtime optimizations.

Safety and Security

Java is inherently safe. There is no direct memory access. Every array access is checked to make sure that an illegal index is not used to access memory outside of the array. The language was also designed with exception support. In addition, before a class file is executed, the VM verifies its bytecodes to make sure it does not contain any inappropriate instructions. By doing so, some bad bytecodes cannot crash the VM and simply result in exceptions that can be caught and dealt with gracefully. The check is extensive, and the details are explained in Chapter 6, “Performance and the Java Virtual Machine.” In addition, features such as runtime type checking are designed to make the language safe.

Making a language safe and secure takes a tremendous amount of effort. By using Java, you have a team of developers keeping you as safe as possible. Some studios have had to spend a substantial amount of effort to make sure that their scripting engine is secure.

Runtime Code Execution

A potential disadvantage of Java is that you cannot execute Java source code during runtime as easily as you can in a language such as Lua. This is because the source must be compiled to bytecodes before it is handed off to the VM. A scripting language such as Lua can readily execute source code. If you want to execute Java source code during runtime, you have to compile it using an appropriate compiler. It is possible to compile Java source code to bytecodes during runtime and then execute it. Javac.exe is simply a wrapper for com.sun.tools.javac.Main, which is part of tools.jar that comes with JDK.



Practical Java Game Programming
Practical Java Game Programming (Charles River Media Game Development)
ISBN: 1584503262
EAN: 2147483647
Year: 2003
Pages: 171

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net