def _method_decl(self, decl, ctx): af = decl.access_flags if self.is_interface(): af |= consts.ACC_ABSTRACT function = decl.prepare_function(ctx, is_method_of=self, static=bool(af & consts.ACC_STATIC)) m = Method(function, af, self) meth_id = function.get_identifier() if meth_id in self.methods: raise CompilerError("Cannot redeclare %s::%s" % (self.name, function.name)) self.methods[meth_id] = m # if m.is_abstract(): if m.is_private(): raise CompilerError("Abstract function %s cannot be " "declared private" % (m.repr())) elif decl.body is not None: raise CompilerError("Abstract function %s cannot " "contain body" % (m.repr())) else: if decl.body is None: raise CompilerError("Non-abstract method %s must " "contain body" % (m.repr()))
def _property_decl(self, decl, ctx): if self.is_interface(): raise CompilerError("Interfaces may not include member variables") name = decl.name if name in self.properties: raise CompilerError("Cannot redeclare %s::$%s" % (self.name, name)) if decl.expr is None: w_initial_value = ctx.space.w_Null else: w_initial_value = decl.expr.wrap(ctx, ctx.space) self._make_property((name, decl.access_flags), w_initial_value)
def use_alias(self, name, short_name): id = short_name.lower() for decl in self.classes: if decl.get_short_name().lower() == id and decl.name != name: raise CompilerError("Cannot use %s as %s because the name is " "already in use" % (name, short_name)) self.use_aliases[short_name] = name
def _property_decl(self, decl, ctx): if self.is_interface(): raise CompilerError("Interfaces may not include member variables") name = decl.name if name in self.property_decl: raise CompilerError("Cannot redeclare %s::$%s" % (self.name, name)) if decl.expr is None: w_initial_value = ctx.space.w_Null else: w_initial_value = decl.expr.wrap(ctx, ctx.space) p = PropertyDeclaration(name, decl.access_flags, w_initial_value) if p.is_abstract(): raise CompilerError("Properties cannot be declared abstract") if p.is_final(): raise CompilerError("Cannot declare property %s::$%s final, " "the final modifier is allowed only for " "methods and classes" % (self.name, decl.name)) self.property_decl[name] = p
def _check_method(self, m): ONE_ARGS = ('__isset', '__unset') NO_ARGS = ('__clone', '__destruct') func = m.func arity = len(func.get_signature().args) ident = func.get_identifier() if ident in ONE_ARGS and arity != 1: raise CompilerError("Method %s::%s() must take " "exactly 1 argument" % (self.name, func.name)) if ident in NO_ARGS and arity != 0: raise CompilerError("Method %s::%s() cannot accept any arguments" % (self.name, func.name)) if ident == '__destruct' and m.is_static(): raise CompilerError("Destructor %s::%s() cannot be static" % (self.name, func.name)) if ident == '__clone' and m.is_static(): raise CompilerError("Clone method %s::%s() cannot be static" % (self.name, func.name))
def _check_abstract_local(self): if self.is_abstract(): return abstract_methods = [] for decl in self.method_decl.itervalues(): if decl.is_abstract(): abstract_methods.append("%s::%s" % (self.name, decl.func.name)) if abstract_methods: msg = _msg_abstract(self.name, abstract_methods) raise CompilerError(msg)
def _check_abstract_local(self): if self.is_abstract(): return abstract_methods = [] for m in self.methods.itervalues(): if m.is_abstract(): abstract_methods.append("%s::%s" % (self.name, m.get_name())) if abstract_methods: msg = _msg_abstract(self.name, abstract_methods) raise CompilerError(msg)
def create_bytecode(self): if self.pending_gotos: raise CompilerError("'goto' to undefined label '%s'" % (self.pending_gotos.keys()[0], )) return ByteCode("".join(self.data), self.consts[:], self.names[:], self.varnames[:], self.decls, self.classes, self.functions, self.filename, self.sourcelines, self.method_of_class, self.startlineno, self.lineno_map[:], self.name, self.superglobals, self.this_var_num, self.static_vars)
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 _init_constructor(self): if '__construct' in self.method_decl: method = self.method_decl['__construct'] elif self.get_identifier() in self.method_decl: method = self.method_decl[self.get_identifier()] else: return if method.is_static(): raise CompilerError("Constructor %s::%s() cannot be static" % (self.name, method.func.name)) self.ctor_id = method.func.get_identifier()
def prepare_function(ctx, name, argdecls, closuredecls, lineno, returns_reference, body, is_method_of=None, static=False): new_context = CompilerContext(ctx.filename, ctx.sourcelines, lineno, ctx.space, name, is_global=False) args = [] typehints = [] for i, arg in enumerate(argdecls): assert isinstance(arg, Argument) name = arg.name if arg.is_reference: opcode = consts.ARG_REFERENCE else: opcode = consts.ARG_ARGUMENT if arg.defaultvalue is None: w_default = None else: w_default = arg.defaultvalue.wrap(ctx, ctx.space) if arg.typehint is not None: typehints.append((i, arg.typehint, w_default is ctx.space.w_Null)) args.append((opcode, name, w_default)) if not new_context.force_var_name(name): ctx.warn("Argument list contains twice '$%s'" % (name,)) if is_method_of is not None: new_context.method_of_class = is_method_of new_context.current_class = is_method_of if not static: new_context.this_var_num = len(argdecls) if not new_context.force_var_name("this"): raise CompilerError("Cannot re-assign $this") new_context.returns_reference = returns_reference # if body is not None: for decl in closuredecls: new_context.force_var_name(decl.name) # for i, typehint, allow_null in typehints: j = i * 2 + allow_null if typehint == 'array': new_context.emit(consts.TYPEHINT_ARRAY, j) else: new_context.emit(consts.LOAD_NAME, new_context.create_name(typehint)) new_context.emit(consts.TYPEHINT_CLASS, j) # body.compile(new_context) new_context.emit(consts.LOAD_NULL) new_context.emit(consts.RETURN) else: new_context.emit(consts.ABSTRACT_METHOD) bytecode = new_context.create_bytecode() return Function(args, closuredecls, typehints, bytecode)
def patch_with(self, pos, a): assert self.data[pos - 3] == '\xA0' assert self.data[pos - 2] == '\x8C' assert self.data[pos - 1] == '\x07' self.data[pos - 3] = chr((a & 0x7f) | 0x80) a ^= 0x80 a >>= 7 self.data[pos - 2] = chr((a & 0x7f) | 0x80) a ^= 0x80 a >>= 7 if a >= 0x80: raise CompilerError("Internal error: bytecode too big") self.data[pos - 1] = chr(a)
def _compile_method(self, decl, ctx): if self.is_interface(): decl.access_flags |= consts.ACC_ABSTRACT meth_id = decl.name.lower() if meth_id in self.method_decl: raise CompilerError("Cannot redeclare %s::%s()" % (self.name, decl.name)) if decl.is_abstract(): if decl.is_private(): raise CompilerError("Abstract function %s::%s() cannot be " "declared private" % (self.name, decl.name)) elif decl.body is not None: raise CompilerError("Abstract function %s::%s() cannot " "contain body" % (self.name, decl.name)) else: if decl.body is None: raise CompilerError("Non-abstract method %s::%s() must " "contain body" % (self.name, decl.name)) function = decl.prepare_function(ctx, is_method_of=self) m = MethodDeclaration(function, decl.access_flags, self) self._check_method(m) self.method_decl[meth_id] = m
def initialize(self, node, ctx): self.access_flags = node.access_flags self.extends_name = node.extends self.base_interface_names = node.baseinterfaces for decl in node.body.getstmtlist(): if isinstance(decl, ConstDecl): if decl.name in self.constants_w: raise CompilerError( "Cannot redefine class constant %s::%s" % (self.name, decl.name)) self.constants_w[decl.name] = decl.const_expr.wrap( ctx, ctx.space) elif isinstance(decl, MethodDecl): self._method_decl(decl, ctx) else: assert isinstance(decl, PropertyDecl) self._property_decl(decl, ctx) self._check_abstract_local() self._init_constructor()
def _emit_break_continue_pop(self, levels): # we must (sometimes) emit a BREAK_CONTINUE_POP instruction assert levels >= 1 looplabels = None # compute the total stack items to remove remove_stack = 0 for i in range(levels): num = len(self.llabels) - i - 1 if num < 0: raise CompilerError("Cannot break/continue %d level%s" % (levels, "s" if levels > 1 else "")) looplabels = self.llabels[num] remove_stack += looplabels.extra_stack # we don't exit the last loop (even a 'break;' jumps just before # the final DISCARD_TOP, in case of 'foreach') if looplabels is not None: remove_stack -= looplabels.extra_stack if remove_stack > 0: self.emit(consts.BREAK_CONTINUE_POP, remove_stack) return looplabels
def __init__(self, access_flags): self.access_flags = normalize_access(access_flags) if self.is_final() and self.is_abstract(): raise CompilerError("Cannot use the final modifier on an " "abstract class member")
def prepare_function(self, name, argdecls, closuredecls, lineno, returns_reference, body, is_method_of=None, static=False): new_context = CompilerContext(self.filename, self.sourcelines, lineno, self.space, name, is_global=False) new_context.current_namespace = self.current_namespace new_context.use_aliases = self.use_aliases args = [] typehints = [] for i, arg in enumerate(argdecls): assert isinstance(arg, Argument) name = arg.name if arg.is_reference: opcode = consts.ARG_REFERENCE else: opcode = consts.ARG_ARGUMENT if arg.defaultvalue is None: w_default = None else: w_default = arg.defaultvalue.wrap(self, self.space) _hint = arg.typehint if _hint is not None: hint_str = _hint.as_unqualified() if hint_str and hint_str.lower() == 'self': if is_method_of is None: raise CompilerError("Cannot use 'self' typehint") hint = is_method_of.name elif hint_str and hint_str.lower() == 'parent': if is_method_of is None or is_method_of.extends_name is None: raise CompilerError("Cannot use 'parent' typehint") hint = is_method_of.extends_name else: hint = _hint.get_qualified_name(self) typehints.append((i, hint, w_default is self.space.w_Null)) args.append((opcode, name, w_default)) if not new_context.force_var_name(name): self.warn("Argument list contains twice '$%s'" % (name, )) if is_method_of is not None: new_context.method_of_class = is_method_of new_context.current_class = is_method_of if not static: new_context.this_var_num = len(argdecls) if not new_context.force_var_name("this"): raise CompilerError("Cannot re-assign $this") new_context.returns_reference = returns_reference # if body is not None: for decl in closuredecls: new_context.force_var_name(decl.name) # for i, typehint, allow_null in typehints: j = i * 2 + allow_null if typehint == 'array': new_context.emit(consts.TYPEHINT_ARRAY, j) else: new_context.emit(consts.LOAD_NAME, new_context.create_name(typehint)) new_context.emit(consts.TYPEHINT_CLASS, j) # body.compile(new_context) new_context.emit(consts.LOAD_NULL) new_context.emit(consts.RETURN) else: new_context.emit(consts.ABSTRACT_METHOD) bytecode = new_context.create_bytecode() return Function(args, closuredecls, typehints, bytecode)