Пример #1
0
 def test_getallkeys(self):
     t = Terminator(klass)
     a = t.add_attribute("a")
     a = a.add_attribute("b")
     a = a.add_attribute("c")
     assert a.klass is klass
     assert a.get_all_keys() == ["a", "b", "c"]
Пример #2
0
 def test_getallkeys(self):
     t = Terminator(klass)
     a = t.add_attribute("a")
     a = a.add_attribute("b")
     a = a.add_attribute("c")
     assert a.klass is klass
     assert a.get_all_keys() == ["a", "b", "c"]
Пример #3
0
 def __init__(self, name):
     self.name = name
     self.constants_w = {}
     self.properties = OrderedDict()
     self.methods = OrderedDict()
     self.all_parents = {self.get_identifier(): None}  # classes and intfs
     self.base_map = Terminator()
     self.initial_storage_w = None
Пример #4
0
 def test_delattr_2(self):
     t = Terminator()
     a = t.add_attribute("a")
     a1 = a.add_attribute("b")
     a2 = a1.add_attribute("c")
     assert a2.lookup("c").index == 2
     a3, _ = a2.del_attribute("a")
     assert a3.lookup("a") is None
     assert a3.lookup("b").index == 0
     assert a3.lookup("c").index == 1
Пример #5
0
 def test_simple(self):
     t = Terminator(klass)
     assert t.lookup("name") is None
     new_attr = t.add_attribute("name")
     a1 = new_attr.lookup("name")
     assert a1 is new_attr
     a2 = t.add_attribute("name")
     assert a2 is a1
     a3 = a1.add_attribute("name2")
     assert a3.lookup("name") is a2
     assert a3.lookup("name2") is a3
     assert a3.lookup("xyz") is None
     assert a3.index == 1
     assert a2.index == 0
     assert t.index == 0
Пример #6
0
 def test_simple(self):
     t = Terminator()
     assert t.lookup("name") is None
     new_attr = t.add_attribute("name")
     a1 = new_attr.lookup("name")
     assert a1 is new_attr
     a2 = t.add_attribute("name")
     assert a2 is a1
     a3 = a1.add_attribute("name2")
     assert a3.lookup("name") is a2
     assert a3.lookup("name2") is a3
     assert a3.lookup("xyz") is None
     assert a3.index == 1
     assert a2.index == 0
     assert t.index == 0
Пример #7
0
 def __init__(self, name):
     self.name = name
     self.constants_w = {}
     self.properties = OrderedDict()
     self.methods = OrderedDict()
     self.all_parents = {self.get_identifier(): None}  # classes and intfs
     self.base_map = Terminator()
     self.initial_storage_w = None
