1#===- common.py - Python LLVM Bindings -----------------------*- python -*--===#
2#
3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4# See https://llvm.org/LICENSE.txt for license information.
5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6#
7#===------------------------------------------------------------------------===#
8
9from ctypes import POINTER
10from ctypes import c_void_p
11from ctypes import cdll
12
13import ctypes.util
14import platform
15
16# LLVM_VERSION: sync with PACKAGE_VERSION in CMakeLists.txt
17#               but leave out the 'svn' suffix.
18LLVM_VERSION = '10.0.0'
19
20__all__ = [
21    'c_object_p',
22    'get_library',
23]
24
25c_object_p = POINTER(c_void_p)
26
27class LLVMObject(object):
28    """Base class for objects that are backed by an LLVM data structure.
29
30    This class should never be instantiated outside of this package.
31    """
32    def __init__(self, ptr, ownable=True, disposer=None):
33        assert isinstance(ptr, c_object_p)
34
35        self._ptr = self._as_parameter_ = ptr
36
37        self._self_owned = True
38        self._ownable = ownable
39        self._disposer = disposer
40
41        self._owned_objects = []
42
43    def take_ownership(self, obj):
44        """Take ownership of another object.
45
46        When you take ownership of another object, you are responsible for
47        destroying that object. In addition, a reference to that object is
48        placed inside this object so the Python garbage collector will not
49        collect the object while it is still alive in libLLVM.
50
51        This method should likely only be called from within modules inside
52        this package.
53        """
54        assert isinstance(obj, LLVMObject)
55
56        self._owned_objects.append(obj)
57        obj._self_owned = False
58
59    def from_param(self):
60        """ctypes function that converts this object to a function parameter."""
61        return self._as_parameter_
62
63    def __del__(self):
64        if not hasattr(self, '_self_owned') or not hasattr(self, '_disposer'):
65            return
66
67        if self._self_owned and self._disposer:
68            self._disposer(self)
69
70class CachedProperty(object):
71    """Decorator that caches the result of a property lookup.
72
73    This is a useful replacement for @property. It is recommended to use this
74    decorator on properties that invoke C API calls for which the result of the
75    call will be idempotent.
76    """
77    def __init__(self, wrapped):
78        self.wrapped = wrapped
79        try:
80            self.__doc__ = wrapped.__doc__
81        except: # pragma: no cover
82            pass
83
84    def __get__(self, instance, instance_type=None):
85        if instance is None:
86            return self
87
88        value = self.wrapped(instance)
89        setattr(instance, self.wrapped.__name__, value)
90
91        return value
92
93def get_library():
94    """Obtain a reference to the llvm library."""
95
96    # On Linux, ctypes.cdll.LoadLibrary() respects LD_LIBRARY_PATH
97    # while ctypes.util.find_library() doesn't.
98    # See http://docs.python.org/2/library/ctypes.html#finding-shared-libraries
99    #
100    # To make it possible to run the unit tests without installing the LLVM shared
101    # library into a default linker search path.  Always Try ctypes.cdll.LoadLibrary()
102    # with all possible library names first, then try ctypes.util.find_library().
103
104    names = ['LLVM-' + LLVM_VERSION, 'LLVM-' + LLVM_VERSION + 'svn']
105    t = platform.system()
106    if t == 'Darwin':
107        pfx, ext = 'lib', '.dylib'
108    elif t == 'Windows':
109        pfx, ext = '', '.dll'
110    else:
111        pfx, ext = 'lib', '.so'
112
113    for i in names:
114        try:
115            lib = cdll.LoadLibrary(pfx + i + ext)
116        except OSError:
117            pass
118        else:
119            return lib
120
121    for i in names:
122        t = ctypes.util.find_library(i)
123        if t:
124            return cdll.LoadLibrary(t)
125    raise Exception('LLVM shared library not found!')
126