PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/translator/jvm/genjvm.py

https://bitbucket.org/kcr/pypy
Python | 303 lines | 276 code | 12 blank | 15 comment | 0 complexity | e575e544177f1088454b992ffc7b8351 MD5 | raw file
Possible License(s): Apache-2.0
  1. """
  2. Backend for the JVM.
  3. """
  4. from __future__ import with_statement
  5. import os
  6. import re
  7. import subprocess
  8. import sys
  9. import py
  10. from rpython.rtyper.ootypesystem import ootype
  11. from rpython.tool.udir import udir
  12. from rpython.translator.backendopt.all import backend_optimizations
  13. from rpython.translator.backendopt.checkvirtual import check_virtual_methods
  14. from rpython.translator.oosupport.genoo import GenOO
  15. from rpython.translator.translator import TranslationContext
  16. from rpython.translator.jvm.constant import (
  17. JVMConstantGenerator, JVMStaticMethodConst, JVMCustomDictConst,
  18. JVMWeakRefConst)
  19. from rpython.translator.jvm.database import Database
  20. from rpython.translator.jvm.generator import JasminGenerator
  21. from rpython.translator.jvm.log import log
  22. from rpython.translator.jvm.node import EntryPoint, Function
  23. from rpython.translator.jvm.opcodes import opcodes
  24. from rpython.translator.jvm.option import getoption
  25. from rpython.translator.jvm.prebuiltnodes import create_interlink_node
  26. MIN_JAVA_VERSION = '1.6.0'
  27. class JvmError(Exception):
  28. """ Indicates an error occurred in JVM backend """
  29. def pretty_print(self):
  30. print str(self)
  31. pass
  32. class JvmSubprogramError(JvmError):
  33. """ Indicates an error occurred running some program """
  34. def __init__(self, res, args, stdout, stderr):
  35. self.res = res
  36. self.args = args
  37. self.stdout = stdout
  38. self.stderr = stderr
  39. def __str__(self):
  40. return "Error code %d running %s" % (self.res, repr(self.args))
  41. def pretty_print(self):
  42. JvmError.pretty_print(self)
  43. print "vvv Stdout vvv\n"
  44. print self.stdout
  45. print "vvv Stderr vvv\n"
  46. print self.stderr
  47. class JvmGeneratedSource(object):
  48. """
  49. An object which represents the generated sources. Contains methods
  50. to find out where they are located, to compile them, and to execute
  51. them.
  52. For those interested in the location of the files, the following
  53. attributes exist:
  54. tmpdir --- root directory from which all files can be found (py.path obj)
  55. javadir --- the directory containing *.java (py.path obj)
  56. classdir --- the directory where *.class will be generated (py.path obj)
  57. package --- a string with the name of the package (i.e., 'java.util')
  58. The following attributes also exist to find the state of the sources:
  59. compiled --- True once the sources have been compiled successfully
  60. """
  61. _cached = None
  62. def __init__(self, tmpdir, package):
  63. """
  64. 'tmpdir' --- the base directory where the sources are located
  65. 'package' --- the package the sources are in; if package is pypy.jvm,
  66. then we expect to find the sources in $tmpdir/pypy/jvm
  67. 'jfiles' --- list of files we need to run jasmin on
  68. """
  69. self.tmpdir = tmpdir
  70. self.package = package
  71. self.compiled = False
  72. self.jasmin_files = None
  73. # Determine various paths:
  74. self.thisdir = py.path.local(__file__).dirpath()
  75. self.rootdir = self.thisdir.join('src')
  76. self.srcdir = self.rootdir.join('pypy')
  77. self.jnajar = self.rootdir.join('jna.jar')
  78. self.jasminjar = self.rootdir.join('jasmin.jar')
  79. # Compute directory where .j files are
  80. self.javadir = self.tmpdir
  81. for subpkg in package.split('.'):
  82. self.javadir = self.javadir.join(subpkg)
  83. # Compute directory where .class files should go
  84. self.classdir = self.javadir
  85. def set_jasmin_files(self, jfiles):
  86. self.jasmin_files = jfiles
  87. def _invoke(self, args, allow_stderr):
  88. import sys
  89. if sys.platform == 'nt':
  90. shell = True
  91. else:
  92. shell = False
  93. subp = subprocess.Popen(
  94. args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
  95. shell=shell, universal_newlines=True)
  96. stdout, stderr = subp.communicate()
  97. res = subp.wait()
  98. if res or (not allow_stderr and stderr):
  99. raise JvmSubprogramError(res, args, stdout, stderr)
  100. return stdout, stderr, res
  101. def _compile_helper(self):
  102. # HACK: compile the Java helper classes. Should eventually
  103. # use rte.py
  104. if JvmGeneratedSource._cached == self.classdir:
  105. return
  106. log.red('Compiling java classes')
  107. javafiles = self.srcdir.listdir('*.java')
  108. javasrcs = [str(jf) for jf in javafiles]
  109. self._invoke([getoption('javac'),
  110. '-nowarn',
  111. '-d', str(self.classdir),
  112. '-classpath', str(self.jnajar),
  113. ] + javasrcs,
  114. True)
  115. # NOTE if you are trying to add more caching: some .java files
  116. # compile to several .class files of various names.
  117. JvmGeneratedSource._cached = self.classdir
  118. def compile(self):
  119. """
  120. Compiles the .java sources into .class files, ready for execution.
  121. """
  122. jascmd = [
  123. getoption('java'),
  124. '-Djava.awt.headless=true',
  125. '-jar', str(self.jasminjar),
  126. '-g',
  127. '-d',
  128. str(self.javadir)]
  129. def split_list(files):
  130. "Split the files list into manageable pieces"
  131. # - On Windows 2000, commands in .bat are limited to 2047 chars.
  132. # - But the 'jasmin' script contains a line like
  133. # path_to_jre/java -jar path_to_jasmin/jasmin.jar $*
  134. # So we limit the length of arguments files to:
  135. MAXLINE = 1500
  136. chunk = []
  137. chunklen = 0
  138. for f in files:
  139. # Account for the space between items
  140. chunklen += len(f) + 1
  141. if chunklen > MAXLINE:
  142. yield chunk
  143. chunk = []
  144. chunklen = len(f)
  145. chunk.append(f)
  146. if chunk:
  147. yield chunk
  148. for files in split_list(self.jasmin_files):
  149. #print "Invoking jasmin on %s" % files
  150. self._invoke(jascmd + files, False)
  151. #print "... completed!"
  152. self.compiled = True
  153. self._compile_helper()
  154. def _make_str(self, a):
  155. if isinstance(a, ootype._string):
  156. return a._str
  157. return str(a)
  158. def execute(self, args):
  159. """
  160. Executes the compiled sources in a separate process. Returns the
  161. output as a string. The 'args' are provided as arguments,
  162. and will be converted to strings.
  163. """
  164. assert self.compiled
  165. strargs = [self._make_str(a) for a in args]
  166. cmd = [getoption('java'),
  167. '-Xmx256M', # increase the heapsize so the microbenchmarks run
  168. '-Djava.awt.headless=true',
  169. '-cp',
  170. str(self.javadir)+os.pathsep+str(self.jnajar),
  171. self.package+".Main"] + strargs
  172. print "Invoking java to run the code"
  173. stdout, stderr, retval = self._invoke(cmd, True)
  174. print "...done!"
  175. sys.stderr.write(stderr)
  176. return stdout, stderr, retval
  177. def generate_source_for_function(func, annotation, backendopt=False):
  178. """
  179. Given a Python function and some hints about its argument types,
  180. generates JVM sources that call it and print the result. Returns
  181. the JvmGeneratedSource object.
  182. """
  183. if hasattr(func, 'im_func'):
  184. func = func.im_func
  185. t = TranslationContext()
  186. ann = t.buildannotator()
  187. ann.build_types(func, annotation)
  188. t.buildrtyper(type_system="ootype").specialize()
  189. if backendopt:
  190. check_virtual_methods(ootype.ROOT)
  191. backend_optimizations(t)
  192. main_graph = t.graphs[0]
  193. if getoption('view'): t.view()
  194. if getoption('wd'): tmpdir = py.path.local('.')
  195. else: tmpdir = udir
  196. jvm = GenJvm(tmpdir, t, EntryPoint(main_graph, True, True))
  197. return jvm.generate_source()
  198. _missing_support_programs = None
  199. def detect_missing_support_programs():
  200. global _missing_support_programs
  201. if _missing_support_programs is not None:
  202. if _missing_support_programs:
  203. py.test.skip(_missing_support_programs)
  204. return
  205. def missing(msg):
  206. global _missing_support_programs
  207. _missing_support_programs = msg
  208. py.test.skip(msg)
  209. for cmd in 'javac', 'java':
  210. if py.path.local.sysfind(getoption(cmd)) is None:
  211. missing("%s is not on your path" % cmd)
  212. if not _check_java_version(MIN_JAVA_VERSION):
  213. missing('Minimum of Java %s required' % MIN_JAVA_VERSION)
  214. _missing_support_programs = False
  215. def _check_java_version(version):
  216. """Determine if java meets the specified version"""
  217. cmd = [getoption('java'), '-version']
  218. with open(os.devnull, 'w') as devnull:
  219. stderr = subprocess.Popen(cmd, stdout=devnull,
  220. stderr=subprocess.PIPE).communicate()[1]
  221. search = re.search('[\.0-9]+', stderr)
  222. return search and search.group() >= version
  223. class GenJvm(GenOO):
  224. """ Master object which guides the JVM backend along. To use,
  225. create with appropriate parameters and then invoke
  226. generate_source(). *You can not use one of these objects more than
  227. once.* """
  228. TypeSystem = lambda X, db: db # TypeSystem and Database are the same object
  229. Function = Function
  230. Database = Database
  231. opcodes = opcodes
  232. log = log
  233. ConstantGenerator = JVMConstantGenerator
  234. CustomDictConst = JVMCustomDictConst
  235. StaticMethodConst = JVMStaticMethodConst
  236. WeakRefConst = JVMWeakRefConst
  237. def __init__(self, tmpdir, translator, entrypoint):
  238. """
  239. 'tmpdir' --- where the generated files will go. In fact, we will
  240. put our binaries into the directory pypy/jvm
  241. 'translator' --- a TranslationContext object
  242. 'entrypoint' --- if supplied, an object with a render method
  243. """
  244. GenOO.__init__(self, tmpdir, translator, entrypoint)
  245. self.jvmsrc = JvmGeneratedSource(tmpdir, getoption('package'))
  246. def append_prebuilt_nodes(self):
  247. create_interlink_node(self.db)
  248. def generate_source(self):
  249. """ Creates the sources, and returns a JvmGeneratedSource object
  250. for manipulating them """
  251. GenOO.generate_source(self)
  252. self.jvmsrc.set_jasmin_files(self.db.jasmin_files())
  253. return self.jvmsrc
  254. def create_assembler(self):
  255. """ Creates and returns a Generator object according to the
  256. configuration. Right now, however, there is only one kind of
  257. generator: JasminGenerator """
  258. return JasminGenerator(self.db, self.jvmsrc.javadir)