[pypy-svn] r33717 - in pypy/dist/pypy/translator/cli: . src test
antocuni at codespeak.net
antocuni at codespeak.net
Wed Oct 25 14:05:53 CEST 2006
Author: antocuni
Date: Wed Oct 25 14:05:51 2006
New Revision: 33717
Added:
pypy/dist/pypy/translator/cli/query.py (contents, props changed)
pypy/dist/pypy/translator/cli/src/query.cs
pypy/dist/pypy/translator/cli/test/test_query.py (contents, props changed)
Removed:
pypy/dist/pypy/translator/cli/dotnetdb.py
Modified:
pypy/dist/pypy/translator/cli/dotnet.py
pypy/dist/pypy/translator/cli/function.py
pypy/dist/pypy/translator/cli/gencli.py
pypy/dist/pypy/translator/cli/rte.py
pypy/dist/pypy/translator/cli/support.py
pypy/dist/pypy/translator/cli/test/test_dotnet.py
Log:
Automatically discover .NET type informations when requested by
executing an external .NET utility.
The type descriptions are cached and saved in the
cli/query-descriptions file, so they don't need to be re-queried again
next time. I coudn't set svn:ignore for query-descriptions because of
a strange svn problem.
Modified: pypy/dist/pypy/translator/cli/dotnet.py
==============================================================================
--- pypy/dist/pypy/translator/cli/dotnet.py (original)
+++ pypy/dist/pypy/translator/cli/dotnet.py Wed Oct 25 14:05:51 2006
@@ -3,7 +3,7 @@
from pypy.rpython.ootypesystem.ootype import meth, overload, Meth, StaticMethod
from pypy.annotation import model as annmodel
from pypy.rpython.rmodel import Repr
-from pypy.translator.cli.support import CLR
+from pypy.translator.cli.support import PythonNet
## Annotation model
@@ -124,9 +124,8 @@
def __init__(self, INSTANCE, static_methods):
self._name = INSTANCE._name
self._INSTANCE = INSTANCE
- self._static_methods = static_methods
- for name, meth in static_methods.iteritems():
- meth._set_attrs(self, name)
+ self._static_methods = {}
+ self._add_methods(static_methods)
def __repr__(self):
return '<%s>' % (self,)
@@ -134,6 +133,11 @@
def __str__(self):
return '%s(%s)' % (self.__class__.__name__, self._INSTANCE._name)
+ def _add_methods(self, methods):
+ self._static_methods.update(methods)
+ for name, meth in methods.iteritems():
+ meth._set_attrs(self, name)
+
def _lookup(self, meth_name, ARGS):
meth = self._static_methods[meth_name]
return meth._get_desc(ARGS)
@@ -145,21 +149,21 @@
def _load_class(self):
names = self._INSTANCE._namespace.split('.')
names.append(self._INSTANCE._classname)
- obj = CLR
+ obj = PythonNet
for name in names:
obj = getattr(obj, name)
- self._CLR_class = obj
+ self._PythonNet_class = obj
def __getattr__(self, attr):
if attr in self._static_methods:
self._load_class()
- return getattr(self._CLR_class, attr)
+ return getattr(self._PythonNet_class, attr)
else:
raise AttributeError
def __call__(self, *args):
self._load_class()
- return self._CLR_class(*args)
+ return self._PythonNet_class(*args)
class Entry(ExtRegistryEntry):
@@ -169,9 +173,21 @@
return SomeCliClass()
-
class CliNamespace(object):
def __init__(self, name):
self._name = name
+ def __fullname(self, name):
+ if self._name is None:
+ return name
+ else:
+ return '%s.%s' % (self._name, name)
+
+ def __getattr__(self, attr):
+ from pypy.translator.cli.query import load_class_or_namespace
+ name = self.__fullname(attr)
+ load_class_or_namespace(name)
+ assert attr in self.__dict__
+ return getattr(self, attr)
+CLR = CliNamespace(None)
Modified: pypy/dist/pypy/translator/cli/function.py
==============================================================================
--- pypy/dist/pypy/translator/cli/function.py (original)
+++ pypy/dist/pypy/translator/cli/function.py Wed Oct 25 14:05:51 2006
@@ -13,11 +13,7 @@
from pypy.translator.cli.metavm import InstructionList, Generator
from pypy.translator.cli.node import Node
from pypy.translator.cli.class_ import Class
-
-from pypy.tool.ansi_print import ansi_log
-import py
-log = py.log.Producer("cli")
-py.log.setconsumer("cli", ansi_log)
+from pypy.translator.cli.support import log
class Function(Node, Generator):
def __init__(self, db, graph, name = None, is_method = False, is_entrypoint = False):
Modified: pypy/dist/pypy/translator/cli/gencli.py
==============================================================================
--- pypy/dist/pypy/translator/cli/gencli.py (original)
+++ pypy/dist/pypy/translator/cli/gencli.py Wed Oct 25 14:05:51 2006
@@ -16,6 +16,7 @@
from pypy.translator.cli.support import Tee
from pypy.translator.cli.prebuiltnodes import get_prebuilt_nodes
from pypy.translator.cli.stackopt import StackOptGenerator
+from pypy.translator.cli import query
try:
set
@@ -66,6 +67,7 @@
self.db.gen_constants(self.ilasm)
out.close()
self.db.const_count.dump(self.const_stat)
+ query.savedesc()
return self.tmpfile.strpath
def gen_entrypoint(self):
Added: pypy/dist/pypy/translator/cli/query.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/cli/query.py Wed Oct 25 14:05:51 2006
@@ -0,0 +1,163 @@
+import cPickle as pickle
+import os.path
+from py.compat import subprocess
+from pypy.rpython.ootypesystem import ootype
+from pypy.translator.cli.rte import Query
+from pypy.translator.cli.sdk import SDK
+from pypy.translator.cli.support import log
+from pypy.translator.cli.dotnet import CLR, CliNamespace, CliClass,\
+ NativeInstance, _overloaded_static_meth, _static_meth
+
+ClassCache = {}
+OOTypeCache = {}
+Descriptions = {}
+
+class Dummy: pass
+fake_root = Dummy()
+fake_root._INSTANCE = ootype.ROOT
+ClassCache['ROOT'] = fake_root
+ClassCache['System.Array'] = fake_root
+del fake_root
+del Dummy
+
+def _descfilename(filename):
+ if filename is None:
+ curdir = os.path.dirname(__file__)
+ return os.path.join(curdir, 'query-descriptions')
+ else:
+ return filename
+
+def savedesc(filename=None):
+ f = open(_descfilename(filename), 'wb')
+ pickle.dump(Descriptions, f, protocol=-1)
+ f.close()
+
+def loaddesc(filename=None):
+ filename = _descfilename(filename)
+ if not os.path.exists(filename):
+ return
+ f = open(filename, 'rb')
+ try:
+ newdesc = pickle.load(f)
+ except pickle.UnpicklingError:
+ log.WARNING('query-descriptions file exits, but failed to unpickle')
+ else:
+ Descriptions.clear()
+ Descriptions.update(newdesc)
+
+def getattr_ex(target, attr):
+ parts = attr.split('.')
+ for part in parts:
+ target = getattr(target, part)
+ return target
+
+def setattr_ex(target, attr, value):
+ if '.' in attr:
+ namespace, attr = attr.rsplit('.', 1)
+ target = getattr_ex(target, namespace)
+ setattr(target, attr, value)
+
+def load_class_or_namespace(name):
+ try:
+ desc = Descriptions[name]
+ except KeyError:
+ desc = query_description(name)
+ Descriptions[name] = desc
+ setattr_ex(CLR, name, desc.build())
+
+def query_description(name):
+ log.query('Loading description for %s' % name)
+ arglist = SDK.runtime() + [Query.get(), name]
+ query = subprocess.Popen(arglist, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = query.communicate()
+ retval = query.wait()
+ if retval == 0:
+ cls = ClassDesc()
+ exec stdout in cls.__dict__
+ del cls.__dict__['__builtins__']
+ return cls
+ elif retval == 1:
+ raise RuntimeError, 'query.exe failed with this message:\n%s' % stderr
+ elif retval == 2:
+ # can't load type, assume it's a namespace
+ return NamespaceDesc(name)
+
+def load_class_maybe(name):
+ if name not in ClassCache:
+ load_class_or_namespace(name)
+
+
+class Desc:
+ def build(self):
+ raise NotImplementedError
+
+ def __eq__(self, other):
+ return self.__dict__ == other.__dict__
+
+ def __hash__(self):
+ raise TypeError
+
+class NamespaceDesc(Desc):
+ def __init__(self, name):
+ self.name = name
+
+ def build(self):
+ return CliNamespace(self.name)
+
+class ClassDesc(Desc):
+ def build(self):
+ assert self.Assembly.startswith('mscorlib') # TODO: support external assemblies
+ namespace, name = self.FullName.rsplit('.', 1)
+
+ # construct OOTYPE and CliClass
+ load_class_maybe(self.BaseType)
+ BASETYPE = ClassCache[self.BaseType]._INSTANCE
+ TYPE = NativeInstance('[mscorlib]', namespace, name, BASETYPE, {}, {})
+ Class = CliClass(TYPE, {})
+ OOTypeCache[self.OOType] = TYPE
+ ClassCache[self.FullName] = Class
+
+ # render dependencies
+ for name in self.Depend:
+ load_class_maybe(name)
+
+ # add both static and instance methods
+ static_meths = self.group_methods(self.StaticMethods, _overloaded_static_meth,
+ _static_meth, ootype.StaticMethod)
+ meths = self.group_methods(self.Methods, ootype.overload, ootype.meth, ootype.Meth)
+ Class._add_methods(static_meths)
+ TYPE._add_methods(meths)
+ return Class
+
+ def group_methods(self, methods, overload, meth, Meth):
+ groups = {}
+ for name, args, result in methods:
+ groups.setdefault(name, []).append((args, result))
+
+ res = {}
+ for name, methlist in groups.iteritems():
+ if len(methlist) == 1:
+ args, result = methlist[0]
+ TYPE = self.get_method_type(Meth, args, result)
+ res[name] = meth(TYPE)
+ else:
+ TYPES = [self.get_method_type(Meth, args, result) for (args, result) in methlist]
+ meths = [meth(TYPE) for TYPE in TYPES]
+ res[name] = overload(*meths)
+ return res
+
+ def get_method_type(self, Meth, args, result):
+ ARGS = [self.get_ootype(arg) for arg in args]
+ RESULT = self.get_ootype(result)
+ return Meth(ARGS, RESULT)
+
+ def get_ootype(self, t):
+ # a bit messy, but works
+ if t.startswith('ootype.'):
+ _, name = t.split('.')
+ return getattr(ootype, name)
+ else:
+ return OOTypeCache[t]
+
+
+loaddesc() ## automatically loads the cached Dependencies
Modified: pypy/dist/pypy/translator/cli/rte.py
==============================================================================
--- pypy/dist/pypy/translator/cli/rte.py (original)
+++ pypy/dist/pypy/translator/cli/rte.py Wed Oct 25 14:05:51 2006
@@ -84,6 +84,10 @@
FLAGS = ['/t:library', '/unsafe', '/r:Mono.Posix', '/r:main.exe']
DEPENDENCIES = [MainStub]
+class Query(Target):
+ SOURCES = ['query.cs']
+ OUTPUT = 'query.exe'
+
def get_pypy_dll():
if os.environ.get('PYPYLIB', '').lower() == 'unix':
DLL = UnixDLL
@@ -91,5 +95,6 @@
DLL = FrameworkDLL
return DLL.get()
+
if __name__ == '__main__':
get_pypy_dll()
Added: pypy/dist/pypy/translator/cli/src/query.cs
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/cli/src/query.cs Wed Oct 25 14:05:51 2006
@@ -0,0 +1,136 @@
+using System;
+using System.Reflection;
+using System.Collections.Generic;
+
+public class Query
+{
+ public static Dictionary<Type, bool> PendingTypes = new Dictionary<Type, bool>();
+
+ public static int Main(string[] argv)
+ {
+ if (argv.Length != 1) {
+ Console.Error.WriteLine("Usage: query full-qualified-name");
+ return 1;
+ }
+
+ string name = argv[0];
+ Type t = Type.GetType(name);
+ if (t == null) {
+ Console.Error.WriteLine("Cannot load type {0}", name);
+ return 2;
+ }
+
+ if (!t.IsPublic) {
+ Console.Error.WriteLine("Cannot load a non-public type");
+ return 2;
+ }
+
+ PrintType(t);
+ return 0;
+ }
+
+ private static void PrintType(Type t)
+ {
+ Console.WriteLine("Assembly = '{0}'", t.Assembly.FullName);
+ Console.WriteLine("FullName = '{0}'", t.FullName);
+ Console.WriteLine("BaseType = '{0}'", GetBaseType(t));
+ Console.WriteLine("OOType = '{0}'", GetOOType(t));
+ PrintMethods("StaticMethods", t.GetMethods(BindingFlags.Static|BindingFlags.Public|BindingFlags.DeclaredOnly));
+ PrintMethods("Methods", t.GetMethods(BindingFlags.Instance|BindingFlags.Public|BindingFlags.DeclaredOnly));
+ PendingTypes.Remove(t);
+ PrintDepend();
+ }
+
+ private static string GetBaseType(Type t)
+ {
+ if (t == typeof(object))
+ return "ROOT"; // special case for System.Object to avoid circular dependencies
+ else if (t.BaseType == null)
+ return "System.Object"; // the only known case is the BaseType of an interface
+ else
+ return t.BaseType.FullName;
+ }
+
+ private static string GetOOType(Type t)
+ {
+ if (t == typeof(void))
+ return "ootype.Void";
+ else if (t == typeof(int))
+ return "ootype.Signed";
+ else if (t == typeof(uint))
+ return "ootype.Unsigned";
+ else if (t == typeof(long))
+ return "ootype.SignedLongLong";
+ else if (t == typeof(ulong))
+ return "ootype.UnsignedLongLong";
+ else if (t == typeof(bool))
+ return "ootype.Bool";
+ else if (t == typeof(double))
+ return "ootype.Float";
+ else if (t == typeof(char))
+ return "ootype.Char"; // maybe it should be unichar?
+ else if (t == typeof(string))
+ return "ootype.String";
+ else {
+ PendingTypes[t] = true;
+ string name = t.FullName.Replace(".", "_"); // TODO: ensure unicity
+ if (t.IsArray)
+ name = name.Replace("[]", "___array___");
+ return name;
+ }
+ }
+
+ private static void PrintMethods(string varname, MethodInfo[] methods)
+ {
+ Console.WriteLine("{0} = [", varname);
+ // MethodName, [ARGS], RESULT
+ foreach(MethodInfo meth in methods) {
+ if (IgnoreMethod(meth))
+ continue;
+ Console.Write(" ('{0}', [", meth.Name);
+ foreach(ParameterInfo par in meth.GetParameters()) {
+ Console.Write("'{0}'", GetOOType(par.ParameterType));
+ Console.Write(", ");
+ }
+ Console.WriteLine("], '{0}'),", GetOOType(meth.ReturnType));
+ }
+ Console.WriteLine(" ]");
+ }
+
+ private static bool IgnoreMethod(MethodInfo meth)
+ {
+ if (!meth.IsPublic)
+ return true;
+
+ // ignore all SpecialName but properties getter/setter
+ if (meth.IsSpecialName && !meth.Name.StartsWith("get_") && !meth.Name.StartsWith("set_"))
+ return true;
+
+ if (IgnoreType(meth.ReturnType))
+ return true;
+ foreach(ParameterInfo par in meth.GetParameters())
+ if (IgnoreType(par.ParameterType))
+ return true;
+
+ return false;
+ }
+
+ private static bool IgnoreType(Type t)
+ {
+ return !t.IsPrimitive && (t == typeof(System.ValueType) ||
+ t == typeof(System.Array) ||
+ t.IsValueType ||
+ t.IsByRef ||
+ t.IsPointer ||
+ t.IsGenericType ||
+ t.IsGenericTypeDefinition);
+ }
+
+ private static void PrintDepend()
+ {
+ Console.Write("Depend = [");
+ foreach(Type t in PendingTypes.Keys)
+ Console.Write("'{0}', ", t.FullName);
+ Console.WriteLine("]");
+ }
+}
Modified: pypy/dist/pypy/translator/cli/support.py
==============================================================================
--- pypy/dist/pypy/translator/cli/support.py (original)
+++ pypy/dist/pypy/translator/cli/support.py Wed Oct 25 14:05:51 2006
@@ -1,14 +1,18 @@
import py
from pypy.rpython.ootypesystem import ootype
+from pypy.tool.ansi_print import ansi_log
+log = py.log.Producer("cli")
+py.log.setconsumer("cli", ansi_log)
+
try:
- import CLR
+ import CLR as PythonNet
except ImportError:
- class _CLR:
+ class _PythonNet:
def __getattr__(self, attr):
py.test.skip('Must use pythonnet for being able to access .NET libraries')
- CLR = _CLR()
- del _CLR
+ PythonNet = _PythonNet()
+ del _PythonNet
# some code has been stolen from genc
def string_literal(s):
Modified: pypy/dist/pypy/translator/cli/test/test_dotnet.py
==============================================================================
--- pypy/dist/pypy/translator/cli/test/test_dotnet.py (original)
+++ pypy/dist/pypy/translator/cli/test/test_dotnet.py Wed Oct 25 14:05:51 2006
@@ -2,11 +2,11 @@
from pypy.annotation import model as annmodel
from pypy.translator.cli.test.runtest import CliTest
from pypy.translator.cli.dotnet import SomeCliClass, SomeCliStaticMethod,\
- NativeInstance
-from pypy.translator.cli.dotnetdb import System
-Math = System.Math
-ArrayList = System.Collections.ArrayList
-StringBuilder = System.Text.StringBuilder
+ NativeInstance, CLR
+
+Math = CLR.System.Math
+ArrayList = CLR.System.Collections.ArrayList
+StringBuilder = CLR.System.Text.StringBuilder
class TestDotnet(CliTest):
Added: pypy/dist/pypy/translator/cli/test/test_query.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/cli/test/test_query.py Wed Oct 25 14:05:51 2006
@@ -0,0 +1,35 @@
+from pypy.translator.cli import query
+from pypy.translator.cli.dotnet import CLR, CliNamespace
+
+def test_load_namespace_simple():
+ query.load_class_or_namespace('System')
+ assert isinstance(CLR.System, CliNamespace)
+ assert CLR.System._name == 'System'
+
+def test_load_namespace_complex():
+ query.load_class_or_namespace('System.Collections')
+ assert isinstance(CLR.System, CliNamespace)
+ assert isinstance(CLR.System.Collections, CliNamespace)
+ assert CLR.System.Collections._name == 'System.Collections'
+
+def test_CLR_getattr():
+ System = CLR.System
+ assert isinstance(System, CliNamespace)
+ assert System._name == 'System'
+ assert hasattr(CLR, 'System')
+
+def test_System_Object():
+ Object = CLR.System.Object
+ assert Object._name == '[mscorlib]System.Object'
+ assert 'Equals' in Object._static_methods
+ assert 'ToString' in Object._INSTANCE._methods
+
+def test_savedesc():
+ from pypy.tool.udir import udir
+ CLR.System.Object # force System.Object to be loaded
+ olddesc = query.Descriptions.copy()
+ tmpfile = str(udir.join('descriptions'))
+ query.savedesc(tmpfile)
+ query.Descriptions.clear()
+ query.loaddesc(tmpfile)
+ assert query.Descriptions == olddesc
More information about the Pypy-commit
mailing list