Пример #8
0
class ClassBase(AbstractFunction, AccessMixin):
    access_flags = 0
    extends_name = None
    base_interface_names = None
    constructor_method = None
    method__clone = None
    method__get = None
    method__set = None
    method__unset = None
    method__isset = None
    method__call = None
    method__callstatic = None
    method__tostring = None
    method__invoke = None
    custom_instance_class = None
    is_iterator = False
    is_array_access = False
    immediate_parents = None
    parentclass = None
    _all_nonstatic_special_properties = None  # lazy

    _immutable_fields_ = ["custom_instance_class", "constructor_method", "parentclass"]

    def __init__(self, name):
        self.name = name
        self.constants_w = {}
        self.properties = OrderedDict()
        self.methods = OrderedDict()
        self.all_parents = {self.get_identifier(): None}  # classes and intfs
        self.base_map = Terminator()
        self.initial_storage_w = None

    def __repr__(self):
        if self.is_interface():
            cat = "Interface"
        else:
            cat = "Class"
        return "<%s %r>" % (cat, self.name)

    def _init_constructor(self):
        if "__construct" in self.methods:
            method = self.methods["__construct"]
        elif self.get_identifier() in self.methods:
            method = self.methods[self.get_identifier()]
        else:
            return
        if method.is_static():
            raise CompilerError("Constructor %s cannot be static" % (method.repr()))
        self.constructor_method = method

    def _collect_all_methods(self):
        all_methods = []
        seen = {}
        seenclasses = {}
        seenclasses[None] = None
        pending = [self]
        while len(pending) > 0:
            fromclass = pending.pop(0)
            if fromclass in seenclasses:
                continue
            seenclasses[fromclass] = None
            for meth_id, m in fromclass.methods.iteritems():
                if meth_id not in seen:
                    seen[meth_id] = None
                    all_methods.append(m)
            pending.extend(fromclass.immediate_parents)
        return all_methods

    def _check_abstract_methods(self):
        if self.is_abstract():
            return
        all_methods = self._collect_all_methods()
        abstract_methods = []
        for m in all_methods:
            if m.is_abstract():
                abstract_methods.append("%s::%s" % (m.getclass().name, m.get_name()))
        if abstract_methods:
            msg = _msg_abstract(self.name, abstract_methods)
            raise ClassDeclarationError(msg)

    def _inherit_method(self, parent_method):
        meth_id = parent_method.get_identifier()
        if meth_id not in self.methods:
            self.methods[meth_id] = parent_method
        else:
            method = self.methods[meth_id]
            self._check_inheritance(method, parent_method)

    def _check_inheritance(self, method, parent_method):
        if parent_method.is_final():
            raise ClassDeclarationError("Cannot override final method %s" % parent_method.repr())
        if parent_method.is_public() and not method.is_public():
            raise ClassDeclarationError(
                "Access level to %s must be public (as in class %s)" % (method.repr(), parent_method.getclass().name)
            )
        if parent_method.is_protected() and method.is_private():
            raise ClassDeclarationError(
                "Access level to %s must be protected (as in class %s) or "
                "weaker" % (method.repr(), parent_method.getclass().name)
            )
        if method.is_static() and not parent_method.is_static():
            raise ClassDeclarationError(
                "Cannot make non static method %s static in class %s" % (parent_method.repr(), method.getclass().name)
            )
        if not method.is_static() and parent_method.is_static():
            raise ClassDeclarationError(
                "Cannot make static method %s non static in class %s" % (parent_method.repr(), method.getclass().name)
            )

    def _make_property(self, prop, w_initial_value):
        if isinstance(prop, tuple):
            name, access_flags = prop
            p = Property(name, self, access_flags, w_initial_value)
            self.properties[name] = p
        elif not we_are_translated():  # compile time only
            prop = prop.build(self)
            self.properties[prop.name] = prop

    def _inherit_property(self, prop):
        if prop.is_private():
            return
        name = prop.name
        if name not in self.properties:
            self.properties[name] = prop.copy()
            return
        derived = self.properties[name]
        if derived.is_static() and not prop.is_static():
            raise ClassDeclarationError("Cannot redeclare non static %s as static %s" % (prop.repr(), derived.repr()))
        if not derived.is_static() and prop.is_static():
            raise ClassDeclarationError("Cannot redeclare static %s as non static %s" % (prop.repr(), derived.repr()))
        if prop.is_public() and not derived.is_public():
            raise ClassDeclarationError(
                "Access level to %s must be public " "(as in class %s)" % (derived.repr(), prop.klass.name)
            )
        if prop.is_protected() and derived.is_private():
            raise ClassDeclarationError(
                "Access level to %s must be protected " "(as in class %s) or weaker" % (derived.repr(), prop.klass.name)
            )

    def _init_magic_methods(self, interp):
        for name in magic_methods_unrolled:
            if name in self.methods:
                method = self.methods[name]
            else:
                continue
            if name == "__call":
                if method.getclass() == self and (method.is_static() or not method.is_public()):
                    interp.warn("The magic method __call() must have " "public visibility and cannot be static")
            setattr(self, "method" + name, method)

    def _create_initial_storage(self, space):
        l = []
        for p in self.properties.itervalues():
            if not p.is_static() and not p.is_special and p.klass is self:
                w_val = p.value.eval_static(space)
                self.base_map = self.base_map.add_attribute(p.mangle_name())
                assert self.base_map.index == len(l)
                l.append(w_val)
        if self.parentclass is not None:
            parent = self.parentclass
            if parent.initial_storage_w is None:
                parent._create_initial_storage(space)
            allattrs = parent.base_map.get_all_attrs()
            allmykeys = self.base_map.get_all_keys()
            d = {}
            for key in allmykeys:
                d[key] = None
            for parent_attr in allattrs:
                key = parent_attr.name
                w_value = parent.initial_storage_w[parent_attr.index]
                if key.startswith("\x00*\x00") and key[3:] in d:
                    continue
                if key in d:
                    continue
                self.base_map = self.base_map.add_attribute(key)
                l.append(w_value)
        self.initial_storage_w = l[:]  # make it non-resizable

    def lookup_property_name(self, TYPE, interp, this, name, contextclass, w_newvalue=None, givenotice=True):
        # Returns the name to use for accessing '$a->name' on instances:
        # usually just 'name', but may be mangled in case it's a non-static
        # protected or private attribute.  This also does access checks and
        # raises if forbidden.  Note that a static member has an effect here
        # too, even though it's not actually used later, as in PHP.
        # (All lookup() methods may raise a VisibilityError.)
        property = self._lookup_property(name, contextclass, not interp.allow_direct_class_access)
        if property is None:
            return name
        elif givenotice and property.is_static():
            interp.strict("Accessing static property %s as non static" % property.repr())
        # the following can raise SpecialPropertyReturn, otherwise does
        # nothing in case it's a builtin property
        property.special_lookup(TYPE, interp, this, w_newvalue)
        return property.mangle_name()

    def can_access_protected_properties_from(self, contextclass):
        return contextclass is not None and (self.is_parent_of(contextclass) or contextclass.is_parent_of(self))

    def check_access_and_demangle_property(self, name, contextclass):
        if not name.startswith("\x00"):
            return name
        i = name.find("\x00", 1)
        if i < 0:
            return name  # malformed name?
        prvname = name[1:i]
        # protected property
        if prvname == "*":
            if self.can_access_protected_properties_from(contextclass):
                return name[i + 1 :]
            return None
        # private property
        if contextclass is not None and contextclass.is_parent_of(self) and prvname == contextclass.name:
            return name[i + 1 :]
        else:
            return None

    def get_methods(self, contextclass):
        """Returns a list of accessible method names for the given context."""
        methods = []
        for method_name, method in self.methods.iteritems():
            try:
                self._visibility_check(method, method_name, contextclass)
                methods.append(method.get_name())
            except VisibilityError:
                pass

        return methods

    def lookup_staticmember(self, name, contextclass, check_visibility):
        property = self._lookup_property(name, contextclass, check_visibility)
        if property is not None and property.is_static():
            return property
        return None

    def _lookup_property(self, name, contextclass, check_visibility):
        self = jit.hint(self, promote=True)
        # name = jit.hint(name, promote=True) should be promote_string
        return self._lookup_property_elidable(name, contextclass, check_visibility)

    @jit.elidable_promote()
    def _lookup_property_elidable(self, name, contextclass, check_visibility):
        # Implementation of the rules described in the big comment in
        # instanceobject.py
        if contextclass is not None and contextclass.is_parent_of(self):
            try:
                result = contextclass.properties[name]
            except KeyError:
                pass
            else:
                if result.is_private():
                    return result
        try:
            result = self.properties[name]
        except KeyError:
            return None
        if check_visibility:
            self._visibility_check(result, name, contextclass)
        return result

    def locate_static_method(self, name, contextclass, check_visibility):
        try:
            return self._lookup_static_method(name, contextclass, check_visibility)
        except VisibilityError:
            callstatic = self.method__callstatic
            if callstatic is None:
                raise
            return Method(W_UnderUnderCall(name, callstatic.method_func), callstatic.access_flags, self)

    @jit.elidable_promote()
    def _lookup_static_method(self, name, contextclass, check_visibility):
        key = name.lower()
        try:
            result = self.methods[key]
        except KeyError:
            raise VisibilityError("undefined", self, name, None)
        if check_visibility:
            self._visibility_check(result, name, contextclass)
        return result

    def locate_method(self, name, contextclass, searchclass=None, check_visibility=True):
        if searchclass is None:
            searchclass = self
        try:
            return self._lookup_method(name, contextclass, check_visibility)
        except VisibilityError:
            call = searchclass.method__call
            if call is None:
                raise
            return Method(W_UnderUnderCall(name, call.method_func), call.access_flags, searchclass)

    @jit.elidable_promote()
    def _lookup_method(self, name, contextclass, check_visibility):
        key = name.lower()
        if contextclass is not None and contextclass.is_parent_of(self):
            try:
                result = contextclass.methods[key]
                if result.is_private():
                    if check_visibility:
                        contextclass._visibility_check(result, name, contextclass)
                    return result
            except KeyError:
                pass
        try:
            result = self.methods[key]
        except KeyError:
            raise VisibilityError("undefined", self, name, None)
        if check_visibility:
            self._visibility_check(result, name, contextclass)
        return result

    def _visibility_check(self, result, name, contextclass):
        if result.is_protected():
            if not self.can_access_protected_properties_from(contextclass):
                raise VisibilityError("protected", result.getclass(), name, contextclass)
        elif result.is_private():
            if result.getclass() is not contextclass:
                raise VisibilityError("private", result.getclass(), name, contextclass)

    @jit.elidable
    def is_parent_of(self, otherclass):
        while otherclass is not self:
            otherclass = otherclass.parentclass
            if otherclass is None:
                return False
        return True

    @jit.elidable_promote()
    def is_subclass_of_class_or_intf_name(self, parent_or_interface_name):
        return parent_or_interface_name.lower() in self.all_parents

    def get_super_method(self, methname, superclass, check_visibility):
        return self.locate_method(methname, superclass, searchclass=superclass, check_visibility=check_visibility)

    def _static_call_warning(self, interp, method, assuming_this):
        interp.strict("Non-static method %s should not be " "called statically%s" % (method.repr(), assuming_this))

    def getstaticmeth(self, methname, contextclass, context_w_this, interp):
        check_vis = not interp.allow_direct_class_access
        if contextclass is not None and self.is_parent_of(contextclass):
            return self.get_super_method(methname, contextclass, check_vis)
        method = self.locate_static_method(methname, contextclass, check_vis)
        if not method.is_static():
            if context_w_this is None:
                self._static_call_warning(interp, method, "")
            elif not self.is_parent_of(context_w_this.klass):
                self._static_call_warning(interp, method, ", assuming $this from incompatible context")
        return method

    def lookup_w_constant(self, space, constantname):
        try:
            w_value = self.constants_w[constantname]
        except KeyError:
            return None
        return w_value.eval_static(space)

    def check_constructor_from_context(self, interp, contextclass):
        if self.is_abstract():
            if self.is_interface():
                cat = "interface"
            else:
                cat = "abstract class"
            interp.fatal("Cannot instantiate %s %s" % (cat, self.name))
        method = self.constructor_method
        visibility_error = ""  # please the annotator
        if method is None:
            return  # no constructor at all
        elif method.is_public():
            return  # public constructor
        elif method.is_protected():
            if contextclass is not None and (self.is_parent_of(contextclass) or contextclass.is_parent_of(self)):
                return  # protected constructor
            visibility_error = "protected"
        elif method.is_private():
            if contextclass is self:
                return  # private constructor
            visibility_error = "private"
        interp.fatal("Call to %s %s from invalid context" % (visibility_error, method.repr()))

    def needs_ref(self, i):
        method = self.constructor_method
        if method is not None:
            return method.method_func.needs_ref(i)
        return False

    def get_fresh_storage_w(self, space):
        return self.get_initial_storage_w(space)[:]

    def get_initial_storage_w(self, space):
        if self.initial_storage_w is None:
            self._create_initial_storage(space)
        return self.initial_storage_w

    def call_args(self, interp, args_w, w_this=None, thisclass=None, closureargs=None):
        storage_w = self.get_fresh_storage_w(interp.space)
        w_res = self.create_instance(interp, storage_w)
        method = self.constructor_method
        if method is not None:
            assert isinstance(method, Method)
            method.method_func.call_args(interp, args_w, w_this=w_res, thisclass=self)
        return w_res

    def create_instance(self, interp, storage_w):
        if self.custom_instance_class is not None:
            w_res = self.custom_instance_class(self, storage_w)
            w_res.setup(interp)
        else:
            w_res = W_InstanceObject(self, storage_w)
        return w_res

    def get_empty_instance(self, space):
        return self.create_instance(space.ec.interpreter, self.get_fresh_storage_w(space))

    def do_clone(self, interp, w_obj1, contextclass):
        assert isinstance(w_obj1, W_InstanceObject)

        w_res = w_obj1._clone(interp)
        method = self.method__clone
        if method is not None:
            try:
                self._visibility_check(method, method.get_identifier(), contextclass)
            except VisibilityError as exc:
                exc.reraise_magic(interp)
        if method is not None:
            method.method_func.call_args(interp, [], w_this=w_res, thisclass=self)
        return w_res

    def do_get(self, interp, w_obj, attr, isref):
        key = (w_obj, attr)
        recursion = rec_custom_get_set
        if key in recursion:
            interp.notice("Undefined property: %s::$%s" % (self.name, attr))
            w_res = interp.space.w_Null
        else:
            recursion[key] = None
            try:
                method = self.method__get
                space = interp.space
                w_res = method.method_func.call_args(interp, [space.newstr(attr)], w_this=w_obj, thisclass=self)
            finally:
                del recursion[key]
        if isref and not isinstance(w_res, W_Reference):
            interp.notice("Indirect modification of overloaded property " "%s::$%s has no effect" % (self.name, attr))
            w_res = W_Reference(w_res)
        return w_res

    def do_set(self, interp, w_obj, attr, w_value):
        key = (w_obj, attr)
        space = interp.space
        recursion = rec_custom_get_set
        if key in recursion:
            return False
        recursion[key] = None
        try:
            method = self.method__set
            method.method_func.call_args(interp, [space.newstr(attr), w_value], w_this=w_obj, thisclass=self)
        finally:
            del recursion[key]
        return True

    def do_unset(self, interp, w_obj, attr):
        key = (w_obj, attr)
        recursion = rec_custom_get_set
        if key in recursion:
            return False
        recursion[key] = None
        try:
            method = self.method__unset
            method.method_func.call_args(interp, [interp.space.newstr(attr)], w_this=w_obj, thisclass=self)
        finally:
            del recursion[key]
        return True

    def do_isset(self, interp, w_obj, attr):
        key = (w_obj, attr)
        recursion = rec_custom_get_set
        if key in recursion:
            return None
        recursion[key] = None
        try:
            method = self.method__isset
            w_res = method.method_func.call_args(interp, [interp.space.newstr(attr)], w_this=w_obj, thisclass=self)
        finally:
            del recursion[key]
        return w_res

    def do_tostring(self, space, w_obj, quiet=False):
        method = self.method__tostring
        if method is None:
            if not quiet:
                space.ec.recoverable_fatal("Object of class %s could not be " "converted to string" % self.name)
                return space.newstr("")
            else:
                return None
        try:
            w_res = method.method_func.call_args(space.ec.interpreter, [], w_this=w_obj, thisclass=self)
        except PHPException:
            raise space.ec.fatal("Method %s::__toString() must not " "throw an exception" % self.name)
        if not isinstance(w_res, W_StringObject):
            space.ec.catchable_fatal("Method %s::__toString() must " "return a string value" % self.name)
        return w_res

    def get_invoke_method(self, w_obj):
        method = self.method__invoke
        if method is None:
            return None
        return W_InvokeCall(self, method.method_func, w_obj)

    def get_all_nonstatic_special_properties(self):
        result = self._all_nonstatic_special_properties
        if result is None:
            lst = []
            for base in self.immediate_parents:
                lst.extend(base.get_all_nonstatic_special_properties())
            for prop in self.properties.values():
                if not prop.is_static() and prop.is_special:
                    lst.append((prop.mangle_name(), prop))
            result = lst[:]
            self._all_nonstatic_special_properties = result
        return result
