Project

General

Profile

Getting Your Project to Compile

Once a project successfully (without any errors) completes the FWD conversion process, the converted Java application code is ready for Java compilation. In particular, the developer must have successfully completed the tasks described in the Running Conversion and Building Converted Code chapters. The Java compilation process is a major part of the application build. After compilation completes successfully, the second portion of the build is to store all of the application's compiled Java class files in a Java Archive or jar file. This jar file is then loaded into the FWD server at runtime to run the converted application code. This chapter describes the process of getting the project to successfully compile. It also documents some of the most common compiler failures.

Compilation Process

The creation of the jar file cannot occur until all of the converted application code is compiled using a Java compiler (usually javac in the normal J2SE distributions). As noted at the end of the Building Converted Code chapter, to run the build (which first compiles the converted code), one normally changes directory to the project root and then runs the Apache ant tool:

cd $P2J_HOME
ant clean jar

If there are any failures, some number of those failures will be displayed. In Java 6 (and most if not all previous J2SE implementations), the javac compiler will display the first 100 failures and then it will halt. If 100 errors are displayed and resolved, the next build may expose additional errors that were not reported the first time. These additional errors may be from files that didn't get completely processed in the prior run. Another possibility is that new errors may be displayed due to the compiler being able to process portions or aspects of the previously failing source files which had not previously been reached. Until the entire application completes compilation without any failures, the following iterative process must be followed:

  1. Run the build (see above).
  2. Review each error reported by javac. Analyze the failure.
  3. Resolve the failure. This may include applying 4GL source changes, modifying the project configuration, fixing FWD conversion and/or fixing the FWD runtime. In any cases where the fix would cause the converted source code to be modified, the project will need to be completely reconverted in a single batch.
  4. Repeat the process starting at step 1 above until there are no remaining javac errors.

Common Failures

Missing Build Dependencies

When javac compiles code, any classes and interfaces that are referenced by that code must be available to javac so that those dependencies can identified and resolved. Those classes and interfaces must be available to in uncompiled (for dependencies within the current project being compiled) or compiled form (for any dependencies on code outside of the project being compiled).

If the compile error reports that a referenced class or interface is unknown, it is likely that there is a problem with one of the following:

  • The CLASSPATH as used by Apache ant is incorrect such that jar files that do exist in the file system are not listed.
  • There are missing jar files in the file system.
  • The wrong version of a necessary jar file is present.

Name Conflicts

The FWD conversion must create valid Java and SQL names to replace 4GL names. While some names are compatible between these different environments, it is also true that the 4GL allows names that are invalid in Java or SQL. This means that there can be lost information when names are converted and anytime lost information can occur, there can be naming conflicts.

In addition, the Progress 4GL has many more namespaces than does Java.

In the Progress 4GL there are 20 or more namespaces including: language keywords, variables, functions, procedures, classes/interfaces, methods, packages, labels, databases, tables, fields, indexes, frames, menus/sub-menus, menu-items, streams, datasets, data-sources, data-relations and queries.

In Java: there are 5 namespaces: classes/interfaces, packages, methods, variables and labels.

Where in the 4GL, there may be FRAME mine, QUERY mine, BROWSE mine and STREAM mine, which are do not cause any conflict. In Java these are all just variable instances which would exist in the same namespace. Cross-namespace conflicts can cause problems when the conversion process does not disambiguate between such conflicts. The result is Java code that may not compile because the same symbol is defined more than once.

Another common cause of name conflicts is the manual encoding of name conversion hints. If the name conversion has been customized (see the chapter on Other Customization), many unexpected name conversions can result, some of which may fail to compile due to conflicts.

Finally, it is possible for the Progress 4GL resources to be named in conflict with Java classes. Whenever a Progress resource name is used as the basis for a class name, there is potential for conflict with Java classes in the runtime or in the automatically imported Java packages (e.g. java.lang).

Possible solutions:

  • Modify the Progress 4GL code to name things in a manner that won't conflict.
  • Modify or fix the FWD conversion to resolve the naming problems automatically.
  • Modify the custom name conversion configuration to resolve unintended name conversions.

Unreachable Code

The Progress 4GL compiler does very little (if any) checking of the flow of control for an input source file. When this code is converted to Java, FWD maintains a compatible structure and compatible flow of control. However, the javac compiler does some limited checks of code which can determine when code is not ever reachable.

For example, consider this Java code:

return;
System.out.println(“Hello!”);

This would cause a javac error since the second line is unreachable. For this reason, it is possible for Progress 4GL code that is unreachable to be converted to Java code that will fail to compile. FWD does include a conversion step called Unreachable Code Analysis. This step is designed to identify and remove unreachable code. The result is an elimination of the javac errors that would normally occur.

If there is any problem in the unreachable processing during FWD conversion, the result may be javac errors. The solution is to either edit the 4GL to remove the unreachable code (which by definition is dead anyway) or to modify/improve the FWD conversion.

Empty Try Blocks

Just as with unreachable code, javac will raise an error when it encounters an empty try block. A try block is a Java construct that is used to implement structured exception processing. The FWD conversion extensively uses such Java features and it is possible that the conversion has a flaw that leaves behind an empty try block. In such a case, the solution is either to edit the 4GL to remove/modify the code which triggers this condition or to modify/improve the FWD conversion.

Missing Runtime APIs

If the converted code looks correct, but the compiler reports that the some method call could not be found in a FWD runtime class, it is possible that there is a missing FWD runtime API. Java method overloading is extensively used in the converted code to provide a wide range of syntax options for the same basic function. The Progress 4GL can generally interchange many data types and their corresponding literals (e.g. a character variable and a string literal that is hard coded in the source in double quotes). The implication is that for a built-in function foo() that takes a single text parameter and has no return value, there must be at least 2 versions in Java:

