class LLVMAsm(Lang): source_ext = '.ll' compiler = Program( 'clang-8', opts=[ # Statically compile the program. '-x', 'ir', # Force the target language to be LLVM AS. '-Wno-override-module' # Because the source file might lack the target module, # we don't want clang to complain about it. ]) interpreter = Program('lli-8') # JIT compiler. reference_source = r"""
class BrainfuckToC(Lang, register=False): source_ext = '.bf' extra_binaries = {'esotope': Program('esotope-bfc', version_opt=None)} compiler = Program('python2', env={'PYTHONPATH': '/usr/lib/python2.7/site-packages'}, opts=['-S', extra_binaries['esotope'].cmd, '-v', '-fc']) def compile_opt_out(self, output): # there is no way to specify an output file, use stderr return [] def read_compiled(self, path, isolator): return isolator.stdout.encode()
class VisualBasic(Lang): source_ext = '.vb' compiler = Program('vbnc', opts=['/optimize+'], version_opt='/help') interpreter = Program('mono') reference_source = r''' Imports System Public Module modmain Sub Main() Console.WriteLine ("42") End Sub End Module ''' def compile_opt_out(self, output): return ['/out:' + output]
class CSharp(Lang, name="C#"): source_ext = '.cs' compiler = Program('mcs', opts=['-optimize+']) interpreter = Program('mono') reference_source = r''' using System; class Program { public static void Main() { Console.WriteLine(42); } } ''' def compile_opt_out(self, output): return ['-out:' + output]
class Copy(Lang, register=False): source_ext = '.a' compiler = Program('cp') def compile_command(self, source, output): return [ self.compiler.cmd, self.filter_box_prefix(source), self.filter_box_prefix(output) ]
class Pascal(Lang): source_ext = '.pas' compiler = Program('fpc', opts=['-XD', '-Fainitc'], version_opt='-h', version_lines=1) reference_source = r''' program main; begin Writeln(42); end. ''' def compile_opt_out(self, output): return ['-o' + output]
class D(Lang): source_ext = '.d' compiler = Program('dmd') allowed_dirs = ['/etc'] reference_source = r''' void main() { import std.stdio: writeln; writeln("42"); } ''' def compile_opt_out(self, output): # '-of' and its value as two distinct arguments is illegal (go figure) return ['-of' + output]
def test_version(): v = Program('cp').version() assert '.' in v
def test_no_version(): echo = Program('echo', version_opt=None) assert echo._version() is None assert echo.long_version() is None assert echo.long_version() is None
class Prolog(Lang): source_ext = '.pl' interpreter = Program('swipl', opts=['--quiet', '-t', 'halt'], version_opt='--version') reference_source = r":- write('42\n')."
class Haskell(Lang): source_ext = '.hs' compiler = Program('ghc', opts=['-dynamic', '-O2']) reference_source = r'module Main where main = putStrLn "42"'
class Lua(Lang): source_ext = '.lua' interpreter = Program('luajit', version_opt='-v') reference_source = r'print("42")'
class Perl(Lang): source_ext = '.pl' interpreter = Program('perl') reference_source = r'print "42\n";'
class BadCompiler(Lang, register=False): source_ext = '.a' compiler = Program('sh', opts=['-c', 'echo BadCompiler is bad >&2']) def compile_command(self, source, output): return [self.compiler.cmd, *self.compiler.opts]
class Ada(Lang): source_ext = '.adb' compiler = Program('gnatmake', opts=['-f']) reference_source = r'''
class Ruby(Lang): source_ext = '.rb' interpreter = Program('ruby') reference_source = r'puts "42"'
class OCaml(Lang): source_ext = '.ml' compiler = Program('ocamlopt', opts=['-w', 'A'], version_opt='-v') reference_source = r'print_int 42; print_string "\n";'
class Go(Lang): source_ext = '.go' compiler = Program('go', opts=['build', '-buildmode=exe'], version_opt='version') reference_source = r'''
class InterpretedLang(Lang): interpreter = Program('echo')
class CompiledLang(Lang): compiler = Program('echo')
def test_long_version(): v = Program('cp').long_version() assert v.startswith('cp')
class C(Lang): source_ext = '.c' compiler = Program('gcc', opts=['-std=c11', '-Wall', '-Wextra', '-O2', '-lm']) reference_source = r'''
class Rust(Lang): source_ext = '.rs' compiler = Program('rustc', opts=['-W', 'warnings', '-O']) reference_source = r'fn main() { println!("42"); }'
class Go(Lang): source_ext = '.go' compiler = Program('go', opts=['build', '-buildmode=exe'], version_opt='version', env={'GOCACHE':'/box/.gocache'}) reference_source = r'''
class Rust(Lang): source_ext = '.rs' compiler = Program('rustc', opts=['-W', 'warnings', '-O']) reference_source = r'''\
class CXX(Lang, name="C++"): source_ext = '.cc' compiler = Program('g++', opts=['-std=c++17', '-Wall', '-Wextra', '-O2']) reference_source = r'''
class Java(Lang): source_ext = '.java' compiled_ext = '.class' compiler = Program('javac', opts=['-encoding', 'UTF-8'], env={'LANG': 'en_US.UTF-8'}, version_opt='-version') interpreter = Program('java', version_opt='-version') # /usr/lib/jvm/java-8-openjdk/jre/lib/amd64/jvm.cfg links to # /etc/java-8-openjdk/amd64/jvm.cfg allowed_dirs = ['/etc/java-8-openjdk'] # ensure we can parse the javac(1) stderr extra_binaries = {'disassembler': Program('javap', version_opt='-version')} reference_source = ''' class MyπClass { static int fortytwo() { return 42; } static class Subclassé { public static void main(String notMe) {} final static public void main(String args[]) { System.out.println(MyπClass.fortytwo()); } } } ''' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # use an illegal class name so that javac(1) will spit out the actual # class named used in the source self.class_name = '1337' # we give priority to the public class, if any, so keep a flag if we # found such a public class self.found_public = False # Don't even try to cgroup the java process: # http://stackoverflow.com/questions/19910468 # https://bugs.launchpad.net/ubuntu/+source/openjdk-7/+bug/1241926 # https://bugs.openjdk.java.net/browse/JDK-8071445 # http://bugs.java.com/view_bug.do?bug_id=8043516 # Instead, pass the memory limit as the maximum heap size of the # runtime. try: self.heapsize = self.opts['execute'].pop('mem') except KeyError: self.heapsize = None def compile_opt_out(self, output): # javac has no output directive, file name is class name return [] async def compile(self): # try to compile with default class name (Main) retcode, info, binary = await super().compile() if retcode != 0: # error: public class name is not '1337' as it's an illegal name, # so find what it actually is try: javac_stderr = info['stderr'].decode() except UnicodeDecodeError: # noqa raise RuntimeError( "could not decode javac stderr to find class name") match = RE_WRONG_FILENAME_ERROR.search(javac_stderr) if match: self.found_public = True self.class_name = match.group(1) # retry with new name retcode, info, binary = await super().compile() return (retcode, info, binary) def source_filename(self): return self.class_name + self.source_ext def execute_filename(self): # return eg. Main.class return self.class_name + self.compiled_ext def execute_command(self, output): cmd = [self.interpreter.cmd] + self.interpreter.opts # Use the memory limit as a maximum heap size if self.heapsize is not None: cmd.append(f'-Xmx{self.heapsize}k') # foo/Bar.class is run with $ java -cp foo Bar cmd += ['-cp', str(Path(self.filter_box_prefix(output)).parent), self.class_name] return cmd def find_class_having_main(self, classes): for file in classes: # run javap(1) with type signatures try: stdout = subprocess.check_output( [self.extra_binaries['disassembler'].cmd, '-s', str(file)], stderr=subprocess.DEVNULL, env=self.compiler.env) except subprocess.SubprocessError: # noqa continue # iterate on lines to find p s v main() signature and then # its descriptor on the line below; we don't rely on the type # from the signature, because it could be String[], String... or # some other syntax I'm not even aware of lines = iter(stdout.decode().split('\n')) for line in lines: line = line.lstrip() if line.startswith('public static') and 'void main(' in line: if next(lines).lstrip() == PSVMAIN_DESCRIPTOR: return file.stem def read_compiled(self, path, isolator): # in case of multiple or nested classes, multiple .class files are # generated by javac classes = list(isolator.path.glob('*.class')) files = [(file.name, file.open('rb').read()) for file in classes] if not self.found_public: # the main() may be anywhere, so run javap(1) on all .class new_class_name = self.find_class_having_main(classes) if new_class_name: self.class_name = new_class_name return files def write_binary(self, path, binary): # see read_compiled(), we need to write back all .class files # but give only the main class name (execute_filename()) to java(1) for file, data in binary: with (path / file).open('wb') as c: c.write(data) return path / self.execute_filename()
class PHP(Lang): source_ext = '.php' interpreter = Program('php') reference_source = r'<?php echo "42\n";'
class Python(Lang): source_ext = '.py' interpreter = Program('python3', opts=['-S']) reference_source = r'print("42")'
class Javascript(Lang): source_ext = '.js' interpreter = Program('node') reference_source = r"process.stdout.write('42\n');"