Пример #9
0
class ClassBase(AbstractFunction, AccessMixin):
    access_flags = 0
    base_interface_names = None
    constructor_method = None
    method__clone = None
    method__get = None
    method__set = None
    method__unset = None
    method__isset = None
    method__call = None
    method__callstatic = None
    method__tostring = None
    method__invoke = None
    custom_instance_class = None
    is_iterator = False
    is_array_access = False
    immediate_parents = None
    parentclass = None
    _all_nonstatic_special_properties = None  # lazy

    _immutable_fields_ = [
        'custom_instance_class', 'constructor_method', 'parentclass'
    ]

    def __init__(self, name):
        self.name = name
        self.constants_w = {}
        self.properties = OrderedDict()
        self.methods = OrderedDict()
        self.all_parents = {self.get_identifier(): None}  # classes and intfs
        self.base_map = Terminator()
        self.initial_storage_w = None

    def __repr__(self):
        if self.is_interface():
            cat = 'Interface'
        else:
            cat = 'Class'
        return '<%s %r>' % (cat, self.name)

    def _init_constructor(self):
        if '__construct' in self.methods:
            method = self.methods['__construct']
        elif self.get_identifier() in self.methods:
            method = self.methods[self.get_identifier()]
        else:
            return
        if method.is_static():
            raise CompilerError("Constructor %s cannot be static" %
                                (method.repr()))
        self.constructor_method = method

    def _collect_all_methods(self):
        all_methods = []
        seen = {}
        seenclasses = {}
        seenclasses[None] = None
        pending = [self]
        while len(pending) > 0:
            fromclass = pending.pop(0)
            if fromclass in seenclasses:
                continue
            seenclasses[fromclass] = None
            for meth_id, m in fromclass.methods.iteritems():
                if meth_id not in seen:
                    seen[meth_id] = None
                    all_methods.append(m)
            pending.extend(fromclass.immediate_parents)
        return all_methods

    def _check_abstract_methods(self):
        if self.is_abstract():
            return
        all_methods = self._collect_all_methods()
        abstract_methods = []
        for m in all_methods:
            if m.is_abstract():
                abstract_methods.append("%s::%s" %
                                        (m.getclass().name, m.get_name()))
        if abstract_methods:
            msg = _msg_abstract(self.name, abstract_methods)
            raise ClassDeclarationError(msg)

    def _inherit_method(self, parent_method):
        meth_id = parent_method.get_identifier()
        if meth_id not in self.methods:
            self.methods[meth_id] = parent_method
        else:
            method = self.methods[meth_id]
            self._check_inheritance(method, parent_method)

    def _check_inheritance(self, method, parent_method):
        if parent_method.is_final():
            raise ClassDeclarationError("Cannot override final method %s" %
                                        parent_method.repr())
        if parent_method.is_public() and not method.is_public():
            raise ClassDeclarationError(
                "Access level to %s must be public (as in class %s)" %
                (method.repr(), parent_method.getclass().name))
        if parent_method.is_protected() and method.is_private():
            raise ClassDeclarationError(
                "Access level to %s must be protected (as in class %s) or "
                "weaker" % (method.repr(), parent_method.getclass().name))
        if method.is_static() and not parent_method.is_static():
            raise ClassDeclarationError(
                "Cannot make non static method %s static in class %s" %
                (parent_method.repr(), method.getclass().name))
        if not method.is_static() and parent_method.is_static():
            raise ClassDeclarationError(
                "Cannot make static method %s non static in class %s" %
                (parent_method.repr(), method.getclass().name))

    def _make_property(self, prop, w_initial_value):
        if isinstance(prop, tuple):
            name, access_flags = prop
            p = Property(name, self, access_flags, w_initial_value)
            self.properties[name] = p
        elif not we_are_translated():  # compile time only
            prop = prop.build(self)
            self.properties[prop.name] = prop

    def _inherit_property(self, prop):
        if prop.is_private():
            return
        name = prop.name
        if name not in self.properties:
            self.properties[name] = prop.copy()
            return
        derived = self.properties[name]
        if derived.is_static() and not prop.is_static():
            raise ClassDeclarationError(
                "Cannot redeclare non static %s as static %s" %
                (prop.repr(), derived.repr()))
        if not derived.is_static() and prop.is_static():
            raise ClassDeclarationError(
                "Cannot redeclare static %s as non static %s" %
                (prop.repr(), derived.repr()))
        if prop.is_public() and not derived.is_public():
            raise ClassDeclarationError("Access level to %s must be public "
                                        "(as in class %s)" %
                                        (derived.repr(), prop.klass.name))
        if prop.is_protected() and derived.is_private():
            raise ClassDeclarationError("Access level to %s must be protected "
                                        "(as in class %s) or weaker" %
                                        (derived.repr(), prop.klass.name))

    def _init_magic_methods(self, interp):
        for name in magic_methods_unrolled:
            if name in self.methods:
                method = self.methods[name]
            else:
                continue
            if name == '__call':
                if (method.getclass() == self
                        and (method.is_static() or not method.is_public())):
                    interp.warn("The magic method __call() must have "
                                "public visibility and cannot be static")
            setattr(self, 'method' + name, method)

    def _create_initial_storage(self, space):
        l = []
        for p in self.properties.itervalues():
            if not p.is_static() and not p.is_special and p.klass is self:
                w_val = p.value.eval_static(space)
                self.base_map = self.base_map.add_attribute(p.mangle_name())
                assert self.base_map.index == len(l)
                l.append(w_val)
        if self.parentclass is not None:
            parent = self.parentclass
            if parent.initial_storage_w is None:
                parent._create_initial_storage(space)
            allattrs = parent.base_map.get_all_attrs()
            allmykeys = self.base_map.get_all_keys()
            d = {}
            for key in allmykeys:
                d[key] = None
            for parent_attr in allattrs:
                key = parent_attr.name
                w_value = parent.initial_storage_w[parent_attr.index]
                if (key.startswith('\x00*\x00') and key[3:] in d):
                    continue
                if key in d:
                    continue
                self.base_map = self.base_map.add_attribute(key)
                l.append(w_value)
        self.initial_storage_w = l[:]  # make it non-resizable

    def lookup_property_name(self,
                             TYPE,
                             interp,
                             this,
                             name,
                             contextclass,
                             w_newvalue=None,
                             givenotice=True):
        # Returns the name to use for accessing '$a->name' on instances:
        # usually just 'name', but may be mangled in case it's a non-static
        # protected or private attribute.  This also does access checks and
        # raises if forbidden.  Note that a static member has an effect here
        # too, even though it's not actually used later, as in PHP.
        # (All lookup() methods may raise a VisibilityError.)
        property = self._lookup_property(name, contextclass,
                                         not interp.allow_direct_class_access)
        if property is None:
            return name
        elif givenotice and property.is_static():
            interp.strict("Accessing static property %s as non static" %
                          property.repr())
        # the following can raise SpecialPropertyReturn, otherwise does
        # nothing in case it's a builtin property
        property.special_lookup(TYPE, interp, this, w_newvalue)
        return property.mangle_name()

    def can_access_protected_properties_from(self, contextclass):
        return contextclass is not None and (self.is_parent_of(contextclass) or
                                             contextclass.is_parent_of(self))

    def check_access_and_demangle_property(self, name, contextclass):
        if not name.startswith('\x00'):
            return name
        i = name.find('\x00', 1)
        if i < 0:
            return name  # malformed name?
        prvname = name[1:i]
        # protected property
        if prvname == "*":
            if self.can_access_protected_properties_from(contextclass):
                return name[i + 1:]
            return None
        # private property
        if (contextclass is not None and contextclass.is_parent_of(self)
                and prvname == contextclass.name):
            return name[i + 1:]
        else:
            return None

    def get_methods(self, contextclass):
        '''Returns a list of accessible method names for the given context.'''
        methods = []
        for method_name, method in self.methods.iteritems():
            try:
                self._visibility_check(method, method_name, contextclass)
                methods.append(method.get_name())
            except VisibilityError:
                pass

        return methods

    def lookup_staticmember(self, name, contextclass, check_visibility):
        property = self._lookup_property(name, contextclass, check_visibility)
        if property is not None and property.is_static():
            return property
        return None

    def _lookup_property(self, name, contextclass, check_visibility):
        self = jit.hint(self, promote=True)
        #name = jit.hint(name, promote=True) should be promote_string
        return self._lookup_property_elidable(name, contextclass,
                                              check_visibility)

    @jit.elidable_promote()
    def _lookup_property_elidable(self, name, contextclass, check_visibility):
        # Implementation of the rules described in the big comment in
        # instanceobject.py
        if (contextclass is not None and contextclass.is_parent_of(self)):
            try:
                result = contextclass.properties[name]
            except KeyError:
                pass
            else:
                if result.is_private():
                    return result
        try:
            result = self.properties[name]
        except KeyError:
            return None
        if check_visibility:
            self._visibility_check(result, name, contextclass)
        return result

    def locate_static_method(self, name, contextclass, check_visibility):
        try:
            return self._lookup_static_method(name, contextclass,
                                              check_visibility)
        except VisibilityError:
            callstatic = self.method__callstatic
            if callstatic is None:
                raise
            return Method(W_UnderUnderCall(name, callstatic.method_func),
                          callstatic.access_flags, self)

    @jit.elidable_promote()
    def _lookup_static_method(self, name, contextclass, check_visibility):
        key = name.lower()
        try:
            result = self.methods[key]
        except KeyError:
            raise VisibilityError("undefined", self, name, None)
        if check_visibility:
            self._visibility_check(result, name, contextclass)
        return result

    def locate_method(self,
                      name,
                      contextclass,
                      searchclass=None,
                      check_visibility=True):
        if searchclass is None:
            searchclass = self
        try:
            return self._lookup_method(name, contextclass, check_visibility)
        except VisibilityError:
            call = searchclass.method__call
            if call is None:
                raise
            return Method(W_UnderUnderCall(name, call.method_func),
                          call.access_flags, searchclass)

    @jit.elidable_promote()
    def _lookup_method(self, name, contextclass, check_visibility):
        key = name.lower()
        if (contextclass is not None and contextclass.is_parent_of(self)):
            try:
                result = contextclass.methods[key]
                if result.is_private():
                    if check_visibility:
                        contextclass._visibility_check(result, name,
                                                       contextclass)
                    return result
            except KeyError:
                pass
        try:
            result = self.methods[key]
        except KeyError:
            raise VisibilityError("undefined", self, name, None)
        if check_visibility:
            self._visibility_check(result, name, contextclass)
        return result

    def _visibility_check(self, result, name, contextclass):
        if result.is_protected():
            if not self.can_access_protected_properties_from(contextclass):
                raise VisibilityError("protected", result.getclass(), name,
                                      contextclass)
        elif result.is_private():
            if result.getclass() is not contextclass:
                raise VisibilityError("private", result.getclass(), name,
                                      contextclass)

    @jit.elidable
    def is_parent_of(self, otherclass):
        while otherclass is not self:
            otherclass = otherclass.parentclass
            if otherclass is None:
                return False
        return True

    @jit.elidable_promote()
    def is_subclass_of_class_or_intf_name(self, parent_or_interface_name):
        return parent_or_interface_name.lower() in self.all_parents

    def get_super_method(self, methname, superclass, check_visibility):
        return self.locate_method(methname,
                                  superclass,
                                  searchclass=superclass,
                                  check_visibility=check_visibility)

    def _static_call_warning(self, interp, method, assuming_this):
        interp.strict("Non-static method %s should not be "
                      "called statically%s" % (method.repr(), assuming_this))

    def getstaticmeth(self, methname, contextclass, context_w_this, interp):
        check_vis = not interp.allow_direct_class_access
        if contextclass is not None and self.is_parent_of(contextclass):
            return self.get_super_method(methname, contextclass, check_vis)
        method = self.locate_static_method(methname, contextclass, check_vis)
        if not method.is_static():
            if context_w_this is None:
                self._static_call_warning(interp, method, "")
            elif not self.is_parent_of(context_w_this.klass):
                self._static_call_warning(
                    interp, method,
                    ", assuming $this from incompatible context")
        return method

    def lookup_w_constant(self, space, constantname):
        try:
            w_value = self.constants_w[constantname]
        except KeyError:
            return None
        return w_value.eval_static(space)

    def check_constructor_from_context(self, interp, contextclass):
        if self.is_abstract():
            if self.is_interface():
                cat = "interface"
            else:
                cat = "abstract class"
            interp.fatal("Cannot instantiate %s %s" % (cat, self.name))
        method = self.constructor_method
        visibility_error = ""  # please the annotator
        if method is None:
            return  # no constructor at all
        elif method.is_public():
            return  # public constructor
        elif method.is_protected():
            if contextclass is not None and (self.is_parent_of(contextclass) or
                                             contextclass.is_parent_of(self)):
                return  # protected constructor
            visibility_error = "protected"
        elif method.is_private():
            if contextclass is self:
                return  # private constructor
            visibility_error = "private"
        interp.fatal("Call to %s %s from invalid context" %
                     (visibility_error, method.repr()))

    def needs_ref(self, i):
        method = self.constructor_method
        if method is not None:
            return method.method_func.needs_ref(i)
        return False

    def get_fresh_storage_w(self, space):
        return self.get_initial_storage_w(space)[:]

    def get_initial_storage_w(self, space):
        if self.initial_storage_w is None:
            self._create_initial_storage(space)
        return self.initial_storage_w

    def call_args(self,
                  interp,
                  args_w,
                  w_this=None,
                  thisclass=None,
                  closureargs=None):
        storage_w = self.get_fresh_storage_w(interp.space)
        w_res = self.create_instance(interp, storage_w)
        method = self.constructor_method
        if method is not None:
            assert isinstance(method, Method)
            method.method_func.call_args(interp,
                                         args_w,
                                         w_this=w_res,
                                         thisclass=self)
        return w_res

    def create_instance(self, interp, storage_w):
        if self.custom_instance_class is not None:
            w_res = self.custom_instance_class(self, storage_w)
            w_res.setup(interp)
        else:
            w_res = W_InstanceObject(self, storage_w)
        return w_res

    def get_empty_instance(self, space):
        return self.create_instance(space.ec.interpreter,
                                    self.get_fresh_storage_w(space))

    def do_clone(self, interp, w_obj1, contextclass):
        assert isinstance(w_obj1, W_InstanceObject)

        w_res = w_obj1._clone(interp)
        method = self.method__clone
        if method is not None:
            try:
                self._visibility_check(method, method.get_identifier(),
                                       contextclass)
            except VisibilityError as exc:
                exc.reraise_magic(interp)
        if method is not None:
            method.method_func.call_args(interp, [],
                                         w_this=w_res,
                                         thisclass=self)
        return w_res

    def do_get(self, interp, w_obj, attr, isref):
        key = (w_obj, attr)
        recursion = rec_custom_get_set
        if key in recursion:
            interp.notice("Undefined property: %s::$%s" % (self.name, attr))
            w_res = interp.space.w_Null
        else:
            recursion[key] = None
            try:
                method = self.method__get
                space = interp.space
                w_res = method.method_func.call_args(interp,
                                                     [space.newstr(attr)],
                                                     w_this=w_obj,
                                                     thisclass=self)
            finally:
                del recursion[key]
        if isref and not isinstance(w_res, W_Reference):
            interp.notice("Indirect modification of overloaded property "
                          "%s::$%s has no effect" % (self.name, attr))
            w_res = W_Reference(w_res)
        return w_res

    def do_set(self, interp, w_obj, attr, w_value):
        key = (w_obj, attr)
        space = interp.space
        recursion = rec_custom_get_set
        if key in recursion:
            return False
        recursion[key] = None
        try:
            method = self.method__set
            method.method_func.call_args(interp, [space.newstr(attr), w_value],
                                         w_this=w_obj,
                                         thisclass=self)
        finally:
            del recursion[key]
        return True

    def do_unset(self, interp, w_obj, attr):
        key = (w_obj, attr)
        recursion = rec_custom_get_set
        if key in recursion:
            return False
        recursion[key] = None
        try:
            method = self.method__unset
            method.method_func.call_args(interp, [interp.space.newstr(attr)],
                                         w_this=w_obj,
                                         thisclass=self)
        finally:
            del recursion[key]
        return True

    def do_isset(self, interp, w_obj, attr):
        key = (w_obj, attr)
        recursion = rec_custom_get_set
        if key in recursion:
            return None
        recursion[key] = None
        try:
            method = self.method__isset
            w_res = method.method_func.call_args(interp,
                                                 [interp.space.newstr(attr)],
                                                 w_this=w_obj,
                                                 thisclass=self)
        finally:
            del recursion[key]
        return w_res

    def do_tostring(self, space, w_obj, quiet=False):
        method = self.method__tostring
        if method is None:
            if not quiet:
                space.ec.recoverable_fatal("Object of class %s could not be "
                                           "converted to string" % self.name)
                return space.newstr("")
            else:
                return None
        try:
            w_res = method.method_func.call_args(space.ec.interpreter, [],
                                                 w_this=w_obj,
                                                 thisclass=self)
        except PHPException:
            raise space.ec.fatal("Method %s::__toString() must not "
                                 "throw an exception" % self.name)
        if not isinstance(w_res, W_StringObject):
            space.ec.catchable_fatal("Method %s::__toString() must "
                                     "return a string value" % self.name)
        return w_res

    def get_invoke_method(self, w_obj):
        method = self.method__invoke
        if method is None:
            return None
        return W_InvokeCall(self, method.method_func, w_obj)

    def get_all_nonstatic_special_properties(self):
        result = self._all_nonstatic_special_properties
        if result is None:
            lst = []
            for base in self.immediate_parents:
                lst.extend(base.get_all_nonstatic_special_properties())
            for prop in self.properties.values():
                if not prop.is_static() and prop.is_special:
                    lst.append((prop.mangle_name(), prop))
            result = lst[:]
            self._all_nonstatic_special_properties = result
        return result
