""" Generate Java source code file for cobra AST. The code in this file should not .throwError, record warnings, etc. All of that happens during .bindFoo phases. """ ## ## Compiler ## class Compiler is partial var _javaMainClass as String = '' var _fullJarFileName as String = '' get javaMainClass from var get fullJarFileName from var # run prior to compilation to determine an output filename for the java source # for testing, for need for compilation. # Filename used may be determined later post AST generation using the first classname in file # see 'fixup output filename' in compileJava below def computeOutNameJava as String outName ='' if .options.boolValue('test') outName = _modules.last.javaFileName else if .options.containsKey('out') outName = .options.getDefault('out', '') if outName == '', outName = .defaultOutName to ! if outName.endsWith('.java') or outName.endsWith('.JAVA'), outName = outName[:-5] if outName.endsWith('.cobra') or outName.endsWith('.COBRA'), outName = outName[:-6] _javaMainClass = outName #outName = Utils.forceExtension(outName, '.class') #v1 outName = Utils.forceExtension(outName, '.jar') _fullJarFileName = outName return outName def createJavaFile(fileName as String, contents as String) #print 'output: ', contents File.writeAllText(fileName, contents) .addIntermediateFile(fileName) def writeJava Node.setCompiler(this) try _moduleFileName_to_curlyToCobraLineNum = Dictionary>() for _curModule in _modules if not _curModule.fileName.endsWith('SystemInterfaces.cobra') javaToCobraLineNum = _curModule.writeJavaDef .storeJavaFileInfo(_curModule.fileName, javaToCobraLineNum) finally Node.setCompiler(nil) def storeJavaFileInfo(fileName as String, javaToCobraLineNum as Dictionary?) # called above and fm Namespace.writeJavaDef after new class file _moduleFileName_to_curlyToCobraLineNum[fileName] = javaToCobraLineNum _moduleFileName_to_curlyToCobraLineNum[Path.getFullPath(fileName)] = javaToCobraLineNum def writeJavaTestInvocation # TODO #fileName = 'test-[dt.year][dt.month][dt.day][dt.hour][dt.minute].java' # to-do: format the numbers to have leading zeros #w = CodeCommentLineWriter() # ... #.createJavaFile(fileName, w.output) print 'TODO: writeJavaTestInvocation' def compileJava .compileJava('') def compileJava(extraJavacOptions as String) require .modules.count body if .verbosity >= 2, print 'Compiling generated code' options = .options # locate the Java compiler # TODO: augment to use Java library compiler when Java native compilerPath = options.getDefault('native-compiler', 'auto') to String if compilerPath <> 'auto' if File.exists(compilerPath) pass else if File.exists(compilerPath+'.exe') compilerPath += '.exe' else print 'Cannot find compiler specified by -native-compiler argument: [compilerPath]' throw StopCompilation(this) else compilerPath = if(Utils.isRunningOnUnix, 'javac', 'javac.exe') optChar = '-' # exeNames - javaTool outName = .computeOutNameJava # compute backEndOptions backEndOptions = '-d . ' # Put namespaced classfiles in own hierarchy backEndOptions += ' -Xlint:all ' # warn about everything (non std option) # -optimize isn't supported by JVM, but it could potentially be used by Cobra itself # TODO: # for refer in .loadedReferences # don't take the library references from options, because the references can grow # backEndOptions += ' "[optChar]r:[refer]"') debug = options.getDefault('debug', '') to String if debug.length # TODO: mono does not support full and pdbonly afaik if debug == '0', debug = '-' else if debug == '1', debug = '+' assert debug in ['-', '+', 'full', 'pdbonly'], debug backEndOptions += ' [optChar]g' + if(debug=='-', ':none', '') # TODO: not until the java backend is more mature # backEndOptions += ' -nowarn' backEndOptions += extraJavacOptions nativeArgs = .options.getDefault('native-compiler-args', '') to String if nativeArgs.length if nativeArgs.length > 2 and nativeArgs[0]=="'" and nativeArgs[nativeArgs.length-1]=="'" # on Windows, you should really use double quotes instead of single, but we try to compensate here. nativeArgs = nativeArgs[1:-1] backEndOptions += ' ' + nativeArgs cpSep = if(CobraCore.isRunningOnUnix, ':', ';') libclassPath = '' for libPath in .options.getStringList('library-directory') libclassPath += '[cpSep][libPath]"' # CobraCore.jar is in the same directory as cobra.exe, # explicitly add reference to it until we get setup to install a copy in extdirs ccPath = Path.getDirectoryName(CobraCore.exePath) to ! ccPath = Path.combine(ccPath, 'CobraCore.jar') classPath = '[ccPath][libclassPath]' backEndOptions += ' -classpath "[classPath]"' # TODO: add libPaths/classpaths for any referenced namespaces # .java files fm first class name in each module javaFileNameSet = Set() for module in _modules[1:] if module.javaFileName.length javaFileNameSet.add(module.javaFileName) #javaFileNameList = _intermediateFileNames for name in _intermediateFileNames if name.length, javaFileNameSet.add(name) javaFileNameList = List(javaFileNameSet) #trace javaFileNameList, _intermediateFileNames # fixup javaMain pkg path all lower case # fixup output filename (classfile) to be in sync with Java filename javaMain = _getFixedMainType if outName <> javaFileNameList[0] outName = javaFileNameList[0] _javaMainClass = javaMain outName = Utils.forceExtension(outName, '.class') if .defaultOutName _fullJarFileName = Utils.forceExtension(.defaultOutName to !, '.jar') # compilation command if _verbosity print 'Compiling to produce [outName]' if _verbosity >= 2 print 'compiler =', compilerPath print ' compilerOptions =', backEndOptions print ' nativeArgs =', nativeArgs print ' fullExeFileName=', _fullJarFileName print ' baseExeFileName =', .baseExeFileName print ' javaFileNameList =', javaFileNameList print ' defaultOutName =', .defaultOutName #output = _execAndCapture(compilerPath, backEndOptions, javaFileNameList) #_parseJavaCompilerOutput(output) _javaTool(compilerPath, backEndOptions, classPath, javaFileNameList) if .errors.count _exitFromErrors _deleteIntermediateFiles _copyCoreJvm(outName) def _copyCoreJvm(outName as String) if not .options.boolValue('copy-core'), return sourceDir = Path.getDirectoryName(Assembly.getEntryAssembly.location) ? '' # above should be something non platform dependent (COBRA_HOME ?) outDir = Path.getDirectoryName(outName) ? '' _copyFile(sourceDir, outDir, 'Cobra.Core.jar') #if .options.getDefault('debug', '') not in ['', '-', '0', 0, false] # copy any debug files # TODO: probably also need to adjust Manifest File Class-Path for cobra RTL def _javaTool(javacPath as String, options as String, classPath as String, fileNameList as List) """ javatool - wrap executing javac and executable jarfile generation for single or multiple files """ # clean directory for java compiled cobraClasses classDir = 'cobraClasses' if Directory.exists(classDir) Directory.delete(classDir, true) Directory.createDirectory(classDir) # put compiled classes in own subdir options = options.replace('-d .', '-d [classDir]') # run java compiler on java srcfiles output = _execAndCapture(javacPath, options, fileNameList) _parseJavaCompilerOutput(output) if _errors.count, return # Make manifest file - entry for Class-Path: cpSep = if(CobraCore.isRunningOnUnix, ':', ';') manFileName = 'in.mf' mfClassPath = classPath.replace(cpSep, ' ') # classpath entries space separated mfClassPath = mfClassPath.replace('C:',' ') # cant handle drive designators mfText = 'Class-Path: [mfClassPath] [Environment.newLine]' try File.writeAllLines(manFileName, [mfText].toArray) catch exc as Exception .warning(CobraWarning(manFileName, nil, 'Cannot write manifest file due to: [exc.message] ([exc.getType.name]).')) # make jarfile _intermediateFileNames.add(manFileName) #outName = Utils.forceExtension(.defaultOutName to !, '.jar') outName = _fullJarFileName mainClass = _javaMainClass #print 'jar cfem [outName] [.baseExeFileName] [manFileName] -C [classDir] .' output = _execAndCapture('jar', 'cfem [outName] [mainClass] [manFileName] -C [classDir]', ['.']) #print output # TODO parse this for errs # Better would be options as optionList as List but ftm we're stuck with space # joined strings coming into caller # TODO: move this to CobraCore to avoid .NET dependency def _execAndCapture(binaryPath as String, options as String, fileNameList as List) as String p = System.Diagnostics.Process() p.startInfo.fileName = binaryPath #options = optionList.join(' ') fileNames = (for fileName in fileNameList get '"' + fileName + '"').join(' ') # MSW quoting p.startInfo.arguments = '[options] [fileNames]' if _verbosity >= 2 print '[p.startInfo.fileName] [p.startInfo.arguments]' try output = p.runAndCaptureAllOutput catch exc as System.ComponentModel.Win32Exception print 'Error: Compiler file [p.startInfo.fileName] not found in path' throw exc # TODO: check p.exitCode, especially if output is empty return output def _parseJavaCompilerOutput(output as String) jcm as JavaCompilationMessage? = nil for line in output.split(c'\n') line0 = line.trim if not line0.length, continue if JavaCompilationMessage.willSkipMessage(line) if jcm, jcm.append(line, this) continue else if _verbosity >= 3 print 'javac message: [line]' jcm = JavaCompilationMessage(line, this) _addMessage(jcm to !) def _getFixedMainType as String """ Fixup mainMethodType for java so that any pkg path (namespace) is all lowercase. We force this in NameSpace.writeJavaDef. """ javaMain = .mainMethodTypeName exeParts = javaMain.split('.').toList if exeParts.count >1 javaPkg = exeParts[0:-1].join('.').toLower javaMain = '[javaPkg].[exeParts.last]' #trace .mainMethodTypeName, javaMain return javaMain #hoisted from CobraModule def newJavaFile( cobraModuleName as String, javaFileName as String) as CurlyWriter #print 'newJavaFile [javaFileName]' sw = CodeCommentLineWriter() sw.start(cobraModuleName) sw.write('// [javaFileName] from [cobraModuleName]\n') sw.write('// Generated by Cobra-[CommandLine.versionString]\n') sw.write('// on [DateTime.now]\n') # TODO: list op sys sw.write('\n') return sw ## ## Node ## class Node is partial def writeJavaDef(sw as CurlyWriter) require .didBindInt or .didBindImp pass class SyntaxNode is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.node(this) interface INamedNode is partial get javaName as String """ Return the name that should be used in Java source code. """ class NamedNode is partial get javaName as String return _name get javaRef as String return .javaName ## ## Module ## class Module is partial var _javaFileName = '' get javaFileName from var get javaSource as String if _javaFileName.length return File.readAllText(_javaFileName) else throw FallThroughException('csFileName is blank') def writeJavaDef as Dictionary? is abstract def writeJavaTestInvocation(sw as CurlyWriter) pass class SharpModule is partial # Sharp Module def writeJavaDef as Dictionary? is override assert false return nil class JavaModule is partial """ The purpose of a Java module is to allow the programmer to include .java source for Cobra to incorporate in the final compilation. """ cue init(file as FileSpec, verbosity as int) base.init(file, verbosity, '') _javaFileName = file.path to ! def writeJavaDef as Dictionary? is override if false # kind of silly, but it works: d = Dictionary() for i in 1 : File.readAllText(_javaFileName).count(c'\n')+1 d[i] = i return d return nil class AssemblyModule is partial get javaSource as String is override return '(no Java source for [this])' def writeJavaDef as Dictionary? is override return nil class CobraModule is partial get javaFileName as String is override if _javaFileName == '' and not _fileName.endsWith('SystemInterfaces.cobra') #java source files are persnickety about naming wrt classname contents. filename = _fileName if filename.endsWith('.cobra') or filename.endsWith('.COBRA'), filename = filename[:-6] #use first className in File classFileName = _firstClassName if classFileName.length filename = classFileName _javaFileName = filename + '.java' return _javaFileName def _firstClassName as String """Find first class name declared in the Module.""" classFileName = '' for decl in .topNameSpace.declsInOrder if decl inherits Class classFileName = decl.name break if decl inherits NameSpace ns = decl for decl1 in ns.declsInOrder if decl1 inherits Class classFileName = decl1.name break return classFileName def writeJavaDef as Dictionary? is override using sw = (.compiler to Compiler).newJavaFile(.fileName, .javaFileName) if .isMainWrapper .synthesizeJavaMainWrapper(sw) else .topNameSpace.writeJavaDef(sw) d = sw.curlyToCobraLineNum (.compiler to Compiler).createJavaFile(.javaFileName, sw.output) return d def writeJavaTestInvocation(sw as CurlyWriter) is override # TODO: .topNameSpace.writeJavaTestInvocation(sw) pass def synthesizeJavaMainWrapper(sw as CurlyWriter) """ Generate a wrapper class with a valid main method for java. The wrapper will instantiate and call the provided user main method. """ # Module top namespace is expected to have a single entry which is a dummy class holding # the method (+ class) for the mainwrapper to instantiate and invoke. assert .topNameSpace.declsInOrder.count assert .topNameSpace.declsInOrder[0] inherits Class placeHolderClass = .topNameSpace.declsInOrder[0] to Class assert placeHolderClass.declsInOrder.count assert placeHolderClass.declsInOrder[0] inherits AbstractMethod mainMethod = placeHolderClass.declsInOrder[0] to AbstractMethod # method data sw.write('// Cobra synthesized program entrypoint wrapping call to user supplied main method\n\n') sw.writeAndIndent('class MainWrapper extends java.lang.Object {\n') sw.bumpLineNum for attr in mainMethod.attributes attr.writeSharpDef(sw, '', false) if mainMethod.attributes.count, sw.writeLine sw.writeAndIndent(r'static public void main(String[] args) {') sw.writeLine sw.bumpLineNum sw.writeLine('cobra.core.CobraCore._recordCommandLine(args); ') sw.writeLine('(new [mainMethod.parentBox.javaRef]()).main(args);') sw.bumpLineNum sw.dedentAndWrite('} // end MainWrapper.main\n\n') sw.bumpLineNum sw.writeLine('public MainWrapper() {') sw.indentAndWrite('//super(); \n') sw.dedentAndWrite('}') sw.writeLine sw.dedentAndWrite('} // class MainWrapper') sw.writeLine ## ## Container and friends ## interface IParentSpace is partial get javaRef as String class Container is partial get javaInit as String # TODO: is abstract """ Initial (default) value for a type (in java). """ return '' get javaNameComponent as String """ Returns a string that can be used in a Java identifier. That means it cannot have special symbols such as period or left bracket. """ ensure result.length > 0 # TODO? Move to interface return .javaRef var _javaRef as String? get javaRef as String is override if _javaRef is nil and .didBindImp _javaRef = _computeJavaRef return _javaRef to ! else return _computeJavaRef get javaParamRef as String return .javaRef def _computeJavaRef as String if .parent if .parent.javaRef <> 'global' # no global (default) namespace in java s = .parent.javaRef # ?'global'? s += '.' s += .javaName else s = .javaName return s def writeJavaIsNames(sw as CurlyWriter) # java access levels: # static, public, protected, private, (package-private), internal, abstract, final # TODO: cache this somewhere accessLevels = ['public', 'protected', 'internal', 'protected internal', 'private'] # CC: accessLevels = 'public,protected,internal,protected internal,private'.split(c',') # TODO: # if isNames is nil # isNames = _isNames isNames = List() isNames.addRange(_isNames) if .defaultAccessLevel.length found = false for level in accessLevels if level in isNames found = true if not found isNames.insert(0, .defaultAccessLevel) isNameJava = { # only have to specify the ones that are different 'shared' : 'static', # virtual/override, nonvirtual/new only on methods and props?? #'nonvirtual': '', # n/a - final is similar but cannot override (new) #'new' : '', # n/a - cant override a final #'virtual' : '', # default #'override' : '', # n/a - TODO mark with annotation #'readonly' : 'final' # Not applicable to boxes # 'public' #'protected': '', #'private', 'internal' : '', # goes to nothing which is protected+package-private which is not quite the same # 'abstract' # 'partial' : '??' # wot to do about this ??? } sep = '' for name in isNames name = isNameJava.get(name, name) sw.write(sep) sw.write(name) sep = ' ' if sep.length, sw.write(' ') def writeJavaTestInvocation(sw as CurlyWriter) pass interface IMember is partial def writeJavaDef(sw as CurlyWriter) """ Write the Java code for this member definition/declaration to the given CurlyWriter. """ def writeJavaTestInvocation(sw as CurlyWriter) """ Write the Java call to the test method for this member. """ ## ## Namespace ## class NameSpace is partial get javaSuffix as String # TODO: not sure how -embed-run-time is going to work on JVM or if it's even needed value as Object? if not .addOnValues.tryGetValue('JavaSuffix', out value) suffix = if(.fullName == 'Cobra.Core', .compiler.embedRunTimeSuffix, '') .addOnValues['javaSuffix'] = suffix return suffix else return value to String #get javaInit as String is override # TODO: remove when not a type # assert false # return '' get javaRef as String is override return .fullName + .javaSuffix get javaQualifier as String ensure result.endsWith('.') or result.length == 0 # suppress naming of 'global' default namespace if .fullName.toLower == 'global' return '' javaName = .fullName.toLower # java idiom is all lowercase for packages return javaName + .javaSuffix + '.' def writeJavaDef(sw as CurlyWriter) is override assert not .isUnified base.writeJavaDef(sw) _writeNameSpaceHeading(sw) # Top Level classes + Interfaces in a Namespace d0 = _declsInOrder[0] firstclass = true for decl in _declsInOrder isNames = List() if decl inherits Box .compiler.boxStack.push(decl) isNames.add(decl.defaultAccessLevel) isNames.addRange(decl.isNames) if firstclass or decl == d0 or not 'public' in isNames decl.writeJavaDef(sw) firstclass = if(decl inherits EnumDecl, true, false) else # java - compile error if public classes are not in their own file - a pox on whoever made this unsuppressable javaFileName = decl.name + '.java' # make it look like this class was made in its own cobra module # TODO insert Module name in new Java file header cobraModName = decl.name + '.cobra' #trace javaFileName, cobraModName compiler =(.compiler to Compiler) using sw = compiler.newJavaFile(cobraModName, javaFileName) _writeNameSpaceHeading(sw) decl.writeJavaDef(sw) javaToCobraLineNum = sw.curlyToCobraLineNum compiler.storeJavaFileInfo(cobraModName, javaToCobraLineNum) (.compiler to Compiler).createJavaFile(javaFileName, sw.output) if decl inherits Box, .compiler.boxStack.pop def _writeNameSpaceHeading(sw as CurlyWriter) if not .isRoot pkgName = .name.toLower # java idiom is all lowercase for packages sw.write('package [pkgName][.javaSuffix]; \n\n') for ud in _useDirectives # imports ud.writeJavaDef(sw) def writeJavaTestInvocation(sw as CurlyWriter) is override for decl in _declsInOrder decl.writeJavaTestInvocation(sw) class UseDirective is partial # Dont use java imports as # -- The semantics between Cobra `use` and java `import` are (probably) different. # -- Using Cobra.Core library type names without qualification in the generated code could lead to confusing errors if the user happens to have a type with the same name. # instead names from 'used' packages/namespaces will be fully qualified in generated java source. # def writeJavaDef(sw as CurlyWriter) is override # sw.write('import [.boundNameSpace.javaRef].*;\n') pass ## ## Types ## class FakeLibraryType is partial get javaInit as String throw NoSourceGenerationException(this) get javaName as String throw NoSourceGenerationException(this) get javaNameComponent as String throw NoSourceGenerationException(this) get javaParamRef as String throw NoSourceGenerationException(this) get javaRef as String throw NoSourceGenerationException(this) def writeJavaDef(sw as CurlyWriter) throw NoSourceGenerationException(this) def writeJavaTestInvocation(sw as CurlyWriter) throw NoSourceGenerationException(this) interface IType is partial get javaInit as String get javaNameComponent as String """ Returns a string that refers to this type and is suitable for embedding in a larger identifier (meaning there will be no punctuation, spaces or Java comments). """ get javaRef as String """ Returns a string that refers to this type. Examples: 'int' 'List' 'object' """ get javaParamRef as String """ # TODO: update doc string to reflect Java accurately Returns a string that refers to this type including any necessary parameter declaration specification such as 'params' or 'out'. Invoked by Param.writeJavaDef. """ class CobraType is partial get javaInit as String is abstract get javaName as String return .name get javaNameComponent as String name = .name i = name.indexOf('/*') # example: /*dynamic*/object if i <> -1 j = name.indexOf('*/', i) name = name[:i] + name[j+2:] name = name.replace('.', '_').replace('<', '_').replace('>', '_').replace(', ', '_') return name get javaRef as String #print 'cobratype - [.javaName]' return .javaName get javaParamRef as String return .javaRef def writeJavaTestInvocation(sw as CurlyWriter) pass class BoolType is partial get javaInit as String is override return 'false' get javaName as String is override #return _nativeType.fullName return 'boolean' class CharType is partial get javaInit as String is override return '(char)0' class DynamicType is partial get javaInit as String is override return 'null' get javaName as String is override return '/*dynamic*/java.lang.Object' class FloatType is partial #get javaInit as String is override # where not implemeneted as a primitive # return 'null' # 'new Float(0)' get javaName as String is override #return _nativeType.fullName # java.lang.{Double,Float} # should name to primitives but not prob with JvmJarSig._aliasPrimitives # TODO fix primitives float, double... vs classes java.lang.Float, java.lang.Double t = _nativeType.fullName branch t on 'java.lang.Double', primName = 'double' on 'java.lang.Float', primName = 'float' else throw FallThroughException() return primName class IntType is partial get javaName as String is override # should name to primitives but not prob with JvmJarSig._aliasPrimitives # TODO fix primitives int, long,... vs classes java.lang.Integer, java.lang.Long #return _nativeType.fullName t = _nativeType.fullName branch t on 'java.lang.Byte', primName = 'byte' on 'java.lang.Short', primName = 'short' on 'java.lang.Integer', primName = 'int' on 'long', primName = _nativeType.fullName on 'java.lang.Long', primName = 'long' else throw FallThroughException() return primName # TODO byte = int8 , short = int16, int = Int32, long = int64 class NilableType is partial get javaInit as String is override return 'null' get javaRef as String is override return _wrappedType.javaRef + if(not _wrappedType.isReference, ' /*?*/ ', '') class NilType is partial get javaInit as String is override return 'null' get javaRef as String is override return '/*nil*/java.lang.Object' class AbstractNumberType is partial get javaInit as String is override return '0' class PassThroughType is partial get javaInit as String is override return 'null' get javaRef as String is override return '/*passthrough*/java.lang.Object' class StreamType is partial get javaInit as String is override return 'new cobra.core.EmptyStream<[.theWrappedType.javaRef]>()' get javaRef as String is override assert .didBindInh and .didBindInt return '/*[.name]*/[.box.javaRef]' class VoidType is partial get javaInit as String is override throw Exception('Cannot init a void type.') class WrappedType is partial get javaInit as String is override return _wrappedType.javaInit class ArrayType is partial get javaRef as String is override return '[_wrappedType.javaRef]' + r'[]' get javaInit as String is override return 'null' class VariType is partial get javaRef as String is override return '[_wrappedType.javaRef]' + r'[]' get javaParamRef as String is override return 'params ' + .javaRef class UnspecifiedType is partial get javaInit as String is override # TODO: throw Exception('Not expecting code gen for unspecified types.') return '!UnspecifiedType!' ## ## Enums ## class EnumDecl is partial get javaRef as String is override if .isUsedAsSet, return 'java.util.EnumSet<[base.javaRef]>' return base.javaRef get javaRefBase as String return base.javaRef get javaInit as String is override if .isUsedAsSet, return 'java.util.EnumSet.noneOf([base.javaRef].class)' return base.javaRef + '.' + _declsInOrder[0].name def writeJavaDef(sw as CurlyWriter) is override base.writeJavaDef(sw) if 'extern' in _isNames return for attr in .attributes attr.writeJavaDef(sw) if not _isNames.count, _isNames.add('internal') # to get nothing .writeJavaIsNames(sw) sw.write('enum [_name]') /# we dont support storageType in java storageType = _storageType ... #/ sw.write(' {\n') sw.indent sep = '' i = 0 for em as EnumMember in .declsInOrder sw.write(sep) sw.write(em.name) if em.value sw.write('([em.value])') complex = true i += 1 sep = ',\n' sw.write(';\n') if complex # Make something like .Net enum - specific int values sw.writeLine sw.writeLine('private static int defValue = 0;') sw.writeLine('private static void setDef(int b){defValue = b;}') sw.writeLine('private static int getDef(){return defValue;}') sw.writeLine('private int value;') sw.writeLine('private [_name](int value) {this.value = value; setDef(value+1);}') sw.writeLine('private [_name]() {this.value = getDef(); setDef(this.value+1);}') sw.writeLine('public int getValue() {return this.value;}') sw.dedent sw.write('}\n\n') class EnumMember is partial def writeJavaTestInvocation(sw as CurlyWriter) pass get javaRef as String is override return _enumDecl.javaRefBase + '.' + .javaName ## ## Boxes ## class Box is partial get javaNameComponent as String is override name = .javaName i = name.indexOf('/*') # example: /*dynamic*/object if i <> -1 j = name.indexOf('*/', i) name = name[:i] + name[j+2:] # qualified types and generics need cleanup for ch in '.<>, ' name = name.replace(ch, c'_') return name get javaThis as String """ Returns 'this' as you would expect. Overridden by Extension. """ return 'this' get javaKeyWord as String is abstract get javaName as String is override return _name.replace('') javaRef = sb.toString else javaRef = .javaName if .parentNameSpace and not .parentNameSpace.isRoot #trace .parentNameSpace.javaQualifier javaRef = .parentNameSpace.javaQualifier + javaRef else if .parentBox #trace .parentBox.javaRef javaRef = .parentBox.javaRef + '.' + javaRef #trace javaRef return javaRef def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if .isExtern, return .compiler.boxStack.push(this) try assert not .isConstructed .writeJavaAttribs(sw) .writeJavaIsNames(sw) sw.write('[.javaKeyWord] ') .writeJavaDefName(sw) .writeJavaInheritance(sw) sep = '' for inter in _baseInterfaces sw.write(sep) sw.write(inter.javaRef) sep = ', ' if _genericParams.count sw.indent for param in _genericParams if param inherits GenericParam param.writeJavaConstraint(sw) sw.dedent sw.writeAndIndent('{\n') .writeJavaInvariantMethod(sw) for tm in _testMethods .compiler.boxMemberStack.push(tm) try, tm.writeJavaDef(sw) finally, .compiler.boxMemberStack.pop for decl in _declsInOrder if not .willWriteDeclJavaDef(decl), continue .compiler.boxMemberStack.push(decl) try decl.writeJavaDef(sw) finally .compiler.boxMemberStack.pop if .compiler.includeTests .writeJavaTest(sw) sw.dedentAndWrite('} // [.javaKeyWord] [.name]\n') sw.write('\n') finally .compiler.boxStack.pop def willWriteDeclJavaDef(decl) as bool return true def writeJavaAttribs(sw as CurlyWriter) for attrib in _attribs attrib.writeJavaDef(sw) def writeJavaDefName(sw as CurlyWriter) sw.write('[.rootName]') if _genericParams.count sw.write('<') sep = '' for param in _genericParams sw.write(sep) param.writeJavaDef(sw) sep = ', ' sw.write('>') def writeJavaInheritance(sw as CurlyWriter) """ Class uses this to write its base class declaration. No box uses this to write interface implementation--that's handled in the Box class. """ pass get javaInvariantVisibility as String is abstract def writeJavaInvariantMethod(sw as CurlyWriter) if .compiler.options['contracts'] <> 'none' sw.write('\nint _ih_invariantGuard;\n\n') if .compiler.options['contracts'] <> 'methods' return sw.write('\n[.javaInvariantVisibility] void invariant_[.rootName]() {\n') sw.indent if _baseClass and (not _baseClass.isFromBinaryLibrary or _baseClass.declForName('invariant_[_baseClass.rootName]')) sw.write('invariant_[_baseClass.rootName]();\n') .writeJavaInvariantChecks(sw) sw.dedent sw.write('}\n\n') def writeAllJavaInvariantChecks(sw as CurlyWriter) """ Writes all Java invariant checks including inherited. In support of -contracts:inline """ _writeAllJavaInvariantChecks(sw, this) def _writeAllJavaInvariantChecks(sw as CurlyWriter, box as Box) if box.baseClass and not box.baseClass.isFromBinaryLibrary _writeAllJavaInvariantChecks(sw, box.baseClass to !) box.writeJavaInvariantChecks(sw) def writeJavaInvariantChecks(sw as CurlyWriter) """ Writes the Java invariant checks just for this class. """ for expr in _invariants javaThis = '[.javaName].class' sw.write('if (!') expr.writeJavaDef(sw) sw.write(')\n') sw.indent sw.write('throw new cobra.core.InvariantException([expr.javaSourceSite(.name, "invariant", javaThis)], ') expr.writeJavaBreakdown(sw) sw.write('[javaThis], null);\n') sw.dedent def writeJavaTest(sw as CurlyWriter) pass var _didWriteJavaTestInvocation = false def writeJavaTestInvocation(sw as CurlyWriter) is override # used by the test option if _didWriteJavaTestInvocation return if .isExtern return if .isGenericDef # sorry, but static methods always require a type arg even if you don't need it. workaround. make another class with a 'test' section that tests the generic class TODO: make Cobra do this, but then watch for use of the generic param! return assert .parentNameSpace sw.write('\t\t[.javaRef].RunTestsIfNeeded();\n') _didWriteJavaTestInvocation = true get newForJavaTest as String return '' class Class is partial get javaInit as String is override return 'null' get javaInvariantVisibility as String is override return 'protected' get javaKeyWord as String is override return 'class' def writeJavaInheritance(sw as CurlyWriter) is override sw.write(' extends ') if _baseClass #trace _baseClass sw.write(_baseClass.javaRef) didWrite = true if didWrite if _baseInterfaces.count sw.write(', ') else sw.write(' ') get newForJavaTest as String is override return if(not _baseClass.isExtern, ' new', '') class Interface is partial get javaInit as String is override return 'null' get javaInvariantVisibility as String is override throw ShouldNotCallException(.getType) get javaKeyWord as String is override return 'interface' def writeJavaInheritance(sw as CurlyWriter) is override if _baseInterfaces.count sw.write(' extends ') def writeJavaInvariantMethod(sw as CurlyWriter) is override # Even when invariants are inherited by the classes and structs that implement them, # the code gen for the interface invariant does not go "here", inside the Java interface definition. pass def writeJavaTest(sw as CurlyWriter) is override # Static methods can't be put in interfaces. However, at some point Cobra should support # tests for interfaces which could then be run against the classes that implement them. # Just requires more code gen smarts. pass def writeJavaTestInvocation(sw as CurlyWriter) is override pass class Mixin is partial get javaName as String name = base.javaName if .canHaveStatements name += 'Mixin' return name get javaKeyWord as String is override return if(.canHaveStatements, 'class', 'interface') def willWriteDeclJavaDef(decl) as bool is override return not decl inherits BoxVar def writeJavaDef(sw as CurlyWriter) # write the interface _canHaveStatements = false base.writeJavaDef(sw) class Struct is partial # public class, public final fields, public methods? get javaInit as String is override return '' # blank to indicate there is no valid init get javaInvariantVisibility as String is override return 'private' get javaKeyWord as String is override return 'struct' def writeJavaInheritance(sw as CurlyWriter) is override if _baseInterfaces.count sw.write(' extends ') class MethodSig is partial pass # TODO STUB class GenericParam is partial get javaInit as String is override return 'null /*default([_name])*/' # not poss in java def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.write(.name) def writeJavaConstraint(sw as CurlyWriter) if _constraints.count # TODO map cobra constraints to Java syntax # TODO before that try and comprehend java syntax for generic constraints sw.write(' where [_name] : ') sep = '' for constraint in _constraints sw.write(sep) constraint.writeJavaDef(sw) sep = ', ' sw.write('\n') class GenericClassConstraint is partial def writeJavaDef(sw as CurlyWriter) is override sw.write('class') class GenericStructConstraint is partial def writeJavaDef(sw as CurlyWriter) is override sw.write('struct') class GenericCallableConstraint is partial def writeJavaDef(sw as CurlyWriter) is override sw.write('new()') class GenericTypeConstraint is partial def writeJavaDef(sw as CurlyWriter) is override sw.write(_representedType.javaRef) class Extension is partial pass # TODO STUB: Implement extensions ## ## Vars ## interface IVar is partial get javaAssignmentNames as List? """ Return the Java names to assign to above and beyond the javaName. This in support of if-inherits. """ class AbstractLocalVar is partial get javaAssignmentNames as List return [_backEndName] # return List(_useBackEndNameStack) get javaName as String is override if _ifInheritsStack.count if _type.isReference return '(([_ifInheritsStack.peek.javaRef])[_backEndName])' else # could it be a subclass? no. value types are structs and cannot be subclassed so this must be `if i` where `i` is nullable struct return '[.name].Value' # Nullable.Value else return _backEndName # return _useBackEndNameStack.peek to ! class Param is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) # TODO #if .type.isReference and not .type inherits NilableType # sw.write(r'[Cobra.Core.NotNull] ') # to-do: annotations on params avail in Java8 branch .direction on Direction.In, pass on Direction.InOut, sw.write('/*inOut*/') # default on Direction.Out, sw.write('/*out*/') # TODO sw.write('[.type.javaParamRef] [.javaName]') if .optionalValue detail = 'For java at the moment you will need to recode the optional parameter method as a set of overloaded (possibly cascaded) methods with the desired number of parameters.' .compiler.warning(this, 'Java does not support optional parameters. Default parameter value setting is ignored. [detail]') # TODO: higher up write variant method that calls full param set method with default value curried in # #sw.write('=') # #_optValue.writeSharpDef(sw) def writeJavaDefSansReferenceLabels(sw as CurlyWriter) """ Writes the parameter declaration but without Java "out" or "ref"--these are not available for contract methods (require_foo and ensure_foo) because contracts cannot modify arguments. """ sw.write('[.type.javaRef] [.javaName]') class LocalVar is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.write('[.type.javaRef] [.javaName]') init = .type.javaInit if init.length, sw.write(' = [init]') sw.write(';\n') class ResultVar is partial get javaName as String is override return _backEndName ## ## Members ## class BoxMember is partial var _javaExtraIsNames as Set? pro javaExtraIsNames from var get javaThis as String #return if(.isShared, 'typeof([.parentBox.javaName])', .parentBox.javaThis) return if(.isShared, '([.parentBox.javaName].class)', .parentBox.javaThis) def writeJavaNotNull(sw as CurlyWriter) if .resultType.isReference and not .resultType inherits NilableType sw.writeLine(r'[' + .javaNotNullPrefix + 'cobra.core.NotNull]') get javaNotNullPrefix as String # TODO make an annotation on BoxMember return '' def writeJavaTest(sw as CurlyWriter) if _testMethods and _testMethods.count for tm in _testMethods, tm.writeJavaDef(sw) def writeJavaTestInvocation(sw as CurlyWriter) if _testMethods and _testMethods.count for tm in _testMethods, sw.write('[tm.javaName]();\n') def writeJavaParams(sw as CurlyWriter) .writeJavaParams(sw, '()') def writeJavaParams(sw as CurlyWriter, parens as String) require parens.length==2 or parens=='' if parens.length sw.write(parens[0]) sep = '' for param in .params sw.write(sep) param.writeJavaDef(sw) sep = ', ' if parens.length sw.write(parens[1].toString+' ') def writeJavaIsNames(sw as CurlyWriter) # TODO: # if isNames is nil # isNames = _isNames isNameJava = { # only have to specify the ones that are different 'shared': 'static', 'nonvirtual': '', # n/a - final is similar but cannot override (new) 'new' : '', # n/a - cant override a final 'virtual' : '', # default 'override' : '', # n/a - TODO mark with annotation 'readonly' : 'final', # 'public', 'protected', 'private', 'internal' : '' # goes to nothing which is package-private which is not quite the same # 'abstract' } wroteNames = Set() sep = '' for name in _isNames name = isNameJava.get(name, name) sw.write(sep) sw.write(name) sep = ' ' wroteNames.add(name) if _javaExtraIsNames for name in _javaExtraIsNames if name not in wroteNames sw.write(sep) sw.write(name) sep = ' ' if sep.length sw.write(' ') def writeJavaAttribs(sw as CurlyWriter) for attrib in _attribs attrib.writeJavaDef(sw) class BoxEvent is partial # TODO: get javaName as String is override return .name #.capitalized def writeJavaDef(sw as CurlyWriter) is override base.writeJavaDef(sw) .writeJavaAttribs(sw) .writeJavaIsNames(sw) sw.write('event ') sw.write(.handlerType.javaRef) sw.write(' [.javaName]') sw.write(';\n') class BoxField is partial # java idiom uses same (sensible) naming style as cobra #get _javaBackEndName from name get _javaBackEndName as String return .name class BoxConst is partial def writeJavaDef(sw as CurlyWriter) is override base.writeJavaDef(sw) .writeJavaAttribs(sw) .writeJavaIsNames(sw) sw.write('static final ') # const sw.write(_type.javaRef) sw.write(' [_javaBackEndName] = ') _initExpr.writeJavaDef(sw) sw.write(';\n') class BoxVar is partial get javaAssignmentNames as List return [_javaBackEndName] # return List(_useBackEndNameStack) get javaName as String is override if _ifInheritsStack.count if _type.isReference return '(([_ifInheritsStack.peek.javaRef])[_javaBackEndName])' else # TODO fix this - .Net specific # could it be a subclass? no. value types are structs and cannot be subclassed so this must be `if i` where `i` is nullable struct return '[_javaBackEndName].Value' # Nullable.Value else return .name # return _useBackEndNameStack.peek to ! def writeJavaDef(sw as CurlyWriter) is override base.writeJavaDef(sw) .writeJavaAttribs(sw) .writeJavaIsNames(sw) sw.write(_type.javaRef) sw.write(' [_javaBackEndName]') if _initExpr sw.write(' = ') _initExpr.writeJavaDef(sw) else if .type.isReference and not .type.nonNil inherits GenericParam javaInit = .type.javaInit if javaInit.length sw.write(' = ') sw.write(javaInit) sw.write(';\n') class AbstractMethod is partial get javaGenericParams as String """ Can return, for example, '' or ''. """ ensure ' ' not in result return '' def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) .compiler.codeMemberStack.push(this) try .innerWriteJavaDef(sw) finally .compiler.codeMemberStack.pop def innerWriteJavaDef(sw as CurlyWriter) pass def writeJavaImp(sw as CurlyWriter, skipFirst as bool) .compiler.codeMemberStack.push(this) try sw.writeAndIndent('{\n') .writeJavaImpHeader(sw) if .compiler.hasDetailedStackTraceOption .writeJavaDSTHead(sw) for param in _params .writeJavaParamCheck(param, sw) if _requirePart and _requirePart.willGenerateCode _requirePart.writeJavaDef(sw) .writeJavaLocals(sw, nil, false) willEnsure = _ensurePart and _ensurePart.willGenerateCode if willEnsure sw.write('boolean _lh_canEnsure = false;\n') # have to set `out` parameters or C# will error on them being passed in the `finally` for the ensure, # even though the _lh_canEnsure guard logically prevents problems at run-time for param in _params if param.isOut javaInit = param.type.javaInit if not javaInit.length # TODO/HACK: yes we need something like this (or a way to convice C# that the out param reference in `finally` really is okay), javaInit = 'DateTime.Today' sw.write('[param.javaName] = [javaInit];') if _backEndResultVarName.length # set in _bindImp sw.write('[_returnType.javaRef] [_backEndResultVarName]') if _returnType.javaInit.length sw.write('= [_returnType.javaInit]') sw.write(';\n') sw.writeAndIndent('try {\n') for stmt in _stmts if skipFirst skipFirst = false continue stmt.writeJavaStmt(sw) if willEnsure if not stmt inherits ReturnStmt and not stmt inherits ThrowStmt sw.write('_lh_canEnsure = true;\n') sw.dedentAndWrite('} finally { // ensure\n') sw.indentAndWrite('if (_lh_canEnsure) {\n') sw.indent _ensurePart.writeJavaDef(sw) sw.dedentAndWrite('}\n') sw.dedentAndWrite('}\n') if .compiler.hasDetailedStackTraceOption .writeJavaDSTTail(sw) .writeJavaImpFooter(sw) sw.dedent sw.write('}\n') sw.write('\n') finally .compiler.codeMemberStack.pop def writeJavaImpHeader(sw as CurlyWriter) pass def writeJavaImpFooter(sw as CurlyWriter) pass def writeJavaPassArgs(sw as CurlyWriter) .writeJavaPassArgs(sw, '()', false) def writeJavaPassArgs(sw as CurlyWriter, parens as String, excludeOutArgs as bool) require parens.length==2 or parens=='' if parens.length sw.write(parens[0]) sep = '' for param in _params if excludeOutArgs and param.isOut continue sw.write(sep) sw.write(param.javaName) sep = ', ' if parens.length sw.write(parens[1]) def writeJavaLocals(sw as CurlyWriter, locals as List?, open as bool) if open sw.writeAndIndent('{\n') # record the `old` expressions for `ensure` .writeJavaOldAssignments(sw) if locals is nil locals = _locals if locals.count sw.write('// locals\n') for local in locals if not local.isImplicit sw.write('[local.type.javaRef] [local.javaName]') init = local.type.javaInit if init.length if not local.type.isReference and init == 'null' pass else sw.write(' = [init]') sw.write(';\n') sw.write('// end locals\n\n') def writeJavaRequireParamDecls(sw as CurlyWriter) sep = '' for param in .params if param.isOut # a requirement cannot say anything useful about a parameter that is output only continue sw.write(sep) param.writeJavaDefSansReferenceLabels(sw) # no `inout/ref` label since a contract cannot modify reference arguments sep = ', ' def writeJavaOldAssignments(sw as CurlyWriter) if .isOverride and .matchingBaseMember (.matchingBaseMember to AbstractMethod).writeJavaOldAssignments(sw) for oldExpr in _oldExprs oldExpr.writeJavaAssignment(sw) def writeJavaEnsureArgs(sw as CurlyWriter) if .backEndResultVarName.length sw.write('[.backEndResultVarName]') sep = [','] else sep = [''] _writeJavaOldArgs(sw, sep) if .params.count sw.write(sep[0]) .writeJavaPassArgs(sw, '', false) # these have to come last, because they might include a C# "params" which has to be last def _writeJavaOldArgs(sw as CurlyWriter, sep as List) if .isOverride and .matchingBaseMember assert .matchingBaseMember is not this (.matchingBaseMember to AbstractMethod)._writeJavaOldArgs(sw, sep) for oldExpr in _oldExprs sw.write(sep[0]) sw.write(oldExpr.javaVarName) sep[0] = ', ' def writeJavaEnsureParamDecls(sw as CurlyWriter) if .backEndResultVarName.length sw.write('[.resultType.javaRef] [.backEndResultVarName]') sep = [','] else sep = [''] _writeJavaOldParamDecls(sw, sep) # these have to come last, because they might include a C# "params" which has to be last for param in .params sw.write(sep[0]) param.writeJavaDefSansReferenceLabels(sw) # no `out` or `inout/ref` labels since a contract cannot modify reference arguments sep[0] = ', ' def _writeJavaOldParamDecls(sw as CurlyWriter, sep as List) if .isOverride and .matchingBaseMember assert .matchingBaseMember is not this (.matchingBaseMember to AbstractMethod)._writeJavaOldParamDecls(sw, sep) for oldExpr in _oldExprs sw.write(sep[0]) sw.write('[oldExpr.type.javaRef] [oldExpr.javaVarName]') sep[0] = ', ' def writeJavaParamCheck(param as Param, sw as CurlyWriter) if .isCompilerGenerated, return if param.type.isReference and not param.type inherits NilableType and not param.isOut if .compiler.options.boolValue('include-nil-checks') sw.write('if (cobra.core.CobraCore._willCheckNil)\n') sw.indent sw.write('cobra.core.CobraImp.checkNotNull([param.javaName], "ParameterGuard Fail: non nilable parameter `[param.javaName]` is null" );\n') sw.dedent #sw.write('if (cobra.core.CobraCore._willCheckNil && [param.javaName]==null) throw new NullPointerException("Non nilable parameter named [param.name] is Null");\n') def writeJavaDSTHead(sw as CurlyWriter) if not .canHaveDetailedStackTrace return # could be a neat option, -trace-methods, but maybe done with events: # sw.writeLine('Console.WriteLine(">> [.parentBox.name].[.name]");') sw.write('cobra.core.CobraImp.pushFrame("[.parentBox.name]", "[.name]", [Utils.javaStringLiteralFor(.token.fullPathName)], [.token.lineNum]') if not .isShared sw.write(', "this", [.compiler.curBox.javaThis]') for param in .params if not param.isImplicit and not param.isOut sw.write(', "[param.name]", [param.javaName]') sw.write(');\n') sw.writeAndIndent('try {\n') def writeJavaDSTTail(sw as CurlyWriter) if not .canHaveDetailedStackTrace return # sw.writeLine('Console.WriteLine("<< [.parentBox.name].[.name]");') # this catch all clashes with yield statements. which is why .canHaveDetailedStackTrace returns false if a method has a yield statement. sw.dedentAndWrite('} catch (java.lang.Exception) {\n') sw.indent sw.write('cobra.core.CobraImp.CaughtUncaughtException();\n') sw.write('throw;\n') sw.dedentAndWrite('} finally {\n') sw.indent sw.write('cobra.core.CobraImp.popFrame();\n') sw.dedentAndWrite('}\n') class Initializer is partial def innerWriteJavaDef(sw as CurlyWriter) base.innerWriteJavaDef(sw) .writeJavaAttribs(sw) .writeJavaIsNames(sw) sw.write(' [.parentBox.rootName]') .writeJavaParams(sw) .writeJavaImp(sw, _hasCallInitializer) # Initializer Body if _requirePart, _requirePart.writeJavaMethod(sw) if _ensurePart, _ensurePart.writeJavaMethod(sw) if .compiler.includeTests .writeJavaTest(sw) def writeJavaImpHeader(sw as CurlyWriter) base.writeJavaImpHeader(sw) first = if(_stmts.count, _stmts[0], nil) callInitializer as String? = nil if first inherits DotExpr if first.left inherits ThisOrBaseLit if (firstRight = first.right) inherits IDotRightExpr if firstRight.name == 'init' callInitializer = (first.left to ThisOrBaseLit).asJava args = firstRight.args Stmt.inInitCall = true didSetInInitCall = true if args.count == 0 sw.writeLine('//super() - default noarg initialiser call happens here') else sw.write('[callInitializer](') sep = '' for arg in args sw.write(sep) arg.writeJavaDef(sw) sep = ', ' sw.write(');') if didSetInInitCall assert Stmt.inInitCall # make sure it wasn't reset somewhere else Stmt.inInitCall = false if .isStructMember and .compiler.options['contracts'] <> 'none' sw.write('\n_ih_invariantGuard = 0;\n\n') # otherwise C# complains: Field "STRUCTNAME._ih_invariantGuard" must be fully assigned before control leaves the constructor def _hasCallInitializer as bool callInitializer as String? = nil first = if(_stmts.count, _stmts[0], nil) if first inherits DotExpr if first.left inherits ThisOrBaseLit if (firstRight = first.right) inherits IDotRightExpr if firstRight.name == 'init' callInitializer = (first.left to ThisOrBaseLit).asJava return callInitializer <> nil class Method is partial get javaGenericParams as String is override params = .parentBox.extendedMethodGenericParams(_genericParams) if params.count sb = StringBuilder() sb.append('<') sep = '' for param in params sb.append(sep) sb.append(param.javaName) sep = ',' sb.append('>') return sb.toString else return '' def innerWriteJavaDef(sw as CurlyWriter) base.innerWriteJavaDef(sw) .writeJavaNotNull(sw) .writeJavaAttribs(sw) name = .javaName returnType = _returnType ? .compiler.voidType if _implementsType name = _implementsType.javaRef + '.' + name .writeJavaIsNames(sw) sw.write('[returnType.javaRef] [name][.javaGenericParams]') if .hasMainParams # insert args for main method for this backend sw.write(r'(String[] args)') # match to CobraModule.synthesizeJavaMainWrapper else .writeJavaParams(sw) if .genericParams.count sw.indent for param in .genericParams if param inherits GenericParam param.writeJavaConstraint(sw) sw.dedent if not .parentBox.canHaveStatements sw.write(';\n') else if .isAbstract, sw.writeLine(';') else, .writeJavaImp(sw, false) if _requirePart, _requirePart.writeJavaMethod(sw) if _ensurePart, _ensurePart.writeJavaMethod(sw) if .compiler.includeTests, .writeJavaTest(sw) def writeJavaImpHeader(sw as CurlyWriter) base.writeJavaImpHeader(sw) if .isMain if .compiler.hasExceptionReportOption sw.writeLine('try { // Exception Report') sw.indent else if .compiler.options.boolValue('debugging-tips') sw.writeLine('try { // -debugging-tips - turn off with -d/-debug (see cobra -h)') sw.indent if .isMain and .compiler.options.boolValue('include-tests') sw.writeLine('cobra.core.CobraCore.runAllTests(); // turn off with -include-tests:no (see cobra -h)') if .isMain and .hasMainParams sw.writeLine('cobra.core.CobraCore._recordCommandLine(args); ') def writeJavaImpFooter(sw as CurlyWriter) base.writeJavaImpFooter(sw) if .isMain if .compiler.hasExceptionReportOption sw.dedent sw.writeLine('} catch (java.lang.Throwable _lh_exceptionReportThrowable) { cobra.core.CobraCore.HandleUnhandledException(_lh_exceptionReportThrowable); }') else if .compiler.options.boolValue('debugging-tips') sw.dedent sw.writeLine('} catch (java.lang.Throwable _lh_debuggingTipsThrowable) {') sw.indent sw.writeLine('cobra.core.CobraCore.printDebuggingTips();') #sw.writeLine('throw _lh_debuggingTipsThrowable.fillInStackTrace();') sw.writeLine('_lh_debuggingTipsThrowable.printStackTrace();') sw.dedent sw.writeLine('}') get javaName as String is override return .name #./capitalized # Java, for its sins, does not have support for properties built into the language... # efforts to correct this stupidity seem to be mired in language navel gazing associated # with efforts to find the most obscure and difficult # corner cases which then leads to it being then dropped in the 'too hard' basket # Historically and currently it instead uses a convention on paired methodNames which leads to a # whole lot of obfuscating boilerplate code. Its obviously too difficult to just copy C# (turnabout # is fair play), accept and then evolve any edge misses class ProperDexer is partial # AbstractBaseClass for Property and Indexer def writeJavaBody(sw as CurlyWriter) #sw.write(' {\n') #sw.indent if _getPart #if .isAbstract or not .parentBox.canHaveStatements # sw.write('\tget;\n') #else # _getPart.writeJavaDef(sw) _getPart.writeJavaDef(sw) if _setPart #if .isAbstract or not .parentBox.canHaveStatements # sw.write('\tset;\n') #else # _setPart.writeJavaDef(sw) _setPart.writeJavaDef(sw) #sw.dedent #sw.write('}\n') class ProperDexerXetter is partial # BaseClass for Indexer get/set, Property get/set def innerWriteJavaDef(sw as CurlyWriter) base.innerWriteJavaDef(sw) sw.write(.xetPartName) .writeJavaImp(sw, false) class Property is partial get javaNotNullPrefix as String is override return '' def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.writeLine('') sw.write('//Property [_returnType.javaRef] [.name]') # .writeJavaImp(sw) getPart = .getPart if getPart .writeJavaNotNull(sw) .writeJavaAttribs(sw) .writeJavaIsNames(sw) sw.write('[_returnType.javaRef] get[.name.capitalized]') sw.write(' {\n') sw.indent getPart.writeJavaDef(sw) sw.dedent sw.write('}\n') setPart = .setPart if setPart .writeJavaNotNull(sw) .writeJavaAttribs(sw) .writeJavaIsNames(sw) sw.write('set[.name.capitalized]([_returnType.javaRef] value)') sw.write(' {\n') sw.indent setPart.writeJavaDef(sw) sw.dedent sw.write('}\n') #.writeJavaBody(sw) if getPart .compiler.codeMemberStack.push(getPart) try if getPart.requirePart getPart.requirePart.writeJavaMethod(sw) if getPart.ensurePart getPart.ensurePart.writeJavaMethod(sw) finally .compiler.codeMemberStack.pop if setPart .compiler.codeMemberStack.push(setPart) try if setPart.requirePart setPart.requirePart.writeJavaMethod(sw) if setPart.ensurePart setPart.ensurePart.writeJavaMethod(sw) finally .compiler.codeMemberStack.pop if .compiler.includeTests .writeJavaTest(sw) class Indexer is partial get javaNotNullPrefix as String is override return '' def writeJavaDef(sw as CurlyWriter) is override #base.writeJavaDef(sw) assert _name==r'[]' sw.write('// Indexer ') #.writeJavaNotNull(sw) #.writeJavaAttribs(sw) #.writeJavaIsNames(sw) sw.write(' [_returnType.javaRef] this') .writeJavaParams(sw, r'[]') #.writeJavaBody(sw) # TODO: finish correctly (method with special attribute??? class MemberOverload is partial pass class TestMethod is partial get javaName as String is override if _forMember name = 'test_' + _forMember.name.capitalized.replace('Cue.', '_cue_') if .overloadId <> -1 name += '_ol_[.overloadId]' return name else return 'test_[_forBox.javaKeyWord]_'+_forBox.javaNameComponent class ContractPart is partial # TODO pass class RequirePart is partial # TODO pass class EnsurePart is partial # TODO pass ## ## Statements ## # uu class Stmt is partial def javaSourceSite as String """ Returns a Java "new SourceSite(...)" expression with the correct arguments for the current code generation. """ if .compiler.codeMemberStack.count codeMember = .compiler.curCodeMember return .javaSourceSite(codeMember.parentBox.name, codeMember.name, .javaThis) else # example: var _x = someExpr ... return .javaSourceSite(.compiler.curBox.name, .compiler.curBoxMember.name, .javaThis) def javaSourceSite(boxName as String, memberName as String, javaThis as String) as String """ Returns a Java "new SourceSite(...)" expression with the correct arguments for the current code generation. The arguments to this method allow for some explicit control in special circumstances, such as `invariant`. """ require boxName.length memberName.length javaThis.length body boxName = Utils.javaStringLiteralFor(boxName) memberName = Utils.javaStringLiteralFor(memberName) tok = .token fileName = tok.fileName if not Path.isPathRooted(fileName) fileName = Path.combine(Environment.currentDirectory, fileName) fileName = Utils.javaStringLiteralFor(fileName) return 'new cobra.core.SourceSite([fileName], [tok.lineNum], [boxName], [memberName], [javaThis])' def javaSourceSiteArgs as String return .javaSourceSiteArgs(.compiler.curBox.name, .compiler.curBoxMember.name, .javaThis) def javaSourceSiteArgs(boxName as String, memberName as String, javaThis as String) as String require boxName.length memberName.length javaThis.length body boxName = Utils.javaStringLiteralFor(boxName) memberName = Utils.javaStringLiteralFor(memberName) tok = .token fileName = tok.fileName if not Path.isPathRooted(fileName) fileName = Path.combine(Environment.currentDirectory, fileName) fileName = Utils.javaStringLiteralFor(fileName) return '[fileName], [tok.lineNum], [boxName], [memberName], [javaThis]' get javaThis as String if _inInitCall # C# won't allow 'this' in a base constructor call -- TODO return '"(uninitialized [.compiler.curBox.name] instance)"' else if .compiler.codeMemberStack.count return .compiler.curCodeMember.javaThis else assert .compiler.boxMemberStack.count # so not a code member return '"(uninitialized [.compiler.curBox.name] instance)"' def writeJavaSetLine(sw as CurlyWriter) if _canSetLine and .token.lineNum > 0 sw.write('cobra.core.CobraImp._curFrame._lineNum = [.token.lineNum];\n') def writeJavaStmt(sw as CurlyWriter) assert .didBindImp sw.node(this) .writeJavaSetLine(sw) .writeJavaDef(sw) class AssertStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if not .compiler.options.boolValue('include-asserts') return sw.write('if (cobra.core.CobraCore._willCheckAssert') sw.write(' && !') _expr.writeJavaDef(sw) sw.write(')\n') sw.indent sw.write('throw new cobra.core.AssertException([.javaSourceSite], ') _expr.writeJavaBreakdown(sw) sw.write('[.javaThis], ') if _info _info.writeJavaDef(sw) else sw.write('null') sw.write(');\n') sw.dedent class BlockStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) .writeJavaDef(sw, true, nil) def writeJavaDef(sw as CurlyWriter, close as bool) .writeJavaDef(sw, close, nil) def writeJavaDef(sw as CurlyWriter, top as String?) .writeJavaDef(sw, true, top) def writeJavaDef(sw as CurlyWriter, close as bool, top as String?) sw.node(this) sw.write(' {\n') sw.indent if top, sw.write(top) if _ifInheritsVar #typeName = _ifInheritsType.javaRef #newName = '_lh_' + _ifInheritsVar.name + '_' + _ifInheritsType.javaNameComponent #sw.write('[typeName] [newName] = ([typeName])[_ifInheritsVar.javaName];\n') #_ifInheritsVar.useBackEndNameStack.push(newName) ifInheritsCount = _ifInheritsVar.ifInheritsStack.count # AssignExpr could pop our _ifInheritsType. We detect that with the count. _ifInheritsVar.ifInheritsStack.push(_ifInheritsType) for stmt in _stmts, stmt.writeJavaStmt(sw) if _ifInheritsVar and _ifInheritsVar.ifInheritsStack.count > ifInheritsCount #_ifInheritsVar.useBackEndNameStack.pop _ifInheritsVar.ifInheritsStack.pop if close sw.dedent sw.write('}\n') class BranchStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if _expr.type.isDynamic # if the expression is dynamic, then use an if-else ladder with CobraImp.equals( sw.writeLine('// branch [_expr.toCobraSource]') varName = '_lh_branch_[.serialNum]' sw.write('[_expr.type.javaRef] [varName] = ') _expr.writeJavaDef(sw) sw.writeLine(';') elseWord = '' for onPart in _onParts sw.write('[elseWord]if (') sep = '' for e in onPart.exprs sw.write(sep) sw.write('cobra.core.CobraImp.equals([varName], ') e.writeJavaDef(sw, false) sw.write(')') sep = ' || ' sw.write(')') onPart.block.writeJavaDef(sw) elseWord = 'else ' if _elsePart sw.write('else ') _elsePart.writeJavaDef(sw) else # for any other kind of expression, use a Java switch. # TODO trap if expression is string and use a if-else ladder ( if not java7) sw.write('switch(') _expr.writeJavaDef(sw) sw.write(') {\n') sw.indent for onPart in _onParts sep = '' for e in onPart.exprs sw.write(sep) sw.write('case ') e.writeJavaDef(sw) sw.write(': ') sep = '\n' onPart.block.writeJavaDef(sw) if not onPart.block.stmts.count or not onPart.block.stmts.last inherits ReturnStmt sw.write('break;\n') if _elsePart sw.write('default: ') _elsePart.writeJavaDef(sw) sw.write('break;\n') sw.dedent sw.write('}\n') class BreakStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.write('break;\n') class ContinueStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.write('continue;\n') class ExpectStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) exceptionTypeRef = _exceptionType.javaRef gotRightExceptionVarName = '_lh_expect_[_varNumber-1]' # lh => "local helper" sw.write('boolean [gotRightExceptionVarName] = false;\n') sw.write('try ') _block.writeJavaDef(sw) sw.writeAndIndent('catch ([exceptionTypeRef] _expExc ) {\n') sw.write('// exactly what is expected\n') sw.write('[gotRightExceptionVarName] = true;\n') sw.dedentAndWrite('}\n') if _exceptionType inherits Box and not (_exceptionType to Box).isJvmSystemExceptionClass gotWrongExceptionVarName = '_lh_expect_[_varNumber]' assert gotRightExceptionVarName <> gotWrongExceptionVarName sw.writeAndIndent('catch (java.lang.Throwable [gotWrongExceptionVarName]) {\n') sw.write('throw new cobra.core.ExpectException([gotWrongExceptionVarName].getClass(), [gotWrongExceptionVarName]);\n') sw.dedentAndWrite('}\n') sw.write('if (![gotRightExceptionVarName]) throw new cobra.core.ExpectException([exceptionTypeRef].class, null);\n') class ForStmt is partial pass class OldForNumericStmt is partial # We dont support this in Java def writeJavaDef(sw as CurlyWriter) is override varName = _var.javaName sw.write('Error-Old-Numeric-Form-unsupported;\n') sw.write('// Old Numeric For form is obsolete and not supported - convert cobra code to new form "for [varName] in start : stop : step "\n') sw.write('// Should become something like "for [varName] in ') _start.writeJavaDef(sw) sw.write(':') _stop.writeJavaDef(sw) if _step sw.write(':') _step.writeJavaDef(sw, false) sw.write('"\n') /# base.writeJavaDef(sw) varName = _var.javaName trackLocals = .compiler.willTrackLocals if trackLocals sw.write('for (cobra.core.CobraImp.setLocal("[.var.name]", [varName]=') else sw.write('for ([varName]=') _start.writeJavaDef(sw) if trackLocals sw.write(')') if _var.type.isDynamic sw.write('; cobra.core.CobraImp.dynamicCompare([varName], ') _stop.writeJavaDef(sw, false) sw.write(')[if(_dir==1, "<", ">")]0') opName = if(_dir==1, 'op_Addition', 'op_Subtraction') sw.write('; [varName]=cobra.core.CobraImp.dynamicOp("[opName]", [varName], ') if _step, _step.writeJavaDef(sw, false) else, sw.write('1') sw.write(')') else sw.write('; [varName]') sw.write(if(_dir==1, '<', '>')) _stop.writeJavaDef(sw) sw.write('; ') if _step if _dir==1, sw.write('[varName]+=') else, sw.write('[varName]-=') _step.writeJavaDef(sw) else if _dir==1, sw.write('[varName]++') else, sw.write('[varName]--') sw.write(')') _block.writeJavaDef(sw) #/ class ForNumericStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if _var.type.isDynamic, _writeJavaDefForDynamic(sw) else, _writeJavaDefForStatic(sw) def _writeJavaDefForDynamic(sw as CurlyWriter) # TODO: this is the old numeric for loop behavior. varName = _var.javaName trackLocals = .compiler.willTrackLocals if trackLocals, sw.write('for (cobra.core.CobraImp.setLocal("[.var.name]", [varName]=') else, sw.write('for ([varName]=') _start.writeJavaDef(sw) if trackLocals sw.write(')') sw.write('; cobra.core.CobraImp.dynamicCompare([varName], ') _stop.writeJavaDef(sw, false) sw.write(')[if(_dir==1, "<", ">")]0') opName = if(_dir==1, 'op_Addition', 'op_Subtraction') sw.write('; [varName]=cobra.core.CobraImp.dynamicOp("[opName]", [varName], ') if _step, _step.writeJavaDef(sw, false) else, sw.write('1') sw.write(')') sw.write(')') _block.writeJavaDef(sw) def _writeJavaDefForStatic(sw as CurlyWriter) """ Pseudo example: Cobra: for x in start : stop : step statements C#: { type _lh_start_1 = start; type _lh_stop_2 = stop; type _lh_step_3 = step; int _lh_dir_4 = (_lh_stop_2 - _lh_start_1) < 0 ? -1 : +1; for (x = start; (_lh_dir_4==-1) ? x > stop : x < stop; x += step) { statements } } Note that it's important that the C# code gen uses a for() loop so that the "continue" statement still increments the control variable. In the case that the 'stop' or 'step' are integer literals, no variables are needed for them. TODO: If 'stop' or 'step' are const or readonly, same thing. """ trackLocals = .compiler.willTrackLocals sw.write('{ // for [.var.name] in [.start.toCobraSource] : [.stop.toCobraSource]' + if(.step, ' : [.step.toCobraSource]', '') + '\n') javaVar = _var.javaName javaStart = '_lh_start_[.serialNum]' javaStop = '_lh_stop_[.serialNum]' javaStep = '_lh_step_[.serialNum]' javaDir = '_lh_dir_[.serialNum]' javaType = _var.type.javaRef sw.write('[javaType] [javaStart] = ') .start.writeJavaDef(sw) sw.write(';\n') if .stop inherits IntegerLit javaStop = (.stop to IntegerLit).asJava else sw.write('[javaType] [javaStop] = ') .stop.writeJavaDef(sw) sw.write(';\n') isSimpleStep = false step = .step if step is nil javaStep = '1' isSimpleStep = true else if step inherits IntegerLit javaStep = step.asJava isSimpleStep = step.valueAsInt > 0 else sw.write('[javaType] [javaStep] = ') step.writeJavaDef(sw) sw.write(';\n') if not isSimpleStep sw.write('int [javaDir] = [javaStep] < 0 ? -1 : +1;\n') sw.write('for(') if trackLocals, sw.write('cobra.core.CobraImp.setLocal("[.var.name]", [javaVar] = [javaStart])') else, sw.write('[javaVar] = [javaStart]') if isSimpleStep, sw.write('; [javaVar] < [javaStop] ; ') else, sw.write('; ([javaDir]==1) ? [javaVar] < [javaStop] : [javaVar] > [javaStop]; ') if trackLocals, sw.write('cobra.core.CobraImp.setLocal("[.var.name]", [javaVar] += [javaStep])') else, sw.write('[javaVar] += [javaStep]') sw.write(') ') _block.writeJavaDef(sw, false) sw.dedent sw.write('}\n') sw.write('} // for [.var.name]\n') class ForEnumerableStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) # in a C# foreach, (1) you must declare a new variable, (2) you cannot reassign it and (3) you cannot access it after the loop # these constraints don't exist in Cobra helperName = '_lh_for_[_var.name]_[_varNumber]' sw.write('for/*each*/ ([_var.type.javaRef] [helperName] : ') # TODO: the javaRef of a type is not qualified and there is no using System.Collections; # _what.writeJavaDefInContext(sw, false) if _what.type.isDynamic sw.write('cobra.core.CobraImp.getEnumerable(') # TODO shld be getIterable ?? _what.writeJavaDef(sw, false) sw.write(')') else _what.writeJavaDef(sw, false) sw.write(')') stmt = '[.var.javaName] = [helperName]' if .compiler.willTrackLocals stmt = 'cobra.core.CobraImp.setLocal("[.var.name]", [stmt])' stmt += ';\n' _block.writeJavaDef(sw, stmt) class IfStmt is partial def writeJavaDef(sw as CurlyWriter) # if you're looking for if-inherits related code gen, see BlockStmt base.writeJavaDef(sw) sw.write('if (') _cond.writeJavaDef(sw, false) sw.write(')') _trueStmts.writeJavaDef(sw, false) sw.dedent if _falseStmts sw.write('} else') _falseStmts.writeJavaDef(sw, false) sw.dedent sw.write('}\n') class ListenOrIgnoreStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) _event.writeJavaDef(sw, false) sw.write(' [.javaOperator] ') _target.writeJavaDef(sw, false) sw.writeLine(';') get javaOperator as String is abstract class ListenStmt is partial get javaOperator as String is override return '+=' class IgnoreStmt is partial get javaOperator as String is override return '-=' class PassStmt is partial pass class PrintStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if _destination # java Writer or PrintStream methodName = if(_stop, 'printStop', 'printLine') sw.write('cobra.core.CobraImp.[methodName](') _destination.writeJavaDef(sw, true) sw.write(', ') # if destination as one type or with constant available methods in each type # i.e if we only allowed destination to be a PrintStream or a PrintWriter... #_destination.writeJavaDef(sw, true) #methodName = if(_stop, 'print', 'println') #sw.write('.[methodName](') else methodName = if(_stop, 'printStop', 'printLine') sw.write('cobra.core.CobraImp.[methodName](') sep = '' for arg in _args sw.write('[sep]cobra.core.CobraImp._printStringMaker.makeString(') arg.writeJavaDef(sw) sw.write(')') sep = '+" "+' sw.writeLine(');') class PrintRedirectStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.write('cobra.core.CobraImp.pushPrintTo(') _destination.writeJavaDef(sw, false) sw.write(');\n') sw.write('try') _block.writeJavaDef(sw) sw.write('finally {\n') sw.indent sw.write('cobra.core.CobraImp.popPrintTo();\n') sw.dedent sw.write('}\n') class RaiseStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) name = .name #.capitalized localName = '_lh_event_[.serialNum]' sw.writeLine('// raise [.name] ...') sw.write('{ [_eventType.javaRef] [localName] = this.[name]; if ([localName]!=null) [localName](') sep = '' for expr in _args sw.write(sep) expr.writeJavaDef(sw, false) sep = ', ' sw.write('); }\n') class ReturnStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) ensurePart = .compiler.curCodeMember.ensurePart willEnsure = ensurePart and ensurePart.willGenerateCode if willEnsure sw.write('_lh_canEnsure = true;\n') if _expr if willEnsure and _backEndResultVarName and _backEndResultVarName.length sw.write('return [_backEndResultVarName]=') _expr.writeJavaDefInContext(sw) sw.write(';\n') else sw.write('return ') _expr.writeJavaDefInContext(sw, false) sw.write(';\n') else sw.write('return;\n') class TraceStmt is partial pass class TraceLocationStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if .includeTraces sw.write('CobraCoreInternal.CobraCore.Tracer.Trace([.javaSourceSite]);\n') class TraceAllStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if .includeTraces sw.write('CobraCoreInternal.CobraCore.Tracer.Trace([.javaSourceSite], "this", [_codePart.javaThis]') for param in _codePart.params sw.write(', "[param.name]", [param.javaName]') for local in _codePart.locals sw.write(', "[local.name]", [local.javaName]') sw.write(');\n') class TraceExprsStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if .includeTraces sw.write('CobraCoreInternal.CobraCore.Tracer.Trace([.javaSourceSite]') sep = ', ' for expr in _exprs sw.write('[sep][Utils.javaStringLiteralFor(expr.toCobraSource)][sep]') expr.writeJavaDef(sw, false) sw.write(');\n') class TryStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) if _successBlock # Java also has no "success" (or in Python, "else") block for the "try" statement # so it has to be simulated helperName = '_lh_success_[_varNumber]' sw.write('boolean [helperName] = true;\n') if _finallyBlock sw.write('try {\n') sw.indent sw.write('try') _tryBlock.writeJavaDef(sw, false) sw.dedent sw.write('}\n') if _catchBlocks.count .writeCatchBlocksJava(sw, helperName) sw.write('finally /*Success block*/{ if ([helperName])') _successBlock.writeJavaDef(sw) sw.write('}\n') if _finallyBlock sw.dedent sw.write('}\n') sw.write('finally') _finallyBlock.writeJavaDef(sw) else sw.write('try') _tryBlock.writeJavaDef(sw) .writeCatchBlocksJava(sw, nil) if _finallyBlock sw.write('finally') _finallyBlock.writeJavaDef(sw) # All exceptions caught will be either # checked exceptions wrapped in a (RuntimeException based) CheckedExceptionWrapper # or other RunTimeException based Exceptions. # Generate code to rethrow these and can then just use the original cobra catchblocks as-is def writeCatchBlocksJava(sw as CurlyWriter, localSuccessVar as String?) sw.write('catch (Exception _exc0) {\n') sw.indent sw.write('try {\n') sw.write(' if (_exc0 instanceof cobra.core.CheckedExceptionWrapper) {\n') sw.write(' cobra.core.CheckedExceptionWrapper cxw = (cobra.core.CheckedExceptionWrapper)_exc0;\n') sw.write(' throw cxw.getCause(); //:codeGen: catchblock Header\n') sw.write(' }\n') sw.write(' else throw (RuntimeException)_exc0;}\n') sw.write('//-- cobra-code catch blocks\n') _writeCatchBlocksJava(sw, localSuccessVar) # cobra code catch blocks sw.write('//-- end cobra-code catch blocks\n') # fallthrough catch clause to propagate uncaught exceptions hasException = false for cb in _catchBlocks if not cb.type or (cb.type and cb.type == .compiler.exceptionType) hasException = true break if not hasException sw.write('catch (Exception _exc1){\n') sw.write(' if (_exc1 instanceof cobra.core.CheckedExceptionWrapper){\n') sw.write(' throw (cobra.core.CheckedExceptionWrapper) _exc0; //rethrow as uncaught\n') sw.write(' }\n') sw.write(' else throw (RuntimeException)_exc1;\n}\n') sw.dedent sw.write('}\n') # This is original writeCatchBlocksJava code generating the cobra catch blocks. # It originally worked alongside writeJavaDef0 in ThrowStmt below def _writeCatchBlocksJava(sw as CurlyWriter, localSuccessVar as String?) for cb in _catchBlocks sw.write('catch (') if cb.type sw.write(' [cb.type.javaRef]') else # no catchAll in Java language so catch Exception sw.write(' java.lang.Exception') helperName = if(cb.varName, cb.sharpHelperName, '_anyExc' ) # using sharpHelperName above is correct(same as sharp) - value set in Statements:CatchBlock.bindInt # should be renamed to be non BE specific tho' cb.javaRethrowName = helperName to ! # stash name for possible rethrow sw.write(' [helperName]') sw.write(')') sb = StringBuilder() if localSuccessVar, sb.append('[localSuccessVar] = false;\n') if .compiler.hasDetailedStackTraceOption, sb.append('CobraCoreInternal.CobraImp.HandledException();\n') if cb.varName trackLocals = .compiler.willTrackLocals if trackLocals, sb.append('CobraCoreInternal.CobraImp.SetLocal("[cb.varName]", ') sb.append('[cb.javaVarName] = [helperName]') if trackLocals, sb.append(')') sb.append(';\n') cb.block.writeJavaDef(sw, sb.toString) class CatchBlock is partial get javaVarName as String return _var.javaName var javaRethrowName as String? class ThrowStmt is partial /# # This version tries to hide cobra mapping of Exceptions to RuntimeExceptions from coders # We allow checked Exceptions (cobra coded) but when thrown codegen encloses them in a runtimeException: # In catch clauses we internally unwrap, examine and handle exceptions as coded # # Feeds into and is related to the (YTBD) determination of what to do with calls to methods throwing # checked exceptions - # Ideally codegen would automatically wrap calls generating a checked exception in a RuntimeException # generating wrapper and remainder handling would be treated as above. # There is the possibility of a lotta repeated duplicated line by line code generated for cases # that a coder writing same would put together in a block using same try-catch... # e.g. multiple sequential file io calls all generating IOException say.. # so as a convenience we may also want to provide some level of user specification # (compiler directive maybe) that specifies the enclosure limits of what should be wrapped in the # automatic code generated exception trapping boilerplate.. # @jvm-wcx # .. lines of code that may gen such an Exception # # As currently implemented we could just detect such calls (not explicitly wrapped) and emit a # diagnostic (actually rely on javac to do it for us) suggesting that the cobra code must explicitly # trap/handle/declare checked exceptions and generate RuntimeException from it. # # This explicitly pushes all the necessary handling onto the user but cruelly exposes our non support for # conveniently remapping checked exceptions... # also it makes for ugly code (and obscures the code control flow). # (see comment below) #/ def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.write('throw ') if _expr if _expr.type.isDynamic, sw.write('((java.lang.RuntimeException)') #throw Runtime Exceptions as is if _expr.type.isDescendantOf(.compiler.libraryType('Java.Lang.RuntimeException') to Box) _expr.writeJavaDef(sw, false) else # Checked exceptions get wrapped in a runtime Exc wrapper sw.write('new cobra.core.CheckedExceptionWrapper("Wrapping checked exception [_expr.type.name]",') _expr.writeJavaDef(sw, false) sw.write(')') if _expr.type.isDynamic, sw.write(')') else # rethrow # Java doesnt support an unadorned throw in a CatchBlock (rethrow), needs an explicit exception # look in enclosing block and if its a catchblock use its stored name (javaVarName) if .superNode and .superNode.superNode inherits CatchBlock rethrow = (.superNode.superNode to CatchBlock).javaRethrowName sw.write('cobra.core.CobraImp.reThrow(') sw.write(rethrow) sw.write(')') sw.write(';\n') /# # This original version of throws codegen was used along with JvmBackEnd # _tagToTypeName = {... # 'Exception' : 'Java.Lang.RuntimeException', # ...} # makes the base Exceptionclass (compiler.ExceptionType) to be set to RuntimeException. # # This forces dvlpr written code to use only RuntimeException based exceptions explicitly # and also forces the explicit trapping of all methods throwing checked exceptions #(to handle or convert to a wrapping RuntimeException) which does obscure the code layout - just like java... # We dont provide a way to specify Exceptions thrown on a method definition so the net effect is to # force cobra code to be written to conform (explicitly) to our forcing of all java Exceptions to # unchecked which may or may not be seen to be a good thing..... # but its extra boilerplate cruft in cobra code, ugly and inconvenient to do #/ def writeJavaDef0(sw as CurlyWriter) base.writeJavaDef(sw) sw.write('throw ') if _expr if _expr.type.isDynamic sw.write('((java.lang.RuntimeException)') _expr.writeJavaDef(sw, false) if _expr.type.isDynamic sw.write(')') else # rethrow # Java doesnt support an unadorned throw in a CatchBlock (rethrow), needs an explicit exception # look in enclosing block and if catchblock use its stored name ( javaVarName) if .superNode and .superNode.superNode inherits CatchBlock rethrow = (.superNode.superNode to CatchBlock).javaRethrowName sw.write(rethrow) sw.write(';\n') class UsingStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) name = _var.javaName sw.write('// using - needs work\n') # should extend this so takes an initialisation block rather than single initExpr sw.write('try ') sw.write('[name] = ') _initExpr.writeJavaDef(sw) sw.write(';\n') _block.writeJavaDef(sw) # by hand ( w/o java7 if false sw.write('[name] = ') _initExpr.writeJavaDef(sw) sw.write(';try\n') _block.writeJavaDef(sw) sw.write('finally {\n') sw.indent if _var.type.isReference sw.write('if ([name]!=null) { ((java.lang.AutoCloseable)[name]).close(); [name] = null; }\n') else sw.write('((java.lang.AutoCloseable)[name]).close();\n') sw.dedentAndWrite('}\n') class WhileStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.write('while (') _expr.writeJavaDef(sw, false) sw.write(')') _block.writeJavaDef(sw) class PostWhileStmt is partial def writeJavaDef(sw as CurlyWriter) is override # base.writeSharpDef(sw) - don't generate the other form of while loop sw.write('do') _block.writeJavaDef(sw, false) sw.dedent sw.write('} while (') _expr.writeJavaDef(sw, false) sw.write(');\n') class YieldStmt is partial pass class YieldBreakStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) sw.write('yield break;\n') class YieldReturnStmt is partial def writeJavaDef(sw as CurlyWriter) base.writeJavaDef(sw) ensurePart = .compiler.curCodeMember.ensurePart willEnsure = ensurePart and ensurePart.willGenerateCode if willEnsure sw.write('_lh_canEnsure = true;\n') if _expr backEndResultVarName = .compiler.curCodeMember.backEndResultVarName if willEnsure and backEndResultVarName.length # TODO: resolve yield return with ensure # sw.write('yield return [backEndResultVarName]=') # so for now: sw.write('yield return ') _expr.writeJavaDefInContext(sw) sw.write(';\n') else sw.write('yield return ') _expr.writeJavaDefInContext(sw) sw.write(';\n') else sw.write('yield return;\n') ## ## Expressions ## # uu class Expr is partial /# TODO axe or be content with SharpGenerator version. def needsContextCast as bool """ Returns true if this expression needs to be cast within its context. One example, is that a dynamically typed expression requires such casting. """ type = .type if type.isDynamic, return true if type inherits IntType if type.size < 32, return true return false #/ def writeJavaDefInContext(sw as CurlyWriter) .writeJavaDefInContext(sw, true) def writeJavaDefInContext(sw as CurlyWriter, parens as bool) """ When an expression is used where a particular type is expected, such as: # assignment x = y # argument passing obj.foo(x, y) obj[x, y] it may need C# typecasting--particularly if it is dynamic typed expression. This method relies on having had its .contextType set beforehand. This method should be invoked instead of `writeJavaDef` in any situation where .contextType was set. """ cast = _contextType and .needsContextCast if cast if parens, sw.write('(') sw.write('([_contextType.javaRef])(') .writeJavaDef(sw) if cast sw.write(')') if parens, sw.write(')') def writeJavaDef(sw as CurlyWriter) is override base.writeJavaDef(sw) branch .direction on Direction.In, pass on Direction.Out, sw.write('/*out*/') # TODO on Direction.InOut, sw.write('/*ref*/ ') # TODO .writeJavaDef(sw, true) def writeJavaDef(sw as CurlyWriter, parens as bool) #print 'Expr.writeJavaDef', this pass def writeJavaStmt(sw as CurlyWriter) is override assert .didBindImp sw.node(this) .writeJavaSetLine(sw) .writeJavaDef(sw, false) sw.write(';\n') def writeJavaBreakdown(sw as CurlyWriter) sw.write(r'new Object[] { 0') .writeJavaBreakdownItems(sw) sw.write('}, ') get willWriteJavaBreakdownItems as bool return _direction <> Direction.Out def writeJavaBreakdownItems(sw as CurlyWriter) if .willWriteJavaBreakdownItems src = Utils.javaStringLiteralFor(.toCobraSource) sw.write(', [src], ') .writeJavaDefForBreakdown(sw) def writeJavaDefForBreakdown(sw as CurlyWriter) .writeJavaDef(sw) def _mapJavaTypeToClass(typeName as String, defaultSuffix as String) as String # support for getClass and primitive mapping if typeName == 'int', typeName = 'Integer.TYPE' else if typeName == 'bool', typeName = 'Boolean.TYPE' else if typeName == 'char', typeName = 'Character.TYPE' else, typeName = typeName + defaultSuffix #else if typeName == 'byte', typeName = 'Byte.TYPE' #else if typeName == 'sbyte', typeName = 'Byte.TYPE' #else if typeName == 'short', typeName = 'Short.TYPE' #else if typeName == 'int16', typeName = 'Short.TYPE' #else if typeName == 'int32', typeName = 'Integer.TYPE' #else if typeName == 'long', typeName = 'Long.TYPE' #else if typeName == 'int64', typeName = 'Long.TYPE' #else if typeName == 'float', typeName = 'Float.TYPE' #else if typeName == 'float32', typeName = 'Float.TYPE' #else if typeName == 'double', typeName = 'Double.TYPE' #else if typeName == 'float64', typeName = 'Double.TYPE' #else, typeName = typeName + defaultSuffix return typeName class NameExpr is partial get asJava as String #print 'Dbg: NameExpr::', _definition.javaName return _definition.javaName class AsExpr is partial def writeJavaStmt(sw as CurlyWriter) is override # this happens for declarations like "i as int" sw.write('// [_name] as [_type.name]\n') def writeJavaDef(sw as CurlyWriter, parens as bool) is override #print .asJava sw.write(.asJava) class AnonymousMethodExpr is partial # TODO pass class LambdaExpr is partial # TODO pass class CallExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override assert .superNode inherits DotExpr name = .name if _definition inherits BoxMember and (_definition to BoxMember).binaryName name = (_definition to BoxMember).binaryName to ! # Tmp while remapping .Net libs if name.startsWithNonLowerLetter name = Utils.cobraNameForNativeMemberName(name) #else # name = name.capitalized #trace name sw.write('[name]') if _genericArgTypes and _genericArgTypes.count sw.write('<') sep = '' for genericArgType in _genericArgTypes sw.write(sep + genericArgType.javaRef) sep = ', ' sw.write('>') sw.write('(') sep = '' for arg in _args sw.write(sep) if arg inherits AssignExpr # Named parameter detail = 'For java at the moment you will need to recode the named parameter call to a call using positional arguments.' .compiler.warning(this, 'Java does not support named parameters in calls so these are not available with this backend. [detail]') # TODO: Rewrite call to positional parameter form using name(s) given ## cant do arg.left.writeSharpDef as binding invariants are incomplete #sw.write((arg.left to IdentifierExpr).name) #sw.write(':') # C# form is name:value arg.right.writeJavaDef(sw) else arg.writeJavaDefInContext(sw) sep = ', ' sw.write(')') def writeJavaBreakdownItems(sw as CurlyWriter) is override # leaving out the base call is intentional: # base.writeJavaBreakdownItems(sw) sw.write(', +1') for expr in _args expr.writeJavaBreakdownItems(sw) sw.write(', -1') class EnumCallExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override isSet = _args.count > 1 if parens if isSet, sw.write('java.util.EnumSet.of') sw.write('(') if _args.count == 0 sw.write('/*default([_definition.javaRef])*/') else sep = '' for member in _members sw.write(sep) sw.write(member.javaRef) sep = ',' if parens, sw.write(')') def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) sw.write(', +1') for member in _members sw.write(', ') sw.write(member.javaRef) sw.write(', -1') class ForExpr is partial # to-do pass class TryCatchExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override # codegen below is specific to tryCatch as target of an assignment assert _superNode inherits BinaryOpExpr and .binarySuperNode.op == 'ASSIGN' sw.write('{') sw.write(_type.javaRef) sw.write(' r; try { r = ') .what.writeJavaDef(sw, false) sw.write(';} catch ') if _excType sw.write('(') sw.write(_excType.javaRef) sw.write(') ') sw.write('{ r =') _getExpr.writeJavaDef(sw, false) sw.write('; } ') lhs = .binarySuperNode.left lhs.writeJavaDef(sw, false) sw.write(' = r; }') # with java8 try this again for any arbitrarily embedded trycatch expr by generating a closure and # immediately executing of it - otherwise some method type inference and auto sig lookup/generation. class IdentifierExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override # recall that this cannot be the right side of "foo.bar" since that is a MemberExpr name = .javaQualification + .javaName if _requiresGetClass name = _getClass(name) #name = _mapJavaTypeToClass(name, '') sw.write(name) get javaQualification as String qual = '' if .definition inherits IVar # the only definition that has no parentNameSpace in practice is IVar pass else pn = .definition.parentNameSpace if pn, qual = pn.javaQualifier return qual get javaName as String assert .didBindImp assert .namedDefinition assert not .type inherits UnspecifiedType if .superNode inherits DotExpr assert this is not (.superNode to DotExpr).right # should be a CallExpr or MemberExpr instead defi = .namedDefinition #print 'Dbg: IdExpr.name::', defi.javaName return defi.javaName def _requiresGetClass as bool # C# often requires typeof(Foo) instead of just plain Foo if not .isTypeReference, return false superNode = .superNode if superNode inherits DotExpr, return false #if superNode inherits InheritsExpr, return false if superNode inherits PostCallExpr if superNode.expr is this return false return true def writeJavaStmt(sw as CurlyWriter) is override assert .isCalling sw.write('[_name]();') get javaAssignmentNames as List? require .didBindImp .namedDefinition body if _definition inherits IVar return _definition.javaAssignmentNames else return nil def writeJavaDefForBreakdown(sw as CurlyWriter) is override name = .javaQualification + .javaName # _requiresGetClass may return false, but in the java def breakdown, .getClass is always required if .isTypeReference, name = _getClass(name) #name = _mapJavaTypeToClass(name, '') sw.write(name) def _getClass(name as String) as String if .javaName.endsWith('.TYPE') # primitive already typed return name if not .javaName.startsWithLowerLetter name = name + '.class' else name = _mapJavaTypeToClass(name, '.getClass()') return name class IfExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override cond, tpart, fpart = .cond, .tpart, .fpart cast = tpart.type <> fpart.type # TODO: is this true?: Java doesn't do the "greatest common denominator" thing like Cobra does, so some casting is in order sw.write('(') cond.writeJavaDef(sw) sw.write('?') if cast, sw.write('([.type.javaRef])(') tpart.writeJavaDef(sw) if cast, sw.write(')') sw.write(':') if cast, sw.write('([.type.javaRef])(') fpart.writeJavaDef(sw) if cast, sw.write(')') sw.write(')') def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) sw.write(', +1') # indent cond, tpart, fpart = .cond, .tpart, .fpart cond.writeJavaBreakdownItems(sw) # only one of the target expressions is actually evaluated # tpart: src = Utils.javaStringLiteralFor(tpart.toCobraSource) sw.write(', [src], new cobra.core.CobraDirectString(') cond.writeJavaDefForBreakdown(sw) sw.write(' ? cobra.core.CobraCore.toTechString(') tpart.writeJavaDefForBreakdown(sw) sw.write(') : "(not-evaluated)")') # fpart: src = Utils.javaStringLiteralFor(fpart.toCobraSource) sw.write(', [src], new cobra.core.CobraDirectString(') cond.writeJavaDefForBreakdown(sw) sw.write(' ? "(not-evaluated)" : cobra.core.CobraCore.toTechString(') fpart.writeJavaDefForBreakdown(sw) sw.write('))') sw.write(', -1') # dedent class IndexExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if _target.type.isDynamic sw.write('cobra.core.CobraImp.getIndexerValue(') _target.writeJavaDef(sw, false) for expr in _args sw.write(', ') expr.writeJavaDef(sw, false) sw.write(')') return if parens, sw.write('(') if _target inherits IdentifierExpr if _target.isKindOf(.compiler.typeType) # here we're favoring "Foo[]" being an array type rather than a shared indexer sw.write(_target.name) handled = true if not handled _target.writeJavaDef(sw) ntIdxr = _lookupIndexer if ntIdxr assert ntIdxr.getter # getter for indexer exists assert ntIdxr.getter.name.length # and has a name set _writeIndexer(sw, '.[ntIdxr.getter.name](', ')') else # fallback to treat as Array Type _writeIndexer(sw, r'[', r']') if parens, sw.write(')') def _lookupIndexer as JavaFieldInfo? nt = JvmNativeType.nativeType(_target.type.nonNil) if not nt # _target.type is genericInstance tt = _target.type.nonNil to Box genDef = tt.genericDef nt = JvmNativeType.nativeType(genDef) #print 'dbg-ReadIdxr', _target.type.nonNil,'\n+ ', nt if nt ntIdxr = nt.lookupIndexer #print 'ntIdxr: [ntIdxr]' if ntIdxr and ntIdxr.isReadable return ntIdxr #TODO: check for Indexer attribute for a Cobra user written version return nil def _writeIndexer(sw as CurlyWriter, prefix as String, suffix as String) sw.write(prefix) _writeIdxArgs(sw) sw.write(suffix) def _writeIdxArgs(sw as CurlyWriter) sep = '' for expr in _args sw.write(sep) expr.writeJavaDefInContext(sw) sep = ', ' def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) sw.write(', +1') _target.writeJavaBreakdownItems(sw) for expr in _args expr.writeJavaBreakdownItems(sw) sw.write(', -1') class IsNilExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') sw.write('(') _expr.writeJavaDef(sw) sw.write(')') sw.write('== null') if parens, sw.write(')') def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) _expr.writeJavaBreakdownItems(sw) class IsNotNilExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') sw.write('(') _expr.writeJavaDef(sw) sw.write(')') sw.write('!= null') if parens, sw.write(')') def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) _expr.writeJavaBreakdownItems(sw) class TruthExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override lparen = if(parens, '(', '') rparen = if(parens, ')', '') branch _treatment on Treatment.AsIs _expr.writeJavaDef(sw, parens) on Treatment.InvokeRuntime sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.isTrue(') _expr.writeJavaDef(sw, false) sw.write(')') on Treatment.CompareToZero sw.write('[lparen]0 != ') _expr.writeJavaDef(sw, true) sw.write(rparen) on Treatment.CompareToZeroChar sw.write("[lparen]'\\0' != ") _expr.writeJavaDef(sw, true) sw.write(rparen) on Treatment.CompareToNull sw.write('[lparen] null!= ') _expr.writeJavaDef(sw, true) sw.write(rparen) def writeJavaBreakdownItems(sw as CurlyWriter) is override # leaving out the base class is intentional: # base.writeJavaBreakdownItems(sw, isFirstExpr) _expr.writeJavaBreakdownItems(sw) class TypeExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override javaRef = _containedType.javaRef # handle the case of "X.X" where namespace and class are both called "X". # C# chokes on it because the first "X" is considered to be the type #if .curBox.name + '.' in javaRef # javaRef = 'global::' + javaRef if _requiresGetClass() # was _requiresTypeOf #sw.write('typeof(') sw.write('(') sw.write(javaRef) sw.write('.getClassX()') sw.write(')') else if _requiresRawType() sw.write(javaRef) # might need to map cobratypes like [u]int{8,16,32,64} else javaRef = _mapJavaTypeToClass(javaRef, '.class') sw.write(javaRef) def _requiresGetClass as bool return false # may need to extend later def _requiresRawType as bool # In Java some places require class refs amd the Type of any primitives being used superNode = .superNode if superNode is nil return false if superNode inherits BinaryOpExpr if this is superNode.right if superNode inherits InheritsExpr return false return true /# def _requiresTypeOf as bool # Cobra never requires that you wrap a type reference in typeof(Foo). # C# requires typeof() in a variety of circumstances and won't accept it in a variety of others. superNode = .superNode if superNode is nil return false else if superNode inherits DotExpr return false if superNode inherits PostCallExpr # could be Type(foo) or _bar(Type) # in C#, the second requires `typeof` and the first won't take it if superNode.expr is this return false if superNode inherits BinaryOpExpr if this is superNode.right if superNode inherits InheritsExpr or superNode inherits AbstractToExpr return false return true #/ def writeJavaDefForBreakdown(sw as CurlyWriter) is override #requiresTypeOf = _requiresTypeOf # using C# version currently - chop ? #if not requiresTypeOf, sw.write('typeof(') #.writeJavaDef(sw) #if not requiresTypeOf, sw.write(')') # Handle mapping primitives to their classes #javaRef = _containedType.javaRef javaRef = _mapJavaTypeToClass(_containedType.javaRef, '.class') sw.write(javaRef) class UnaryOpExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if _op == 'PLUS' if parens sw.write('(') _expr.writeJavaDef(sw, false) if parens sw.write(')') return if _expr.type.isDynamic specs = OperatorSpecs.unaryOpSpecsByCobraText assert specs.containsKey(.token.text) spec = specs[.token.text] opText = Utils.javaStringLiteralFor(spec.opMethodName) sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.dynamicOp([opText], ') _expr.writeJavaDef(sw, false) sw.write(')') return if parens sw.write('(') branch _op on 'MINUS' sw.write('-') on 'TILDE' sw.write('~') on 'NOT' sw.write('!') else throw FallThroughException(_op) _expr.writeJavaDef(sw) if parens sw.write(')') def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) sw.write(', +1') _expr.writeJavaBreakdownItems(sw) sw.write(', -1') class Literal is partial get asJava as String return '/* TODO_Literal */' def writeJavaDef(sw as CurlyWriter, parens as bool) is override sw.write(.asJava) class AtomicLiteral is partial get willWriteJavaBreakdownItems as bool is override return false class BoolLit is partial get asJava as String is override return if(_value, 'true', 'false') class CharLit is partial get asJava as String is override if _value[0] to int == 39, return "'\\''" # single quote else, return "'" + _value.toString + "'" class DecimalLit is partial get asJava as String is override return _value.toString(Utils.cultureInfoForNumbers) + '/*decimal*/' class FractionalLit is partial get asJava as String is override if _type == .compiler.floatType(32), suffix = 'f' else if _type == .compiler.floatType(64), suffix = '' else if _type == .compiler.decimalType, suffix = '/*decimal*/' else, throw FallThroughException(_type) return _value.toString(Utils.cultureInfoForNumbers) + suffix class FloatLit is partial get asJava as String is override # suppose you have the literal: 0.00001 # using 'F' format for 'fixed point' gives '0.00' which loses the original value # using 'R' format for 'round trip' gives '1E-05' s = _value.toString('R', Utils.cultureInfoForNumbers) if s.indexOf('.') == -1 and 'E' not in s, s += '.0' if (.type to FloatType).size == 32, s += 'f' else, s += 'd' return s class IntegerLit is partial get asJava as String is override s = '' if (info = .token.info) inherits int branch info on - 8, s = 'byte' # java.lang.Byte on + 8, s = 'short' # unsigned Byte uint8 on -16, s = 'short' #'Int16' on +16, s = 'int' # unsigned short UInt16' on -32, s = '' # int32 on +32, s = 'long' # unsigned int32 on -64, s = 'L' #'long' on +64, s = 'long' # unsigned int64 else, throw FallThroughException(info) if s.length <= 2 return _value.toString + s else return '([s])[_value.toString]' class NilLiteral is partial get asJava as String is override return 'null' class StringLit is partial get asJava as String is override return Utils.javaStringLiteralFor(_string) class StringSubstLit is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if _items.count>1 sw.write('cobra.core.CobraImp._printStringMaker.makeString(') sep = '' for item in _items sw.write(sep) if item inherits StringLit item.writeJavaDef(sw, true) # CC: axe the "true" when the bug about overload groups crossing inheritance is fixed else if item inherits FormattedExpr sw.write('cobra.core.CobraImp._printStringMaker.makeString(') item.expr.writeJavaDef(sw) sw.write(',') sw.write(Utils.javaStringLiteralFor(item.format)) sw.write(')') else sw.write('cobra.core.CobraImp._printStringMaker.makeString(') item.writeJavaDef(sw, false) sw.write(')') sep = ',' if _items.count>1 sw.write(')') class BaseLit is partial get asJava as String is override return 'super' class ThisLit is partial get asJava as String is override return .compiler.curBox.javaThis class VarLit is partial get asJava as String is override return _name class SequenceLit is partial def writeJavaBreakdownItems(sw as CurlyWriter) is override # CC: axe is override base.writeJavaBreakdownItems(sw) sw.write(', +1') for expr in _exprs expr.writeJavaBreakdownItems(sw) sw.write(', -1') class ListLit is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override innerType = (_type to Box).genericParams[0] sw.write('cobra.core.CobraImp.makeList/*<[innerType.javaRef]>*/(') if _exprs.count sep = '' for expr in _exprs sw.write(sep) expr.writeJavaDef(sw, false) sep = ', ' sw.write(')') class ArrayLit is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override sw.write('new [_type.javaRef] { ') if _exprs.count sep = '' for expr in _exprs sw.write(sep) expr.writeJavaDef(sw, false) sep = ', ' sw.write(' }') class SetLit is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override innerType = (_type to Box).genericParams[0] sw.write('cobra.core.CobraImp.makeSet/*<[innerType.javaRef]>*/(') if _exprs.count sw.write(', ') sep = '' for expr in _exprs sw.write(sep) expr.writeJavaDef(sw, false) sep = ', ' sw.write(')') class ToNilableOrNotExpr is partial def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) _expr.writeJavaBreakdownItems(sw) class ToNilableExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if _expr.type.isReference # In C#/.NET, reference types are always "nilable" _expr.writeJavaDef(sw) else # ex: ((int?)x) # ex: ((Color?)Color.Black) #sw.write('(([_expr.type.javaRef]?)') sw.write('(([_expr.type.javaRef])') _expr.writeJavaDef(sw, false) sw.write(')') class ToNonNilableExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override type = _expr.type if type inherits NilableType and not type.nonNil.isReference # ex: (x).Value # ex: (obj.foo).Value sw.write('(') _expr.writeJavaDef(sw, false) #sw.write(').value') sw.write(')') else if .compiler.options.boolValue('include-nil-checks') #sw.write('(cobra.core.CobraCore._willCheckNil ? cobra.core./*CobraCoreInternal.*/CobraImp.checkNonNil<[type.javaRef]>([.javaThis], [Utils.javaStringLiteralFor(_expr.toCobraSource)], ') sw.write('(cobra.core.CobraCore._willCheckNil ? cobra.core./*CobraCoreInternal.*/CobraImp.checkNonNil([.javaThis], [Utils.javaStringLiteralFor(_expr.toCobraSource)], ') _expr.writeJavaDef(sw, false) sw.write(', [.javaSourceSiteArgs]) :') _expr.writeJavaDef(sw, false) sw.write(')') else _expr.writeJavaDef(sw, parens) ## ## Binary Expressions ## class BinaryOpExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') _writeJavaDef(sw) if parens, sw.write(')') def _writeJavaDef(sw as CurlyWriter) sw.write('/* TODO_BinaryOp */') # pass def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) sw.write(', +1') _writeJavaBreakdownItemsLeft(sw) _writeJavaBreakdownItemsRight(sw) sw.write(', -1') def _writeJavaBreakdownItemsLeft(sw as CurlyWriter) _left.writeJavaBreakdownItems(sw) def _writeJavaBreakdownItemsRight(sw as CurlyWriter) _right.writeJavaBreakdownItems(sw) class AbstractAssignExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if _trackLocal sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.setLocal("[_trackName]", ') else if parens sw.write('(') _writeJavaDef(sw) if _trackLocal or parens sw.write(')') class AssignExpr is partial var isJavaSetProperty = false def _writeJavaDef(sw as CurlyWriter) is override # special case handling/trap for tryCatch within assignment if _right inherits TryCatchExpr # expand trycatch and do assignment within the expansion _right.writeJavaDef(sw) return #trace _right # TODO: # if trackLocal: # out.write('CobraCoreInternal.CobraImp.SetLocal("%s", ' % .left.name) if _left inherits IndexExpr if _left.target.type.isDynamic # assigning to an indexer of a dynamically typed target requires special code gen sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.setIndexerValue(') _left.target.writeJavaDef(sw, false) sw.write(', ') _right.writeJavaDef(sw, false) for expr in _left.args sw.write(', ') expr.writeJavaDef(sw, false) sw.write(')') return handled = false if _left inherits IdentifierExpr names = _left.javaAssignmentNames if names and names.count names.reverse for i in names.count sw.write(if(i==0, '', '=')) assert names[i].length sw.write(names[i]) else assert _left.javaName <> '' sw.write(_left.javaName) handled = true if not handled # `obj.foo = bar` where `obj` is dynamic, requires special handling if _left inherits DotExpr if _left.left.type.isDynamic assert _left.right inherits MemberExpr sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.setPropertyValue(') _left.left.writeJavaDef(sw, false) sw.write(', ') sw.write(Utils.javaStringLiteralFor((_left.right to MemberExpr).name)) sw.write(', ') _right.writeJavaDef(sw) sw.write(')') return if not handled # for something like "p.total = 0", cannot generate "(p.Total)=0" because then C# sees # the left hand side as an r-value instead an l-value/target. hence the false below. _left.writeJavaDef(sw, false) if .isJavaSetProperty # set in MemberExpr.writeJavaDef sw.write('(') _right.writeJavaDefInContext(sw) sw.write(')') else sw.write('=') _right.writeJavaDefInContext(sw) # handle the case where a type got backed up because of assignment inside of an if-inherits if _backUpIfInheritsStack assert _left.definition inherits IVar _left.definition.ifInheritsStack = Stack(_backUpIfInheritsStack) class NumericPromoExpr is partial def _writeJavaDefDynamic(sw as CurlyWriter) specs = OperatorSpecs.binaryOpSpecsByCobraText assert specs.containsKey(.token.text) spec = specs[.token.text] opText = Utils.javaStringLiteralFor(spec.opMethodName) sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.DynamicOp([opText], ') _left.writeJavaDef(sw) sw.write(', ') _right.writeJavaDef(sw) sw.write(')') class AugAssignMathExpr is partial def _writeJavaDef(sw as CurlyWriter) is override if .isConcated _writeJavaDefConcated(sw) else if _type.isDynamic _writeJavaDefDynamic(sw) else _writeJavaDefOperation(sw) def _writeJavaDefConcated(sw as CurlyWriter) _left.writeJavaDef(sw, false) sw.write(' = ') sw.write('(' + _left.type.javaRef + ')') sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.concated(') _left.writeJavaDef(sw, false) sw.write(', ') _right.writeJavaDef(sw, false) sw.write(')') def _writeJavaDefDynamic(sw as CurlyWriter) is override _left.writeJavaDef(sw) sw.write(' = ') specs = OperatorSpecs.binaryOpSpecsByCobraText assert specs.containsKey(.token.text) spec = specs[.token.text] opText = Utils.javaStringLiteralFor(spec.opMethodName) sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.dynamicOp([opText], ') _left.writeJavaDef(sw) # TODO: add , false sw.write(', ') _right.writeJavaDef(sw) # TODO: add , false sw.write(')') def _writeJavaDefOperation(sw as CurlyWriter) op = '' sep = ',' # for the method call case branch _op on 'PLUS_EQUALS' op = '+=' on 'MINUS_EQUALS' op = '-=' on 'STAR_EQUALS' op = '*=' on 'STARSTAR_EQUALS' #op = 'CobraCoreInternal.CobraImp.PowerToEquals(' op = 'java.lang.Math.pow(' on 'SLASH_EQUALS' op = '/=' # TODO: finish this on 'SLASHSLASH_EQUALS' op = '/=' # TODO: finish this on 'PERCENT_EQUALS' op = '%=' else throw FallThroughException(_op) assert op.length if op.length==2 _left.writeJavaDef(sw) sw.write(op) _right.writeJavaDef(sw) else sw.write(op) # ex: 'CobraCoreInternal.CobraImp.Foo(' _left.writeJavaDef(sw) sw.write(sep) _right.writeJavaDef(sw) sw.write(')') class AugAssignBitwiseExpr is partial def _writeJavaDef(sw as CurlyWriter) is override if _left.type.isDynamic _left.writeJavaDef(sw) sw.write(' = ') specs = OperatorSpecs.binaryOpSpecsByCobraText assert specs.containsKey(.token.text) spec = specs[.token.text] opText = Utils.javaStringLiteralFor(spec.opMethodName) sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.dynamicOp([opText], ') _left.writeJavaDef(sw) # TODO: add , false sw.write(', ') _right.writeJavaDef(sw) # TODO: add , false sw.write(')') return op = '' branch _op on 'AMPERSAND_EQUALS' op = '&=' on 'VERTICAL_BAR_EQUALS' op = '|=' on 'CARET_EQUALS' op = '^=' on 'DOUBLE_LT_EQUALS' op = '<<=' on 'DOUBLE_GT_EQUALS' op = '>>=' else throw FallThroughException(_op) assert op.length _left.writeJavaDef(sw) sw.write(op) _right.writeJavaDef(sw) class BinaryBoolExpr is partial def _writeJavaDef(sw as CurlyWriter) is override if _op=='IMPLIES' sw.write('!') _left.writeJavaDef(sw) branch _op on 'AND' sw.write('&&') on 'OR' or 'IMPLIES' sw.write('||') _right.writeJavaDef(sw) def _writeJavaBreakdownItemsRight(sw as CurlyWriter) is override # have to respect the short circuit otherwise something like "s and s.length" will give # NullReference exception for "s.length" # solution is: given L and R, # write: "R", (!(L) ? "short-circuted" : (R).ToString()) # that's for 'AND'. for, 'OR', remove the !, for 'IMPLIES' it's like AND # so rather than: # _right.writeJavaBreakdownItems(sw) # do this: src = Utils.javaStringLiteralFor(_right.toCobraSource) javaNot = if(_op=='OR', '', '!') sw.write(', [src], new cobra.core.CobraDirectString([javaNot]') _left.writeJavaDefForBreakdown(sw) sw.write(' ? "(short-circuited)" : cobra.core.CobraCore.toTechString(') _right.writeJavaDefForBreakdown(sw) sw.write('))') class BinaryBitwiseExpr is partial def _writeJavaDef(sw as CurlyWriter) is override if _type.isDynamic _writeJavaDefDynamic(sw) return op = '' branch _op on 'AMPERSAND' op = '&' on 'VERTICAL_BAR' op = '|' on 'CARET' op = '^' on 'DOUBLE_LT' op = '<<' on 'DOUBLE_GT' op = '>>' else throw FallThroughException(_op) assert op.length _left.writeJavaDef(sw) sw.write(op) _right.writeJavaDef(sw) class BinaryMathExpr is partial def _writeJavaDef(sw as CurlyWriter) is override if .isConcated _writeJavaDefConcated(sw) else if _type.isDynamic _writeJavaDefDynamic(sw) else _writeJavaDefOperation(sw) def _writeJavaDefConcated(sw as CurlyWriter) sw.write('(' + _left.type.javaRef + ')') sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.concated(') _left.writeJavaDef(sw, false) sw.write(', ') _right.writeJavaDef(sw, false) sw.write(')') def _writeJavaDefOperation(sw as CurlyWriter) intType = .compiler.anyIntType left = _left right = _right op = '' pre = '' sep = ',' # for the method call case branch _op on 'PLUS' op = '+' on 'MINUS' op = '-' on 'STAR' op = '*' on 'STARSTAR' #op = 'CobraCoreInternal.CobraImp.PowerTo(' op = 'System.Math.Pow(' if _left.isKindOf(intType) and _right.isKindOf(intType) pre = '(' + .compiler.intType.javaRef + ')' on 'SLASH' op = '/' if _left.isKindOf(intType) and _right.isKindOf(intType) pre = '(' + .compiler.numberType.javaRef + ')' on 'SLASHSLASH' if left.isKindOf(intType) and right.isKindOf(intType) op = '/' else if left.isKindOf(.compiler.decimalType) or right.isKindOf(.compiler.decimalType) #op = 'System.Decimal.Floor(' op = 'java.lang.Math.floor(' sep = '/' else if left.isKindOf(.compiler.anyFloatType) or right.isKindOf(.compiler.anyFloatType) op = 'java.lang.Math.floor(' sep = '/' else throw FallThroughException([left.type, right.type]) on 'PERCENT' op = '%' else throw FallThroughException(_op) assert op.length sw.write(pre) if op.length==1 _left.writeJavaDef(sw) sw.write(op) _right.writeJavaDef(sw) else sw.write(op) # ex: 'CobraCoreInternal.CobraImp.Foo(' _left.writeJavaDef(sw) sw.write(sep) _right.writeJavaDef(sw) sw.write(')') class CompareExpr is partial var _cobraToJava = { 'EQ': '==', 'NE': '!=', 'GT': '>', 'LT': '<', 'GE': '>=', 'LE': '<=', 'IS': '==', 'ISNOT': '!=', } def _writeJavaDef(sw as CurlyWriter) is override left = _left right = _right op = _op wrapLeft = wrapRight = '' # Compute the java operation which will be an operator or method call if op=='EQ' or op=='NE' if not left.type.isReference and not right.type.isReference # struct/value types #if not left.type inherits PrimitiveType and not right.type inherits PrimitiveType # # user-defined structs # op = '.Equals(' #else # # primitives like ints and decimals (even mixed like someDecimal == someInt) # op = _cobraToJava[_op] op = _cobraToJava[_op] else done = false stringType = .compiler.stringType charType = .compiler.charType if left.isKindOfNonNil(stringType) and right.isKindOfNonNil(stringType) # TODO: check for static comparison operations instead op = if(op in ['EQ', 'NE'], '.equals(', '.compareTo(') done = true else if left.isKindOfNonNil(charType) and right.isKindOfNonNil(stringType) op = if(op in ['EQ', 'NE'], '.equals(', '.compareTo(') wrapLeft = 'String.valueOf(' done = true else if left.isKindOfNonNil(stringType) and right.isKindOfNonNil(charType) op = if(op in ['EQ', 'NE'], '.equals(', '.compareTo(') wrapRight = 'String.valueOf(' done = true else if left.type inherits Box # TODO: try skipping on requiring that the type is a Box leftBox = left.type to Box compareTo = leftBox.memberForName('compareTo') if compareTo and compareTo.isMethod and compareTo.resultType.isDescendantOf(.compiler.anyIntType) and right.type.isDescendantOf(leftBox) # need last condition to prevent someString.CompareTo(someChar) op = '.compareTo(' done = true if not done assert op in ['EQ', 'NE'] assert op == .token.which op = '.equals(' else if op=='IS' or op=='ISNOT' if left.type inherits PrimitiveType and right.type inherits PrimitiveType op = _cobraToJava[_op] else if left.type.isReference and right.type.isReference op = _cobraToJava[_op] #op = if(op=='IS', ' instanceof ', '! instanceof ') else # non-trivial situation.. fall back to runtime support op = if(op=='IS', 'cobra.core./*CobraCoreInternal.*/CobraImp.is(', 'cobra.core./*CobraCoreInternal.*/CobraImp.isNot(') else if left.type.isDynamic or right.type.isDynamic sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.dynamicCompare(') left.writeJavaDef(sw, false) sw.write(', ') right.writeJavaDef(sw, false) sw.write(')[_cobraToJava[_op]]0') return else done = false if left.type inherits Box # TODO: try skipping on requiring that the type is a Box leftBox = left.type to Box compareTo = leftBox.memberForName('compareTo') if compareTo and compareTo.isMethod and compareTo.resultType.isDescendantOf(.compiler.anyIntType) and left.type == right.type # need last condition to prevent someString.CompareTo(someChar) op = '.compareTo(' done = true if not done op = _cobraToJava[_op] # Write the Java code if op.length <= 2 _writeWrapped(sw, wrapLeft, left) sw.write(op) _writeWrapped(sw, wrapRight, right) else if op == '.equals(' if _op == 'NE', sw.write('!(') _writeWrapped(sw, wrapLeft, left) sw.write(op) _writeWrapped(sw, wrapRight, right) sw.write(')') if _op == 'NE', sw.write(')') else if op == '.compareTo(' _writeWrapped(sw, wrapLeft, left) sw.write(op) _writeWrapped(sw, wrapRight, right) sw.write(') [_cobraToJava[_op]] 0') else if op.endsWith('(') sw.write(op) _writeWrapped(sw, wrapLeft, left) sw.write(',') _writeWrapped(sw, wrapRight, right) sw.write(')') else print op throw FallThroughException(op) def _writeWrapped( sw as CurlyWriter, wrap as String, e as Expr) if wrap.length sw.write(wrap) e.writeJavaDef(sw) sw.write(')') else e.writeJavaDef(sw) class ChainedCompareExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') .writeJavaDef(sw) if parens, sw.write(')') def writeJavaDef(sw as CurlyWriter) is override sw.write('cobra.core./*CobraCoreInternal.*/CobraCore.chainedComparison(') _items[0].writeJavaDef(sw) itemIndex = 1 for operation in _operations sw.write(', "[operation]", ') _items[itemIndex].writeJavaDef(sw) itemIndex += 1 sw.write(')') class DotExpr is partial /# def needsContextCast as bool # in SharpGenerator r = base.needsContextCast if not r r = .left.type.isDynamic # Why isn't the base implementation sufficient? # Because for `d.getType` where `d` is dynamic, the type of `d.getType` is *not*. # It`s System.Type because `getType` is recognized as an Object method. # This would confuse the code generation into thinking that `d.getType` is a statically typed expression. return r #/ def _writeJavaDef(sw as CurlyWriter) is override if _left.receiverType.isDynamic # handle dynamic typing if _dotRightExpr.definition sw.write('(' + _dotRightExpr.type.javaRef + ')') if _right inherits MemberExpr sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.getPropertyValue(') _left.writeJavaDef(sw, not _left inherits DotExpr) sw.write(', ') sw.write(Utils.javaStringLiteralFor(_right.name)) # was .capitalized sw.write(')') else if _right inherits CallExpr sw.write('cobra.core.CobraImp.invokeMethod(') _left.writeJavaDef(sw, not _left inherits DotExpr) sw.write(', ') sw.write(Utils.javaStringLiteralFor(_right.name)) # was .capitalized for arg in _right.args sw.write(', ') arg.writeJavaDef(sw, false) sw.write(')') else throw FallThroughException(_right) else # Given Cobra "A.B.C()" where C is a class/type/struct, then the java needs to be "new A.B.C()" # (But stuff like this makes me wonder if the ASTs should be doing some transformations, like collapsing qualified types.) if _dotRightExpr.memberDefinition inherits BoxMember backing = _dotRightExpr.definition.sharedMethodBacking if backing # example: Cobra: c.isUpper C#: char.IsUpper(c) # also used for extension methods under .NET 2.0 sw.write(backing+'(') if not (_dotRightExpr.memberDefinition to BoxMember).sharedMethodBackingIsAlias # guard against Cobra "decimal.parse('5.0')" --> C# "Decimal.Parse(decimal, "5.0")" _left.writeJavaDef(sw, false) sep = ', ' else sep = '' if _right inherits CallExpr for arg in _right.args sw.write(sep) arg.writeJavaDefInContext(sw) sep = ', ' sw.write(')') return aliasedName = _dotRightExpr.definition.aliasedMethodBacking # handle static typing # don't write 'this' for shared members writeThis = true if _left inherits ThisLit writeThis = not _dotRightExpr.memberDefinition.isShared else if .curCodeMember inherits Initializer and _right inherits IDotRightExpr and (_right to IDotRightExpr).name == 'init' and _left inherits ThisOrBaseLit Stmt.inInitCall = true didSetInInitCall = true writeThis = false if writeThis if _left.needsContextCast _left.writeJavaDefInContext(sw) else _left.writeJavaDef(sw, not _left inherits DotExpr) sw.write('.') #print 'Dbg: dotExpr::', _left.toCobraSource, _right.toCobraSource if aliasedName sw.write(aliasedName+'()') # FTM assume no args else _right.writeJavaDef(sw, false) if didSetInInitCall Stmt.inInitCall = false def _writeJavaBreakdownItemsLeft(sw as CurlyWriter) is override _left.writeJavaBreakdownItems(sw) class InExpr is partial """ v in container""" def writeJavaDef(sw as CurlyWriter, parens as bool) is override v = true branch _op on 'NOTIN', sw.write('!') on 'IN', pass else, throw FallThroughException(_op) if .needsNilCheck # Constraints: # (1) Don't evaluate the left expression twice # (2) Don't evaluate the right expression if the left is nil # In general, don't cause side effects from extra evaluation. if .left inherits IdentifierExpr and (.left to IdentifierExpr).definition inherits IVar # If the left is a var, there is no danger in double evaluation causing side effects. # java: (someVar==null ? false : ) sw.write('(') .left.writeJavaDef(sw, false) sw.write('==null ? false : ') # may have to cast int? to int, for example if .left.type inherits NilableType _ and not .left.type.nonNil.isReference _ and .containsExpr # well this last condition doesn't work so well due to generic args like List.Contains(T item) # and not (((.containsExpr to DotExpr).dotRight.memberDefinition to Method).params[0].type inherits NilableType) # then need cast assert .left.type inherits NilableType .right.writeJavaDef(sw, false) sw.write('.contains(([.left.type.nonNil.javaRef])') .left.writeJavaDef(sw) sw.write(')') if v, sw.write('/* A */') else .writeInCheckJava(sw, false, v) if v, sw.write('/* B */') sw.write(')') else # cobra: left in right # java: cobra.core.CobraImp.inWithNullCheck(, delegate(__lh_value) { return .Contains(__lh_value); }) # use: static public bool inWithNullCheck(T a, Predicate predicate) # cobra: left in right # java: cobra.core.CobraImp.in(left, delegate() { return right; }) sw.write('cobra.core/*CobraCoreInternal*/.CobraImp.inWithNullCheck(') .left.writeJavaDef(sw, false) sw.write(', delegate([.left.type.javaRef] __lh_value_[.serialNum]) { return ') if .containsExpr # Do not write the .containsExpr here because it's argument to .contains in the # full left expression. Instead, we want to pass the argument passed to the delegate. .right.writeJavaDef(sw, false) sw.write('.contains(([.left.type.nonNil.javaRef]) __lh_value_[.serialNum]);') if v, sw.write('/* C */') else # No .containsExpr means we use CobraImp.In(a, b) for the delegate sw.write('cobra.core/*CobraCoreInternal*/.CobraImp.in(__lh_value_[.serialNum], ') .right.writeJavaDef(sw, false) if v, sw.write('/* D */') sw.write(' })') else .writeInCheckJava(sw, parens, v) if v, sw.write('/* E */') def writeInCheckJava(sw as CurlyWriter, parens as bool, v as bool) if .containsExpr .containsExpr.writeJavaDef(sw, parens) if v, sw.write('/* F */') else sw.write('cobra.core/*CobraCoreInternal*/.CobraImp.in(') .left.writeJavaDef(sw, false) sw.write(',') .right.writeJavaDef(sw, false) sw.write(')') if v, sw.write('/* G */') class InheritsExpr is partial def _writeJavaDef(sw as CurlyWriter) is override #_left.writeJavaDef(sw) #sw.write(' instanceof ') #_right.writeJavaDef(sw, false) # have to handle primitives ( bool, char, byte,short, int, long,float, double) specially # Compiler generates error with instanceof for non intersecting instance and Type so # instead use runtime instance lookup # instead of 'e instanceof T' becomes 'T.class.isInstance(e)' # Have to map primitives to wrappers cos autoboxing makes it wrong if _right inherits TypeExpr # primitives ctjref = _right.containedType.javaRef if ctjref == 'int', ctjref = 'Integer.class' if ctjref == 'char', ctjref = 'Character.class' if ctjref == 'short', ctjref = 'Short.class' if ctjref == 'long', ctjref = 'Long.class' if ctjref == 'boolean', ctjref = 'Boolean.class' if ctjref == 'byte', ctjref = 'Byte.class' if ctjref == 'float', ctjref = 'Float.class' if ctjref == 'double', ctjref = 'Double.class' sw.write(ctjref) isPrim = true if not isPrim _right.writeJavaDef(sw, false) sw.write('.isInstance(') _left.writeJavaDef(sw) sw.write(' )') class ToExpr is partial /# def needsContextCast as bool is override if .type.isDynamic, return base.needsContextCast else, return false # the user is already casting, so go with their cast #/ def _writeJavaDef(sw as CurlyWriter) is override rightType = _potentialTypeExpr.potentialType to ! # it's not nil or _bindImp would have thrown an error if rightType is .compiler.passThroughType _left.writeJavaDef(sw) return sw.write('(') _right.writeJavaDef(sw, false) sw.write(')') if not rightType inherits NilableType and rightType.isReference and .compiler.options.boolValue('include-nil-checks') sw.write('cobra.core./*CobraCoreInternal.*/CobraImp.checkNonNil([.javaThis], [Utils.javaStringLiteralFor(_left.toCobraSource)], ') _left.writeJavaDef(sw, false) sw.write(', [.javaSourceSiteArgs])') else _left.writeJavaDef(sw) class ToQExpr is partial def _writeJavaDef(sw as CurlyWriter) is override # x to? int --> ((x is int || x is int?) ? (int?)x : (int?)null) # x to? int? --> ((x is int || x is int?) ? (int?)x : (int?)null) # ^ but this would cause multiple evaluation of x, so... # x to? int? --> (int?)CobraCoreInternal.CobraImp.ToOrNil(x) # x to? string --> (x as String) # x to? string? --> (x as String) right = .right t = right.definition to IType t = t.nonNil typeJavaRef = t.javaRef #if t.isReference # sw.write('(') # .left.writeJavaDef(sw) # sw.write(') as [typeJavaRef]') #else # sw.write('[typeJavaRef])CobraCoreInternal.CobraImp.ToOrNil<[typeJavaRef]>(') # .left.writeJavaDef(sw) sw.write('[typeJavaRef])cobra.core.CobraImp.toOrNil<[typeJavaRef]>(') .left.writeJavaDef(sw) sw.write(')') class CoalesceExpr is partial def _writeJavaDef(sw as CurlyWriter) is override # (L != null ? L : R ) sw.write('((') _left.writeJavaDef(sw) sw.write(') != null ? ') _left.writeJavaDef(sw) sw.write(' : ') _right.writeJavaDef(sw) class CoalesceAssignExpr is partial def _writeJavaDef(sw as CurlyWriter) is override # L = (L != null ? L : R ) _left.writeJavaDef(sw) sw.write(' = ') sw.write('((') _left.writeJavaDef(sw) sw.write(') != null ? ') _left.writeJavaDef(sw) sw.write(' : ') _right.writeJavaDef(sw) class MemberExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override assert .superNode inherits DotExpr if _definition inherits BoxMember, name = _definition.binaryName if name is nil, name = _name #print 'Dbg: MemberExpr::', name if _definition inherits Property lValue = _setPropInAssignExpr if lValue # TODO chk for use of 'is' prefixed props xet = 'set' sw.write(xet+name) lValue.isJavaSetProperty = true # flag assignExpr for remainder of setProp handling else xet = 'get' sw.write(xet+name) sw.write('()') else sw.write(name) if _definition and (_definition.isMethod or _name=='toString') and not _isReference sw.write('()') def _setPropInAssignExpr as AssignExpr? """ Return ancestor assignExpr if member is the lvalue in an AssignExpr, nil otherwise.""" if not (.superNode and .superNode inherits BinaryOpExpr) return nil bsn0 = this to INode bsn = .binarySuperNode while true if bsn inherits AssignExpr and bsn0 is bsn.left return bsn to AssignExpr if not (bsn.superNode and bsn.superNode inherits BinaryOpExpr) return nil bsn0 = bsn bsn = bsn.binarySuperNode def writeJavaBreakdownItems(sw as CurlyWriter) is override pass class OldExpr is partial var _javaVarName as String? pro javaVarName from var def writeJavaAssignment(sw as CurlyWriter) require .didBindImp .javaVarName .type body sw.write('[.type.javaRef] [_javaVarName] = ') _expr.writeJavaDef(sw) sw.write(';\n') def writeJavaDef(sw as CurlyWriter, parens as bool) is override assert _javaVarName # this gets called when generating the `ensure` code sw.write(_javaVarName) class PostCallExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if _helperMethod sw.write(_helperMethod.name + '(') sep = '' for arg in _args sw.write(sep) if arg inherits AssignExpr arg = arg.right arg.writeJavaDefInContext(sw, false) sep = ',' sw.write(')') else if parens, sw.write('(') expr = _expr isMethodSig = false isDynamic = false if expr inherits TypeExpr if expr.containedType inherits ArrayType # arrays sw.write('new ') sw.write((expr.containedType to ArrayType).theWrappedType.javaRef) sw.write(r'[') .writeJavaArgs(sw) sw.write(r']') else sw.write('new ') expr.writeJavaDef(sw) sw.write('(') .writeJavaArgs(sw) sw.write(')') else if expr inherits IdentifierExpr if expr.isTypeReference sw.write('new ') expr.writeJavaDef(sw) sw.write('(') .writeJavaArgs(sw) sw.write(')') else if expr.receiverType inherits GenericParam # TODO: shouldn't expr.isTypeReference above have caught this? sw.write('new [expr.receiverType.javaRef](') .writeJavaArgs(sw) sw.write(')') else if expr.isKindOfNonNil(.compiler.delegateType) isMethodSig = true else if expr.type.isSystemTypeClass or _type.isDynamic isDynamic = true else assert false, expr else if expr inherits IndexExpr if expr.isKindOfNonNil(.compiler.delegateType) isMethodSig = true else if expr.type.isSystemTypeClass or _type.isDynamic isDynamic = true else assert false, expr else assert false, expr if isMethodSig expr.writeJavaDef(sw, false) sw.write('.invoke(') .writeJavaArgs(sw) sw.write(')') else if isDynamic defi = expr.definition assert not defi inherits Box, expr # TODO: just curious what = if(defi inherits IType, '([defi.javaName].class)', defi.javaName to String) whatType = defi.javaName to String if defi inherits IVar if defi.type.isDynamic what = '(java.lang.Class)' + what #sw.write('System.Activator.CreateInstance([what]') if _args.count # maybe sw.write('([what].getConstructor(') .writeJavaArgs(sw, ', ') sw.write(')).newInstance(') .writeJavaArgs(sw, ', ') sw.write(')') else sw.write('[what].newInstance()') # maybe better do this #sw.write('cobra.core.CobraImp.newInstance<[whatType]>([what],') #.writeJavaArgs(sw, ', ') #sw.write(')') if parens, sw.write(')') def writeJavaArgs(sw as CurlyWriter) .writeJavaArgs(sw, '') def writeJavaArgs(sw as CurlyWriter, sep as String) for arg in _args sw.write(sep) if arg inherits AssignExpr # Named parameter detail = 'For java at the moment you will need to recode the named parameter call to a call using positional arguments.' .compiler.warning(this, 'Java does not support named parameters in calls so these are are not available with this backend. [detail]') # TODO: Rewrite call to positional parameter form using name(s) given ## cant do arg.left.writeSharpDef as binding invariants are incomplete #sw.write((arg.left to IdentifierExpr).name) #sw.write(':') # C# form is name:value arg.right.writeJavaDef(sw) else arg.writeJavaDefInContext(sw, false) sep = ',' def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) sw.write(', +1') _expr.writeJavaBreakdownItems(sw) for expr in _args expr.writeJavaBreakdownItems(sw) sw.write(', -1') class RefExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if parens sw.write('(') _expr.writeJavaDef(sw, false) if parens sw.write(')') def writeJavaBreakdownItems(sw as CurlyWriter) base.writeJavaBreakdownItems(sw) # TODO class SharpExpr # should become JavaExpr or BackEndExpr is partial def writeJavaDef(sw as CurlyWriter, parens as bool) is override if parens, sw.write('(') if _sharpSource sw.write(_sharpSource) else sw.write(_expr.token.value) if parens, sw.write(')') ## ## Temporary ## # uu class AssemblyDecl is partial def writeJavaTestInvocation(sw as CurlyWriter) pass class Extension is partial get javaKeyWord as String is override return 'TODO_Extension' get javaInvariantVisibility as String is override return '' class Utils is partial shared def javaStringLiteralFor(args as vari dynamic) as String sb = StringBuilder() for arg in args sb.append(Utils.sharpStringLiteralFor(arg to String)) return sb.toString class ThisOrBaseLit is partial get asJava as String is override return 'this' class ContractPart is partial def writeJavaMethod(args as vari dynamic) pass class JavaBackEndUtils # copied fm SharpGenerator and keywds corrected: chg entire method Vars.cobra:AbstractLocalVar,_init shared var _backEndKeyWordList = 'assert boolean break byte case catch char class const continue default do double else extends false final finally float for goto if implements import instanceof int interface long native new null package private protected public return short static strictfp super switch synchronized this throw throws transient true try void volatile while'.split # C# 'abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual void volatile while'.split var _backEndKeyWordSet = Set() def isBackEndKeyWord(word as String) as bool """ Returns true if the given word is a keyword in Java6 """ require word.length test assert .isBackEndKeyWord('byte') assert .isBackEndKeyWord('if') assert .isBackEndKeyWord('while') assert not .isBackEndKeyWord('total') body if _backEndKeyWordSet.count == 0 for word in _backEndKeyWordList _backEndKeyWordSet.add(word) return word in _backEndKeyWordSet def backEndNameForLocalVarName(name as String) as String if .isBackEndKeyWord(name) return '$' + name # Java supports $ as a prefix return name