void foo(character)
void foo(String)

As the numbers of parameters increase this can explode into a much larger number of variants.

In Progress, there are optional parameters in many functions. This is possible in Java too, but it means that there are variants of methods that have differing numbers of parameters.

For these and other similar reasons, there may be valid 4GL code that is seemingly converted in a correct manner, but which cannot compile since the backing FWD API is missing.

The solution is either to edit the 4GL to remove/modify the code which triggers this condition or to modify/improve the FWD conversion. Usually editing the FWD runtime for such a case is trivial since most often there is a working and testing implementation already available and the edit only requires the minor addition of a new variant that can call the current code to do any real work. If the variant relies upon logic that is not yet existing, then the effort may be larger.

Broken 4GL

It is possible for broken Progress 4GL source code to get all the way through the conversion process without a detected failure or error. If the javac error is traced back to broken 4GL source code, the solution is to fix that broken 4GL source and re-convert.

Broken Conversion Process

The FWD conversion process may have flaws that result in broken Java code. Likewise the conversion process may have partial or no support for a 4GL feature. In any case where an incomplete or flawed conversion results in broken Java code, the solution is to modify the 4GL source code to remove the /modify the code which triggers this condition or to modify/improve the FWD conversion.

Manually Coded Java

In projects which have had a great deal of customization, manually coded Java source can be integrated with the rest of the application. Even if the converted code is not integrated with manually coded Java source, that source can still have been placed into the project. If javac reports an error in any application-specific manually coded Java source, that source code will need to be removed or modified.

Issues Requiring 4GL Code Refactoring

javac error: code too large

javac may report a problem like this:

[javac] /some/path/to/converted/code/TooManyLinesOfCodeInAProcedure.java:1: error: code too large

This is not a common case, but if it occurs it may be a fundamental mismatch between the 4GL code and the Java class file format. This can be caused by having too many lines of code in a single Java method. Each Java method is limited to 64KB in byte code. Each procedure, function or OO class method converts to a single Java method. If you have a procedure, function or OO class method that has too many (e.g. 20,000) lines of 4GL code, then this will be too much for a single Java method.

At this time, FWD does not detect and refactor this case. The code must be manually split into small-enough chunks in the 4GL and then re-converted. It may be as simple as creating multiple procedures/functions and calling them from the original procedure or function. This does not necessarily mean that the code must be in different external procedures, but that will also work. The key thing is splitting the 4GL code across callable program blocks (e.g multiple internal procedures instead of just one internal procedure).

javac error: too many constants

javac may report a problem like this:

[javac] /some/path/to/converted/code/TooManyLiteralsFieldsMethodsAndOtherConstants.java:1: error: too many constants

A single Java class as a 64KB 'constant pool'. This is an index into all literals (String, int, long, boolean...), method names, field names, class names, interface names, references to classes and methods and so forth. Any single class may only have 65,536 of such constants.

At this time, FWD does not detect and refactor this case. The code must be manually split into 2 or more external procedures/OO 4GL classes. Each external procedure/OO 4GL class converts to a single business logic Java class. Since this 64KB constant pool is per Java class, the solution requires that the 4GL code be split between multiple Java classes, which will occur naturally if the 4GL code is split into separate external procedures/OO 4GL classes.

An alternative would be 4GL code changes to reduce the amount of code. For example, if there is a large amount of dead code in the program, it could be deleted to reduce the number of constants used on the Java side.

javac error: too many parameters

javac may report a problem like this:

[javac] /some/path/to/converted/code/TooManyLocalVariables.java:150: error: too many parameters

Generated FWD code heavily uses lambdas (anonymous methods). Almost every block in the original 4GL code is a lambda in Java. This allows the converted 4GL code to be passed to the FWD runtime so that the execution is delegated to FWD. That allows FWD to implement all the block processing in the runtime, instead of emitting it into the converted code, Lambdas appear (from a Java source code perspective) to be a separate scope that has direct access to the variables in the containing scopes. Under the "covers", javac creates a list of all local variables and passes these as hidden arguments to the compiled lambda method. Since methods in Java are only allowed to have a maximum of 255 arguments, if there are more than 255 local variables in the 4GL, then this error will occur.

It can also be the case that there are not more than 255 explicit local variables in the 4GL code, but that some implicit aspect of how FWD converts the code emits additional local variables in Java that are just implicit in the 4GL. For example, some loop control structures (e.g. a TO clause in a block definition) or some kinds of accumulator usage can generate additional local variables in FWD.

Another case is how FWD will take resources that are scoped to an external procedure and move them into local variables. It is a best practice to use local variables whenever possible (as opposed to variables that are class members or scoped to an external procedure). This yields code that better encapsulates its data. It also may enable recursion, which can be a cleaner design (or at least one with less code). FWD strives to scope resources to the most specific scope possible. This means that some external-program scoped variables get emitted in FWD as local variables. This is usually related to the 'execute' method which is the equivalent to the external procedure.

Any of the above explicit or implicit reasons may cause more than 255 local variables to exist and be passed to the nested lambdas. Depending on the case, it is possible that changes to FWD could resolve the issue. For example, some variables could be "promoted" to instance members of the Java class. But there is no guarantee that this will work. For example, promoting to instance members will break any recursive usage of a method. It may be the case that the original 4GL code needs to be refactored to reduce/remove some number of local variables. This may require factoring the code into multiple procedures, functions or OO class methods. By splitting the original logic up, this spreads the local variable usage across multiple lexical scopes keeping the total number of local variables within the limit of each top level scope.


© 2004-2021 Golden Code Development Corporation. ALL RIGHTS RESERVED.