Пример #10
0
 def __init__(self, name):
     self.name = name
     self.base_map = Terminator(self)
Пример #11
0
class ClassBase(AbstractFunction):
    access_flags = 0
    extends_name = None
    base_interface_names = None
    constructor_method = None
    method__clone = None
    method__get = None
    method__set = None
    method__unset = None
    method__isset = None
    method__call = None
    method__callstatic = None
    method__tostring = None
    method__invoke = None
    custom_instance_class = None
    is_iterator = False
    immediate_parents = None
    parentclass = None

    def __init__(self, name):
        self.name = name
        self.identifier = name.lower()
        self.properties = OrderedDict()
        self.property_decl = []
        self.methods = OrderedDict()
        self.constants_w = {}
        self.all_parents = {self.identifier: None}  #classes and intfs
        self.base_map = Terminator()
        self.initial_storage_w = None

    def __repr__(self):
        if self.is_interface():
            cat = 'Interface'
        else:
            cat = 'Class'
        return '<%s %r>' % (cat, self.name)

    def is_interface(self):
        return (self.access_flags & consts.ACC_INTERFACE) != 0

    def is_final(self):
        return bool(self.access_flags & consts.ACC_FINAL)

    def is_abstract(self):
        return bool(self.access_flags & consts.ACC_ABSTRACT)

    def _init_constructor(self):
        if '__construct' in self.methods:
            method = self.methods['__construct']
        elif self.identifier in self.methods:
            method = self.methods[self.identifier]
        else:
            return
        if method.is_static():
            raise CompilerError("Constructor %s cannot be static" %
                                (method.repr()))
        self.constructor_method = method

    def _make_property(self, prop, w_initial_value):
        if isinstance(prop, tuple):
            name, access_flags = prop
            p = Property(name, self, access_flags)
            self.properties[name] = p
            self.property_decl.append(p)
            if not p.is_static():
                p.value = w_initial_value
            else:
                if not isinstance(w_initial_value, W_Object):
                    r_value = w_initial_value
                else:
                    r_value = W_Reference(w_initial_value)
                p.r_value = r_value
        elif not we_are_translated():  # compile time only
            prop = prop.build(self)
            self.properties[prop.name] = prop
            self.property_decl.append(prop)

    def _collect_all_methods(self):
        all_methods = []
        seen = {}
        seenclasses = {}
        seenclasses[None] = None
        pending = [self]
        while len(pending) > 0:
            fromclass = pending.pop(0)
            if fromclass in seenclasses:
                continue
            seenclasses[fromclass] = None
            for meth_id, m in fromclass.methods.iteritems():
                if meth_id not in seen:
                    seen[meth_id] = None
                    all_methods.append(m)
            pending.extend(fromclass.immediate_parents)
        return all_methods

    def _check_abstract_methods(self):
        if self.is_abstract():
            return
        all_methods = self._collect_all_methods()
        abstract_methods = []
        for m in all_methods:
            if m.is_abstract():
                abstract_methods.append("%s::%s" %
                                        (m.getclass().name, m.get_name()))
        if abstract_methods:
            msg = _msg_abstract(self.name, abstract_methods)
            raise ClassDeclarationError(msg)

    def _inherit_method(self, parent_method):
        meth_id = parent_method.get_identifier()
        if meth_id not in self.methods:
            self.methods[meth_id] = parent_method
        else:
            method = self.methods[meth_id]
            self._check_inheritance(method, parent_method)

    def _check_inheritance(self, method, parent_method):
        if parent_method.is_final():
            raise ClassDeclarationError("Cannot override final method %s" %
                                        parent_method.repr())
        if parent_method.is_public() and not method.is_public():
            raise ClassDeclarationError(
                "Access level to %s must be public (as in class %s)" %
                (method.repr(), parent_method.getclass().name))
        if parent_method.is_protected() and method.is_private():
            raise ClassDeclarationError(
                "Access level to %s must be protected (as in class %s) or "
                "weaker" % (method.repr(), parent_method.getclass().name))
        if method.is_static() and not parent_method.is_static():
            raise ClassDeclarationError(
                "Cannot make non static method %s static in class %s" %
                (parent_method.repr(), method.getclass().name))
        if not method.is_static() and parent_method.is_static():
            raise ClassDeclarationError(
                "Cannot make static method %s non static in class %s" %
                (parent_method.repr(), method.getclass().name))

    def _inherit_property(self, prop):
        if prop.is_private():
            return
        name = prop.name
        if name not in self.properties:
            self.properties[name] = prop.copy_to(self)
            return
        derived = self.properties[name]
        if derived.is_static() and not prop.is_static():
            raise ClassDeclarationError(
                "Cannot redeclare non static %s as static %s" %
                (prop.repr(), derived.repr()))
        if not derived.is_static() and prop.is_static():
            raise ClassDeclarationError(
                "Cannot redeclare static %s as non static %s" %
                (prop.repr(), derived.repr()))
        if prop.is_public() and not derived.is_public():
            raise ClassDeclarationError("Access level to %s must be public "
                                        "(as in class %s)" %
                                        (derived.repr(), prop.klass.name))
        if prop.is_protected() and derived.is_private():
            raise ClassDeclarationError("Access level to %s must be protected "
                                        "(as in class %s) or weaker" %
                                        (derived.repr(), prop.klass.name))

    def _init_magic_methods(self):
        for name in magic_methods_unrolled:
            if name in self.methods:
                method = self.methods[name]
            else:
                continue
            if name == '__call':
                if (method.getclass() == self
                        and (method.is_static() or not method.is_public())):
                    self.space.ec.warn(
                        "The magic method __call() must have "
                        "public visibility and cannot be static")
            setattr(self, 'method' + name, method)

    def _create_initial_storage(self, space):
        l = []
        for p in self.property_decl:
            if not p.is_static() and not p.is_special:
                w_val = p.value.eval_static(space)
                self.base_map = self.base_map.add_attribute(p.mangle_name())
                assert self.base_map.index == len(l)
                l.append(w_val)
        if self.parentclass is not None:
            parent = self.parentclass
            if parent.initial_storage_w is None:
                parent._create_initial_storage(space)
            allattrs = parent.base_map.get_all_attrs()
            allmykeys = self.base_map.get_all_keys()
            d = {}
            for key in allmykeys:
                d[key] = None
            for parent_attr in allattrs:
                key = parent_attr.name
                w_value = parent.initial_storage_w[parent_attr.index]
                if (key.startswith('\x00*\x00') and key[3:] in d):
                    continue
                if key in d:
                    continue
                self.base_map = self.base_map.add_attribute(key)
                l.append(w_value)
        self.initial_storage_w = l[:]  # make it non-resizable

    def get_identifier(self):
        return self.identifier

    def lookup_property_name(self,
                             TYPE,
                             interp,
                             this,
                             name,
                             contextclass,
                             w_newvalue=None,
                             givenotice=True):
        # Returns the name to use for accessing '$a->name' on instances:
        # usually just 'name', but may be mangled in case it's a non-static
        # protected or private attribute.  This also does access checks and
        # raises if forbidden.  Note that a static member has an effect here
        # too, even though it's not actually used later, as in PHP.
        # (All lookup() methods may raise a VisibilityError.)
        property = self._lookup_property(name, contextclass)
        if property is None:
            return name
        elif givenotice and property.is_static():
            self.space.ec.strict("Accessing static property %s as non static" %
                                 property.repr())
        # the following can raise SpecialPropertyReturn, otherwise does
        # nothing in case it's a builtin property
        property.special_lookup(TYPE, interp, this, w_newvalue)
        return property.mangle_name()

    def can_access_protected_properties_from(self, contextclass):
        return contextclass is not None and (self.is_parent_of(contextclass) or
                                             contextclass.is_parent_of(self))

    def check_access_and_demangle_property(self, name, contextclass):
        if not name.startswith('\x00'):
            return name
        i = name.find('\x00', 1)
        if i < 0:
            return name  # malformed name?
        prvname = name[1:i]
        # protected property
        if prvname == "*":
            if self.can_access_protected_properties_from(contextclass):
                return name[i + 1:]
            return None
        # private property
        if (contextclass is not None and contextclass.is_parent_of(self)
                and prvname == contextclass.name):
            return name[i + 1:]
        else:
            return None

    def lookup_staticmember(self, name, contextclass):
        try:
            property = self._lookup_property(name, contextclass)
        except VisibilityError, e:
            raise e.reraise_property(self.space.ec.interpreter)
        if property is not None and property.is_static():
            return property
        return None
