= Java Backend Gen TODO = Require Base version Java to be java7 gain strings in case stmt for free (!) Indexers on Generics ( List) - Not recognising/setup indexer record on JavaClass (or searching inheritance hier List ^ List... - seems to be missing NativeType entry should be List -> List - (unfortunately) like commandLineArgs (065j-indexing.cobra) Add code for generating attributes from 'has XXXXX' - chk cobra syntax allows AttribName(value[, value]) like ctor not just AttribName ? executable autgen jarfile gen srcs into own dir ./java compile classes into own dir ./classes generate exe jarfile ( auto manifest file) ./.jar remove unlise -kif generated java files remove intermediate classfiles run as java -cp '' -jar App.jar args.... include-tests:no - remove Test invocation call _fixNativeMethods - static method entries on box instance Get Testifyrunner working on java gen passthru compilation of *.java files - seems to work for single files But - calc of main class fm passthru *.java files cobra namespaces capitalized (as .Net) Java all lowercase - convert namespaces in java code to lowcase DONE 31-Jan-2011 Unworkable Jul-2011 - cobra assumptions in code Change to cobra casing 8-Jul-2011 revisit later (all lowcase) - backend variances on .canBeUndottedMemberName?? java names lowcase method Names - DONE 31-Jan-2011 var names - DONE 31-Jan-2011, corrected 10-Feb-2011 BoxField._backendName -> BoxField.{sharp,java}BackEndName properties params events local variables -> remove/reverse cobra conversion to upcase for C#/.Net ( look for calls to .capitalized) DONE (I think) properties - pro/get/set - auto convert to java getters/setters and map calls - Whats the equiv for indexers? Look at Beans spec but otherwise use/convert to {get,set}Indexer method call. getIndexer(T) and setIndexer(T, indexedT) DONE codegen for props: 'propName' -> getPropName (DONE) setPropName convert propName = a.b.c -> setPropName(a.b.c) (DONE - minimally at least) codegen for is property accessor isPropName Indexers: AutoMap map indexers '[]' to methods X.get(idx) v = X[idx] , X.set(idx,val) and X.put(idx,val) -> X[idx] = val - unworkable - too many false positives Handling of indexing '[]' on Strings, Maps and Lists x[i]=v : String: - map: x.put(i,v) list: x.set(i,v) v = x[i]: String: v = x.charAt(i) map: v = map.get(i) list: v = list.get(i) DONE Nov-2011 codegen for user code indexers pro/get/set [index as idxT] as T get return _data[index] set _data[index] = value accessor: T getIndexer(idxT index) { return _data[index]; } mutator: void setIndexer(idxT index, T value) { _data[index] = value; } String Substitutions: format handling: e.g. m=42 assert '[m:N]' == '42.00' 1) ignore initially 2) either map .Net fmt to java equiv (shudder) or treat as java specific form - m=42.0 assert '[m:%.2f]' == '42.00' Arrays Array init from Array literal Array indexing Events - auto add boiler plate for listener (de)registration - event firing partial classes Add extra phase to aggregate partial classes together into first Module or AST Tree (Chk if cobra is doing sommat like this or relying on C# compiler) Extensions convert to static classes methods taking instance as first param If lookup for name fails see if sig matches extension and remap call to extn static class method single file: generate to java src files in namespace dir hier compile to class file ( in class dir hier) run class file If specify multiple files generate them all to java src in in namespace dir hier compile to class files (in class dir hier) generate manifest file and generate classfiles to jar file Leave jar file at root of hierarchy -> single or multiple files just gen an executable jarfile add synonym for sharp''/sharp"" -> java''/java"" or general be''/be""/backend'' (for backend) see http://www.25hoursaday.com/CsharpVsJava.html java 'synchronized' (block is exact equivalent of C# 'lock' stmt. varargs 'params' keyword in C# (qualifier to the last argument to the method which should be an array). In Java, the same effect is achieved by appending the string "..." to the typename of the last argument to the method. Attributes C# extend Attribute - metainfo also ([AttributeUsage(AttributeTargets.Class)]) java specify with @interface and Metainfo e.g @Documented //we want the annotation to show up in the Javadocs @Retention(RetentionPolicy.RUNTIME)) Java has Set in its collections framework so use that instead of Cobra lib version. Cobra methods nonVirtual (C# not marked virtual) in java mark as final (Cannot redefine or override). out and inout (ref) x as ? = nil method(...., out x) # def method(.... out x as ) # use X here becomes in java Type x = nil Object xRef = x method(..., xRef) // void method(..., xRef as Object) - inmethod xRef assigned to whatever x calculated as x = xRef // use x here Or in call wrap arg in a SoftRef (or make own version HardRef subclass j.l.ref.Reference) Chg param to be a SoftRef to the arg passed (e.g. 'arg_Ref') In the method pull it out from the softref as a local name ('arg') nilable/nonNilable enforcement nonNilable - require use of Java7 and use Objects.requireNonNull() call Add new compiler directive @platform portable|jvm|clr|objc Where down the road, portable would be the default and would have its own libs (which would need impls for each of the other supported platforms). For now the default will be clr, obviously. And then possibly rename -back-end to -platform. Decimal Type Initially wrap it as new type thats is a Double with an associated DecimalFormatter (giving same precision as .Net). Calcs are all as doubles, toString uses formatter... still get rounding error etc... Decimal Type internally and emit as BigDecimal, trap math ops and map -> try and use BigDecimal and convert all calls (:-( Maybe easier to just default to Integer and ignore Decimal (initially) or default Decimal to Double ?? tests :050-decimal.cobra Java7 adds java.nio Files and (interface) Path - modify codegen/handling to use that ( wire to >java7) use Paths to create Path and Files to convert Path for use legacy Path p = fileInst.toPath(); // java.io.File to java.nio.Path Add convenience methods to java.io.File for remember (Buffered)Reader(FileReader(File(path) - read text (Buffered)InputStream(FileInputStream(File(path) - read bytestream static FileInputStream/FileOutputStream openRead/openWrite(String path)- r/w bytestream static BufferedInputStream createRead(String path) - open or create - bytes static BufferedOutputStream createWrite(String path) - open+truncate or create - bytes static BufferedWriter createText(String path) - open or create Textfile for writing static BufferedReader openText(String path) - open textfile for reading static byte[] readAllBytes(String path) static String readAllText(String path) static String[] readAllLines(String path) static IEnumerable readLines(String path) Instead use google collections/Guava - support for List, Set and Map literals com.google.common.collect l = [lit,lit,...] List l = Lists.newArrayList( lit, lit, ....); OR (immutable) List l = new ArrayList( Arrays.asList(lit, lit, ....)); (mutable) List l = new ArrayList( new ArrayList(Arrays.asList(lit, lit, ....))); m = {a:b, ...} Map m = Maps.newHashMap<>(); m.put(a,b); ... (Immutable - up to 5 entries) ImmutableMap m = ImmutableMap.of(a, b, ...); (Immutable any #) ImmutableMap m = ImmutableMap.Builder().put(a, b)... (.put(c.d)...}.build(); (Mutable) Make immutable map and Map m = new HashMap(immutableMap) OR Map m = Maps.newHashMap(mutableMap) OR add CobraMutableMap inherits HashMap - adds ctor taking varargs list of k,v) Map m = new CobraMutableMap(a,b,...) OR static factory CobraCore.fillHashMap(new HashMap(), a,b,...) OR (http://blogs.steeplesoft.com/2011/10/funky-object-initialization/ ) Map m = new HashMap() { { put(a,b); ...; } }; Wait for Java8 and hope have implemented List and Map literals - Simple file reading API ensure support ( as builtin alternative) for jikes compiler. https://sourceforge.net/projects/jikes/files/Jikes/ Cobra backend targetting stab language (explicitly) ?? backend 'stab-jvm' ----------------------------------------------------------------- = Decisions = Look for and use java and javax in path. - verify version >= 1.6 Loading external jars Rely on jar or Package PkgSig files for description of Class publicly accessible contents Structure so can revisit later (IKVM) if need be PkgSig tool for generating pkgSig files start: explicitly load rtl jar up front (rt.jar.sig cf mscorlib.dll) - but vv big, contents mostly unused -> chg to load packages separately (separate pkgsig files) initial default java.lang java.io java.util java.text cobra.lang smaller + faster ?? Java Source Turn Cobra source file into Java Source file named per first class in file. - possible issues with multiple public classes ( javac warn-and-quit) -> Cobra Source file into Java Source files one for each class -> place all generated java source in own (sub) dir ( ./java) Running Generated/compiled classfiles Assume compiled classfiles in same cwd as cobra source, - run java specifying -cp . so java can find rest of classfiles (if >1) ( java -cp '.;' AppMainClass args....) -> roll all generated source into own jar file ( cf .Net exe) with autogen manifest describing main class - run with classpath specifying name of jarfile ( java -cp '' -jar App.jar args....) Namespaces: 0: namespaces low cased in cobra-jvm as per java. Unworkable with Cobra assumptions Change to CapCase as per cobra-clr translate case on jar load (in) and JavaGeneration (out) Testing Implement Test support i/fstructure Add new 'require' forms so can wire tests clr or jvm Passthrough to BackEnd ensure java Native modules compile -> support determination of main class/method on pure java modules Syntax - support sharp'' as currently add additional: be'' or backEnd'' for any others/more, deprecate sharp'' Indexes '[]' 0:autodetect from method sig - Unworkable, too many false positives -> Wire Maps/Lists/... - is that sufficient? -> For default packages generate fm internal description as per fixNilable may need to expose this data structure for augmentation? - allow faked/added attrib placed in pkgsig file. Vari params Use java vari args In and Inout parameters Inline wrap and post call unwrap - Invent java RefType class Use java SoftRef as Param, pack and unpack in call/callee Decimal support 0: Treat default as Integer, map explicit decimal to float Default Number type to non-decimal ( float??) Delegates Need some sort of placeholder for setProperties Drop something into CobraCore - Cobra.Lang.Delegate? (DONE) Delegate - dcl - returnType and paramList d = new Delegate( Class ret, Class... paramList) - store ret and paramList types - init (assign) method ref d.init( Object target, String methodName) d.init( Class target, String methodName) lookup target MethodName chk param types match, get and store its Method m and Object target - call d.invoke(Object... paramList) m.invoke(target, paramList) Events wot to do? Exception Adaption ? See if can get any clever hack working as detailed below ? JavaGenerater auto wrap with try/catch exception block at place of use probably unworkable for deeply embedded expressions ->? make call to individual adaptor that does wrapping, autogenerate the adaptor on demand ( see if can cobble a genera adaptor that does wrapping and pass ref to method and args to it ... Dynamic Delegate??) ------------------------------------------------------------------------- = Issues = RTL name mangling How support yield and conversion to enumerator/iterator? ------------------------------------------------------------------------- Checked to Unchecked exceptions Fm http://www.mindview.net/Etc/Discussions/CheckedExceptions See also ExceptionChaining since JDK1.4 ExceptionAdapter Here's a tool that I developed with the help of Heinz Kabutz. It converts any checked exception into a RuntimeException while preserving all the information from the checked exception. import java.io.*; class ExceptionAdapter extends RuntimeException { private final String stackTrace; public Exception originalException; public ExceptionAdapter(Exception e) { super(e.toString()); originalException = e; StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); stackTrace = sw.toString(); } public void printStackTrace() { printStackTrace(System.err); } public void printStackTrace(java.io.PrintStream s) { synchronized(s) { s.print(getClass().getName() + ": "); s.print(stackTrace); } } public void printStackTrace(java.io.PrintWriter s) { synchronized(s) { s.print(getClass().getName() + ": "); s.print(stackTrace); } } public void rethrow() { throw originalException; } } The original exception is stored in originalException, so you can always recover it. In addition, its stack trace information is extracted into the stackTrace string, which will then be printed using the usual printStackTrace() if the exception gets all the way out to the console. However, you can also put a catch clause at a higher level in your program to catch an ExceptionAdapter and look for particular types of exceptions, like this: catch(ExceptionAdapter ea) { try { ea.rethrow(); } catch(IllegalArgumentException e) { // ... } catch(FileNotFoundException e) { // ... } // etc. } Here, you're still able to catch the specific type of exception but you're not forced to put in all the exception specifications and try-catch clauses everywhere between the origin of the exception and the place that it's caught. An even more importantly, no one writing code is tempted to swallow the exception and thus erase it. If you forget to catch some exception, it will show up at the top level. If you want to catch exceptions somewhere in between, you can. Or, since originalException is public, you can also use RTTI to look for particular types of exceptions. Here's some test code, just to make sure it works (not the way I suggest using it, however): public class ExceptionAdapterTest { public static void main(String[] args) { try { try { throw new java.io.FileNotFoundException("Bla"); } catch(Exception ex) { ex.printStackTrace(); throw new ExceptionAdapter(ex); } } catch(RuntimeException e) { e.printStackTrace(); } System.out.println("That's all!"); } } By using this tool you can get the benefits of the unchecked exception approach (less code, cleaner code) without losing the core of the information about the exception. If you were writing code where you wanted to throw a particular type of checked exception, you could use (or modify, if it isn't already possible) the ExceptionAdapter like this: if(futzedUp) throw new ExceptionAdapter(new CloneNotSupportedException()); This means you can easily use all the exceptions in their original role, but with unchecked-style coding. ------------------------------------------------------------------------- I've managed to fiddle javac to not check exceptions anymore *evil grin*. Very simple really. Add a directory java/lang to your project and copy into that directory Exception.java and RuntimeException.java Change Exception to extend RuntimeException and change RuntimeException to extend Throwable. Now the compile-time exception checking is turned off. -- hops: Doesnt work unfortunately --- ------------------------------------------------------------------------- ------------------------------------------------------------------------- = Current compile cmd (X-compile) = cobra -back-end:jvm Name.cobra cobc0 -back-end:jvm -kif Name.cobra = MSW Default compiler cmd = File.cobra (containing class File) javac -cp '[CobraCore.exePath][pathSep]CobraLang.jar File.java javac -cp '../wkspace/Source/CobraLang.jar' File.java = MSW run cmd = java -cp ".;../wkspace/Source/CobraLang.jar" File = Other back ends = Fantom : compile to jvm or dotNet ----------------------------------------------- Done fixup Object namespace for X-compile so items like System.Object are rooted off java.lang namespace ( rather than .Net system) DONE June-2011 computeOutName from name of main class (or first/only) class in file rather than permutation of input name DONE 31-Jan-2011 remove all uses of ClrNativeType in core compiler ( ->NativeType??) Compiler/Enums/Types.cobra DONE Parse error lines fm java giving source line, find (trailing comment) cobra line number and modify the java error msg line with the cobra lineno printing that in any java error mesages so to relate the java error back to the generating cobra code line. - start with just spew the javac lines to output - assemble into ErrorMessage Line and 'supporting lines associated with it - keeps error line count in sync DONE