Пример #12
0
class ClassBase(AbstractFunction):
    access_flags = 0
    extends_name = None
    base_interface_names = None
    constructor_method = None
    method__clone = None
    method__get = None
    method__set = None
    method__unset = None
    method__isset = None
    method__call = None
    method__callstatic = None
    method__tostring = None
    method__invoke = None
    custom_instance_class = None
    is_iterator = False
    immediate_parents = None
    parentclass = None

    def __init__(self, name):
        self.name = name
        self.identifier = name.lower()
        self.properties = OrderedDict()
        self.property_decl = []
        self.methods = OrderedDict()
        self.constants_w = {}
        self.all_parents = {self.identifier: None}  #classes and intfs
        self.base_map = Terminator()
        self.initial_storage_w = None

    def __repr__(self):
        if self.is_interface():
            cat = 'Interface'
        else:
            cat = 'Class'
        return '<%s %r>' % (cat, self.name)

    def is_interface(self):
        return (self.access_flags & consts.ACC_INTERFACE) != 0

    def is_final(self):
        return bool(self.access_flags & consts.ACC_FINAL)

    def is_abstract(self):
        return bool(self.access_flags & consts.ACC_ABSTRACT)

    def _init_constructor(self):
        if '__construct' in self.methods:
            method = self.methods['__construct']
        elif self.identifier in self.methods:
            method = self.methods[self.identifier]
        else:
            return
        if method.is_static():
            raise CompilerError("Constructor %s cannot be static" %
                                (method.repr()))
        self.constructor_method = method

    def _make_property(self, prop, w_initial_value):
        if isinstance(prop, tuple):
            name, access_flags = prop
            p = Property(name, self, access_flags)
            self.properties[name] = p
            self.property_decl.append(p)
            if not p.is_static():
                p.value = w_initial_value
            else:
                if not isinstance(w_initial_value, W_Object):
                    r_value = w_initial_value
                else:
                    r_value = W_Reference(w_initial_value)
                p.r_value = r_value
        elif not we_are_translated(): # compile time only
            prop = prop.build(self)
            self.properties[prop.name] = prop
            self.property_decl.append(prop)

    def _collect_all_methods(self):
        all_methods = []
        seen = {}
        seenclasses = {}
        seenclasses[None] = None
        pending = [self]
        while len(pending) > 0:
            fromclass = pending.pop(0)
            if fromclass in seenclasses:
                continue
            seenclasses[fromclass] = None
            for meth_id, m in fromclass.methods.iteritems():
                if meth_id not in seen:
                    seen[meth_id] = None
                    all_methods.append(m)
            pending.extend(fromclass.immediate_parents)
        return all_methods

    def _check_abstract_methods(self):
        if self.is_abstract():
            return
        all_methods = self._collect_all_methods()
        abstract_methods = []
        for m in all_methods:
            if m.is_abstract():
                abstract_methods.append("%s::%s" % (
                    m.getclass().name, m.get_name()))
        if abstract_methods:
            msg = _msg_abstract(self.name, abstract_methods)
            raise ClassDeclarationError(msg)

    def _inherit_method(self, parent_method):
        meth_id = parent_method.get_identifier()
        if meth_id not in self.methods:
            self.methods[meth_id] = parent_method
        else:
            method = self.methods[meth_id]
            self._check_inheritance(method, parent_method)

    def _check_inheritance(self, method, parent_method):
        if parent_method.is_final():
            raise ClassDeclarationError(
                "Cannot override final method %s" % parent_method.repr())
        if parent_method.is_public() and not method.is_public():
            raise ClassDeclarationError(
                "Access level to %s must be public (as in class %s)" %
                (method.repr(), parent_method.getclass().name))
        if parent_method.is_protected() and method.is_private():
            raise ClassDeclarationError(
                "Access level to %s must be protected (as in class %s) or "
                "weaker" % (method.repr(), parent_method.getclass().name))
        if method.is_static() and not parent_method.is_static():
            raise ClassDeclarationError(
                "Cannot make non static method %s static in class %s" %
                (parent_method.repr(), method.getclass().name))
        if not method.is_static() and parent_method.is_static():
            raise ClassDeclarationError(
                "Cannot make static method %s non static in class %s" %
                (parent_method.repr(), method.getclass().name))

    def _inherit_property(self, prop):
        if prop.is_private():
            return
        name = prop.name
        if name not in self.properties:
            self.properties[name] = prop.copy_to(self)
            return
        derived = self.properties[name]
        if derived.is_static() and not prop.is_static():
            raise ClassDeclarationError("Cannot redeclare non static %s as static %s" %
                    (prop.repr(), derived.repr()))
        if not derived.is_static() and prop.is_static():
            raise ClassDeclarationError("Cannot redeclare static %s as non static %s" %
                    (prop.repr(), derived.repr()))
        if prop.is_public() and not derived.is_public():
            raise ClassDeclarationError("Access level to %s must be public "
                    "(as in class %s)" %
                    (derived.repr(), prop.klass.name))
        if prop.is_protected() and derived.is_private():
            raise ClassDeclarationError("Access level to %s must be protected "
                    "(as in class %s) or weaker" %
                    (derived.repr(), prop.klass.name))

    def _init_magic_methods(self):
        for name in magic_methods_unrolled:
            if name in self.methods:
                method = self.methods[name]
            else:
                continue
            if name == '__call':
                if (method.getclass() == self and
                        (method.is_static() or not method.is_public())):
                    self.space.ec.warn("The magic method __call() must have "
                            "public visibility and cannot be static")
            setattr(self, 'method' + name, method)

    def _create_initial_storage(self, space):
        l = []
        for p in self.property_decl:
            if not p.is_static() and not p.is_special:
                w_val = p.value.eval_static(space)
                self.base_map = self.base_map.add_attribute(p.mangle_name())
                assert self.base_map.index == len(l)
                l.append(w_val)
        if self.parentclass is not None:
            parent = self.parentclass
            if parent.initial_storage_w is None:
                parent._create_initial_storage(space)
            allattrs = parent.base_map.get_all_attrs()
            allmykeys = self.base_map.get_all_keys()
            d = {}
            for key in allmykeys:
                d[key] = None
            for parent_attr in allattrs:
                key = parent_attr.name
                w_value = parent.initial_storage_w[parent_attr.index]
                if (key.startswith('\x00*\x00') and
                        key[3:] in d):
                    continue
                if key in d:
                    continue
                self.base_map = self.base_map.add_attribute(key)
                l.append(w_value)
        self.initial_storage_w = l[:] # make it non-resizable

    def get_identifier(self):
        return self.identifier

    def lookup_property_name(self, TYPE, interp, this, name, contextclass,
                             w_newvalue=None, givenotice=True):
        # Returns the name to use for accessing '$a->name' on instances:
        # usually just 'name', but may be mangled in case it's a non-static
        # protected or private attribute.  This also does access checks and
        # raises if forbidden.  Note that a static member has an effect here
        # too, even though it's not actually used later, as in PHP.
        # (All lookup() methods may raise a VisibilityError.)
        property = self._lookup_property(name, contextclass)
        if property is None:
            return name
        elif givenotice and property.is_static():
            self.space.ec.strict("Accessing static property %s as non static" %
                                 property.repr())
        # the following can raise SpecialPropertyReturn, otherwise does
        # nothing in case it's a builtin property
        property.special_lookup(TYPE, interp, this, w_newvalue)
        return property.mangle_name()

    def can_access_protected_properties_from(self, contextclass):
        return contextclass is not None and (
            self.is_parent_of(contextclass) or
            contextclass.is_parent_of(self))

    def check_access_and_demangle_property(self, name, contextclass):
        if not name.startswith('\x00'):
            return name
        i = name.find('\x00', 1)
        if i < 0:
            return name      # malformed name?
        prvname = name[1:i]
        # protected property
        if prvname == "*":
            if self.can_access_protected_properties_from(contextclass):
                return name[i+1:]
            return None
        # private property
        if (contextclass is not None and contextclass.is_parent_of(self)
                and prvname == contextclass.name):
            return name[i+1:]
        else:
            return None

    def lookup_staticmember(self, name, contextclass):
        try:
            property = self._lookup_property(name, contextclass)
        except VisibilityError, e:
            raise e.reraise_property(self.space.ec.interpreter)
        if property is not None and property.is_static():
            return property
        return None