+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-# Base module for nit tools that load, manipulate or transform NIT models
-module abstracttool
-
-import mmloader
-import syntax
-private import nit_version
-
-abstract class AbstractCompiler
- super ToolContext
-
- init(tool_name: String)
- do
- _tool_name = tool_name
- super
- register_loader(new SrcModuleLoader)
- end
-
- # The name of the tool
- # Used in help messages for instance
- readable var _tool_name: String
-
- fun exec_cmd_line
- do
- process_options
-
- if opt_version.value then
- print "{tool_name} version {nit_version}"
- exit(0)
- end
-
- if opt_help.value then
- print "usage: {tool_name} [options] file..."
- option_context.usage
- exit(0)
- end
-
- if option_context.rest.length == 0 then
- print "usage: {tool_name} [options] file..."
- option_context.usage
- exit(1)
- end
-
- var rest = option_context.rest
- var to_do = new Array[MMModule]
- info("Syntax analysis",1)
- for i in [0..rest.length[ do
- var mod = get_module_from_filename(rest[i])
- to_do.add(mod)
- end
- if opt_log.value then
- dump_context_info
- end
-
- if not to_do.is_empty and not opt_only_metamodel.value and not opt_only_parse.value then
- perform_work(to_do)
- end
- end
-
- fun perform_work(mods: Array[MMModule]) is abstract
-
- fun dump_context_info
- do
- for mod in module_hierarchy do
- mod.dump_module_info(log_directory)
- end
- var tab = new Array[MMModule]
- tab.add_all(module_hierarchy)
- var name = module_hierarchy.select_smallests(tab).join("-")
-
- var f = new OFStream.open("{log_directory}/{name}.full_class_hierarchy.new.dot")
- f.write(class_hierarchy.to_dot)
- f.close
-
- f = new OFStream.open("{log_directory}/{name}.module_hierarchy.new.dot")
- f.write(module_hierarchy.to_dot)
- f.close
- end
-end
-
-redef class MMModule
- fun dump_module_info(directory: String)
- do
- var fname = "{directory}/{name}"
- var f = new OFStream.open("{fname}.class_hierarchy.new.dot")
- f.write(class_specialization_hierarchy.to_dot)
- f.close
-
- f = new OFStream.open("{fname}.properties.log")
- for cla in local_classes do
- cla.dump_properties(f)
- f.write("\n")
- end
- f.close
- end
-end
-
-redef class MMLocalClass
- fun dump_properties(file: OStream)
- do
- file.write("class {self}\n")
- if global.visibility_level == 3 and not self == global.intro then
- file.write("\tclass not visible in this module\n")
- else if mmmodule.visibility_for(global.mmmodule) == 0 then
- file.write("\tclass is defined later in the hierarchy\n")
- else
- for p in global_properties do
- var lp = self[p]
- file.write("\t{lp}{lp.signature_for(get_type)}\n")
- end
- end
- file.write("end # {self}\n")
- end
-end
-
-redef class AAnnotations
- # Shortcut annotations and skip them completely
- redef fun visit_all(v)
- do
- # Do notning
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# iregisters slot allocation
-module allocate_iregister_slots
-
-import icode
-private import primitive_info
-
-# The main class that performs the iregister slot allocation
-# The algorithm is quite naive but works
-# Things TODO:
-# * flow control
-# * register aliasing
-# * IMove optimization
-private class IRegisterSlotAllocationVisitor
- super ICodeVisitor
- # The visitor works in two pass:
- # First pass is used to detect first and last iregisters occurences and slot groups
- # Second pass is used to assign an effective slot to iregsiters
- var _pass: Int = 0
-
- # Update locality information of r according to the current iroutine
- fun mark_locality(r: IRegister)
- do
- if r._is_local and r._local_iroutine != _current_ir then
- if r._local_iroutine == null then
- r._local_iroutine = _current_ir
- else
- r._is_local = false
- end
- end
- end
-
- redef fun visit_iregister_read(ic: ICode, r: IRegister)
- do
- # Note: the algo considers that a read implies that a write occured before:
- # it is why there is no _first assigment nor register call
- var pass = _pass
- if pass == 0 then
- mark_locality(r)
- r._last = ic
- r._slot_index = null
- else if pass == 1 and r._last == ic then
- free(r)
- end
- end
-
- redef fun visit_iregister_write(ic: ICode, r: IRegister)
- do
- var pass = _pass
- if pass == 0 then
- mark_locality(r)
- r._slot_index = null
- # The first write make it live
- if r._first == null then r._first = ic
- # A read iregistre is still live on a write
- if r._last != null then r._last = ic
- else if pass == 1 then
- if r._first == ic then
- register(r)
- else if r._last == ic then
- free(r)
- end
- end
- end
-
-
- # The current loop/closure identifier.
- # Iregister from outside a loop/closure cannot die inside the loop/closure
- # So, the only register that can die in loops/closure are those born inside the loop/closure
- var _current_rank: Int = 0
-
- # The number of the last loop/closure created
- var _max_rank: Int = 0
-
- # List of iregisters whose free is delayed because of a loop/closure
- # They will be freed at the end of each loop/closure if possible
- var _deferred_list: List[IRegister] = new List[IRegister]
-
- # Free the deferred registers (from _deferred_list) that can be freed
- # Registers that cannot be freed remain in the list
- fun deferred_free
- do
- var def = _deferred_list.iterator
- var cur = _current_rank
- while def.is_ok do
- var r = def.item
- if r._born_rank >= cur then
- free(r)
- def.delete
- end
- def.next
- end
- end
-
- redef fun visit_icode(ic)
- do
- if _pass == 1 and ic isa ILoop then
- var old_rank = _current_rank
- _max_rank += 1
- _current_rank = _max_rank
- super
- _current_rank = old_rank
- deferred_free
- else
- super
- end
- end
-
- redef fun visit_closure_defs(closdefs)
- do
- if _pass == 1 then
- var old_rank = _current_rank
- _max_rank += 1
- _current_rank = _max_rank
- super
- _current_rank = old_rank
- deferred_free
- else
- super
- end
- end
-
- # The current iroutine
- # Used to detect locality of iregisters
- var _current_ir: IRoutine
-
- redef fun visit_iroutine(ir)
- do
- var res = ir.result
- if _pass == 0 then
- var old_ir = _current_ir
- _current_ir = ir
- for r in ir.params do
- r._first = self
- mark_locality(r)
- end
- super
- if res != null then
- res._last = self
- mark_locality(res)
- end
- _current_ir = old_ir
- else
- var old_tag_slots = _tag_slots
- _tag_slots = new SlotGroup
- var old_std_slots = _std_slots
- if ir isa IClosureDef then
- _std_slots = new SlotGroup
- end
- for r in ir.params do
- register(r)
- end
- super
- if res != null then free(res)
- ir._tag_slots_nb = _tag_slots._next_index
- _tag_slots = old_tag_slots
- ir._std_slots_nb = _std_slots._next_index
- _std_slots = old_std_slots
- end
- end
-
- # Global slots are used for non local registers and some main iroutine registers
- var _global_slots: SlotGroup = new SlotGroup
-
- # Standad slots are used for local generic registers
- # In main iroutine, _global_slots == _std_slots
- var _std_slots: SlotGroup
-
- # Tag slots are used to store local tagged object registers
- var _tag_slots: SlotGroup = new SlotGroup
-
- # Assign a slot to a register according to its locality and its type
- fun register(r: IRegister)
- do
- if r._last == null then return
- assert r._slot_index == null
- r._born_rank = _current_rank
- if not r._is_local then
- _global_slots.register(r)
- else if r.stype.is_tagged then
- r._in_tag_slots = true
- _tag_slots.register(r)
- else
- _std_slots.register(r)
- end
- end
-
- # Release a register, thus its slot can be reused
- # If the register is not freable (born in a enclosing loop/closure), then the freeing is deferred
- fun free(r: IRegister)
- do
- var i = r._slot_index
- if i == null then return
- if r._born_rank < _current_rank then
- _deferred_list.add(r)
- else if r._last != null then
- if r._in_tag_slots then
- _tag_slots.free(r)
- else if r._is_local then
- _std_slots.free(r)
- else
- _global_slots.free(r)
- end
- r._last = null # Free a register only once
- end
- end
-
- # Run the slot allocation
- fun iroutine_slot_allocation
- do
- var ir = _current_ir
- visit_iroutine(ir)
- _pass = 1
- visit_iroutine(ir)
- assert _deferred_list.is_empty
- end
-
- init(ir: IRoutine)
- do
- _current_ir = ir
- _std_slots = _global_slots
- end
-end
-
-# Group or equivalent slots shared by registers
-private class SlotGroup
- # The free slots in the group
- var _free: List[Int] = new List[Int]
-
- # The next free slot
- var _next_index: Int = 0
-
- # Assign a slot to the register
- fun register(r: IRegister)
- do
- if _free.is_empty then
- r._slot_index = _next_index
- _next_index += 1
- else
- r._slot_index = _free.pop
- end
- end
-
- # Reuse the slot of the register
- fun free(r: IRegister)
- do
- _free.push(r._slot_index.as(not null))
- end
-end
-
-redef class IRoutine
- # The number of standard slots (stored in an array)
- readable var _std_slots_nb: Int = 0
-
- # The number of tag slots
- readable var _tag_slots_nb: Int = 0
-
- fun allocate_iregister_slots
- do
- var v = new IRegisterSlotAllocationVisitor(self)
- v.iroutine_slot_allocation
- end
-end
-
-redef class IRegister
- # The slot index of the register in its slot group
- # Is null if the iregister is dead
- # (or if iregister slot allocation is not performed yet)
- readable writable var _slot_index: nullable Int
-
- # Is the register local to a single iroutine?
- readable writable var _is_local: Bool = true
-
- # If is_local, then what iroutine
- readable writable var _local_iroutine: nullable IRoutine
-
- # Is the register stored in the tag slot group?
- readable writable var _in_tag_slots: Bool = false
-
- # What is the first occurences of the iregister
- # So that a slot is not needed before
- var _first: nullable Object = null
-
- # What is the last occurences of the iregister
- # So that a slot may be no more needed after
- # ("may" because of loops/closure)
- var _last: nullable Object = null
-
- # Rank of the loop/closure where the iregister is born
- # So it can die immediatly if this is the current loop/closure
- # 0 means top-level
- var _born_rank: Int = 0
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Intermediate code local analysis and optimizations
-module analysis
-
-# Global imports
-import icode
-import icode_dump
-import program
-
-# Local Analysis/Optimization
-private import allocate_iregister_slots
-private import inline_methods
-
-redef class IRoutine
- # Perfom all local optimizations
- fun optimize(m: MMModule)
- do
- inline_methods(m)
- allocate_iregister_slots
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Output the intermediate code unded a textual representation
-module icode_dump
-
-import icode
-import allocate_iregister_slots
-
-redef class IRoutine
- # Output the intermediate code representation of the iroutine
- fun dump(icd: ICodeDumper)
- do
- if not params.is_empty then
- var a = new Array[String]
- for p in params do
- a.add(icd.register(p))
- end
- icd.write "Parameters: {a.join(", ")}"
- end
- var r = result
- if r != null then
- icd.write "Result: {icd.register(r)}"
- end
- if std_slots_nb > 0 then
- icd.write "StdSlots: {std_slots_nb}"
- end
- if tag_slots_nb > 0 then
- icd.write "TagSlots: {tag_slots_nb}"
- end
- var closdecls = closure_decls
- if closdecls != null then
- for c in closdecls do
- c.dump(icd)
- end
- end
- body.dump(icd)
- end
-end
-
-redef class IClosureDecl
- # Output the intermediate code representation of the iclosuredecl
- fun dump(icd: ICodeDumper)
- do
- icd.write "Closure: {icd.closdecl(self)}"
- if default != null then
- icd.indent
- default.dump(icd)
- icd.unindent
- end
- end
-end
-
-class ICodeDumper
- readable var _dump_locations: Bool
- readable var _dump_line_numbers: Bool
- var _ids: HashMap[Object, String] = new HashMap[Object, String]
- var _last_value: Int = 0
-
- init(dump_locations: Bool, dump_line_numbers: Bool) do
- _dump_locations = dump_locations
- _dump_line_numbers = dump_line_numbers
- end
-
- # Return the name of e
- # If e is unknown, a new name is gived
- fun register(e: IRegister): String
- do
- if _ids.has_key(e) then
- return _ids[e]
- else
- var i = e.slot_index
- if i == null then
- _last_value += 1
- var s = "r{_last_value}"
- _ids[e] = s
- return s
- else
- _last_value += 1
- var s: String
- if e.in_tag_slots then
- s = "BREG{i}(r{_last_value})"
- else if e.is_local then
- s = "LREG{i}(r{_last_value})"
- else
- s = "REG{i}(r{_last_value})"
- end
- _ids[e] = s
- return s
- end
- end
- end
-
- # Returns the names of es (separated with comma)
- fun register_all(es: nullable Collection[IRegister]): String
- do
- if es == null then return ""
- var a = new Array[String]
- for e in es do
- a.add(register(e))
- end
- return a.join(", ")
- end
-
- var _last_clos: Int = 0
-
- # Return the name of e
- # If e is unknown, a new name is gived
- fun closdecl(e: IClosureDecl): String
- do
- if _ids.has_key(e) then
- return _ids[e]
- else
- _last_clos += 1
- var s = "clos{_last_clos}"
- _ids[e] = s
- return s
- end
- end
-
- var _last_label: Int = 0
- # Return the name of e
- # If e is unknown, a new name is gived
- fun lab(e: IEscapeMark): String
- do
- if _ids.has_key(e) then
- return _ids[e]
- else
- _last_label += 1
- var s = "[l{_last_label}]"
- _ids[e] = s
- return s
- end
- end
-
- var _last_line: Int = 0
- # Return the line index of e
- fun line(e: ICode): String
- do
- if _ids.has_key(e) then
- return _ids[e]
- else
- _last_line += 1
- var s = "{_last_line}"
- _ids[e] = s
- return s
- end
- end
-
- # Is the label e known? (because we goto to it)
- fun has_lab(e: IEscapeMark): Bool
- do
- return _ids.has_key(e)
- end
-
- # Output something
- fun write(s: String)
- do
- for i in [0.._indent_level[ do
- printn " "
- end
- print s
- end
-
- readable var _indent_level: Int = 0
-
- # Indent the next writes
- fun indent do _indent_level += 1
-
- # Outdent the next writes
- fun unindent do _indent_level -= 1
-end
-
-redef class ICode
- # Output the intermediate code representation
- fun dump(icd: ICodeDumper)
- do
- var result = result
- var s_loc = ""
- var s_line = ""
- var loc = location
- if loc != null and icd.dump_locations then
- s_loc = " ... {loc}"
- end
- if icd.dump_line_numbers then
- s_line = "{icd.line(self)}: "
- end
- if result == null then
- icd.write "{s_line}{dump_intern(icd)}{s_loc}"
- else
- icd.write "{s_line}{icd.register(result)} := {dump_intern(icd)}{s_loc}"
- end
- end
-
- # Output the intermediate code representation (inner method)
- fun dump_intern(icd: ICodeDumper): String do return "???"
-end
-
-redef class ICodeN
- redef fun dump(icd: ICodeDumper)
- do
- super
- var closure_defs = closure_defs
- if closure_defs != null then
- for clos in closure_defs do
- if clos == null then
- icd.write "CLOSURE = NULL"
- else
- icd.write "CLOSURE"
- icd.indent
- clos.dump(icd)
- icd.unindent
- end
- end
- end
- end
-end
-
-redef class ISeq
- redef fun dump(icd)
- do
- for ic in icodes do
- ic.dump(icd)
- end
- var mark = iescape_mark
- if mark != null and icd.has_lab(mark) then icd.write("{icd.lab(mark)}:")
- end
-end
-
-redef class IIf
- redef fun dump(icd)
- do
- icd.write "IF({icd.register(expr)}) \{"
- icd.indent
- then_seq.dump(icd)
- icd.unindent
- icd.write "} ELSE \{"
- icd.indent
- else_seq.dump(icd)
- icd.unindent
- icd.write "}"
- end
-end
-
-redef class ILoop
- redef fun dump(icd)
- do
- icd.write "LOOP \{"
- icd.indent
- for ic in icodes do
- ic.dump(icd)
- end
- icd.unindent
- icd.write "}"
- var mark = iescape_mark
- if mark != null and icd.has_lab(mark) then icd.write("{icd.lab(mark)}:")
- end
-end
-
-redef class IEscape
- redef fun dump_intern(icd)
- do
- return "ESCAPE {icd.lab(iescape_mark)}"
- end
-end
-
-redef class IAbort
- redef fun dump_intern(icd)
- do
- return "ABORT (\"{texts.join("\", \"")}\")"
- end
-end
-
-redef class ICall
- redef fun dump_intern(icd)
- do
- return "CALL {property.full_name}({icd.register_all(exprs)})"
- end
-end
-
-redef class INew
- redef fun dump_intern(icd)
- do
- return "NEW {stype}.{property.full_name}({icd.register_all(exprs)})"
- end
-end
-
-redef class ISuper
- redef fun dump_intern(icd)
- do
- return "SUPER {property.full_name}({icd.register_all(exprs)})"
- end
-end
-
-redef class IStaticCall
- redef fun dump_intern(icd)
- do
- return "STATIC_CALL {property.full_name}({icd.register_all(exprs)})"
- end
-end
-
-redef class IAllocateInstance
- redef fun dump_intern(icd)
- do
- return "ALLOCATE NEW_{stype}"
- end
-end
-
-redef class ICheckInstance
- redef fun dump_intern(icd)
- do
- return "CHECK_INSTANCE CHECKNEW_{stype}({icd.register(expr)})"
- end
-end
-
-redef class IInitAttributes
- redef fun dump_intern(icd)
- do
- return "INIT_ATTRIBUTES INIT_ATTRIBUTES_{stype}({icd.register(expr)})"
- end
-end
-
-redef class IClosCall
- redef fun dump_intern(icd)
- do
- return "CLOS_CALL {icd.closdecl(closure_decl)}({icd.register_all(exprs)})"
- end
-end
-
-redef class IAttrRead
- redef fun dump_intern(icd)
- do
- return "ATTR_READ {property.full_name}({icd.register(expr)})"
- end
-end
-
-redef class IAttrWrite
- redef fun dump_intern(icd)
- do
- return "ATTR_WRITE {property.full_name}({icd.register(expr1)}) := {icd.register(expr2)}"
- end
-end
-
-redef class IAttrIsset
- redef fun dump_intern(icd)
- do
- return "ATTR_ISSET {property.full_name}({icd.register(expr)})"
- end
-end
-
-redef class ITypeCheck
- redef fun dump_intern(icd)
- do
- return "CHECKTYPE {icd.register(expr2)} isa {stype}"
- end
-end
-
-redef class INative
- redef fun dump_intern(icd)
- do
- return "NATIVE \"{method.full_name}\"({icd.register_all(exprs)})"
- end
-end
-
-redef class IIntValue
- redef fun dump_intern(icd)
- do
- return "INTVALUE {value}"
- end
-end
-
-redef class IBoolValue
- redef fun dump_intern(icd)
- do
- return "BOOLVALUE {value}"
- end
-end
-
-redef class IStringValue
- redef fun dump_intern(icd)
- do
- return "STRINGVALUE {value}"
- end
-end
-
-redef class ICharValue
- redef fun dump_intern(icd)
- do
- return "CHARVALUE {value}"
- end
-end
-
-redef class IFloatValue
- redef fun dump_intern(icd)
- do
- return "FLOATVALUE {value}"
- end
-end
-
-redef class IMove
- redef fun dump_intern(icd)
- do
- return "{icd.register(expr)}"
- end
-end
-
-redef class IIs
- redef fun dump_intern(icd)
- do
- return "{icd.register(expr1)} is {icd.register(expr2)}"
- end
-end
-
-redef class INot
- redef fun dump_intern(icd)
- do
- return "NOT {icd.register(expr)}"
- end
-end
-
-redef class IOnce
- redef fun dump(icd)
- do
- icd.write "{icd.register(result.as(not null))} := ONCE \{"
- icd.indent
- body.dump(icd)
- icd.unindent
- icd.write "}"
- end
-end
-
-redef class IHasClos
- redef fun dump_intern(icd)
- do
- return "HASCLOS {icd.closdecl(closure_decl)}"
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Detect inlinable methods and inline them
-module inline_methods
-
-import icode
-
-private class InlineMethodVisitor
- super ICodeVisitor
- var _pass: Int = 0
- var _icb: ICodeBuilder
-
- redef fun visit_icode(ic)
- do
- if ic isa ICall and not ic.is_explicit_from_extern then
- var m = ic.property
- var ir = m.iroutine
- if ir != null and ic.is_inlinable then
- var icb = _icb
- if icb.iroutine == ir then
- # We cannot inline ir
- # So, at least transform the call to a static one
- current_icode.delete
- var icall = new IStaticCall(ic.property, ic.exprs)
- icall.closure_defs = ic.closure_defs
- icall.result = ic.result
- current_icode.insert_before(icall)
- current_icode.delete
- else
- var seq = new ISeq
- var old_seq = icb.seq
- icb.seq = seq
- current_icode.insert_before(seq)
- var e = icb.inline_routine(ir, ic.exprs, ic.closure_defs)
- var r = ic.result
- if r != null then
- assert e != null
- current_icode.insert_before(new IMove(r, e))
- end
- current_icode.delete
- icb.seq = old_seq
- visit_icode(seq)
- end
- end
- end
- super
- end
-
- init(m: MMModule, r: IRoutine)
- do
- _icb = new ICodeBuilder(m, r)
- end
-end
-
-redef class ICall
- fun is_inlinable: Bool
- do
- var m = property
- var mn = m.name
- var cn = m.local_class.name
- return (m.is_intern and cn != once ("Object".to_symbol)) or
- (cn == (once ("Int".to_symbol)) and (mn == (once ("enumerate_to".to_symbol)) or mn == (once ("enumerate_before".to_symbol)))) or
- (cn == (once ("Array".to_symbol)) and (mn == (once ("length".to_symbol)) or mn == (once ("[]".to_symbol)) or mn == (once ("iterate".to_symbol)))) or
- (cn == (once ("AbstractArrayRead".to_symbol)) and (mn == (once ("length".to_symbol)) or mn == (once ("[]".to_symbol)))) or
- (m.global.intro.local_class.name == (once ("Inline__".to_symbol)))
- end
-end
-
-redef class IRoutine
- fun inline_methods(m: MMModule)
- do
- var v = new InlineMethodVisitor(m, self)
- v.visit_iroutine(self)
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Compute and generate tables for classes and modules.
-module compiling
-
-import table_computation
-import compiling_base
-private import icode_generator
-private import compiling_global
-private import compiling_icode
-private import analysis
-
-redef class Program
- # The type of code generation to use
- readable writable var _output_format: String = "none"
-
- # Compile the program depending on the output format
- fun compile_prog
- do
- if output_format == "none" then
- # Nothing to do
- else
- # Optimize all iroutines
- with_each_iroutines !action(i, m) do i.optimize(m)
-
- if output_format == "C" then
- compile_prog_to_c
- else if output_format == "icode" then
- generate_icode_files
- end
- end
- end
-
- # Compile the program to C
- # Generate all files (_sep.[ch] or _glob.[ch]), the main file (_table.c) and the build file (_build.sh)
- # Then execute the build.sh
- fun compile_prog_to_c
- do
- var cprogram = new CProgram(self)
-
- cprogram.compdir.mkdir
-
- cprogram.files.add("$CLIBDIR/nit_main.c")
- cprogram.files.add("$CLIBDIR/gc.c")
- cprogram.files.add("$CLIBDIR/gc_static_objects_list.c")
-
- tc.info("Generating C code",1)
- for m in main_module.mhe.greaters_and_self do m.compile_separate_module(cprogram)
-
- tc.info("Generating main, tables and makefile ...",1)
- compile_main(cprogram)
-
- cprogram.generate_build_file
-
- if not tc.no_cc then cprogram.run_c_compiler
- end
-
- # Compile the main file
- private fun compile_main(cprogram: CProgram)
- do
- var v = new CompilerVisitor(main_module, cprogram)
- v.add_decl("#include <nit_common.h>")
- compile_tables_to_c(v)
- compile_main_part(v)
- var filename = "{cprogram.compdir}/{main_module.cname}._tables.c"
- cprogram.files.add(filename)
- var f = new OFStream.open(filename)
- f.write("/* This C file is generated by NIT to compile program {main_module.cname}. */\n")
- for m in main_module.mhe.greaters_and_self do
- f.write("#include \"{cprogram.module_header_name(m)}\"\n")
- end
- v.header_writer.write_to_stream(f)
- v.writer.write_to_stream(f)
- f.close
- end
-end
-
-redef class MMModule
- # Compile the sep or glob files (of the current module only)
- fun compile_separate_module(cprogram: CProgram)
- do
- var tc = cprogram.program.tc
- tc.info("Generating C code for module: {full_name}",2)
- var v = new CompilerVisitor(self, cprogram)
- v.add_decl("#include <nit_common.h>")
-
- if is_extern_hybrid then
- # adds reference to frontier files
- # if module uses the native interface
- var nitni_header_name = "{name}._nitni.h"
- v.add_decl("#include \"{nitni_header_name}\"")
- var nitni_body_name = "{name}._nitni.c"
- cprogram.files.add( "{cprogram.compdir}/{nitni_body_name}" )
-
- # add reference to extern implementation
- var native_name = location.file.filename.strip_extension(".nit")
- var native_body = native_name + ".nit.c"
- if native_body.file_exists then
- cprogram.files.add(native_body)
- else # try old style filename
- native_body = native_name + "_nit.c"
- if native_body.file_exists then cprogram.files.add(native_body)
- end
- if uses_ffi then
- var ffi_header_name = "{cname}._ffi.h"
- v.add_decl("#include \"{ffi_header_name}\"")
- var ffi_body_name = "{cname}._ffi.c"
- cprogram.files.add( "{cprogram.compdir}/{ffi_body_name}" )
- end
- end
-
- declare_class_tables_to_c(v)
- compile_mod_to_c(v)
-
- var hfilename = cprogram.module_header_name(self)
- var f = new OFStream.open("{cprogram.compdir}/{hfilename}")
- f.write("/* This C header file is generated by NIT to compile modules and programs that requires {full_name}. */\n")
- f.write("#ifndef {cname}{cprogram.get_file_ending}\n")
- f.write("#define {cname}{cprogram.get_file_ending}\n")
- for m in mhe.direct_greaters do f.write("#include \"{cprogram.module_header_name(m)}\"\n")
- v.header_writer.write_to_stream(f)
- f.write("#endif\n")
- f.close
-
- var cfilename = "{cprogram.compdir}/{cname}.{cprogram.get_file_ending}.c"
- cprogram.files.add(cfilename)
- f = new OFStream.open("{cfilename}")
- f.write("/* This C file is generated by NIT to compile module {cname}. */\n")
- f.write("#include \"{hfilename}\"\n")
- v.top_writer.write_to_stream(f)
- f.close
- end
-end
-
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Common things for NIT compilation and C generation
-module compiling_base
-
-import mmloader
-private import utils
-private import primitive_info
-import program
-import compiling_writer
-
-redef class ToolContext
- readable writable var _compdir: nullable String = null
- readable writable var _clibdir: nullable String = null
- readable writable var _bindir: nullable String = null
- readable writable var _output_file: nullable String = null
- readable writable var _boost: Bool = false
- readable writable var _no_cc: Bool = false
- readable writable var _cc_link: Bool = false
- readable writable var _cc_libs: Array[String] = new Array[String]
- readable writable var _cc_lib_paths: Array[String] = new Array[String]
- readable writable var _cc_include_paths: Array[String] = new Array[String]
- readable writable var _ext_prefix: String = ""
-end
-
-# A program that is compiled to C
-class CProgram
- init(p: Program)
- do
- _program = p
- _compdir = p.tc.compdir.as(not null)
- _build_file = "{compdir}/{program.main_module.cname}._build.sh"
- end
-
- # The Nit program compiled to C
- readable var _program: Program
-
- # C files (full path) required to compile
- readable var _files: Array[String] = new Array[String]
-
- # Includes paths (gcc -I) required to find the headers (.h) of native modules
- readable var _include_dirs: ArraySet[String] = new ArraySet[String]
-
- # The path of the building script
- readable var _build_file: String
-
- # The directory where all files are generated
- readable var _compdir: String
-
- # Return the basename of the public header file (.h) of a module
- fun module_header_name(m: MMModule): String
- do
- if _module_include.has_key(m) then
- return _module_include[m]
- end
- var filename = "{m.cname}.{get_file_ending}.h"
- _module_include[m] = filename
- return filename
- end
-
- # Cache for module_header_name
- var _module_include: Map[MMModule, String] = new HashMap[MMModule, String]
-
- # When we are using global compilation, we generate _glob files instead
- # of _sep files so that we do not corrupt separate compilation
- fun get_file_ending: String do return if program.tc.global then "_glob" else "_sep"
-
- # Generate the shell script that build the program by calling gccx
- fun generate_build_file
- do
- var f = new OFStream.open(_build_file)
- var verbose = ""
- var tc = program.tc
-
- if tc.verbose_level == 1 then
- verbose = "-v"
- else if tc.verbose_level >= 2 then
- # We catch tc.verbose_level >= 2, since 3+ is not valid with gccx
- verbose = "-vv"
- end
-
- # include compdir to find frontier files (._nitni.h) from native
- # implementations as .nit.h must have an import of <{name}._nitni.h>
- include_dirs.add( "-I {compdir}" )
-
- f.write("#!/bin/sh\n")
- f.write("# This shell script is generated by NIT to compile the program {program.main_module.full_name}.\n")
- f.write("CLIBDIR=\"{tc.clibdir.as(not null)}\"\n")
- f.write("{tc.bindir.as(not null)}/gccx {verbose} -d {compdir} -I $CLIBDIR {include_dirs.join(" ")}")
- if tc.output_file != null then
- f.write(" -o {tc.output_file.as(not null)}")
- else if tc.ext_prefix.is_empty then
- f.write(" -o {program.main_module.name}")
- else
- f.write(" -o {program.main_module.name}_{tc.ext_prefix}")
- end
- if tc.boost then f.write(" -O")
- if not tc.cc_link then f.write(" -x \"-c\"")
- for l in tc.cc_libs do f.write(" -l {l}")
- for lp in tc.cc_lib_paths do f.write(" -x \"-L {lp}\"")
- for ip in tc.cc_include_paths do f.write(" -x \"-I {ip}\"")
- f.write(" \"$@\" \\\n {files.join("\\\n ")}\n")
- f.close
- end
-
- # Invoke the build_file
- fun run_c_compiler
- do
- program.tc.info("Building",1)
- sys.system("sh {_build_file}")
- end
-end
-
-# Class used to generate files.
-# Note that in fact it is not a visitor.
-# Note also that this class is unefficient and poorly designed thus requires love.
-class CompilerVisitor
- # Add a line in the current declaration block
- fun add_decl(s: String)
- do
- add_line_to(_decl_writer, s)
- end
-
- # Add a line in the current instr block
- fun add_instr(s: String)
- do
- add_line_to(_writer, s)
- end
-
-
- fun add_indent(w: Writer)
- do
- if _indent_level >= 8 then
- w.add("\t\t")
- else
- for i in [0.._indent_level[ do
- w.add(" ")
- end
- end
- end
-
- fun add_line_to(w: Writer, s: String)
- do
- add_indent(w)
- w.add(s)
- w.add("\n")
- end
-
- # Add a assignment between a variable and an expression
- fun add_assignment(v: String, s: String)
- do
- if v != s then
- var w = _writer
- add_indent(w)
- w.add(v)
- w.add(" = ")
- w.add(s)
- w.add(";\n")
- end
- end
-
- # Return a unique new number for the instance
- fun new_number: Int
- do
- var res = _number_cpt
- _number_cpt = res + 1
- return res
- end
- # next number for new_number
- var _number_cpt: Int = 0
-
- # Add an indent level.
- # New decl and instr will be indented.
- fun indent do _indent_level += 1
-
- # Remove an indent level.
- fun unindent
- do
- _indent_level -= 1
- if _indent_level < 0 then _indent_level = 0
- end
-
- # The processed mmmodule
- readable var _mmmodule: MMModule
-
- # Where header decl are stored (public stuff)
- readable writable var _header_writer: Writer
-
- # Where current instr are stored (current function declaration)
- readable writable var _writer: Writer
-
- # Where current decl are stored (current function instructions)
- readable writable var _decl_writer: Writer
-
- # Where body instr are stored (C functions body)
- readable writable var _top_writer: Writer
-
- # Where body decl are stored (private C function proptypes and typedefs)
- readable writable var _top_decl_writer: Writer
-
- # The current indent lever
- readable writable var _indent_level: Int = 0
-
- # The program we are compiling
- readable var _program: Program
-
- # The cprogram associed with program
- readable var _cprogram: CProgram
-
- # Create a new CompilerVisitor based on a module
- init(mod: MMModule, cp: CProgram)
- do
- _mmmodule = mod
- _cprogram = cp
- _program = cp.program
-
- var w = new Writer
- _header_writer = w
- _decl_writer = w
- w = new Writer
- _writer = w
- _top_writer = w
- _top_decl_writer = w.sub
- end
-end
-
-redef class MMGlobalProperty
- # C symbol refering a method inocation
- fun meth_call: String
- do
- return "CALL_{intro.cname}"
- end
-
- # C symbol refering an attribure access
- fun attr_access: String
- do
- return "ATTR_{intro.cname}"
- end
-
- # C symbol refering a virtual type class color
- fun vt_class_color: String
- do
- return "VTCOLOR_{intro.cname}"
- end
-
- # C symbol refering a virtual type class id
- fun vt_class_id: String
- do
- return "VTID_{intro.cname}"
- end
-end
-
-redef class MMGlobalClass
- # Cacher result of cname
- var _cname_cache: nullable String
-
- # The mangled name of the global class
- fun cname: String
- do
- var cname = _cname_cache
- if cname == null then
- cname = intro.mmmodule.cname + "___" + cmangle(intro.name)
- _cname_cache = cname
- end
- return cname
- end
-
- # C symbol refering the identifier of the class
- fun id_id: String
- do
- return "ID_{cname}"
- end
-
- # C symbol refering the color of the class (for subtype tests)
- fun color_id: String
- do
- return "COLOR_{cname}"
- end
-
- # C symbol refering the init table position of the class (for constructor linearization)
- fun init_table_pos_id: String
- do
- return "INIT_TABLE_POS_{cname}"
- end
-end
-
-redef class MMModule
- # Cacher result of cname
- var _cname_cache: nullable String
-
- # The mangled name of the module
- fun cname: String
- do
- var cname = _cname_cache
- if cname == null then
- var l = new List[String]
- var m: nullable MMModule = self
- while m != null do
- l.unshift(cmangle(m.name))
- var d: nullable MMDirectory = m.directory
- while d != null and d.owner == m do d = d.parent
- if d == null then m = null else m = d.owner
- end
- cname = l.to_a.join("___")
- _cname_cache = cname
- end
- return cname
- end
-end
-
-redef class MMLocalClass
- # The mangled name of the global class
- fun cname: String do return global.cname
-end
-
-redef class MMLocalProperty
- # Cacher result of cname
- var _cname_cache: nullable String
-
- # The mangled name of the method
- fun cname: String
- do
- var cname = _cname_cache
- if cname == null then
- cname = mmmodule.cname + "___" + cmangle(local_class.name, name)
- _cname_cache = cname
- end
- return cname
- end
-
- # C macro used to get the function for the call of a super property
- fun super_meth_call: String
- do
- return "CALL_SUPER_{cname}"
- end
-end
-
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Compute and generate tables for classes and modules.
-module compiling_global
-
-import table_computation
-private import compiling_icode
-
-redef class Program
- # Compile module and class tables
- fun compile_tables_to_c(v: CompilerVisitor)
- do
- for m in main_module.mhe.greaters_and_self do
- m.compile_local_table_to_c(v)
- end
-
- with_each_live_local_classes !action(c) do
- if c.global.is_abstract or c.global.is_interface then continue
- c.compile_tables_to_c(v)
- end
-
- var s = new Buffer.from("classtable_t TAG2VFT[4] = \{NULL")
- for t in ["Int","Char","Bool"] do
- if main_module.has_global_class_named(t.to_symbol) then
- var c = main_module.class_by_name(t.to_symbol)
- s.append(", (const classtable_t)VFT_{c.cname}")
- else
- s.append(", NULL")
- end
- end
- s.append("};")
- v.add_instr(s.to_s)
- end
-
- # Compile main part (for _table.c)
- fun compile_main_part(v: CompilerVisitor)
- do
- v.add_instr("int main(int argc, char **argv) \{")
- v.indent
- v.add_instr("prepare_signals();")
- v.add_instr("glob_argc = argc; glob_argv = argv;")
- if v.program.main_method == null then
- print("No main")
- else
- var c = v.program.main_class
- v.add_instr("G_sys = NEW_{c.cname}();")
- v.add_instr("register_static_object(&G_sys);")
- v.add_instr("{v.program.main_method.cname}(G_sys);")
- end
- v.add_instr("return 0;")
- v.unindent
- v.add_instr("}")
- end
-end
-
-redef class MMModule
- # Declare class table (for _sep.h or _glob.h)
- fun declare_class_tables_to_c(v: CompilerVisitor)
- do
- for c in local_classes do
- if c.global.mmmodule == self then
- c.declare_tables_to_c(v)
- end
- end
- end
-
- # Compile sep files
- fun compile_mod_to_c(v: CompilerVisitor)
- do
- v.add_decl("extern const char LOCATE_{cname}[];")
- if not v.program.tc.use_SFT_optimization then
- v.add_decl("extern const int SFT_{cname}[];")
- end
- var i = 0
- for e in local_table do
- var value: String
- if v.program.tc.use_SFT_optimization then
- value = "{e.value(v.program)}"
- else
- value = "SFT_{cname}[{i}]"
- i = i + 1
- end
- e.compile_macros(v, value)
- end
- for c in local_classes do
- if not c isa MMConcreteClass then continue
- for pg in c.global_properties do
- var p = c[pg]
- if p.local_class == c and p isa MMMethod then
- p.compile_property_to_c(v)
- end
- if pg.is_init_for(c) then
- # Declare constructors
- var params = new Array[String]
- for j in [0..p.signature.arity[ do
- params.add("val_t p{j}")
- end
- v.add_decl("val_t NEW_{c}_{p.global.intro.cname}({params.join(", ")});")
- end
- end
- end
- end
-
- # Compile module file for the current module
- fun compile_local_table_to_c(v: CompilerVisitor)
- do
- v.add_instr("const char LOCATE_{cname}[] = \"{location.file.filename}\";")
-
- if v.program.tc.use_SFT_optimization or local_table.is_empty then
- return
- end
-
- v.add_instr("const int SFT_{cname}[{local_table.length}] = \{")
- v.indent
- for e in local_table do
- v.add_instr(e.value(v.program) + ",")
- end
- v.unindent
- v.add_instr("\};")
- end
-end
-
-###############################################################################
-
-redef class AbsTableElt
- # Compile the macro needed to use the element and other related elements
- fun compile_macros(v: CompilerVisitor, value: String) is abstract
-end
-
-redef class TableElt
- # Return the value of the element for a given class
- fun compile_to_c(v: CompilerVisitor, c: MMLocalClass): String is abstract
-end
-
-redef class ModuleTableElt
- # Return the value of the element once the global analisys is performed
- fun value(prog: Program): String is abstract
-end
-
-redef class ModuleTableEltGroup
- redef fun value(prog) do return "{prog.table_information.color(elements.first)} /* Group of ? */"
- redef fun compile_macros(v, value)
- do
- var i = 0
- for e in elements do
- e.compile_macros(v, "{value} + {i}")
- i += 1
- end
- end
-end
-
-redef class TableEltMeth
- redef fun compile_macros(v, value)
- do
- var pg = property.global
- v.add_decl("#define {pg.meth_call}(recv) (({pg.intro.cname}_t)CALL((recv), ({value})))")
- end
-
- redef fun compile_to_c(v, c)
- do
- var p = c[property.global]
- return p.cname
- end
-end
-
-redef class TableEltSuper
- redef fun compile_macros(v, value)
- do
- var p = property
- v.add_decl("#define {p.super_meth_call}(recv) (({p.cname}_t)CALL((recv), ({value})))")
- end
-
- redef fun compile_to_c(v, c)
- do
- var pc = property.local_class
- var g = property.global
- var lin = c.che.linear_extension
- var found = false
- for s in lin do
- #print "{c.mmmodule}::{c} for {pc.mmmodule}::{pc}::{property} try {s.mmmodule}:{s}"
- if not s.has_global_property(g) then continue
-
- var p = s[g]
- if p.local_class != s then continue
- if s == pc then
- found = true
- else if found and c.che < s then
- if s.has_global_property(g) then
- #print "found {s.mmmodule}::{s} {p.local_class}::{p} {p.cname}"
- return p.cname
- end
- end
- end
- abort
- end
-end
-
-redef class TableEltVTClassColor
- redef fun compile_macros(v, value)
- do
- var pg = property.global
- v.add_decl("#define {pg.vt_class_color}(recv) (VAL2VFT(recv)[{value}].i)")
- end
-
- redef fun compile_to_c(v, c)
- do
- var prog = v.program
- var p = c[property.global]
- var g = p.signature_for(c.get_type).return_type.local_class.global
- var col = g.intro.as(MMConcreteClass).class_color_pos
- return "{prog.table_information.color(col)} /* {prog.table_information.color(self)}: VT {c}::{p} : color of {g} */"
- end
-end
-
-redef class TableEltVTClassId
- redef fun compile_macros(v, value)
- do
- var pg = property.global
- v.add_decl("#define {pg.vt_class_id}(recv) (VAL2VFT(recv)[{value}].i)")
- end
-
- redef fun compile_to_c(v, c)
- do
- var prog = v.program
- var p = c[property.global]
- var g = p.signature_for(c.get_type).return_type.local_class.global
- return "{prog.compiled_classes[g].id} /* {prog.table_information.color(self)}: VT {c}::{p} : id of {g} */"
- end
-end
-
-redef class TableEltAttr
- redef fun compile_macros(v, value)
- do
- var pg = property.global
- v.add_decl("#define {pg.attr_access}(recv) ATTR(recv, ({value}))")
- end
-
- redef fun compile_to_c(v, c)
- do
- var prog = v.program
- var p = c[property.global]
- return "/* {prog.table_information.color(self)}: Attribute {c}::{p} */"
- end
-end
-
-
-redef class AbsTableEltClass
- # The C macro name refering the value
- fun symbol: String is abstract
-
- redef fun compile_macros(v, value)
- do
- v.add_decl("#define {symbol} ({value})")
- end
-end
-
-redef class TableEltClassId
- redef fun symbol do return local_class.global.id_id
-
- redef fun value(prog)
- do
- return "{prog.compiled_classes[local_class.global].id} /* Id of {local_class} */"
- end
-end
-
-redef class TableEltClassInitTable
- redef fun symbol do return local_class.global.init_table_pos_id
-
- redef fun compile_to_c(v, c)
- do
- var prog = v.program
- var cc = prog.compiled_classes[local_class.global]
- var linext = c.cshe.reverse_linear_extension
- var i = 0
- while linext[i].global != local_class.global do
- i += 1
- end
- return "{i} /* {prog.table_information.color(self)}: {c} < {cc.local_class}: superclass init_table position */"
- end
-end
-
-redef class TableEltClassColor
- redef fun symbol do return local_class.global.color_id
-
- redef fun value(prog)
- do
- return "{prog.table_information.color(self)} /* Color of {local_class} */"
- end
-
- redef fun compile_to_c(v, c)
- do
- var prog = v.program
- var cc = prog.compiled_classes[local_class.global]
- return "{cc.id} /* {prog.table_information.color(self)}: {c} < {cc.local_class}: superclass typecheck marker */"
- end
-end
-
-redef class TableEltComposite
- redef fun compile_to_c(v, c) do abort
-end
-
-redef class TableEltClassSelfId
- redef fun compile_to_c(v, c)
- do
- var prog = v.program
- return "{prog.compiled_classes[c.global].id} /* {prog.table_information.color(self)}: Identity */"
- end
-end
-
-redef class TableEltClassSelfName
- redef fun compile_to_c(v, c)
- do
- var prog = v.program
- return "\"{c.global.name}\" /* {prog.table_information.color(self)}: Class Name */"
- end
-end
-
-redef class TableEltClassObjectSize
- redef fun compile_to_c(v, c)
- do
- var nb = 0
- var p = v.program
- if c.name == "NativeArray".to_symbol then
- nb = -1
- else
- var cc = p.compiled_classes[c.global]
- var itab = cc.instance_table
- for e in itab do
- nb += 1
- end
- end
- return "{nb} /* {p.table_information.color(self)}: Object size (-1 if a NativeArray)*/"
- end
-end
-
-redef class TableEltObjectId
- redef fun compile_to_c(v, c)
- do
- var p = v.program
- return "/* {p.table_information.color(self)}: Object_id */"
- end
-end
-
-redef class TableEltVftPointer
- redef fun compile_to_c(v, c)
- do
- var prog = v.program
- return "/* {prog.table_information.color(self)}: Pointer to the classtable */"
- end
-end
-
-###############################################################################
-
-redef class MMLocalClass
- # Declaration and macros related to the class table
- fun declare_tables_to_c(v: CompilerVisitor)
- do
- v.add_decl("")
- var pi = primitive_info
- v.add_decl("extern const classtable_elt_t VFT_{cname}[];")
- if pi != null and not pi.tagged then
- var t = pi.cname
- var tbox = "struct TBOX_{name}"
- v.add_decl("{tbox} \{ const classtable_elt_t * vft; bigint object_id; {t} val;};")
- v.add_decl("val_t BOX_{name}({t} val);")
- v.add_decl("#define UNBOX_{name}(x) ((({tbox} *)(VAL2OBJ(x)))->val)")
- end
- end
-
- # Compilation of table and new (or box)
- fun compile_tables_to_c(v: CompilerVisitor)
- do
- var cc = v.program.compiled_classes[self.global]
- var ctab = cc.class_table
- var clen = ctab.length
- if v.program.table_information.max_class_table_length > ctab.length then
- clen = v.program.table_information.max_class_table_length
- end
-
- v.add_instr("const classtable_elt_t VFT_{cname}[{clen}] = \{")
- v.indent
- for e in ctab do
- if e == null then
- v.add_instr("\{0} /* Class Hole :( */,")
- else
- v.add_instr("\{(bigint) {e.compile_to_c(v, self)}},")
- end
- end
- if clen > ctab.length then
- v.add_instr("\{0},"*(clen-ctab.length))
- end
- v.unindent
- v.add_instr("};")
- var itab = cc.instance_table
- for e in itab do
- if e == null then
- v.add_instr("/* Instance Hole :( */")
- else
- v.add_instr(e.compile_to_c(v, self))
- end
- end
-
- var pi = primitive_info
- if name == "NativeArray".to_symbol then
- v.add_instr("val_t NEW_NativeArray(size_t length, size_t size) \{")
- v.indent
- v.add_instr("Nit_NativeArray array;")
- v.add_instr("array = (Nit_NativeArray)alloc(sizeof(struct Nit_NativeArray) + ((length - 1) * size));")
- v.add_instr("array->vft = (classtable_elt_t*)VFT_{cname};")
- v.add_instr("array->object_id = object_id_counter;")
- v.add_instr("object_id_counter = object_id_counter + 1;")
- v.add_instr("array->size = length;")
- v.add_instr("return OBJ2VAL(array);")
- v.unindent
- v.add_instr("}")
- else if pi == null then
- do
- # Generate INIT_ATTRIBUTES routine
- var cname = "INIT_ATTRIBUTES__{cname}"
- var args = init_var_iroutine.compile_signature_to_c(v, cname, "init var of {name}", null, null)
- var decl_writer_old = v.decl_writer
- v.decl_writer = v.writer.sub
- init_var_iroutine.compile_to_c(v, cname, args)
- v.decl_writer = decl_writer_old
- v.unindent
- v.add_instr("}")
- end
- do
- # Generate NEW routine
- v.add_decl("val_t NEW_{cname}(void);")
- v.add_instr("val_t NEW_{cname}(void)")
- v.add_instr("\{")
- v.indent
- v.add_instr("obj_t obj;")
- v.add_instr("obj = alloc(sizeof(val_t) * {itab.length});")
- v.add_instr("obj->vft = (classtable_elt_t*)VFT_{cname};")
- v.add_instr("obj[1].object_id = object_id_counter;")
- v.add_instr("object_id_counter = object_id_counter + 1;")
- v.add_instr("return OBJ2VAL(obj);")
- v.unindent
- v.add_instr("}")
- end
- do
- # Compile CHECKNAME
- var cname = "CHECKNEW_{cname}"
- var args = checknew_iroutine.compile_signature_to_c(v, cname, "check new {name}", null, null)
- var decl_writer_old = v.decl_writer
- v.decl_writer = v.writer.sub
- checknew_iroutine.compile_to_c(v, cname, args)
- v.decl_writer = decl_writer_old
- v.unindent
- v.add_instr("}")
- end
-
- var init_table_size = cshe.greaters.length + 1
- var init_table_decl = "int init_table[{init_table_size}] = \{0{", 0" * (init_table_size-1)}};"
-
- for g in global_properties do
- var p = self[g]
- # FIXME skip invisible constructors
- if not p.global.is_init_for(self) then continue
- assert p isa MMMethod
-
- var cname = "NEW_{self}_{p.global.intro.cname}"
- var new_args = new_instance_iroutine[p].compile_signature_to_c(v, cname, "new {self} {p.full_name}", null, null)
- var decl_writer_old = v.decl_writer
- v.decl_writer = v.writer.sub
- v.add_instr(init_table_decl)
- var e = new_instance_iroutine[p].compile_to_c(v, cname, new_args).as(not null)
- v.add_instr("return {e};")
- v.decl_writer = decl_writer_old
- v.unindent
- v.add_instr("}")
- end
- else if not pi.tagged then
- var t = pi.cname
- var tbox = "struct TBOX_{name}"
- v.add_instr("val_t BOX_{name}({t} val) \{")
- v.indent
- v.add_instr("{tbox} *box = ({tbox}*)alloc(sizeof({tbox}));")
- v.add_instr("box->vft = VFT_{cname};")
- v.add_instr("box->val = val;")
- v.add_instr("box->object_id = object_id_counter;")
- v.add_instr("object_id_counter = object_id_counter + 1;")
- v.add_instr("return OBJ2VAL(box);")
- v.unindent
- v.add_instr("}")
- end
- end
-end
-
-redef class MMMethod
- fun compile_property_to_c(v: CompilerVisitor)
- do
- var ir = iroutine
- assert ir != null
-
- var more_params: nullable String = null
- if global.is_init then more_params = "int* init_table"
- var args = ir.compile_signature_to_c(v, cname, full_name, null, more_params)
- var writer_old = v.writer
- v.writer = v.writer.sub
- var decl_writer_old = v.decl_writer
- v.decl_writer = v.writer.sub
-
- var itpos: nullable String = null
- if global.is_init then
- itpos = "itpos{v.new_number}"
- v.add_decl("int {itpos} = VAL2OBJ({args.first})->vft[{local_class.global.init_table_pos_id}].i;")
- v.add_instr("if (init_table[{itpos}]) return;")
- end
-
- var s = ir.compile_to_c(v, cname, args)
-
- if itpos != null then
- v.add_instr("init_table[{itpos}] = 1;")
- end
- if s == null then
- v.add_instr("return;")
- else
- v.add_instr("return {s};")
- end
- v.unindent
- v.add_instr("}")
-
- v.writer = writer_old
- v.decl_writer = decl_writer_old
- end
-end
-
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Generate C code from intermediate code representation
-module compiling_icode
-
-import icode
-private import analysis
-import primitive_info
-import compiling_base
-
-# Compiler context from ICode to C
-class I2CCompilerVisitor
- # Associate things
- var _ids: HashMap[Object, String] = new HashMap[Object, String]
- # Associate other things
- var _ids2: HashMap[Object, String] = new HashMap[Object, String]
-
- # Return the string associated with a register
- fun register(e: IRegister): String
- do
- if e.stype isa MMTypeNone then return "NIT_NULL"
- var ids = _ids
- if closure and not e.is_local then ids = _ids2
- if ids.has_key(e) then
- return ids[e]
- else
- var i = e.slot_index
- if i == null then
- # The register is dead
- var s = "NIT_NULL"
- ids[e] = s
- return s
- else
- var s: String
- var strs: HashMap[Int, String]
- if e.in_tag_slots then
- strs = once new HashMap[Int, String]
- if not strs.has_key(i) then strs[i] = "REGB{i}"
- else if closure and not e.is_local then
- strs = once new HashMap[Int, String]
- if not strs.has_key(i) then strs[i] = "closctx->REG[{i}]"
- else
- strs = once new HashMap[Int, String]
- if not strs.has_key(i) then strs[i] = "fra.me.REG[{i}]"
- end
- s = strs[i]
- ids[e] = s
- return s
- end
- end
- end
-
- # Return the strings associated with registers
- fun registers(a: Collection[IRegister]): Array[String]
- do
- var r = new Array[String].with_capacity(a.length)
- for e in a do
- r.add(register(e))
- end
- return r
- end
-
- var _last_number: Int = 0
- # Give a new unique number (unique for the visitor)
- fun new_number: Int
- do
- _last_number += 1
- return _last_number
- end
-
- # Return the string associated with a escape label
- fun lab(e: ISeq): String
- do
- if _ids.has_key(e) then
- return _ids[e]
- else
- var s = "label{new_number}"
- _ids[e] = s
- return s
- end
- end
-
- # The rank (number) of each closure
- readable var _closures: HashMap[IClosureDecl, String] = new HashMap[IClosureDecl, String]
-
- # The functionnal type of each closure
- readable var _clostypes: HashMap[IClosureDecl, String] = new HashMap[IClosureDecl, String]
-
- # label locally accessibles
- readable writable var _local_labels: HashSet[ISeq] = new HashSet[ISeq]
-
- # Not local escaped labels
- # The integer value is an index identifying the label
- readable writable var _escaped_labels: HashMap[ISeq, Int] = new HashMap[ISeq, Int]
-
- # Register a escape to a non local label and return an index identifying the label
- fun register_escape_label(e: ISeq): Int
- do
- if _escaped_labels.has_key(e) then
- return _escaped_labels[e]
- else
- var res = _escaped_labels.length + 1
- _escaped_labels[e] = res
- return res
- end
- end
-
- # Add a C label mark (if needed)
- fun add_label(e: ISeq)
- do
- if _ids.has_key(e) then
- add_instr("{_ids[e]}: while(0);")
- end
- end
-
- # Add a goto to a label (even outside a closure)
- fun add_goto(seq: ISeq)
- do
- if local_labels.has(seq) then
- add_instr("goto {lab(seq)};")
- else
- assert closure
- var ind = register_escape_label(seq)
- add_instr("closctx->has_broke = {ind};")
- add_instr("goto {lab(return_label.as(not null))};")
- end
- end
-
- # Association between IEscapeMarks and visited ISeq
- readable var _marks_to_seq: Map[IEscapeMark, ISeq] = new HashMap[IEscapeMark, ISeq]
-
- # Are we in a closure ?
- readable writable var _closure: Bool = false
-
- # The current compiler visitor
- readable var _visitor: CompilerVisitor
-
- # The current compiled iroutine
- readable var _iroutine: IRoutine
-
- # The return label of the current compiling C function
- readable writable var _return_label: nullable ISeq = null
-
- fun add_decl(s: String)
- do
- visitor.add_decl(s)
- end
-
- # Prepare a new instuction (indent, comment)
- # Caller must ensure to add a new line to finish its instr
- fun new_instr: Writer
- do
- var w = visitor.writer
- var l = _next_location
- if l != null then
- visitor.add_indent(w)
- w.add("/* ")
- w.add(l.file.filename)
- w.add(":")
- w.add(l.line_start.to_s)
- w.add(" */\n")
- _next_location = null
- end
- visitor.add_indent(w)
- return w
- end
-
- fun add_instr(s: String)
- do
- new_instr.add(s).add("\n")
- end
-
- fun indent
- do
- visitor.indent
- end
-
- fun unindent
- do
- visitor.unindent
- end
-
- fun add_assignment(to, from: String)
- do
- visitor.add_assignment(to, from)
- end
-
- var _last_location: nullable Location = null
- var _next_location: nullable Location = null
-
- # Add location information in a comment
- # Do nothing if the last location added is the same
- fun add_location(l: nullable Location)
- do
- var last = _last_location
- if last == l or l == null then return
- _last_location = l
- if last != null and last.file == l.file and last.line_start == l.line_start then
- return
- else
- _next_location = l
- end
- end
-
- # The C fonction name of the iroutine
- readable var _basecname: String
-
- init(v: CompilerVisitor, ir: IRoutine, cname: String)
- do
- _visitor = v
- _iroutine = ir
- _basecname = cname
- end
-end
-
-redef class IRoutine
- # Declare and start a C function that match the routine
- # Return what must be given to compile_inside_to_c or to compile_to_c
- # After the method, an openinig { and and indent is added.
- # So, do not forget to add a sub_context, to unintent and to add a closing }
- fun compile_signature_to_c(v: CompilerVisitor, cname: String, human_name: nullable String, before_params, after_params: nullable String): Array[String]
- do
- var cargs = new Array[String]
- var cparams = new Array[String]
- if before_params != null then cparams.add(before_params)
- for i in [0..params.length[ do
- cargs.add("p{i}")
- cparams.add("val_t p{i}")
- end
- if closure_decls != null then
- cparams.add("struct stack_frame_t *closctx_param")
- for i in [0..closure_decls.length[ do
- var closcn = "CLOS_{cname}_{i}"
- var cs = closure_decls[i].closure.signature
- var subparams = new Array[String] # Parameters of the closure
- subparams.add("struct stack_frame_t *")
- for j in [0..cs.arity[ do
- subparams.add("val_t")
- end
- var rr = "void"
- if cs.return_type != null then rr = "val_t"
- v.add_decl("typedef {rr} (*{closcn})({subparams.join(", ")});")
- cargs.add("clos_fun{i}")
- cparams.add("fun_t clos_fun{i}")
- end
- end
- if after_params != null then cparams.add(after_params)
- var r = "void"
- if result != null then r = "val_t"
- var p: String
- if cparams.is_empty then
- p = "void"
- else
- p = cparams.join(", ")
- end
- if human_name != null then
- v.add_instr("static const char LOCATE_{cname}[] = \"{human_name}\";")
- end
- v.add_decl("{r} {cname}({p});")
- v.add_decl("typedef {r} (*{cname}_t)({p});")
- v.add_instr("{r} {cname}({p})\{")
- v.indent
- return cargs
- end
-
- # Compile the body of the routine, return the result value is any
- fun compile_inside_to_c(v: I2CCompilerVisitor, args: Array[String]): nullable String
- do
- # Create and push the stack frame
- var ll = 0
- if location != null then
- ll = location.line_start
- end
- # Encapsulate the frame ('me') in a larger structure ('fra') that has enough space to store the local variables (REG)
- if std_slots_nb > 1 then
- v.add_decl("struct \{struct stack_frame_t me; val_t MORE_REG[{std_slots_nb-1}];\} fra;")
- else
- v.add_decl("struct \{struct stack_frame_t me;\} fra;")
- end
- v.add_instr("fra.me.prev = stack_frame_head; stack_frame_head = &fra.me;")
- v.add_instr("fra.me.file = LOCATE_{v.visitor.mmmodule.cname};")
- v.add_instr("fra.me.line = {ll};")
- v.add_instr("fra.me.meth = LOCATE_{v.basecname};")
- v.add_instr("fra.me.has_broke = 0;")
- v.add_instr("fra.me.REG_size = {std_slots_nb};")
- v.add_instr("fra.me.nitni_local_ref_head = NULL;")
-
- # Declare/initialize local variables
- for i in [0..std_slots_nb[ do
- v.add_instr("fra.me.REG[{i}] = NIT_NULL;")
- end
- for i in [0..tag_slots_nb[ do
- v.add_decl("val_t REGB{i};")
- end
- var iclosdecls = closure_decls
- if iclosdecls != null then
- v.add_decl("fun_t CREG[{iclosdecls.length}];")
- v.add_instr("fra.me.closure_ctx = closctx_param;")
- v.add_instr("fra.me.closure_funs = CREG;")
- end
- var k = 0
- for r in params do
- if r.slot_index != null then v.add_assignment(v.register(r), args[k])
- k += 1
- end
- if iclosdecls != null then
- for i in [0..iclosdecls.length[ do
- var iclosdecl = iclosdecls[i]
- v.add_instr("CREG[{i}] = {args[params.length+i]};")
- v.closures[iclosdecl] = i.to_s
- var cs = iclosdecl.closure.signature # Closure signature
- var subparams = new Array[String] # Parameters of the closure
- subparams.add("struct stack_frame_t *")
- for j in [0..cs.arity[ do
- var p = "val_t"
- subparams.add(p)
- end
- var r = "void"
- if cs.return_type != null then r = "val_t"
- v.clostypes[iclosdecl] = "{r} (*)({subparams.join(", ")})"
- end
- end
- v.add_decl("val_t tmp;")
-
- # Prepare return
- var old_rl = v.return_label
- v.return_label = body
-
- # Compile body
- body.compile_to_c(v)
-
- v.add_instr("stack_frame_head = fra.me.prev;")
- v.return_label = old_rl
- var r = result
- if r != null then
- return v.register(r)
- else
- return null
- end
- end
-
- # Full compilation of the routine
- # cv must be in the correct function
- fun compile_to_c(cv: CompilerVisitor, cname: String, args: Array[String]): nullable String
- do
- var v = new I2CCompilerVisitor(cv, self, cname)
- return compile_inside_to_c(v, args)
- end
-end
-
-redef class ICode
- # Full compilation of the icode
- fun compile_to_c(v: I2CCompilerVisitor) is abstract
-
- # Is a result really needed
- private fun need_result: Bool
- do
- var r = result
- return r != null and r.slot_index != null
- end
-
- # Store s in the result value of self
- private fun store_result(v: I2CCompilerVisitor, w: nullable Writer)
- do
- var r = result
- if r != null and r.slot_index != null then
- assert w != null
- var w2 = v.new_instr
- w2.add(v.register(r))
- w2.add(" = ")
- w2.append(w)
- w2.add(";\n")
- else if w != null and not is_pure then
- # ICode with side effects must be evaluated
- # even if the result is not wanted
- var w2 = v.new_instr
- w2.append(w)
- w2.add(";\n")
- end
- end
-
- # Prepare a writer if the expression icode need to be compiled
- # * Result assigment is automatic if needed
- private fun new_result(v: I2CCompilerVisitor): Writer
- do
- assert need_result or not is_pure
- var w2 = v.new_instr
- var r = result
- if r != null and r.slot_index != null then
- w2.add(v.register(r))
- w2.add(" = ")
- end
- var w = w2.sub
- w2.add(";\n")
- return w
- end
-end
-
-redef class ISeq
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- v.local_labels.add(self)
- var mark = iescape_mark
- if mark != null then v.marks_to_seq[mark] = self
- for ic in icodes do
- ic.compile_to_c(v)
- end
- v.add_label(self)
- end
-end
-
-redef class IIf
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = v.new_instr
- w.add("if (UNTAG_Bool(")
- w.add(v.register(expr))
- w.add(")) \{\n")
- if not then_seq.icodes.is_empty then
- v.indent
- then_seq.compile_to_c(v)
- v.unindent
- end
- if not else_seq.icodes.is_empty then
- v.add_instr("\} else \{")
- v.indent
- else_seq.compile_to_c(v)
- v.unindent
- end
- v.add_instr("\}")
- end
-end
-
-redef class ILoop
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- v.local_labels.add(self)
- var mark = iescape_mark
- if mark != null then v.marks_to_seq[mark] = self
- v.add_instr("while(1) \{")
- v.indent
- for ic in icodes do
- ic.compile_to_c(v)
- end
- v.unindent
- v.add_instr("\}")
- v.add_label(self)
- end
-end
-
-redef class IEscape
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- v.add_goto(v.marks_to_seq[iescape_mark])
- end
-end
-
-redef class IAbsCall
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var args = v.registers(exprs)
-
- # Compile closure definitions
- var old_el = v.escaped_labels
- var closdefs = closure_defs
- var closctx: nullable String = null # The closure context of closdefs
- if closdefs != null then
- # Get the closure context
- if v.closure then
- closctx = "closctx"
- else
- closctx = "(&(fra.me))"
- end
-
- # First aditionnal arguments is the closure context
- args.add(closctx)
-
- # We are in a new escape boundary
- v.escaped_labels = new HashMap[ISeq, Int]
-
- # Compile each closures and add each sub-function as an other additionnal parameter
- for cd in closdefs do
- if cd != null then
- var cn = cd.compile_closure(v)
- args.add(cn)
- else
- args.add("NULL")
- end
- end
- end
-
- # Compile the real call
- var call = compile_call_to_c(v, args)
- var res: nullable Writer = call
-
- # Intercept escapes
- if closctx != null then
- var els = v.escaped_labels
- v.escaped_labels = old_el
- # Is there possible escapes?
- if not els.is_empty then
- # Call in a tmp variable to avoid 'break' overwrite
- var w = v.new_instr
- if need_result then
- w.add("tmp")
- w.add(" = ")
- w.append(call)
- w.add(";\n")
- res = new Writer
- res.add("tmp")
- else
- res = null
- w.append(call)
- w.add(";\n")
- end
- # What are the expected escape indexes
- v.new_instr.add("switch (").add(closctx).add("->has_broke) \{\n")
- v.indent
- # No escape occured, continue as usual
- v.add_instr("case 0: break;")
- var lls = v.local_labels
- var iels = els.iterator
- var forward_escape = false
- while iels.is_ok do
- var seq = iels.key
- if lls.has(seq) then
- # Local escape occured
- # Clear the has_broke information and go to the target
- v.new_instr.add("case ").add(iels.item.to_s).add(": ").add(closctx).add("->has_broke = 0; goto ").add(v.lab(seq)).add(";\n")
- else
- # Forward escape occured: register the escape label
- assert v.closure
- v.register_escape_label(seq)
- forward_escape = true
- end
- iels.next
- end
- # If forward escape occured, just pass to the next one
- if forward_escape then
- # Do not need to copy 'has_broke' value since it is shared by the next one.
- # So just exit the C function.
- v.new_instr.add("default: goto ").add(v.lab(v.return_label.as(not null))).add(";\n")
- end
- v.unindent
- v.add_instr("\}")
- end
- end
-
- if res != null then
- var w = new_result(v)
- w.append(res)
- end
- end
-
- # The single invocation witout fancy stuffs
- private fun compile_call_to_c(v: I2CCompilerVisitor, args: Array[String]): Writer is abstract
-end
-
-redef class ICall
- redef fun compile_call_to_c(v, args)
- do
- var w = new Writer
-
- # do not compile explicit calls from native methods
- # theses are really manually called in the native implementation
- if is_explicit_from_extern then return w
-
- var prop = property
- if prop.global.is_init then args.add("init_table")
- w.add(prop.global.meth_call)
- w.add("(")
- w.add(args.first)
- w.add(")(")
- w.add_all(args, ", ")
- w.add(")")
- return w
- end
-end
-
-redef class ISuper
- redef fun compile_call_to_c(v, args)
- do
- # do not compile explicit calls from native methods
- # theses are really manually called in the native implementation
- if is_explicit_from_extern then return new Writer
-
- var prop = property
- if prop.global.is_init then args.add("init_table")
- var w = new Writer
- w.add(prop.super_meth_call)
- w.add("(")
- w.add(args.first)
- w.add(")(")
- w.add_all(args, ", ")
- w.add(")")
- return w
- end
-end
-
-redef class INew
- redef fun compile_call_to_c(v, args)
- do
- var w = new Writer
-
- # do not compile explicit calls from native methods
- # theses are really manually called in the native implementation
- if is_explicit_from_extern then return w
-
- w.add("NEW_")
- w.add(stype.local_class.to_s)
- w.add("_")
- w.add(property.global.intro.cname)
- w.add("(")
- w.add_all(args, ", ")
- w.add(")")
- return w
- end
-end
-
-redef class IAllocateInstance
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = new_result(v)
- w.add("NEW_")
- w.add(stype.local_class.cname)
- w.add("()")
- end
-end
-
-redef class ICheckInstance
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = new_result(v)
- w.add("CHECKNEW_")
- w.add(stype.local_class.cname)
- w.add("(")
- w.add(v.register(expr))
- w.add(")")
- end
-end
-
-redef class IInitAttributes
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = v.new_instr
- w.add("INIT_ATTRIBUTES__")
- w.add(stype.local_class.cname)
- w.add("(")
- w.add(v.register(expr))
- w.add(");\n")
- end
-end
-
-redef class IStaticCall
- redef fun compile_call_to_c(v, args)
- do
- var prop = property
- if prop.global.is_init then args.add("init_table")
- var w = new Writer
- w.add(property.cname)
- w.add("(")
- w.add_all(args, ", ")
- w.add(")")
- return w
- end
-end
-
-redef class INative
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- if method.is_intern then
- compile_intern_method_to_c(v)
- else if not method.global.is_init then
- compile_extern_method_to_c(v)
- end
- end
-
- fun compile_extern_method_to_c(v: I2CCompilerVisitor)
- do
- var ename = "{method.friendly_extern_name(method.local_class)}___out"
-
- var sig = method.signature
- assert exprs.length == sig.arity + 1
-
- var regs = v.registers(exprs)
-
- var args = new Array[String]
- args.add(regs[0])
- for i in [0..sig.arity[ do
- args.add(regs[i+1])
- end
- var s = "{ename}({args.join(", ")})"
-
- if need_result then s = s # sig.return_type.boxtype(s)
- var w = new_result(v)
- w.add(s)
- end
-
- fun compile_intern_method_to_c(v: I2CCompilerVisitor)
- do
- var sig = method.signature
- assert exprs.length == sig.arity + 1
- var c = method.local_class.name
- var n = method.name
- var regs = v.registers(exprs)
- var s: nullable String = null
- if c == once "Int".to_symbol then
- if n == once "object_id".to_symbol then
- s = regs[0]
- else if n == once "unary -".to_symbol then
- s = "TAG_Int(-UNTAG_Int({regs[0]}))"
- else if n == once "output".to_symbol then
- s = "printf(\"%ld\\n\", UNTAG_Int({regs[0]}));"
- else if n == once "ascii".to_symbol then
- s = "TAG_Char(UNTAG_Int({regs[0]}))"
- else if n == once "succ".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})+1)"
- else if n == once "prec".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})-1)"
- else if n == once "to_f".to_symbol then
- s = "BOX_Float((float)UNTAG_Int({regs[0]}))"
- else if n == once "+".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})+UNTAG_Int({regs[1]}))"
- else if n == once "-".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})-UNTAG_Int({regs[1]}))"
- else if n == once "*".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})*UNTAG_Int({regs[1]}))"
- else if n == once "/".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})/UNTAG_Int({regs[1]}))"
- else if n == once "%".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})%UNTAG_Int({regs[1]}))"
- else if n == once "<".to_symbol then
- s = "TAG_Bool(UNTAG_Int({regs[0]})<UNTAG_Int({regs[1]}))"
- else if n == once ">".to_symbol then
- s = "TAG_Bool(UNTAG_Int({regs[0]})>UNTAG_Int({regs[1]}))"
- else if n == once "<=".to_symbol then
- s = "TAG_Bool(UNTAG_Int({regs[0]})<=UNTAG_Int({regs[1]}))"
- else if n == once ">=".to_symbol then
- s = "TAG_Bool(UNTAG_Int({regs[0]})>=UNTAG_Int({regs[1]}))"
- else if n == once "lshift".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})<<UNTAG_Int({regs[1]}))"
- else if n == once "rshift".to_symbol then
- s = "TAG_Int(UNTAG_Int({regs[0]})>>UNTAG_Int({regs[1]}))"
- else if n == once "==".to_symbol then
- s = "TAG_Bool(({regs[0]})==({regs[1]}))"
- else if n == once "!=".to_symbol then
- s = "TAG_Bool(({regs[0]})!=({regs[1]}))"
- end
- else if c == once "Float".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int((bigint)UNBOX_Float({regs[0]}))"
- else if n == once "unary -".to_symbol then
- s = "BOX_Float(-UNBOX_Float({regs[0]}))"
- else if n == once "output".to_symbol then
- s = "printf(\"%f\\n\", UNBOX_Float({regs[0]}));"
- else if n == once "to_i".to_symbol then
- s = "TAG_Int((bigint)UNBOX_Float({regs[0]}))"
- else if n == once "+".to_symbol then
- s = "BOX_Float(UNBOX_Float({regs[0]})+UNBOX_Float({regs[1]}))"
- else if n == once "-".to_symbol then
- s = "BOX_Float(UNBOX_Float({regs[0]})-UNBOX_Float({regs[1]}))"
- else if n == once "*".to_symbol then
- s = "BOX_Float(UNBOX_Float({regs[0]})*UNBOX_Float({regs[1]}))"
- else if n == once "/".to_symbol then
- s = "BOX_Float(UNBOX_Float({regs[0]})/UNBOX_Float({regs[1]}))"
- else if n == once "<".to_symbol then
- s = "TAG_Bool(UNBOX_Float({regs[0]})<UNBOX_Float({regs[1]}))"
- else if n == once ">".to_symbol then
- s = "TAG_Bool(UNBOX_Float({regs[0]})>UNBOX_Float({regs[1]}))"
- else if n == once "<=".to_symbol then
- s = "TAG_Bool(UNBOX_Float({regs[0]})<=UNBOX_Float({regs[1]}))"
- else if n == once ">=".to_symbol then
- s = "TAG_Bool(UNBOX_Float({regs[0]})>=UNBOX_Float({regs[1]}))"
- end
- else if c == once "Char".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int(UNTAG_Char({regs[0]}))"
- else if n == once "unary -".to_symbol then
- s = "TAG_Char(-UNTAG_Char({regs[0]}))"
- else if n == once "output".to_symbol then
- s = "printf(\"%c\", (unsigned char)UNTAG_Char({regs[0]}));"
- else if n == once "ascii".to_symbol then
- s = "TAG_Int((unsigned char)UNTAG_Char({regs[0]}))"
- else if n == once "succ".to_symbol then
- s = "TAG_Char(UNTAG_Char({regs[0]})+1)"
- else if n == once "prec".to_symbol then
- s = "TAG_Char(UNTAG_Char({regs[0]})-1)"
- else if n == once "to_i".to_symbol then
- s = "TAG_Int(UNTAG_Char({regs[0]})-'0')"
- else if n == once "+".to_symbol then
- s = "TAG_Char(UNTAG_Char({regs[0]})+UNTAG_Char({regs[1]}))"
- else if n == once "-".to_symbol then
- s = "TAG_Char(UNTAG_Char({regs[0]})-UNTAG_Char({regs[1]}))"
- else if n == once "*".to_symbol then
- s = "TAG_Char(UNTAG_Char({regs[0]})*UNTAG_Char({regs[1]}))"
- else if n == once "/".to_symbol then
- s = "TAG_Char(UNTAG_Char({regs[0]})/UNTAG_Char({regs[1]}))"
- else if n == once "%".to_symbol then
- s = "TAG_Char(UNTAG_Char({regs[0]})%UNTAG_Char({regs[1]}))"
- else if n == once "<".to_symbol then
- s = "TAG_Bool(UNTAG_Char({regs[0]})<UNTAG_Char({regs[1]}))"
- else if n == once ">".to_symbol then
- s = "TAG_Bool(UNTAG_Char({regs[0]})>UNTAG_Char({regs[1]}))"
- else if n == once "<=".to_symbol then
- s = "TAG_Bool(UNTAG_Char({regs[0]})<=UNTAG_Char({regs[1]}))"
- else if n == once ">=".to_symbol then
- s = "TAG_Bool(UNTAG_Char({regs[0]})>=UNTAG_Char({regs[1]}))"
- else if n == once "==".to_symbol then
- s = "TAG_Bool(({regs[0]})==({regs[1]}))"
- else if n == once "!=".to_symbol then
- s = "TAG_Bool(({regs[0]})!=({regs[1]}))"
- end
- else if c == once "Bool".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int(UNTAG_Bool({regs[0]}))"
- else if n == once "unary -".to_symbol then
- s = "TAG_Bool(-UNTAG_Bool({regs[0]}))"
- else if n == once "output".to_symbol then
- s = "(void)printf(UNTAG_Bool({regs[0]})?\"true\\n\":\"false\\n\");"
- else if n == once "ascii".to_symbol then
- s = "TAG_Bool(UNTAG_Bool({regs[0]}))"
- else if n == once "to_i".to_symbol then
- s = "TAG_Int(UNTAG_Bool({regs[0]}))"
- else if n == once "==".to_symbol then
- s = "TAG_Bool(({regs[0]})==({regs[1]}))"
- else if n == once "!=".to_symbol then
- s = "TAG_Bool(({regs[0]})!=({regs[1]}))"
- end
- else if c == once "NativeArray".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int(((Nit_NativeArray){regs[0]})->object_id)"
- else if n == once "[]".to_symbol then
- s = "((Nit_NativeArray){regs[0]})->val[UNTAG_Int({regs[1]})]"
- else if n == once "[]=".to_symbol then
- s = "((Nit_NativeArray){regs[0]})->val[UNTAG_Int({regs[1]})]={regs[2]}"
- else if n == once "copy_to".to_symbol then
- s = "(void)memcpy(((Nit_NativeArray ){regs[1]})->val, ((Nit_NativeArray){regs[0]})->val, UNTAG_Int({regs[2]})*sizeof(val_t))"
- end
- else if c == once "NativeString".to_symbol then
- if n == once "object_id".to_symbol then
- s = "TAG_Int(UNBOX_NativeString({regs[0]}))"
- else if n == once "atoi".to_symbol then
- s = "TAG_Int(atoi(UNBOX_NativeString({regs[0]})))"
- else if n == once "[]".to_symbol then
- s = "TAG_Char(UNBOX_NativeString({regs[0]})[UNTAG_Int({regs[1]})])"
- else if n == once "[]=".to_symbol then
- s = "UNBOX_NativeString({regs[0]})[UNTAG_Int({regs[1]})]=UNTAG_Char({regs[2]});"
- else if n == once "copy_to".to_symbol then
- s = "(void)memcpy(UNBOX_NativeString({regs[1]})+UNTAG_Int({regs[4]}), UNBOX_NativeString({regs[0]})+UNTAG_Int({regs[3]}), UNTAG_Int({regs[2]}));"
- end
- else if c == once "Sys".to_symbol then
- if n == once "force_garbage_collection".to_symbol then
- s = "Nit_gc_force_garbage_collection()"
- else if n == once "native_argc".to_symbol then
- s = "TAG_Int(glob_argc)"
- else if n == once "native_argv".to_symbol then
- s = "BOX_NativeString(glob_argv[UNTAG_Int({regs[1]})])"
- end
- else if n == once "object_id".to_symbol then
- s = "TAG_Int((bigint)((obj_t){regs[0]})[1].object_id)"
- else if n == once "sys".to_symbol then
- s = "(G_sys)"
- else if n == once "is_same_type".to_symbol then
- s = "TAG_Bool((VAL2VFT({regs[0]})==VAL2VFT({regs[1]})))"
- else if n == once "exit".to_symbol then
- s = "exit(UNTAG_Int({regs[1]}));"
- else if n == once "calloc_array".to_symbol then
- s = "NEW_NativeArray(UNTAG_Int({regs[1]}), sizeof(val_t))"
- else if n == once "calloc_string".to_symbol then
- s = "BOX_NativeString((char*)raw_alloc((UNTAG_Int({regs[1]}) * sizeof(char))))"
- # Add output_class_name native implementation
- else if n == once "output_class_name".to_symbol then
- s = "printf(\"%s\\n\", VAL2VFT({regs[0]})[2].cname);"
- # Add class_name implementation
- else if n == once "native_class_name".to_symbol then
- s = "BOX_NativeString(VAL2VFT({regs[0]})[2].cname);"
- end
-
- if s == null then
- var ll = location
- if ll != null then v.add_instr("fprintf(stderr, \"{ll.to_s}: \");")
- v.add_instr("fprintf(stderr, \"Fatal error: unknown intern method {method.full_name}.\\n\");")
- v.add_instr("nit_exit(1);")
- s = "NIT_NULL"
- end
- if result == null then
- v.new_instr.add(s).add(";\n")
- else if need_result then
- var w = new_result(v)
- w.add(s)
- end
- end
-end
-
-redef class IIntValue
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = new_result(v)
- w.add("TAG_Int(").add(value.to_s).add(")")
- end
-end
-
-redef class IBoolValue
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = new_result(v)
- w.add("TAG_Bool(")
- if value then w.add("1") else w.add("0")
- w.add(")")
- end
-end
-
-redef class ICharValue
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = new_result(v)
- w.add("TAG_Char(").add(value).add(")")
- end
-end
-
-redef class IFloatValue
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = new_result(v)
- w.add("BOX_Float(").add(value).add(")")
- end
-end
-
-redef class IStringValue
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = new_result(v)
- w.add("BOX_NativeString(\"").add(value).add("\")")
- end
-end
-
-redef class IAbort
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = v.new_instr
- w.add("nit_abort(\"")
- w.add(texts[0])
- if texts.length > 1 then
- w.add("\", \"")
- w.add(texts[1])
- w.add("\"")
- else
- w.add("\", NULL")
- end
- w.add(", LOCATE_")
- w.add(module_location.cname)
- var ll = location
- if ll != null then
- w.add(", ")
- w.add(ll.line_start.to_s)
- else
- w.add(", 0")
- end
- w.add(");\n")
- end
-end
-
-redef class IMove
- redef fun compile_to_c(v)
- do
- if not need_result then return
- var e = v.register(expr)
- var r = v.register(result.as(not null))
- if e == r then return
- v.add_location(location)
- var w = v.new_instr
- w.add(r)
- w.add(" = ")
- w.add(e)
- w.add(";\n")
- end
-end
-
-redef class IAttrRead
- redef fun compile_to_c(v)
- do
- if not need_result then return
- v.add_location(location)
- var w = new_result(v)
- w.add(property.global.attr_access)
- w.add("(")
- w.add(v.register(expr))
- w.add(")")
- end
-end
-
-redef class IAttrIsset
- redef fun compile_to_c(v)
- do
- if not need_result then return
- v.add_location(location)
- var w = new_result(v)
- w.add("TAG_Bool(")
- w.add(property.global.attr_access)
- w.add("(")
- w.add(v.register(expr))
- w.add(")!=NIT_NULL)")
- end
-end
-
-redef class IAttrWrite
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var w = v.new_instr
- w.add(property.global.attr_access)
- w.add("(")
- w.add(v.register(expr1))
- w.add(") = ")
- w.add(v.register(expr2))
- w.add(";\n")
- end
-end
-
-redef class ITypeCheck
- redef fun compile_to_c(v)
- do
- if not need_result then return
- v.add_location(location)
- var recv = v.register(expr2)
- var w = new_result(v)
- w.add("TAG_Bool(")
- if expr2.stype.is_nullable then
- if stype.is_nullable then
- w.add("(")
- w.add(recv)
- w.add("==NIT_NULL) || ")
- else if stype.as_nullable == expr2.stype then
- w.add(recv)
- w.add("!=NIT_NULL)")
- return
- else
- w.add("(")
- w.add(recv)
- w.add("!=NIT_NULL) && ")
- end
- end
- # FIXME handle formaltypes
- var t = stype
- if t isa MMVirtualType then
- var slf = v.register(expr1)
- var g = t.property.global
- w.add("VAL_ISA(")
- w.add(recv)
- w.add(", ")
- w.add(g.vt_class_color)
- w.add("(")
- w.add(slf)
- w.add(")")
- w.add(", ")
- w.add(g.vt_class_id)
- w.add("(")
- w.add(slf)
- w.add(")")
- w.add(")) /*cast ")
- w.add(t.to_s)
- w.add("*/")
- else
- var g = t.local_class.global
- w.add("VAL_ISA(")
- w.add(recv)
- w.add(", ")
- w.add(g.color_id)
- w.add(", ")
- w.add(g.id_id)
- w.add(")) /*cast ")
- w.add(t.to_s)
- w.add("*/")
- end
- end
-end
-
-redef class IIs
- redef fun compile_to_c(v)
- do
- if not need_result then return
- v.add_location(location)
- var w = new_result(v)
- w.add("TAG_Bool(")
- var t1 = expr1.stype
- var t2 = expr2.stype
- if t1 isa MMTypeNone then
- if t2 isa MMTypeNone then
- w.add("1)")
- return
- else if t2.is_nullable then
- w.add(v.register(expr2))
- w.add("==NIT_NULL)")
- return
- else
- w.add("0)")
- return
- end
- else if t1.is_nullable then
- if t2 isa MMTypeNone then
- w.add(v.register(expr1))
- w.add("==NIT_NULL)")
- return
- else if t2.is_nullable then
- w.add("IS_EQUAL_NN(")
- else
- w.add("IS_EQUAL_ON(")
- w.add(v.register(expr2))
- w.add(",")
- w.add(v.register(expr1))
- w.add("))")
- return
- end
- else
- if t2 isa MMTypeNone then
- w.add("0)")
- return
- else if t2.is_nullable then
- w.add("IS_EQUAL_ON(")
- else
- w.add("IS_EQUAL_OO(")
- end
- end
- w.add(v.register(expr1))
- w.add(",")
- w.add(v.register(expr2))
- w.add("))")
- end
-end
-
-redef class INot
- redef fun compile_to_c(v)
- do
- if not need_result then return
- v.add_location(location)
- var w = new_result(v)
- w.add("TAG_Bool(!UNTAG_Bool(")
- w.add(v.register(expr))
- w.add("))")
- end
-end
-
-redef class IOnce
- redef fun compile_to_c(v)
- do
- v.add_location(location)
- var i = v.new_number
- var res = result.as(not null)
- if res.stype.is_nullable then
- v.add_decl("static val_t once_value_{i}; static int once_bool_{i}; /* Once value */")
- v.add_instr("if (!once_bool_{i}) \{")
- else
- # Since the value is not nullable, we use the null value to represent the boolean
- v.add_decl("static val_t once_value_{i}; /* Once value */")
- v.add_instr("if (!once_value_{i}) \{")
- end
- v.indent
- body.compile_to_c(v)
- var e = v.register(res)
- v.add_instr("once_value_{i} = {e};")
- v.add_instr("register_static_object(&once_value_{i});")
- if res.stype.is_nullable then v.add_instr("once_bool_{i} = true;")
- v.unindent
- v.add_instr("\} else {e} = once_value_{i};")
- var w = new_result(v)
- w.add(e)
- end
-end
-
-redef class IClosCall
- redef fun compile_to_c(v: I2CCompilerVisitor)
- do
- v.add_location(location)
- var ivar: String
- var args: Array[String]
- if v.closure then
- ivar = "closctx->closure_funs[{v.closures[closure_decl]}]"
- args = ["closctx->closure_ctx"]
- else
- ivar = "CREG[{v.closures[closure_decl]}]"
- args = ["closctx_param"]
- end
- args.append(v.registers(exprs))
-
- var s = "(({v.clostypes[closure_decl]})({ivar}))({args.join(", ")})"
- var w = new Writer
- w.add(s)
- store_result(v, w)
-
- # Intercept escape
- v.add_instr("if ({args.first}->has_broke) \{")
- v.indent
- var bs = break_seq
- if bs != null then
- bs.compile_to_c(v)
- end
- v.add_goto(v.iroutine.body)
- v.unindent
- v.add_instr("\}")
- end
-end
-
-redef class IHasClos
- redef fun compile_to_c(v)
- do
- if not need_result then return
- v.add_location(location)
- var w = new_result(v)
- w.add("TAG_Bool(")
- if v.closure then
- w.add("closctx->closure_funs[")
- w.add(v.closures[closure_decl])
- w.add("]")
- else
- w.add("CREG[")
- w.add(v.closures[closure_decl])
- w.add("]")
- end
- w.add(" != NULL)")
- end
-end
-
-redef class IClosureDef
- # Compile the closure as a separate C function in the visitor out_contexts.
- # Return a fun_t pointer to the function.
- fun compile_closure(v: I2CCompilerVisitor): String
- do
- var cv = v.visitor
-
- # We are now in a closure
- var cfc_old = v.closure
- v.closure = true
-
- # We are now in a escape boundary
- var lls_old = v.local_labels
- v.local_labels = new HashSet[ISeq]
-
- # We are now in a new C context
- var decl_writer_old = cv.decl_writer
- var writer_old = cv.writer
- cv.writer = cv.top_writer.sub
- cv.decl_writer = cv.header_writer.sub
-
- # Generate the C function
- var cname = "OC_{v.basecname}_{v.new_number}"
- var args = compile_signature_to_c(v.visitor, cname, null, "struct stack_frame_t *closctx", null)
- cv.decl_writer = cv.writer.sub
-
- var s = compile_inside_to_c(v, args)
- if s == null then
- v.add_instr("return;")
- else
- v.add_instr("return {s};")
- end
- v.unindent
- v.add_instr("\}")
-
- # Restore things
- cv.writer = writer_old
- cv.decl_writer = decl_writer_old
- v.closure = cfc_old
- v.local_labels = lls_old
- return "((fun_t){cname})"
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Generates ICode as an output format
-module icode_generator
-
-private import compiling_base
-import program
-private import analysis
-private import primitive_info
-
-redef class Program
- # Generates ICode for the whole program
- fun generate_icode_files do
- tc.compdir.mkdir
-
- with_each_live_local_classes !action(c) do
- c.generate_icode_file(tc.compdir.as(not null))
- end
- end
-end
-
-# A class to dump ICode to a file
-class FileICodeDumper
- super ICodeDumper
- var _file: OFStream
-
- init(f: OFStream) do
- # We don't want to output line numbers and locations
- super(false, false)
- _file = f
- end
-
- redef fun write(s) do
- for i in [0..indent_level[ do
- _file.write(" ")
- end
- _file.write(s)
- _file.write("\n")
- end
-end
-
-redef class MMLocalClass
- # Generates ICode for all properties of this class in a file
- fun generate_icode_file(dir: String) do
- var file = new OFStream.open("{dir}/{self}.icode")
- if primitive_info == null then
- # Initialization methods
- if init_var_iroutine != null then
- var icd = new FileICodeDumper(file)
- icd.indent
- file.write("Init var iroutine::\n")
- init_var_iroutine.dump(icd)
- file.write("\n\n")
- end
- if checknew_iroutine != null then
- var icd = new FileICodeDumper(file)
- icd.indent
- file.write("Check new instance iroutine::\n")
- checknew_iroutine.dump(icd)
- file.write("\n\n")
- end
-
- # 'new' methods
- for pg in global_properties do
- if not pg.is_init_for(self) then continue
- var p = self[pg]
- assert p isa MMMethod
- if not new_instance_iroutine.has_key(p) then continue
- var icd = new FileICodeDumper(file)
- icd.indent
- file.write("New instance:: {p.full_name}\n")
- new_instance_iroutine[p].dump(icd)
- file.write("\n\n")
- end
- end
-
- # Other methods
- for pg in global_properties do
- var p = self[pg]
- if p.local_class == self and p isa MMMethod then
- p.generate_icode(file)
- end
- end
- file.close
- end
-end
-
-redef class MMMethod
- # Generates ICode for this method with a little header
- fun generate_icode(file: OFStream) do
- var icd = new FileICodeDumper(file)
- icd.indent
- file.write("Method:: {full_name}\n")
- iroutine.dump(icd)
- file.write("\n\n")
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Compute tables for classes and modules.
-module table_computation
-
-import mmloader
-private import primitive_info
-import program
-
-# Something that store color of table elements
-abstract class ColorContext
- var _colors: HashMap[TableElt, Int] = new HashMap[TableElt, Int]
-
- # The color of a table element.
- fun color(e: TableElt): Int
- do
- return _colors[e]
- end
-
- # Is a table element already colored?
- fun has_color(e: TableElt): Bool
- do
- return _colors.has_key(e)
- end
-
- # Assign a color to a table element.
- fun color=(e: TableElt, c: Int)
- do
- _colors[e] = c
- var idx = c
- for i in [0..e.length[ do
- _colors[e.item(i)] = idx
- idx = idx + 1
- end
- end
-end
-
-# All information and results of the global analysis.
-class TableInformation
- super ColorContext
- # FIXME: do something better.
- readable writable var _max_class_table_length: Int = 0
-end
-
-# A compiled class is a class in a program
-class CompiledClass
- super ColorContext
- # The corresponding local class in the main module of the prgram
- readable var _local_class: MMLocalClass
-
- # The identifier of the class
- readable writable var _id: Int = 0
-
- # The full class table of the class
- readable var _class_table: Array[nullable TableElt] = new Array[nullable TableElt]
-
- # The full instance table of the class
- readable var _instance_table: Array[nullable TableElt] = new Array[nullable TableElt]
-
- # The proper class table part (no superclasses but all refinements)
- readable writable var _class_layout: TableEltComposite = new TableEltComposite(self)
-
- # The proper instance table part (no superclasses but all refinements)
- readable writable var _instance_layout: TableEltComposite = new TableEltComposite(self)
-
- init(c: MMLocalClass) do _local_class = c
-end
-
-redef class MMConcreteClass
- # The table element of the subtype check
- fun class_color_pos: TableEltClassColor do return _class_color_pos.as(not null)
- var _class_color_pos: nullable TableEltClassColor
-
- # The proper local class table part (nor superclasses nor refinments)
- readable var _class_layout: Array[TableElt] = new Array[TableElt]
-
- # The proper local instance table part (nor superclasses nor refinments)
- readable var _instance_layout: Array[TableElt] = new Array[TableElt]
-
- # Build the local layout of the class and feed the module table
- private fun build_layout_in(module_table: Array[ModuleTableElt])
- do
- var clt = _class_layout
- var ilt = _instance_layout
-
- if global.intro == self then
- module_table.add(new TableEltClassId(self))
- var cpp = new TableEltClassColor(self)
- _class_color_pos = cpp
- module_table.add(cpp)
- clt.add(new TableEltClassInitTable(self))
- end
- for p in local_local_properties do
- var pg = p.global
- if pg.intro == p then
- if p isa MMAttribute then
- ilt.add(new TableEltAttr(p))
- else if p isa MMMethod then
- clt.add(new TableEltMeth(p))
- else if p isa MMTypeProperty then
- clt.add(new TableEltVTClassId(p))
- clt.add(new TableEltVTClassColor(p))
- end
- end
- if p isa MMMethod and p.need_super then
- clt.add(new TableEltSuper(p))
- end
- end
-
- if not ilt.is_empty then
- var teg = new ModuleTableEltGroup
- teg.elements.append(ilt)
- module_table.add(teg)
- end
-
- if not clt.is_empty then
- var teg = new ModuleTableEltGroup
- teg.elements.append(clt)
- module_table.add(teg)
- end
- end
-end
-
-redef class Program
- # Information about the class tables
- readable var _table_information: TableInformation = new TableInformation
-
- # Associate global classes to compiled classes
- readable var _compiled_classes: HashMap[MMGlobalClass, CompiledClass] = new HashMap[MMGlobalClass, CompiledClass]
-
- fun do_table_computation
- do
- tc.info("Building tables",1)
- for m in main_module.mhe.greaters_and_self do
- tc.info("Building tables for module: {m.name}",2)
- m.local_analysis
- end
-
- tc.info("Merging all tables",2)
- do_global_table_analysis
- end
-
- # Do the complete global analysis
- private fun do_global_table_analysis
- do
- var smallest_classes = new Array[MMLocalClass]
- var global_properties = new HashSet[MMGlobalProperty]
- var ctab = new Array[TableElt]
- var itab = new Array[TableElt]
-
- ctab.add(new TableEltClassSelfId)
- ctab.add(new TableEltClassObjectSize)
- ctab.add(new TableEltClassSelfName)
- itab.add(new TableEltVftPointer)
- itab.add(new TableEltObjectId)
-
- var pclassid = -1
- var classid = 3
-
- # We have to work on ALL the classes of the module
- var classes = new Array[MMLocalClass]
- for c in main_module.local_classes do classes.add(c)
- classes.sort !cmp(x,y) = x.total_order_compare(y)
-
- for c in classes do
- # Associate a CompiledClass to the class
- var cc = new CompiledClass(c)
- compiled_classes[c.global] = cc
-
- # Assign a unique class identifier
- # (negative are for primitive classes)
- var gc = c.global
- var bm = gc.mmmodule
- if c.primitive_info != null then
- cc.id = pclassid
- pclassid = pclassid - 4
- else
- cc.id = classid
- classid = classid + 4
- end
-
- # Register is the class is a leaf
- if c.cshe.direct_smallers.is_empty then
- smallest_classes.add(c)
- end
-
- # Store the colortableelt in the class table pool
- var bc = c.global.intro
- assert bc isa MMConcreteClass
- ctab.add(bc.class_color_pos)
- end
-
- # Compute core and crown classes for colorization
- var crown_classes = new HashSet[MMLocalClass]
- var core_classes = new HashSet[MMLocalClass]
- for c in smallest_classes do
- while c.cshe.direct_greaters.length == 1 do
- c = c.cshe.direct_greaters.first
- end
- crown_classes.add(c)
- core_classes.add_all(c.cshe.greaters_and_self)
- end
- #print("nbclasses: {classes.length} leaves: {smallest_classes.length} crown: {crown_classes.length} core: {core_classes.length}")
-
- # Colorize core color for typechecks
- colorize(ctab, crown_classes, 0)
-
- # Compute tables for typechecks
- var maxcolor = 0
- for c in classes do
- var cc = compiled_classes[c.global]
- if core_classes.has(c) then
- # For core classes, just build the table
- build_tables_in(cc.class_table, c, ctab)
- if maxcolor < cc.class_table.length then maxcolor = cc.class_table.length
- else
- # For other classes, it's easier: just append to the parent tables
- var sc = c.cshe.direct_greaters.first
- var scc = compiled_classes[sc.global]
- assert cc.class_table.is_empty
- cc.class_table.add_all(scc.class_table)
- var bc = c.global.intro
- assert bc isa MMConcreteClass
- var colpos = bc.class_color_pos
- var colposcolor = cc.class_table.length
- table_information.color(colpos) = colposcolor
- cc.class_table.add(colpos)
- if maxcolor < colposcolor then maxcolor = colposcolor
- end
- end
- table_information.max_class_table_length = maxcolor + 1
-
- # Fill class table and instance tables pools
- for c in classes do
- var cc = compiled_classes[c.global]
- var cte = cc.class_layout
- var ite = cc.instance_layout
- for sc in c.crhe.greaters_and_self do
- if sc isa MMConcreteClass then
- cte.add(sc, sc.class_layout)
- ite.add(sc, sc.instance_layout)
- end
- end
-
- if core_classes.has(c) then
- if cte.length > 0 then
- ctab.add(cte)
- end
- if ite.length > 0 then
- itab.add(ite)
- end
- end
- end
-
- # Colorize all elements in pools tables
- colorize(ctab, crown_classes, maxcolor+1)
- colorize(itab, crown_classes, 0)
-
- # Build class and instance tables now things are colored
- table_information.max_class_table_length = 0
- for c in classes do
- var cc = compiled_classes[c.global]
- if core_classes.has(c) then
- # For core classes, just build the table
- build_tables_in(cc.class_table, c, ctab)
- build_tables_in(cc.instance_table, c, itab)
- else
- # For other classes, it's easier: just append to the parent tables
- var sc = c.cshe.direct_greaters.first
- var scc = compiled_classes[sc.global]
- cc.class_table.clear
- cc.class_table.add_all(scc.class_table)
- var bc = c.global.intro
- assert bc isa MMConcreteClass
- var colpos = bc.class_color_pos
- cc.class_table[table_information.color(colpos)] = colpos
- while cc.class_table.length <= maxcolor do
- cc.class_table.add(null)
- end
- append_to_table(cc.class_table, cc.class_layout)
- assert cc.instance_table.is_empty
- cc.instance_table.add_all(scc.instance_table)
- append_to_table(cc.instance_table, cc.instance_layout)
- end
- end
- end
-
- # Perform coloring
- fun colorize(elts: Array[TableElt], classes: Collection[MMLocalClass], startcolor: Int)
- do
- var colors = new HashMap[Int, Array[TableElt]]
- var rel_classes = new Array[MMLocalClass]
- for e in elts do
- var color = -1
- var len = e.length
- if table_information.has_color(e) then
- color = table_information.color(e)
- else
- rel_classes.clear
- for c in classes do
- if e.is_related_to(c) then
- rel_classes.add(c)
- end
- end
- var trycolor = startcolor
- while trycolor != color do
- color = trycolor
- for c in rel_classes do
- var idx = 0
- while idx < len do
- if colors.has_key(trycolor + idx) and not free_color(colors[trycolor + idx], c) then
- trycolor = trycolor + idx + 1
- idx = 0
- else
- idx = idx + 1
- end
- end
- end
- end
- table_information.color(e) = color
- end
- for idx in [0..len[ do
- if colors.has_key(color + idx) then
- colors[color + idx].add(e)
- else
- colors[color + idx] = [e]
- end
- end
- end
- end
-
- private fun free_color(es: Array[TableElt], c: MMLocalClass): Bool
- do
- for e2 in es do
- if e2.is_related_to(c) then
- return false
- end
- end
- return true
- end
-
- private fun append_to_table(table: Array[nullable TableElt], cmp: TableEltComposite)
- do
- for j in [0..cmp.length[ do
- var e = cmp.item(j)
- table_information.color(e) = table.length
- table.add(e)
- end
- end
-
- private fun build_tables_in(table: Array[nullable TableElt], c: MMLocalClass, elts: Array[TableElt])
- do
- var tab = new HashMap[Int, TableElt]
- var len = 0
- for e in elts do
- if e.is_related_to(c) then
- var col = table_information.color(e)
- var l = col + e.length
- tab[col] = e
- if len < l then
- len = l
- end
- end
- end
- var i = 0
- while i < len do
- if tab.has_key(i) then
- var e = tab[i]
- for j in [0..e.length[ do
- table[i] = e.item(j)
- i = i + 1
- end
- else
- table[i] = null
- i = i + 1
- end
- end
- end
-end
-
-redef class MMModule
- # The local table of the module (refers things introduced in the module)
- readable var _local_table: Array[ModuleTableElt] = new Array[ModuleTableElt]
-
- # Builds the local tables and local classes layouts
- private fun local_analysis
- do
- for c in local_classes do
- if c isa MMConcreteClass then
- c.build_layout_in(_local_table)
- end
- end
- end
-end
-
-###############################################################################
-
-# An element of a class, an instance or a module table
-interface AbsTableElt
-end
-
-# An element of a class or an instance table
-# Such an elements represent method function pointers, attribute values, etc.
-interface TableElt
- super AbsTableElt
- # Is the element conflict to class `c' (used for coloring)
- fun is_related_to(c: MMLocalClass): Bool is abstract
-
- # Number of sub-elements. 1 if none
- fun length: Int do return 1
-
- # Access the ith subelement.
- fun item(i: Int): TableElt do return self
-end
-
-# An element of a module table
-# Such an elements represent colors or identifiers
-interface ModuleTableElt
- super AbsTableElt
-end
-
-# An element of a module table that represents a group of TableElt defined in the same local class
-class ModuleTableEltGroup
- super ModuleTableElt
- readable var _elements: Array[TableElt] = new Array[TableElt]
-end
-
-# An element that represents a class property
-abstract class TableEltProp
- super TableElt
- readable var _property: MMLocalProperty
-
- init(p: MMLocalProperty)
- do
- _property = p
- end
-end
-
-# An element that represents a function pointer to a global method
-class TableEltMeth
- super TableEltProp
-end
-
-# An element that represents a class color value for a virtual type
-class TableEltVTClassColor
- super TableEltProp
-end
-
-# An element that represents a class id value for a virtual type
-class TableEltVTClassId
- super TableEltProp
-end
-
-# An element that represents a function pointer to the super method of a local method
-class TableEltSuper
- super TableEltProp
-end
-
-# An element that represents the value stored for a global attribute
-class TableEltAttr
- super TableEltProp
-end
-
-# An element representing a class information
-abstract class AbsTableEltClass
- super AbsTableElt
- # The local class where the information comes from
- readable var _local_class: MMLocalClass
-
- init(c: MMLocalClass)
- do
- _local_class = c
- end
-end
-
-# An element of a class table representing a class information
-abstract class TableEltClass
- super TableElt
- super AbsTableEltClass
- redef fun is_related_to(c)
- do
- var bc = c.mmmodule[_local_class.global]
- return c.cshe <= bc
- end
-end
-
-# An element representing the id of a class in a module table
-class TableEltClassId
- super ModuleTableElt
- super AbsTableEltClass
-end
-
-# An element representing the constructor marker position in a class table
-class TableEltClassInitTable
- super TableEltClass
-end
-
-# An element used for a cast
-# Note: this element is both a TableElt and a ModuleTableElt.
-# At the TableElt offset, there is the id of the super-class
-# At the ModuleTableElt offset, there is the TableElt offset (ie. the color of the super-class).
-class TableEltClassColor
- super TableEltClass
- super ModuleTableElt
-end
-
-# A Group of elements introduced in the same global-class that are colored together
-class TableEltComposite
- super TableElt
- var _table: Array[TableElt]
- var _cc: CompiledClass
- var _offsets: HashMap[MMLocalClass, Int]
- redef fun length do return _table.length
- redef fun is_related_to(c) do return c.cshe <= _cc.local_class
-
- fun add(c: MMLocalClass, tab: Array[TableElt])
- do
- _offsets[c] = _table.length
- _table.append(tab)
- end
-
- redef fun item(i) do return _table[i]
-
- init(cc: CompiledClass)
- do
- _cc = cc
- _table = new Array[TableElt]
- _offsets = new HashMap[MMLocalClass, Int]
- end
-end
-
-# The element that represent the class id
-class TableEltClassSelfId
- super TableElt
- redef fun is_related_to(c) do return true
-end
-
-# The element that represent the class name
-class TableEltClassSelfName
- super TableElt
- redef fun is_related_to(c) do return true
-end
-
-# The element that represent the Object Size
-class TableEltClassObjectSize
- super TableElt
- redef fun is_related_to(c) do return true
-end
-
-# The element that represent the object id
-class TableEltObjectId
- super TableElt
- redef fun is_related_to(c) do return true
-end
-
-# The element that
-class TableEltVftPointer
- super TableElt
- redef fun is_related_to(c) do return true
-end
fi
}
-# Are we in the nitc.nit directory?
-if [ ! -f nitc.nit ]; then
- if [ -f src/nitc.nit ]; then
+# Are we in the nit.nit directory?
+if [ ! -f nit.nit ]; then
+ if [ -f src/nit.nit ]; then
cd src
else
- echo "Error: no nitc.nit found." >&2
+ echo "Error: no nit.nit found." >&2
exit 1
fi
fi
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This module implements Class Hierarchy Analysis (CHA)
-module cha_analysis
-
-import reachable_method_analysis
-import icode
-import program
-
-class ChaContext
- super ReachableMethodAnalysis
- readable var _reachable_iroutines: HashSet[IRoutine] = new HashSet[IRoutine]
-
- redef fun is_iroutine_reachable(ir: nullable IRoutine): Bool do
- return ir != null and reachable_iroutines.has(ir)
- end
-
- redef fun is_method_reachable(method: MMMethod): Bool do
- return is_iroutine_reachable(method.iroutine)
- end
-end
-
-class ChaBuilder
- readable var _iroutine_to_search: List[IRoutine] = new List[IRoutine]
- readable var _context: ChaContext
- readable var _program: Program
-
- init(p: Program) do
- _program = p
- _context = new ChaContext
- end
-
- private fun add_search(method: nullable MMMethod, iroutine: nullable IRoutine, is_static: Bool, is_super: Bool) do
- # Add the exact method
- if iroutine != null and not context.is_iroutine_reachable(iroutine) then
- context.reachable_iroutines.add(iroutine)
- _iroutine_to_search.add(iroutine)
- end
-
- if method != null then
- # Add every redefinition
- if not is_static then
- for other in method.prhe.smallers do
- if other isa MMMethod then
- add_search(other, other.iroutine, true, false)
- end
- end
- end
-
- # Add all parents since we don't know which one will get called !
- if is_super then
- for greater in method.prhe.greaters do
- if greater isa MMMethod then
- add_search(greater, greater.iroutine, true, false)
- end
- end
- end
- end
- end
-
- # Build the context associated with this builder
- fun work do
- var main_method = program.main_method
- if main_method == null then return
-
- add_search (main_method, main_method.iroutine, true, false)
-
- while not iroutine_to_search.is_empty do
- var v = new ChaVisitor(self)
- var iroutine = iroutine_to_search.pop
- v.visit_icode(iroutine.body)
- end
- end
-end
-
-class ChaVisitor
- super ICodeVisitor
- readable var _builder: ChaBuilder
-
- redef fun visit_icode(ic)
- do
- if ic isa IStaticCall then
- # FIXME: take only the last property on the redef. hierarchie
- builder.add_search(ic.property, ic.property.iroutine, true, false)
- else if ic isa INew then
- # FIXME: take only the last property on the redef. hierarchie
- var t = ic.stype
- var cls = t.for_module(builder.program.main_module).local_class
- if not cls.global.is_extern then
- var m = cls[ic.property.global].as(MMMethod)
- var r = cls.new_instance_iroutine[m]
- builder.add_search(ic.property, r, false, false)
- end
- else if ic isa ISuper then
- builder.add_search(ic.property, ic.property.iroutine, false, true)
- else if ic isa ICall then
- builder.add_search(ic.property, ic.property.iroutine, false, false)
- else if ic isa ICheckInstance then
- var t = ic.stype
- var cls = t.for_module(builder.program.main_module).local_class
- var ir = cls.checknew_iroutine
- builder.add_search(null, ir, true, false)
- else if ic isa IInitAttributes then
- var t = ic.stype
- var cls = t.for_module(builder.program.main_module).local_class
- var ir = cls.init_var_iroutine
- builder.add_search(null, ir, true, false)
- end
- super
- end
-
- init(b: ChaBuilder)
- do
- _builder = b
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This module introduces an algorithm to remove the body of dead methods
-module dead_method_removal
-
-import reachable_method_analysis
-
-redef class Program
- readable var _nb_removed_iroutines: Int = 0
- readable var _nb_not_removed_iroutines: Int = 0
-
- # Calling this method will change all iroutines that are dead
- # and put an abort in them
- fun optimize_dead_methods do
- with_each_iroutines !action(i,m) do
- if not rma.is_iroutine_reachable(i) then
- i.set_not_reachable(m)
- _nb_removed_iroutines = nb_removed_iroutines + 1
- else
- _nb_not_removed_iroutines = nb_not_removed_iroutines + 1
- end
- end
- end
-
- # This method will create a file and output informations about this optimization
- fun dump_dead_method_optimization(directory_name: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.dmr_opt.log")
-
- f.write("Nb. dead iroutines removed: {nb_removed_iroutines}\n")
- f.write("Nb. live iroutines: {nb_not_removed_iroutines}\n")
-
- f.close
- end
-end
-
-redef class IRoutine
- # Simple helper function ...
- private fun set_not_reachable(m: MMModule) do
- var icb = new ICodeBuilder(m, self)
- icb.seq.icodes.clear
- icb.add_abort("This method should not be called !")
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Intermediate code global analysis and optimizations
-module global
-
-# Global imports
-import icode
-import program
-import abstracttool
-
-# Global Analysis types
-private import instantiated_type_analysis
-private import reachable_method_analysis
-private import reachable_as_init
-private import reachable_from_init_method_analysis
-
-# Global Analysis implementation
-private import cha_analysis
-private import rta_analysis
-private import reachable_as_init_impl
-private import reachable_from_init_method_analysis_impl
-
-# Global Optimizations
-private import dead_method_removal
-private import inline_get_and_set
-private import remove_out_of_init_get_test
-
-redef class ToolContext
- readable writable var _global_callgraph: String = "rta"
-
- readable var _opt_global: OptionBool = new OptionBool("Use global compilation", "--global")
- readable var _opt_global_no_STF_opt: OptionBool = new OptionBool("Do not use SFT optimization", "--no-global-SFT-optimization")
- readable var _opt_global_no_DMR_opt: OptionBool = new OptionBool("Do not use dead method removal optimization", "--no-global-DMR-optimization")
- readable var _opt_global_no_inline_get_set: OptionBool = new OptionBool("Do not automatically inline getters/setters", "--no-global-get-set-inlining")
- readable var _opt_global_no_out_of_init_get_test_opt: OptionBool = new OptionBool("Do not remove get tests outside object initialization", "--no-global-OOIT-optimization")
- readable var _opt_global_no_RFIMA: OptionBool = new OptionBool("Do not use a specialized algorithm to find reachable methods from initializers", "--no-global-RFIM-analysis")
- readable var _opt_global_callgraph: OptionEnum = new OptionEnum(["none", "cha", "rta"], "The algorithm to use to build the callgraph", 2, "--global-callgraph")
-
- redef init
- do
- super
- option_context.add_option(opt_global, opt_global_no_STF_opt, opt_global_no_DMR_opt, opt_global_callgraph, opt_global_no_inline_get_set, opt_global_no_RFIMA, opt_global_no_out_of_init_get_test_opt)
- end
-end
-
-redef class AbstractCompiler
- redef fun process_options
- do
- # FIXME: for some reason (a bug in the metamodel obviously) redefining process_options in ToolContext does not work: the compilation goes fine but the caal-mext-method skips it.
- super
- global = opt_global.value
- use_SFT_optimization = not opt_global_no_STF_opt.value
- global_callgraph = opt_global_callgraph.value_name
- end
-end
-
-redef class Program
- # This method will analyse the program and store results (in global compilation only)
- fun do_global_analysis do
- assert tc.global
- # Pre optimizations:
- if not tc.opt_global_no_inline_get_set.value then inline_get_set
-
- if tc.global_callgraph == "cha" then
- var cha = new ChaBuilder(self)
- cha.work
- rma = cha.context
- else if tc.global_callgraph == "rta" then
- var rta = new RtaBuilder(self)
- rta.work
- rma = rta.context
- ita = rta.context
- end
-
- # Ensure we have all analysis created
- if rma == null then rma = new DefaultReachableMethodAnalysis
- if ita == null then ita = new DefaultInstantiatedTypeAnalysis
-
- var rai_builder = new ReachableAsInitBuilder(self)
- rai_builder.work
- rai = rai_builder.context
-
- if not tc.opt_global_no_RFIMA.value then
- var b = new RFIMABuilder(self)
- b.work
- rfima = b.context
- end
-
- if rfima == null then rfima = new DefaultReachableFromInitMethodAnalysis
-
- # Post optimizations
- if not tc.opt_global_no_DMR_opt.value then optimize_dead_methods
- if not tc.opt_global_no_out_of_init_get_test_opt.value then optimize_out_of_init_getters
-
- # LOG
- if tc.opt_log.value then
- dump_global_optimizations_information(tc.log_directory)
- dump_global_analysis_information(tc.log_directory)
- end
- end
-
- fun dump_global_optimizations_information(directory_name: String) do
- dump_out_of_init_information(directory_name)
- dump_dead_method_optimization(directory_name)
- dump_inline_get_set(directory_name)
- end
-
- # This method will create log files storing analysis information
- fun dump_global_analysis_information(directory_name: String) do
- dump_reachable_methods(directory_name, tc.global_callgraph)
- dump_unreachable_methods(directory_name, tc.global_callgraph)
- dump_instantiated_types(directory_name)
- dump_not_instantiated_types(directory_name)
- dump_reachable_as_init_methods(directory_name)
- dump_reachable_methods_from_init(directory_name)
- dump_unreachable_methods_from_init(directory_name)
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Inline every automatic 'getters' and 'setters'
-# This optimization is done BEFORE analysis
-module inline_get_and_set
-
-import icode
-private import syntax
-import program
-
-redef class Program
- var _number_getter_setter_inlined : Int = 0
-
- # This method will create a file and output this optimization's stats in it
- fun dump_inline_get_set(directory_name: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.inline_get_set.log")
-
- f.write ("Number of getters and setters inlined: {_number_getter_setter_inlined}\n")
-
- f.close
- end
-
- fun inline_get_set do
- with_each_iroutines !action(i, m) do
- var v = new InlineGetSetVisitor(m, i)
- v.visit_iroutine(i)
-
- _number_getter_setter_inlined += v.number_inlined
- end
- end
-end
-
-private class InlineGetSetVisitor
- super ICodeVisitor
- var _icb: ICodeBuilder
- readable var _number_inlined: Int = 0
-
- redef fun visit_icode(ic)
- do
- # Algo mostly from inline_methods.nit, by Jean Privat
- if ic isa ICall and not ic.is_explicit_from_extern then
- var m = ic.property
- var ir = m.iroutine
- if ir != null and m isa MMAttrImplementationMethod then
- var icb = _icb
- var seq = new ISeq
- var old_seq = icb.seq
- icb.seq = seq
- current_icode.insert_before(seq)
- var e = icb.inline_routine(ir, ic.exprs, ic.closure_defs)
- var r = ic.result
- if r != null then
- assert e != null
- current_icode.insert_before(new IMove(r, e))
- end
- current_icode.delete
- icb.seq = old_seq
- _number_inlined += 1
- visit_icode(seq)
- end
- end
- super
- end
-
- init(m: MMModule, r: IRoutine)
- do
- _icb = new ICodeBuilder(m, r)
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Module containing all bases for instantiated type analysis
-module instantiated_type_analysis
-
-import program
-
-redef class Program
- # This attribute is the InstantiatedTypeAnalysis results
- readable writable var _ita: nullable InstantiatedTypeAnalysis = null
-
- # This method will create a file and output the name of all types that are instantiated in it
- fun dump_instantiated_types(directory_name: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.instantiated_types.log")
- with_each_live_local_classes !action(c) do
- f.write("{c}\n")
- end
- f.close
- end
-
- # This method will create a file and output the names of all types that are not instantiated in it
- fun dump_not_instantiated_types(directory_name: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.not_instantiated_types.log")
- # Must overwrite 'with_each_local_classes' since we are looking at non-instantiated classes
- for c in main_module.local_classes do
- if not ita.is_class_instantiated(c) then
- f.write("{c}\n")
- end
- end
- f.close
- end
-
- # We know which are really live, use that information !
- redef fun with_each_live_local_classes
- !action(m: MMLocalClass)
- do
- for c in main_module.local_classes do
- if ita == null or ita.as(not null).is_class_instantiated(c) then action(c)
- end
- end
-end
-
-# Subclasses of this class would represent an analysis that produces
-# at least a way of knowing if a class is instantiated somewhere in a
-# method that is reachable from the entry point of the program
-interface InstantiatedTypeAnalysis
- fun is_class_instantiated(local_class: MMLocalClass): Bool is abstract
-end
-
-# Default behavior is to say that all types are instantiated
-class DefaultInstantiatedTypeAnalysis
- super InstantiatedTypeAnalysis
- redef fun is_class_instantiated(local_class: MMLocalClass): Bool do return true
-
- init do end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Module containing all bases for the reachable from init method analysis
-module reachable_as_init
-
-import icode
-import program
-
-redef class Program
- # This attribute is the ReachableAsInitAnalysis results
- readable writable var _rai: nullable ReachableAsInitAnalysis = null
-
- # This method will create a file and output all inits reachable as init in it
- fun dump_reachable_as_init_methods(directory_name: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.reachable_methods_as_init.log")
- with_each_live_local_classes !action(c) do
- for g in c.global_properties do
- var p = c[g]
- if not p.global.is_init_for(c) then continue
- assert p isa MMMethod
- if rai.is_method_reachable_as_init(p, c) then
- f.write("{p.full_name}\n")
- end
- end
- end
- f.close
- end
-end
-
-# Subclasses of this class would represent an analysis that produces
-# at least a way of knowing if an initializer is called at least once
-# as an initializer (and not as part of another initializer)
-# in a specific class
-interface ReachableAsInitAnalysis
- fun is_method_reachable_as_init(method: MMMethod, c: MMLocalClass): Bool is abstract
-end
-
-# Default behavior is to say that all initializers are called as init
-class DefaultReachableAsInitAnalysis
- super ReachableAsInitAnalysis
- redef fun is_method_reachable_as_init(method: MMMethod, c: MMLocalClass): Bool do
- if method.global.is_init and method.global.is_init_for(c) then return true
- return false
- end
-
- init do end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This module introduces an analysis that flags all initializers called as new A.x
-module reachable_as_init_impl
-
-import reachable_method_analysis
-import reachable_as_init
-
-class ReachableAsInitBuilder
- readable var _context: ReachableAsInitAnalysisImpl = new ReachableAsInitAnalysisImpl
- readable var _program: Program
-
- fun work do
- program.with_each_iroutines !action(i, m) do
- if program.rma.is_iroutine_reachable(i) then
- var v = new RAIVisitor(self)
- v.visit_iroutine(i)
- end
- end
- end
-
- init(p: Program) do
- _program = p
- end
-end
-
-# Visitor will add only initializers in the _methods list.
-# If the checked method is in this list, it is reachable as init !
-class ReachableAsInitAnalysisImpl
- super ReachableAsInitAnalysis
- var _methods: HashMap[MMLocalClass, List[MMMethod]] = new HashMap[MMLocalClass, List[MMMethod]]
-
- redef fun is_method_reachable_as_init(method: MMMethod, c: MMLocalClass): Bool do
- if _methods.has_key(c) then return _methods[c].has(method)
- return false
- end
-
- init do end
-end
-
-class RAIVisitor
- super ICodeVisitor
- readable var _builder: ReachableAsInitBuilder
-
- redef fun visit_icode(ic)
- do
- if ic isa INew then
- # FIXME: take only the last property on the redef. hierarchie
- var t = ic.stype
- var cls = t.for_module(builder.program.main_module).local_class
- var m = cls[ic.property.global].as(MMMethod)
- assert m.global.is_init
- if not builder.context._methods.has_key(cls) then builder.context._methods[cls] = new List[MMMethod]
- if not builder.context._methods[cls].has(m) then builder.context._methods[cls].add(m)
- end
- super
- end
-
- init(b: ReachableAsInitBuilder)
- do
- _builder = b
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Module containing all bases for the reachable from init method analysis
-module reachable_from_init_method_analysis
-
-import icode
-import program
-import reachable_method_analysis
-
-redef class Program
- # This attribute is the ReachableFromInitMethodAnalysis results
- readable writable var _rfima: nullable ReachableFromInitMethodAnalysis = null
-
- # This method will create a file and output the name of all methods reachable
- # from an init in it
- fun dump_reachable_methods_from_init(directory_name: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.reachable_methods_from_init.log")
- with_each_methods !action(m) do
- if rfima.is_method_reachable_from_init(m) then
- f.write("{m.full_name}\n")
- end
- end
- f.close
- end
-
- # This method will create a file and output the name of all reachable methods that
- # can not be reached from an init in it
- fun dump_unreachable_methods_from_init(directory_name: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.unreachable_methods_from_init.log")
- with_each_methods !action(m) do
- if not rfima.is_method_reachable_from_init(m) and rma.is_method_reachable(m) then
- f.write("{m.full_name}\n")
- end
- end
- f.close
- end
-end
-
-# Subclasses of this class would represent an analysis that produces
-# at least a way of knowing if a property is reachable from at least
-# one init in the program
-interface ReachableFromInitMethodAnalysis
- fun is_iroutine_reachable_from_init(ir: nullable IRoutine): Bool is abstract
- fun is_method_reachable_from_init(method: MMMethod): Bool is abstract
-end
-
-# Default behavior is to say that all methods/iroutines are reachable
-# from at least one init
-class DefaultReachableFromInitMethodAnalysis
- super ReachableFromInitMethodAnalysis
- redef fun is_iroutine_reachable_from_init(ir: nullable IRoutine): Bool do return true
- redef fun is_method_reachable_from_init(method: MMMethod): Bool do return true
-
- init do end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This package introduces an algorithm to find all methods reachable
-# from at least one reachable initializer
-module reachable_from_init_method_analysis_impl
-
-import reachable_from_init_method_analysis
-import reachable_method_analysis
-
-class RFIMABuilder
- readable var _program: Program
- readable var _context: RFIMAContext = new RFIMAContext
-
- init (p: Program) do
- _program = p
- end
-
- fun work do
- program.with_each_live_local_classes !action(c) do
- # For each reachable constructors for this class
- for g in c.global_properties do
- var p = c[g]
- if not p.global.is_init_for(c) then continue
- assert p isa MMMethod
- if not program.rma.as(not null).is_method_reachable(p) then continue
- if not c.new_instance_iroutine.has_key(p) then continue
- var ir = c.new_instance_iroutine[p]
-
- # Process this constructor
- context.reachable_from_init_iroutines.add(ir)
- (new RFIMAVisitor(context, program)).visit_iroutine(ir)
- end
- end
- end
-end
-
-class RFIMAContext
- super ReachableFromInitMethodAnalysis
- readable var _reachable_from_init_iroutines: HashSet[IRoutine] = new HashSet[IRoutine]
-
- redef fun is_iroutine_reachable_from_init(ir: nullable IRoutine): Bool do
- return ir != null and reachable_from_init_iroutines.has(ir)
- end
-
- redef fun is_method_reachable_from_init(method: MMMethod): Bool do
- return is_iroutine_reachable_from_init(method.iroutine)
- end
-end
-
-class RFIMAVisitor
- super ICodeVisitor
- readable var _context: RFIMAContext
- readable var _program: Program
-
- init (context: RFIMAContext, p: Program) do
- _context = context
- _program = p
- end
-
- fun process_call(iroutine: IRoutine) do
- if context.is_iroutine_reachable_from_init(iroutine) then return
- context.reachable_from_init_iroutines.add(iroutine)
- visit_iroutine(iroutine)
- end
-
- redef fun visit_icode(ic)
- do
- if ic isa IStaticCall then
- # FIXME: take only the last property on the redef. hierarchie
- var ir = ic.property.iroutine
- if ir != null then process_call(ir)
- else if ic isa INew then
- # FIXME: take only the last property on the redef. hierarchie
- var t = ic.stype
- var cls = t.for_module(program.main_module).local_class
- var m = cls[ic.property.global].as(MMMethod)
- if not m.is_extern then
- var r = cls.new_instance_iroutine[m]
- process_call(r)
- end
- else if ic isa ISuper then
- # Process possible calls
- var method = ic.property
- for greater in method.prhe.greaters_and_self do
- if greater isa MMMethod then
- var ir = greater.iroutine
- if ir != null then process_call(ir)
- end
- end
- else if ic isa ICall then
- # Process possible calls
- var method = ic.property
- var ir = method.iroutine
- if ir != null then process_call(ir)
- for other in method.prhe.smallers do
- if other isa MMMethod then
- ir = other.iroutine
- if ir != null then process_call(ir)
- end
- end
- else if ic isa ICheckInstance then
- var t = ic.stype
- var cls = t.for_module(program.main_module).local_class
- var ir = cls.checknew_iroutine
- if ir != null then process_call(ir)
- else if ic isa IInitAttributes then
- var t = ic.stype
- var cls = t.for_module(program.main_module).local_class
- var ir = cls.init_var_iroutine
- if ir != null then process_call(ir)
- end
- super
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Module containing all bases for the reachable method analysis
-module reachable_method_analysis
-
-import icode
-import program
-
-redef class Program
- # This attribute is the ReachableMethodAnalysis results
- readable writable var _rma: nullable ReachableMethodAnalysis = null
-
- # This method will create a file and output all reachable method names in it
- fun dump_reachable_methods(directory_name: String, algo: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.reachable_methods.{algo}.log")
- with_each_methods !action(m) do
- if rma.is_method_reachable(m) then
- f.write("{m.full_name}\n")
- end
- end
- f.close
- end
-
- # This method will create a file and output all unreachable method names in it
- fun dump_unreachable_methods(directory_name: String, algo: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.unreachable_methods.{algo}.log")
- with_each_methods !action(m) do
- if not rma.is_method_reachable(m) then
- f.write("{m.full_name}\n")
- end
- end
- f.close
- end
-end
-
-# Subclasses of this class would represent an analysis that produces
-# at least a way of knowing if a property is reachable from the entry
-# point of the program
-interface ReachableMethodAnalysis
- fun is_iroutine_reachable(ir: nullable IRoutine): Bool is abstract
- fun is_method_reachable(method: MMMethod): Bool is abstract
-end
-
-# Default behavior is to say that all methods/iroutines are reachable
-class DefaultReachableMethodAnalysis
- super ReachableMethodAnalysis
- redef fun is_iroutine_reachable(ir: nullable IRoutine): Bool do return true
- redef fun is_method_reachable(method: MMMethod): Bool do return true
-
- init do end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This module introduces an optimization that removes 'get' tests when
-# not reachable from an initializer
-module remove_out_of_init_get_test
-
-import reachable_from_init_method_analysis
-
-redef class Program
- readable var _nb_optimized_isset: Int = 0
-
- # Calling this method will remove all 'isset' that were generated automaticaly
- # before a attribute read if this attribute read is done in a method that
- # cannot be reached by a initializer
- fun optimize_out_of_init_getters do
- with_each_iroutines !action(i,m) do
- if not rfima.is_iroutine_reachable_from_init(i) then
- var remover = new GetterTestRemover
- remover.visit_iroutine(i)
- _nb_optimized_isset = nb_optimized_isset + remover.nb_optimized_isset
- end
- end
- end
-
- # This method will create a file and output informations about this optimization
- fun dump_out_of_init_information(directory_name: String) do
- var f = new OFStream.open("{directory_name}/{main_module.name}.out_of_init_opt.log")
- var nb_not_optimized = 0
-
- with_each_iroutines !action(i,m) do
- var counter = new IssetCounter
- counter.visit_iroutine(i)
- nb_not_optimized = nb_not_optimized + counter.nb_isset
- end
-
- f.write("Nb. optimized isset: {nb_optimized_isset}\n")
- f.write("Nb. not optimized: {nb_not_optimized}\n")
-
- f.close
- end
-end
-
-class IssetCounter
- super ICodeVisitor
- readable var _nb_isset: Int = 0
-
- redef fun visit_icode(ic)
- do
- if ic isa IAttrIsset then
- _nb_isset = nb_isset + 1
- end
-
- super
- end
-end
-
-class GetterTestRemover
- super ICodeVisitor
- readable var _nb_optimized_isset: Int = 0
-
- redef fun visit_icode(ic)
- do
- # Replace 'x = isset(y)' by 'x = true'
- if ic isa IAttrIsset then
- var result = ic.result
- assert result != null
- var e = new IBoolValue(true)
- e.result = result
- current_icode.insert_before(e)
- current_icode.delete
- _nb_optimized_isset = nb_optimized_isset + 1
- end
-
- super
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This package implements Reachable Type Analysis(RTA)
-module rta_analysis
-
-import instantiated_type_analysis
-import reachable_method_analysis
-import icode
-import program
-
-class RtaContext
- super ReachableMethodAnalysis
- super InstantiatedTypeAnalysis
- init do end
- readable var _instanciated_classes: HashSet[MMLocalClass] = new HashSet[MMLocalClass]
-
- redef fun is_class_instantiated(local_class: MMLocalClass): Bool do return instanciated_classes.has(local_class)
-
- readable var _reachable_iroutines: HashSet[IRoutine] = new HashSet[IRoutine]
-
- redef fun is_iroutine_reachable(ir: nullable IRoutine): Bool do
- return ir != null and reachable_iroutines.has(ir)
- end
-
- redef fun is_method_reachable(method: MMMethod): Bool do
- return is_iroutine_reachable(method.iroutine)
- end
-end
-
-class RtaBuilder
- readable var _context : RtaContext
- readable var _program: Program
- readable var _iroutine_to_search: List[IRoutine] = new List[IRoutine]
- readable var _call_sites: HashSet[IAbsCall] = new HashSet[IAbsCall]
- readable var _called_methods: HashSet[MMMethod] = new HashSet[MMMethod]
-
- init (p: Program) do
- _context = new RtaContext
- _program = p
- end
-
- # Check if the class where the method was introduced is instantiated
- # Also check every subclasses if they use the same 'version' of the
- # method
- fun check_method(m: MMMethod): Bool do
- var m_cls = m.local_class.for_module(program.main_module)
-
- if context.is_class_instantiated(m_cls) then return true
-
- for cls in m_cls.cshe.smallers do
- if not cls[m.global] == m then continue
- if context.is_class_instantiated(cls) then return true
- end
- return false
- end
-
- # Check all call sites to find those that just became 'activated'
- # by some new type instantiation
- fun check_call_sites do
- var calls_to_remove = new List[IAbsCall]
- for call in call_sites do
- var m = call.property
- var all_added = true
-
- # Check for this method
- if check_method(m) then
- add_reachable_iroutine(m.iroutine)
- else
- all_added = false
- end
-
- # Check all methods 'under' this one in the hierarchy
- for other in m.prhe.smallers do
- if not other isa MMMethod then continue
- if check_method(other) then
- add_reachable_iroutine(other.iroutine)
- else
- all_added = false
- end
- end
-
- if all_added then calls_to_remove.add(call)
- end
-
- # If all sub-methods are added, no need to keep checking this call !
- for call in calls_to_remove do
- call_sites.remove(call)
- end
- end
-
- fun add_instantiated_class(cls: MMLocalClass) do
- if context.is_class_instantiated(cls) then return
- context.instanciated_classes.add(cls)
-
- check_call_sites
- end
-
- fun add_reachable_call(call: IAbsCall) do
- var m = call.property
- if called_methods.has(m) then return
- if call_sites.has(call) then return
-
- call_sites.add(call)
- called_methods.add(m)
- check_call_sites
- end
-
- fun add_reachable_iroutine(ir: nullable IRoutine) do
- if ir == null or context.is_iroutine_reachable(ir) then return
- context.reachable_iroutines.add(ir)
- iroutine_to_search.add(ir)
- end
-
- # Need to hard-code some automaticaly instanciated types !
- private fun force_some_type_analysis do
- var forced_types = ["Object", "Bool", "Float", "Int", "String", "NativeString", "Range", "Array", "ArrayIterator", "Inline__"]
-
- for some_type in forced_types do
- if not program.main_module.has_global_class_named(some_type.to_symbol) then continue
- var cls_type = program.main_module.class_by_name(some_type.to_symbol)
- add_instantiated_class(cls_type)
- end
-
- if program.main_module.has_global_class_named("Inline__".to_symbol) then
- var ptr_class = program.main_module.class_by_name("Inline__".to_symbol)
- # Assume that all classes that are subclasses of Inline__
- # can be inlined without notice ...
- # and are always counted as instantiated
- for ptr_sub_class in ptr_class.cshe.smallers do
- add_instantiated_class(ptr_sub_class)
- end
- end
-
- if program.main_module.has_global_class_named("Pointer".to_symbol) then
- var ptr_class = program.main_module.class_by_name("Pointer".to_symbol)
- # Assume that all classes that are subclasses of Pointer
- # can be returned by the native interface
- # and are always counted as instantiated
- for ptr_sub_class in ptr_class.cshe.smallers do
- add_instantiated_class(ptr_sub_class)
- end
- end
-
- for cls in program.main_module.global_classes do
- if not cls.is_enum and not cls.is_extern then continue
- add_instantiated_class(program.main_module[cls])
- end
-
- # defines all extern classes as used to make sure that static
- # C functions in frontier compile.
- program.with_each_methods !action( prop ) do
- if prop.is_extern then
- add_reachable_iroutine(prop.iroutine)
- end
- end
- end
-
- # Build the context associated with this builder
- fun work do
- if program.main_method == null then
- # Add primitive type (so that compiling works)
- for t in ["Int","Char","Bool"] do
- if program.main_module.has_global_class_named(t.to_symbol) then
- add_instantiated_class(program.main_module.class_by_name(t.to_symbol))
- end
- end
- return
- end
-
- add_instantiated_class(program.main_class.as(not null))
- add_reachable_iroutine(program.main_method.as(not null).iroutine)
- force_some_type_analysis
-
- while not iroutine_to_search.is_empty do
- var v = new RtaVisitor(self)
- var iroutine = iroutine_to_search.pop
- v.visit_icode(iroutine.body)
- end
- end
-end
-
-class RtaVisitor
- super ICodeVisitor
- readable var _builder: RtaBuilder
-
- redef fun visit_icode(ic)
- do
- if ic isa IStaticCall then
- # FIXME: take only the last property on the redef. hierarchie
- builder.add_reachable_iroutine(ic.property.iroutine)
- else if ic isa INew then
- # FIXME: take only the last property on the redef. hierarchie
- var t = ic.stype
- var cls = t.for_module(builder.program.main_module).local_class
- if not cls.global.is_extern then
- var m = cls[ic.property.global].as(MMMethod)
- var r = cls.new_instance_iroutine[m]
- builder.add_reachable_iroutine(r)
- end
- builder.add_instantiated_class(cls)
- else if ic isa ISuper then
- # Add every parents ...
- for p in ic.property.prhe.greaters_and_self do
- if p isa MMMethod then
- builder.add_reachable_iroutine(p.iroutine)
- end
- end
- else if ic isa ICall then
- builder.add_reachable_call(ic)
- else if ic isa ICheckInstance then
- var t = ic.stype
- var cls = t.for_module(builder.program.main_module).local_class
- var ir = cls.checknew_iroutine
- builder.add_reachable_iroutine(ir)
- else if ic isa IInitAttributes then
- var t = ic.stype
- var cls = t.for_module(builder.program.main_module).local_class
- var ir = cls.init_var_iroutine
- builder.add_reachable_iroutine(ir)
- end
- super
- end
-
- init(b: RtaBuilder)
- do
- _builder = b
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Nit intermediate code representation
-module icode
-import icode_base
-import icode_tools
-import icode_builder
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Base classes for Nit intermediate code representation
-module icode_base
-
-import metamodel
-
-## UTILITY CLASSES ##
-
-# Register store local variable and intermediate results
-class IRegister
- # The static type
- readable var _stype: MMType
- init(s: MMType)
- do
- _stype = s
- end
-end
-
-# A mark used to associate IEscapes to ISeqs
-class IEscapeMark
-end
-
-# A Closure declaration
-class IClosureDecl
- # The associated closure definition
- readable var _closure: MMClosure
-
- # The default implementation
- readable writable var _default: nullable IRoutine
-
- init(c: MMClosure) do _closure = c
-end
-
-# A routine is a sequence of icodes with entry iregisters (params) and an exit iregister (result)
-class IRoutine
- # The parameters of the routine
- readable var _params: Sequence[IRegister]
-
- # The closure declared
- readable writable var _closure_decls: nullable Sequence[IClosureDecl] = null
-
- # The local variables (excluding params and result)
- readable var _registers: Set[IRegister] = new HashSet[IRegister]
-
- # The result of the routine
- readable var _result: nullable IRegister
-
- # The local escapes marks of the routine
- readable var _escape_marks: Set[IEscapeMark] = new HashSet[IEscapeMark]
-
- # The sequence of icode
- readable var _body: ISeq = new ISeq
-
- # The location of the iroutine (if any)
- readable writable var _location: nullable Location = null
-
- init(p: Sequence[IRegister], r: nullable IRegister)
- do
- _params = p.to_a
- _result = r
- end
-end
-
-# A closure definition in a iroutine body
-class IClosureDef
- super IRoutine
- init(p: Array[IRegister], r: nullable IRegister)
- do
- super(p, r)
- end
-end
-
-## INTERMEDIATE CODE ##
-
-# The root of the intermediate code representation
-abstract class ICode
- # The number of registers used by the icode
- fun arity: Int is abstract
-
- # The result of the icode (if any)
- readable writable var _result: nullable IRegister = null
-
- # The location of the icode (if any)
- readable writable var _location: nullable Location = null
-
- # Is the icode side effect free?
- fun is_pure: Bool do return false
-end
-
-# An icode that uses no registers (no args)
-abstract class ICode0
- super ICode
- redef fun arity do return 0
-end
-
-# An icode that uses a single register (1 arg)
-abstract class ICode1
- super ICode
- redef fun arity do return 1
-
- # The single argument
- readable var _expr: IRegister
-
- init(e: IRegister) do _expr = e
-end
-
-# An icode that uses two single registers (2 args)
-abstract class ICode2
- super ICode
- redef fun arity do return 2
-
- # The first argument
- readable var _expr1: IRegister
-
- # The second argument
- readable var _expr2: IRegister
-
- init(e1, e2: IRegister)
- do
- _expr1 = e1
- _expr2 = e2
- end
-end
-
-# An icode that uses a variable number of registers (n args) and a variable number of closure definitions
-abstract class ICodeN
- super ICode
- redef fun arity do return _exprs.length
-
- # All arguments
- readable var _exprs: Sequence[IRegister]
-
- # All closure definition
- readable writable var _closure_defs: nullable Sequence[nullable IClosureDef]
-
- init(e: nullable Sequence[IRegister])
- do
- if e == null then
- _exprs = new Array[IRegister]
- else
- _exprs = e
- end
- end
-end
-
-#################################################
-
-# A linear sequence of ICode
-class ISeq
- super ICode0
- # The sequence of icode
- readable var _icodes: List[ICode] = new List[ICode]
-
- # The associated iescape_mark (if any)
- readable writable var _iescape_mark: nullable IEscapeMark
-
- init do end
-end
-
-# An infinite loop of ICode
-# Use IEscape to exit
-class ILoop
- super ISeq
- init do end
-end
-
-# A Condidianal if-then-else statement
-# expr is the condition
-class IIf
- super ICode1
- # The 'then' sequence of icode
- readable var _then_seq: ISeq = new ISeq
- # The 'else' sequence of icode
- readable var _else_seq: ISeq = new ISeq
- init(e: IRegister) do super
-end
-
-# Escape to to end of a parent sequence
-class IEscape
- super ICode0
- # The seqeuence to escape
- # The control flow continues at the next icode after the associated sequence
- readable var _iescape_mark: IEscapeMark
- init(mark: IEscapeMark) do _iescape_mark = mark
-end
-
-# An abort statement
-class IAbort
- super ICode0
- # The reason the abort occured
- # tests.first is the format
- readable var _texts: Array[String]
- # The module that has the abort
- readable var _module_location: MMModule
- init(t: Array[String], ml: MMModule)
- do
- _texts = t
- _module_location = ml
- end
-end
-
-#################################################
-
-# The root of all method invocations
-abstract class IAbsCall
- super ICodeN
- # The called method
- readable var _property: MMMethod
-
- # if this call is to be made from native code
- var is_explicit_from_extern : Bool writable = false
-
- init(p: MMMethod, e: Sequence[IRegister])
- do
- super(e)
- _property = p
- end
-end
-
-# A simple procedure or function call
-# exprs.first is the reciever, other are arguments
-class ICall
- super IAbsCall
- init(p, e) do super
-end
-
-# A super method call
-# exprs.first is the reciever, other are arguments
-class ISuper
- super IAbsCall
- init(p, e) do super
-end
-
-# An instantiation
-# no reciever, all exprs are arguments
-# Will call in order:
-# - IAllocateInstance
-# - IInitAttributes
-# - IStaticCall -> target Initializer
-# - ICheckInstance
-class INew
- super IAbsCall
- # The type to instantiate
- readable var _stype: MMType
- init(t: MMType, p: MMMethod, a: Sequence[IRegister])
- do
- super(p, a)
- _stype = t
- end
-end
-
-# An allocation of a new object
-# No receivers, returns a new object of type 't'
-# Will allocate memory and ensure dynamic type and object identity
-class IAllocateInstance
- super ICode0
- # The type to allocate
- readable var _stype: MMType
- init(t: MMType)
- do
- _stype = t
- end
-end
-
-# A static call to a specific method
-class IStaticCall
- super IAbsCall
- init(p: MMMethod, a: Sequence[IRegister]) do super
-end
-
-# A validation of a newly constructed instance
-class ICheckInstance
- super ICode1
- # The type to allocate
- readable var _stype: MMType
- init(t: MMType, e: IRegister)
- do
- super(e)
- _stype = t
- end
-end
-
-# Initialisation of default attributes of a new instance
-class IInitAttributes
- super ICode1
- # The type to initialize
- readable var _stype: MMType
- init(t: MMType, e: IRegister)
- do
- super(e)
- _stype = t
- end
-end
-
-# A closure call
-# exprs are the arguments
-class IClosCall
- super ICodeN
- # The called closure
- readable var _closure_decl: IClosureDecl
-
- # The !break sequence (if any)
- readable writable var _break_seq: nullable ISeq = null
-
- init(c: IClosureDecl, e: Sequence[IRegister])
- do
- super(e)
- _closure_decl = c
- end
-end
-
-# A native inlined call
-# Native are associated to local properties to distinguish them
-# expr are the arguments
-class INative
- super ICodeN
- # The associated local property
- readable var _method: MMMethod
-
- init(m: MMMethod, e: nullable Sequence[IRegister])
- do
- # Checks that arguments contains at least one IRegister element
- assert e.length == m.signature.arity + 1
-
- super(e)
- _method = m
- end
-
- redef readable writable var _is_pure: Bool = false
-end
-
-# A literal Int value
-class IIntValue
- super ICode0
- # The value
- readable var _value: String
-
- init(v: String) do _value = v
-
- redef fun is_pure do return true
-end
-
-# A literal Bool value
-class IBoolValue
- super ICode0
- # The value
- readable var _value: Bool
-
- init(v: Bool) do _value = v
-
- redef fun is_pure do return true
-end
-
-# A literal NativeString value
-class IStringValue
- super ICode0
- # The value
- readable var _value: String
-
- init(v: String) do _value = v
-
- redef fun is_pure do return true
-end
-
-# A literal Float value
-class IFloatValue
- super ICode0
- # The value
- readable var _value: String
-
- init(v: String) do _value = v
-
- redef fun is_pure do return true
-end
-
-# A literal Char value
-class ICharValue
- super ICode0
- # The value
- readable var _value: String
-
- init(v: String) do _value = v
-
- redef fun is_pure do return true
-end
-
-# A register assigment
-# expr is the assigned value
-# result is the register assigned
-class IMove
- super ICode1
- init(r: IRegister, e: IRegister)
- do
- super(e)
- _result = r
- end
-
- redef fun is_pure do return true
-end
-
-# An attribute read access
-# expr is the reciever
-class IAttrRead
- super ICode1
- # The accessed attribute
- readable var _property: MMAttribute
-
- init(p: MMAttribute, r: IRegister)
- do
- super(r)
- _property = p
- end
-
- redef fun is_pure do return true
-end
-
-# An attribute assignment
-# expr1 is the receiver, expr2 is the assigned value
-class IAttrWrite
- super ICode2
- # The accessed attribute
- readable var _property: MMAttribute
-
- init(p: MMAttribute, r: IRegister, v: IRegister)
- do
- super(r, v)
- _property = p
- end
-end
-
-
-# An attribute is_set check
-# expr is the reciever
-class IAttrIsset
- super ICode1
- # The accessed attribute
- readable var _property: MMAttribute
-
- init(p: MMAttribute, r: IRegister)
- do
- super(r)
- _property = p
- end
-
- redef fun is_pure do return true
-end
-
-# A type check
-# expr1 is the type reciever (self)
-# expr2 is the expression checked
-class ITypeCheck
- super ICode2
- # The static type checkes to
- readable var _stype: MMType
-
- init(e1, e2: IRegister, t: MMType)
- do
- super(e1, e2)
- _stype = t
- end
-
- redef fun is_pure do return true
-end
-
-# The 'is' operator
-# expr1 and expr2 are both operands
-class IIs
- super ICode2
- init(e1, e2: IRegister)
- do
- super
- end
-
- redef fun is_pure do return true
-end
-
-# The unary 'not' operation
-# expr is the operand
-class INot
- super ICode1
- init(e: IRegister)
- do
- super
- end
-
- redef fun is_pure do return true
-end
-
-# Evaluate body once them return the same value again and again
-# if result is not null, then it must also be assigned in the body
-class IOnce
- super ICode0
- readable var _body: ISeq = new ISeq
- init do end
-end
-
-# Is a closure given as a parameter?
-class IHasClos
- super ICode0
- # The called closure
- readable var _closure_decl: IClosureDecl
-
- init(c: IClosureDecl)
- do
- _closure_decl = c
- end
-
- redef fun is_pure do return true
-end
-
-#################################################
-
-redef class MMAttribute
- # The attached initialisation iroutine if any
- # To call between the allocate-instance and the initialize-instance
- fun iroutine: nullable IRoutine is abstract
-end
-
-redef class MMMethod
- # The attached body iroutine if any
- fun iroutine: nullable IRoutine is abstract
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Help generation of icode
-module icode_builder
-
-import icode_base
-
-# Helps to generate icodes in a iroutine
-class ICodeBuilder
- # Add a new statment in the current icode sequence
- # Can be used with expression if the result is ignored of if the result is already set
- fun stmt(s: ICode)
- do
- s.location = _current_location
- _seq.icodes.add(s)
- end
-
- # Add a new expression in the current icode sequence and return a new associated result register
- fun expr(e: ICode, s: MMType): IRegister
- do
- stmt(e)
- assert e.result == null
- var reg = new_register(s)
- e.result = reg
- return reg
- end
-
- # Add an assignement (IMove) in the current icode sequence
- fun add_assignment(reg: IRegister, v: IRegister)
- do
- stmt(new IMove(reg, v))
- end
-
- # Check that the reciever e is not null (IIs + IAbort)
- fun add_null_reciever_check(e: IRegister)
- do
- var nul = lit_null_reg
- var c = expr(new IIs(e, nul), mmmodule.type_bool)
- var iif = new IIf(c)
- stmt(iif)
- var old_seq = seq
- seq = iif.then_seq
- add_abort("Reciever is null")
- seq = old_seq
- end
-
- # Add a type cast (ITypeCheck + IAbort) in the current icode sequence
- fun add_type_cast(e: IRegister, stype: MMType)
- do
- var c = expr(new ITypeCheck(iroutine.params.first, e, stype), mmmodule.type_bool)
- var iif = new IIf(c)
- stmt(iif)
- var old_seq = seq
- seq = iif.else_seq
- add_abort("Cast failed")
- seq = old_seq
- end
-
- # Add an attr check (IAttrIsset + IAbort) in the current icode sequence
- fun add_attr_check(prop: MMAttribute, e: IRegister)
- do
- if not prop.signature.return_type.is_nullable then
- var cond = expr(new IAttrIsset(prop, e), mmmodule.type_bool)
- var iif = new IIf(cond)
- stmt(iif)
- var seq_old = seq
- seq = iif.else_seq
- add_abort("Uninitialized attribute %s", prop.name.to_s)
- seq = seq_old
- end
- end
-
- # Add an IAttrRead guarded by an add_attr_check in the current icode sequence
- fun add_attr_read(prop: MMAttribute, e: IRegister): IRegister
- do
- add_attr_check(prop, e)
- return expr(new IAttrRead(prop, e), prop.signature.return_type.as(not null))
- end
-
- # Add a localized IAbort
- fun add_abort(s: String...)
- do
- stmt(new IAbort(s, mmmodule))
- end
-
- # Add an assigment to the iroutine return value
- # Beware, no jump is generated
- fun add_return_value(reg: IRegister)
- do
- add_assignment(iroutine.result.as(not null), reg)
- end
-
- # Add an ICall with possible simple inlining in the current icode sequence
- fun add_call(prop: MMMethod, args: Array[IRegister], closcns: nullable Array[nullable IClosureDef]): nullable IRegister
- do
- var ee = once "==".to_symbol
-
- # Inline "x!=y" as "not x==y"
- var ne = once "!=".to_symbol
- if prop.name == ne then
- var eqp = prop.signature.recv.local_class.select_method(ee)
- var eqcall = add_call(eqp, args, closcns).as(not null)
- return expr(new INot(eqcall), mmmodule.type_bool)
- end
-
- # TODO: Inline x==y as "x is y or (x != null and (== is not the Object one) and x.==(y))"
- # inline "x==y" as "x is y or x != null and x.==(y)"
- var icall = new ICall(prop, args)
- icall.closure_defs = closcns
- if prop.name == ee then
- # Prepare the result
- var reg = new_register(mmmodule.type_bool)
- # "x is y"
- var cond = expr(new IIs(args[0], args[1]), mmmodule.type_bool)
- var iif = new IIf(cond)
- stmt(iif)
- var seq_old = seq
- seq = iif.then_seq
- add_assignment(reg, cond)
- # "or"
- seq = iif.else_seq
- # Do the "x != null" part iff x is nullable
- if args[0].stype.is_nullable then
- var nul = lit_null_reg
- cond = expr(new IIs(args[0], nul), mmmodule.type_bool)
- iif = new IIf(cond)
- stmt(iif)
- seq = iif.then_seq
- add_assignment(reg, lit_false_reg)
- seq = iif.else_seq
- end
- # "x.==(y)"
- add_assignment(reg, expr(icall, mmmodule.type_bool))
- seq = seq_old
- return reg
- end
-
- if args.first.stype.is_nullable then add_null_reciever_check(args.first)
- var rtype = prop.signature.return_type
- if rtype != null then
- return expr(icall, rtype)
- else
- stmt(icall)
- return null
- end
- end
-
- # Add an escape to a given sequence
- # Create a new IEscapeMark if required
- fun add_escape(seq: ISeq)
- do
- var mark = seq.iescape_mark
- if mark == null then
- mark = new IEscapeMark
- iroutine.escape_marks.add(mark)
- seq.iescape_mark = mark
- end
- stmt(new IEscape(mark))
- end
-
- # Return a literal "null" value
- fun lit_null_reg: IRegister
- do
- return new_register(mmmodule.type_none)
- end
-
- # Return a literal "true" value
- fun lit_true_reg: IRegister
- do
- var e = new IBoolValue(true)
- return expr(e, mmmodule.type_bool)
- end
-
- # Return a literal "false" value
- fun lit_false_reg: IRegister
- do
- var e = new IBoolValue(false)
- return expr(e, mmmodule.type_bool)
- end
-
- # Get a new register
- fun new_register(s: MMType): IRegister
- do
- var r = new IRegister(s)
- iroutine.registers.add(r)
- return r
- end
-
- # The module where classes and types are extracted
- readable var _mmmodule: MMModule
-
- # The current iroutine build
- readable var _iroutine: IRoutine
-
- # The current sequence of icodes
- readable writable var _seq: ISeq
-
- init(mod: MMModule, r: IRoutine)
- do
- _mmmodule = mod
- _current_location = r.location
- _iroutine = r
- _seq = r.body
- end
-
- # New ICodes are set to this location
- readable writable var _current_location: nullable Location = null
-end
-
-redef class MMSignature
- # Create an empty IRoutine that match the signature
- fun generate_empty_iroutine: IRoutine
- do
- var args = new Array[IRegister]
- args.add(new IRegister(recv)) # Self
- for i in [0..arity[ do
- args.add(new IRegister(self[i]))
- end
- var res: nullable IRegister = null
- var rtype = return_type
- if rtype != null then
- res = new IRegister(rtype)
- end
- var iroutine = new IRoutine(args, res)
- var clos: nullable Array[IClosureDecl] = null
- if not closures.is_empty then
- clos = new Array[IClosureDecl]
- for c in closures do
- clos.add(new IClosureDecl(c))
- end
- iroutine.closure_decls = clos
- end
- # Add automatic test for virtual types
- var icb = new ICodeBuilder(recv.mmmodule, iroutine)
- for i in [0..arity[ do
- var t = self[i]
- if t isa MMVirtualType then
- icb.add_type_cast(args[i+1], t)
- end
- end
- return iroutine
- end
-
- # Create an empty IClosureDef that match the signature
- fun generate_empty_iclosuredef(icb: ICodeBuilder): IClosureDef
- do
- var args = new Array[IRegister]
- for i in [0..arity[ do
- args.add(icb.new_register(self[i]))
- end
- var res: nullable IRegister = null
- var rtype = return_type
- if rtype != null then
- res = icb.new_register(rtype)
- end
- var iroutine = new IClosureDef(args, res)
- var clos: nullable Array[IClosureDecl] = null
- if not closures.is_empty then
- clos = new Array[IClosureDecl]
- for c in closures do
- clos.add(new IClosureDecl(c))
- end
- iroutine.closure_decls = clos
- end
- # TODO: add automatic test for virtual types?
- return iroutine
- end
-end
-
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Tools to manipulate intermediace nit code representation
-module icode_tools
-import icode_builder
-
-# A simple visitor to visit icode structures
-abstract class ICodeVisitor
- # Called when a iregister is read in a icode
- fun visit_iregister_read(ic: ICode, r: IRegister) do end
-
- # Called when a iregister is wrote in a icode
- fun visit_iregister_write(ic: ICode, r: IRegister) do end
-
- # The current icode iterator.
- # Can be used to insert_before, used to change the item or deleted
- readable var _current_icode: nullable ListIterator[ICode] = null
-
- # Called when a icode is visited
- # Automatically visits iregisters and sub-icodes
- fun visit_icode(ic: nullable ICode)
- do
- if ic == null then return
- if ic isa ISeq then
- var old_icode = _current_icode
- var cur = ic.icodes.iterator
- while cur.is_ok do
- _current_icode = cur
- var ic2 = cur.item
- visit_icode(ic2)
- cur.next
- end
- _current_icode = old_icode
- else if ic isa IIf then
- visit_iregister_read(ic, ic.expr)
- visit_icode(ic.then_seq)
- visit_icode(ic.else_seq)
- else if ic isa IOnce then
- visit_icode(ic.body)
- else if ic isa ICode1 then
- visit_iregister_read(ic, ic.expr)
- else if ic isa ICode2 then
- visit_iregister_read(ic, ic.expr1)
- visit_iregister_read(ic, ic.expr2)
- else if ic isa ICodeN then
- for e in ic.exprs do
- visit_iregister_read(ic, e)
- end
- var closdefs = ic.closure_defs
- if ic isa IClosCall then
- visit_icode(ic.break_seq)
- end
- if closdefs != null then
- visit_closure_defs(closdefs)
- end
- end
- var r = ic.result
- if r != null then visit_iregister_write(ic, r)
- end
-
- # Called when closure definitions are visited
- # Automatically visits each closure definition
- fun visit_closure_defs(closdefs: Collection[nullable IClosureDef])
- do
- for e in closdefs do
- if e != null then
- visit_iroutine(e)
- end
- end
- end
-
- # Called when an iroutine is visited
- # Automatically visits the body
- # Warning: parameters of result registers are not visited
- fun visit_iroutine(ir: IRoutine)
- do
- visit_icode(ir.body)
- end
-end
-
-redef class ICodeBuilder
- # Inline an iroutine in the current icode sequence
- # Require that routine != self.iroutine
- fun inline_routine(routine: IRoutine, args: Sequence[IRegister], closdefs: nullable Sequence[nullable IClosureDef]): nullable IRegister
- do
- assert routine != self.iroutine
- var d = new ICodeDupContext(self)
- assert args.length == routine.params.length
- var closdecls = routine.closure_decls
- var cdefsa = if closdefs != null then closdefs.length else 0
- var cdeclsa = if closdecls != null then closdecls.length else 0
- assert cdefsa <= cdeclsa
-
- # Fill register duplicate association
- var dico = d._registers
- var res = routine.result
- if res != null then
- var res2 = new_register(res.stype)
- dico[res] = res2
- res = res2
- end
- for reg in routine.registers do
- assert not dico.has_key(reg)
- dico[reg] = new_register(reg.stype)
- end
- for i in [0..args.length[ do
- # FIXME The following assumes that params are readonly.
- # The alternative is safe but add one move :/
- dico[routine.params[i]] = args[i]
- #seq.icodes.add(new IMove(dico[routine.params[i]]), args[i]))
- end
-
- # Fill escape mark association
- for m in routine.escape_marks do
- var m2 = new IEscapeMark
- iroutine.escape_marks.add(m2)
- d._marks[m] = m2
- end
-
- # Fill closure association
- if closdecls != null then
- var cdico = d._closures
- for i in [0..cdefsa[ do
- cdico[closdecls[i]] = closdefs[i]
- end
- for i in [cdefsa..cdeclsa[ do
- cdico[closdecls[i]] = null
- end
- end
-
- # Process inlining
- routine.body.dup_with(d)
- return res
- end
-end
-
-# This class stores reference to allow correct duplication of icodes
-private class ICodeDupContext
- # Return the correct register
- # * a duplicate of the local register 'r' of the inlined iroutine
- # * 'r' else (it is a register of the caller iroutine)
- fun dup_ireg(r: IRegister): IRegister
- do
- var rs = _registers
- if rs.has_key(r) then
- return rs[r]
- else
- return r
- end
- end
-
- # Return a correct bunch of registers
- fun dup_iregs(regs: Sequence[IRegister]): Array[IRegister]
- do
- var a = new Array[IRegister].with_capacity(regs.length)
- for r in regs do
- a.add(dup_ireg(r))
- end
- return a
- end
-
- # The assocation between old_ireg and new_ireg
- # Used by dup_ireg
- var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
-
- # Return the correct escape mark
- # * a duplicate of the local escape mark 'm' of the inlined iroutine
- # * 'r' else (it is a escape mark of the caller iroutine)
- fun dup_mark(m: IEscapeMark): IEscapeMark
- do
- var ms = _marks
- if ms.has_key(m) then
- return ms[m]
- else
- return m
- end
- end
-
- # The associoation between old_seq and new_seq
- # Used by dup_mark
- var _marks: Map[IEscapeMark, IEscapeMark] = new HashMap[IEscapeMark, IEscapeMark]
-
- # The association between a closure_decl and its closure_def (if any)
- var _closures: Map[IClosureDecl, nullable IClosureDef] = new ArrayMap[IClosureDecl, nullable IClosureDef]
-
- # The current code builder
- var _icb: ICodeBuilder
-
- init(icb: ICodeBuilder)
- do
- _icb = icb
- end
-end
-
-redef class ICode
- # Duplicate the current icode in the icode builder of the ICodeDupContext
- private fun dup_with(d: ICodeDupContext)
- do
- var c = inner_dup_with(d)
- if self isa ICodeN then
- assert c isa ICodeN
- var cdecl = closure_defs
- if cdecl != null then
- # Duplicate the colsure definitions
- var cdecl2 = new Array[nullable IClosureDef].with_capacity(cdecl.length)
- for cd in cdecl do
- if cd == null then
- cdecl2.add(null)
- else
- var r = cd.result
- if r != null then r = d.dup_ireg(r)
- var cd2 = new IClosureDef(d.dup_iregs(cd.params), r)
- cdecl2.add(cd2)
- cd.body.dup_seq_to(d, cd2.body)
- end
- end
- c.closure_defs = cdecl2
- end
- end
- var r = result
- if r != null then c.result = d.dup_ireg(r)
- c.location = location
- d._icb.seq.icodes.add(c)
- end
-
- # Simle partial duplication of the current icode
- private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
-end
-
-redef class ISeq
- redef fun inner_dup_with(d)
- do
- var c2 = new ISeq
- dup_seq_to(d, c2)
- return c2
- end
-
- # Duplicate each icode and store them in dest
- # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
- private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
- do
- var old_seq = d._icb.seq
- d._icb.seq = dest
- for c in icodes do
- c.dup_with(d)
- end
- d._icb.seq = old_seq
- assert dest.iescape_mark == null
- var mark = iescape_mark
- if mark != null then
- dest.iescape_mark = d.dup_mark(mark)
- end
- end
-end
-
-redef class ILoop
- redef fun inner_dup_with(d)
- do
- var c2 = new ILoop
- dup_seq_to(d, c2)
- return c2
- end
-end
-
-redef class IIf
- redef fun inner_dup_with(d)
- do
- var c2 = new IIf(d.dup_ireg(expr))
- then_seq.dup_seq_to(d, c2.then_seq)
- else_seq.dup_seq_to(d, c2.else_seq)
- return c2
- end
-end
-
-redef class IEscape
- redef fun inner_dup_with(d)
- do
- # Get the associated mark (duplicated of not)
- var mark = d.dup_mark(iescape_mark)
- # Jump to the mark
- return new IEscape(mark)
- end
-end
-
-redef class IAbort
- redef fun inner_dup_with(d)
- do
- return new IAbort(texts, module_location)
- end
-end
-
-redef class ICall
- redef fun inner_dup_with(d)
- do
- return new ICall(property, d.dup_iregs(exprs))
- end
-end
-
-redef class ISuper
- redef fun inner_dup_with(d)
- do
- return new ISuper(property, d.dup_iregs(exprs))
- end
-end
-
-redef class INew
- redef fun inner_dup_with(d)
- do
- return new INew(stype, property, d.dup_iregs(exprs))
- end
-end
-
-redef class IAllocateInstance
- redef fun inner_dup_with(d)
- do
- return new IAllocateInstance(stype)
- end
-end
-
-redef class IStaticCall
- redef fun inner_dup_with(d)
- do
- return new IStaticCall(property, d.dup_iregs(exprs))
- end
-end
-
-redef class ICheckInstance
- redef fun inner_dup_with(d)
- do
- return new ICheckInstance(stype, d.dup_ireg(expr))
- end
-end
-
-redef class IInitAttributes
- redef fun inner_dup_with(d)
- do
- return new IInitAttributes(stype, d.dup_ireg(expr))
- end
-end
-
-redef class IClosCall
- redef fun dup_with(d)
- do
- if d._closures.has_key(closure_decl) then
- # The icloscall need to be replaced with an inlined closdef
- var closdef = d._closures[closure_decl]
- if closdef == null then
- # Default is already guarded and inlined
- return
- end
- # Break sequence cannot be inlined :/
- assert break_seq == null
- # Inline the closdef
- var res = d._icb.inline_routine(closdef, d.dup_iregs(exprs), null)
- if result != null then
- assert res != null
- d._icb.stmt(new IMove(d.dup_ireg(result.as(not null)), res))
- end
- else
- # Standard icloscall duplication
- super
- end
- end
-
- redef fun inner_dup_with(d)
- do
- return new IClosCall(closure_decl, exprs)
- end
-end
-
-redef class INative
- redef fun inner_dup_with(d)
- do
- var c2 = new INative(method, d.dup_iregs(exprs))
- c2.is_pure = is_pure
- return c2
- end
-end
-
-redef class IIntValue
- redef fun inner_dup_with(d)
- do
- return new IIntValue(value)
- end
-end
-
-redef class IBoolValue
- redef fun inner_dup_with(d)
- do
- return new IBoolValue(value)
- end
-end
-
-redef class IStringValue
- redef fun inner_dup_with(d)
- do
- return new IStringValue(value)
- end
-end
-
-redef class IFloatValue
- redef fun inner_dup_with(d)
- do
- return new IFloatValue(value)
- end
-end
-
-redef class ICharValue
- redef fun inner_dup_with(d)
- do
- return new ICharValue(value)
- end
-end
-
-redef class IMove
- redef fun inner_dup_with(d)
- do
- return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr))
- end
-end
-
-redef class IAttrRead
- redef fun inner_dup_with(d)
- do
- return new IAttrRead(property, d.dup_ireg(expr))
- end
-end
-
-redef class IAttrWrite
- redef fun inner_dup_with(d)
- do
- return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2))
- end
-end
-
-redef class IAttrIsset
- redef fun inner_dup_with(d)
- do
- return new IAttrIsset(property, d.dup_ireg(expr))
- end
-end
-
-redef class ITypeCheck
- redef fun inner_dup_with(d)
- do
- return new ITypeCheck(d.dup_ireg(expr1), d.dup_ireg(expr2), stype)
- end
-end
-
-redef class IIs
- redef fun inner_dup_with(d)
- do
- return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2))
- end
-end
-
-redef class INot
- redef fun inner_dup_with(d)
- do
- return new INot(d.dup_ireg(expr))
- end
-end
-
-redef class IOnce
- redef fun inner_dup_with(d)
- do
- var c2 = new IOnce
- body.dup_seq_to(d, c2.body)
- return c2
- end
-end
-
-redef class IHasClos
- redef fun inner_dup_with(d)
- do
- if d._closures.has_key(closure_decl) then
- # closdef will be inlined
- var closdef = d._closures[closure_decl]
- var res: IRegister
- if closdef != null then
- res = d._icb.lit_true_reg
- else
- res = d._icb.lit_false_reg
- end
- return new IMove(d.dup_ireg(result.as(not null)), res)
- else
- return new IHasClos(closure_decl)
- end
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2004-2008 Jean Privat <jean@pryen.org>
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# core NIT metamodel classes featuring the minimal and simpliest entities
-module abstractmetamodel
-
-import partial_order
-import location
-import symbol
-
-# The main singleton which knows everything
-abstract class MMContext
-
- init do end
-
- # The module dependence hierarchy
- readable var _module_hierarchy: PartialOrder[MMModule] = new PartialOrder[MMModule]
-
- # The class refinement and specialization hierarchy
- # It is not the real hierarchy since non concrete classes can only be leaves
- readable var _class_hierarchy: PartialOrder[MMLocalClass] = new PartialOrder[MMLocalClass]
-
- # All known global classes
- var _global_classes: Array[MMGlobalClass] = new Array[MMGlobalClass]
-
- # All known modules
- readable var _modules: Array[MMModule] = new Array[MMModule]
-
- # Register a new module with the modules it depends on
- fun add_module(mod: MMModule, supers: Array[MMModule])
- do
- _module_hierarchy.add(mod, _module_hierarchy.select_smallests(supers))
- _modules.add(mod)
- mod._mhe = _module_hierarchy[mod]
- end
-
- # Register a global class
- private fun add_global_class(c: MMGlobalClass) do _global_classes.add(c)
-
- # Register a local class
- fun add_local_class(c: MMLocalClass, sup: Array[MMLocalClass])
- do
- var csup = new Array[MMLocalClass]
- for s in sup do
- if s isa MMConcreteClass then
- csup.add(s)
- else
- for ss in s.che.direct_greaters do
- if csup.has(ss) then continue
- csup.add(ss)
- end
- end
- end
- var che = _class_hierarchy.add(c, csup)
- c._che = che
- end
-end
-
-# Directory of modules
-class MMDirectory
- # Full name of the directory
- readable var _name: Symbol
-
- # Full path
- readable var _path: String
-
- # Parent directory
- # null if none
- readable var _parent: nullable MMDirectory
-
- # The module that introduces the directory if any
- readable writable var _owner: nullable MMModule = null
-
- # Known modules in the directory
- readable var _modules: Map[Symbol, MMModule] = new HashMap[Symbol, MMModule]
-
- # Register a new module
- fun add_module(mod: MMModule)
- do
- assert not _modules.has_key(mod.name)
- _modules[mod.name] = mod
- end
-
- init(name: Symbol, path: String, parent: nullable MMDirectory) do
- _name = name
- _path = path
- _parent = parent
- end
-
- # The fullname of a a potentiel module in the directory
- fun full_name_for(module_name: Symbol): Symbol do
- return "{name}/{module_name}".to_symbol
- end
-end
-
-# A module is a Nit file
-abstract class MMModule
- # Global context
- readable var _context: MMContext
-
- # Short name of the module
- readable var _name: Symbol
-
- # Full name of the module
- readable var _full_name: Symbol
-
- # The directory of the module
- readable var _directory: MMDirectory
-
- # Location of this module
- readable var _location: Location
-
- # Module dependence hierarchy element
- readable var _mhe: nullable PartialOrderElement[MMModule]
-
- # All global classes of the module (defined and imported)
- readable var _global_classes: Set[MMGlobalClass] = new HashSet[MMGlobalClass]
-
- # All local classes of the module (defined and imported)
- readable var _local_classes: Set[MMLocalClass] = new HashSet[MMLocalClass]
-
- # Class specialization hierarchy of the module.
- readable var _class_specialization_hierarchy: PartialOrder[MMLocalClass] = new PartialOrder[MMLocalClass]
-
- # Modules intruded (directly or not)
- var _intrude_modules: Set[MMModule] = new HashSet[MMModule]
-
- # Module publicly imported (directly or not)
- var _public_modules: Set[MMModule] = new HashSet[MMModule]
-
- # Module privately imported (directly or not)
- var _private_modules: Set[MMModule] = new HashSet[MMModule]
-
- # Explicit imported modules
- readable var _explicit_imported_modules: Set[MMModule] = new HashSet[MMModule]
-
- # Association between local class and global classes
- var _local_class_by_global: Map[MMGlobalClass, MMLocalClass] = new HashMap[MMGlobalClass, MMLocalClass]
-
- # Dictionary of global classes
- var _global_class_by_name: Map[Symbol, MMGlobalClass] = new HashMap[Symbol, MMGlobalClass]
-
- # Is a hybrid module partially implemented in extern code
- # It is if it contains a new extern class or an
- # extern class declaration
- var is_extern_hybrid : Bool writable = false
-
- # Uses foreign function interface
- fun uses_ffi : Bool is abstract
-
- protected init(name: Symbol, dir: MMDirectory, context: MMContext, loc: Location)
- do
- _name = name
- _directory = dir
- _context = context
- _full_name = dir.full_name_for(name)
- _location = loc
- end
-
- # Register that a module is imported with a visibility
- # 0 -> intrude
- # 1 -> public
- # 3 -> private
- fun add_super_module(m: MMModule, visibility_level: Int)
- do
- _explicit_imported_modules.add(m)
- if visibility_level == 0 then
- _intrude_modules.add(m)
- _intrude_modules.add_all(m._intrude_modules)
- _public_modules.add_all(m._public_modules)
- _private_modules.add_all(m._private_modules)
- else if visibility_level == 1 then
- _public_modules.add(m)
- _public_modules.add_all(m._intrude_modules)
- _public_modules.add_all(m._public_modules)
- else
- _private_modules.add(m)
- _private_modules.add_all(m._intrude_modules)
- _private_modules.add_all(m._public_modules)
- end
-
- end
-
- # Return the visibiliy level of a super-module
- # 3 -> self or intruded => see all
- # 2 -> public => see public and protected
- # 1 -> private => see public and protected
- # 0 -> nothing => see nothing
- fun visibility_for(m: MMModule): Int
- do
- if m == self or _intrude_modules.has(m) then
- return 3
- else if _public_modules.has(m) then
- return 2
- else if _private_modules.has(m) then
- return 1
- else
- return 0
- end
- end
-
-
- # Get the local class associated with a global class
- fun [](c: MMGlobalClass): MMLocalClass
- do
- return _local_class_by_global[c]
- end
-
- # Get a local class by its name
- fun class_by_name(n: Symbol): MMLocalClass
- do
- return self[_global_class_by_name[n]]
- end
-
- # Is there a global class with this name
- fun has_global_class_named(n: Symbol): Bool
- do
- return _global_class_by_name.has_key(n)
- end
-
- # Get a global class by its name.
- # Return null if not class match this name
- fun global_class_named(n: Symbol): MMGlobalClass
- do
- return _global_class_by_name[n]
- end
-
- redef fun to_s do return name.to_s
-
- # Assign super_classes for a local class
- fun set_supers_class(c: MMLocalClass, supers: Array[MMLocalClass])
- do
- var tab = _class_specialization_hierarchy.select_smallests(supers)
- c._cshe = _class_specialization_hierarchy.add(c,tab)
- var tab_all = c.crhe.direct_greaters.to_a
- tab_all.add_all(tab)
- context.add_local_class(c,tab_all)
- end
-
- # Register a local class and its global class in the module
- private fun register_global_class(c: MMLocalClass)
- do
- _local_class_by_global[c.global] = c
- end
-end
-
-class MMGlobalClass
- # The introducing local class
- readable var _intro: MMLocalClass
-
- # Class refinement hierarchy
- # It is not the real hierarchy since non concrete classes can only be leaves
- readable var _class_refinement_hierarchy: PartialOrder[MMLocalClass] = new PartialOrder[MMLocalClass]
-
- # Create a new global class introduced with a given local class
- init(c: MMLocalClass)
- do
- _intro = c
- c.context.add_global_class(self)
- end
-
- # The name of the global class
- fun name: Symbol
- do
- return intro.name
- end
-
- # The module that introduces the global class
- fun mmmodule: MMModule
- do
- return intro.mmmodule
- end
-
- redef fun to_s
- do
- return intro.to_s
- end
-
- # register a new Local class to local class hierarchy (set the crhe value)
- private fun register_local_class(c: MMLocalClass)
- do
- var sup = new Array[MMLocalClass]
- for s in class_refinement_hierarchy do
- if c.mmmodule.mhe < s.mmmodule and s isa MMConcreteClass then
- sup.add(s)
- end
- end
- c._crhe = _class_refinement_hierarchy.add(c, sup)
- end
-
- # Is the global class an interface?
- readable writable var _is_interface: Bool = false
-
- # Is the global class an abstract class?
- readable writable var _is_abstract: Bool = false
-
- # Is the global class a enum class?
- readable writable var _is_enum: Bool = false
-
- # Is the global class an extern class?
- readable writable var _is_extern: Bool = false
-
- # Visibility of the global class
- # 1 -> public
- # 3 -> private
- readable writable var _visibility_level: Int = 1 # FIXME: why this should be defined ?
-
- # Is the global class a mixin class?
- # A mixin class inherits all constructors from a superclass
- fun is_mixin: Bool
- do
- return _mixin_of != self
- end
-
- # Indicate the superclass the class is a mixin of.
- # If mixin_of == self then the class is not a mixin
- # Is two classes have the same mixin_of value, then they both belong to the same mixing group
- readable writable var _mixin_of: MMGlobalClass = self
-
-end
-
-# Local classes are classes defined, refined or imported in a module
-abstract class MMLocalClass
- # The name of the local class
- readable var _name: Symbol
-
- # Arity of the local class (if generic)
- # FIXME: How to move this into the generic module in a sane way?
- readable var _arity : Int
-
- # The module of the local class
- readable var _mmmodule: MMModule
-
- # The global class of the local class
- fun global: MMGlobalClass do return _global.as(not null)
- var _global: nullable MMGlobalClass
-
- # The local class refinement hierarchy element
- fun crhe: PartialOrderElement[MMLocalClass] do return _crhe.as(not null)
- var _crhe: nullable PartialOrderElement[MMLocalClass]
-
- # The local class specialization hierarchy element
- fun cshe: PartialOrderElement[MMLocalClass] do return _cshe.as(not null)
- var _cshe: nullable PartialOrderElement[MMLocalClass]
-
- # The local class full hierarchy element
- fun che: PartialOrderElement[MMLocalClass] do return _che.as(not null)
- var _che: nullable PartialOrderElement[MMLocalClass]
-
- # Association between local properties and global properties
- var _local_property_by_global: Map[MMGlobalProperty, MMLocalProperty] = new HashMap[MMGlobalProperty, MMLocalProperty]
-
- # All known global properties
- readable var _global_properties: Set[MMGlobalProperty] = new HashSet[MMGlobalProperty]
-
- # All locally defined local properties
- readable var _local_local_properties: Set[MMLocalProperty] = new HashSet[MMLocalProperty]
-
- # Dictionnary of global properties
- var _properties_by_name: Map[Symbol, Array[MMGlobalProperty]] = new HashMap[Symbol, Array[MMGlobalProperty]]
-
- # Create a new class with a given name and arity
- protected init(mod: MMModule, name: Symbol, arity: Int)
- do
- _mmmodule = mod
- _name = name
- _arity = arity
- mod._local_classes.add(self)
- end
-
- # The corresponding local class in another module
- fun for_module(m: MMModule): MMLocalClass
- do
- return m[global]
- end
-
- # Introduce a new global class to a new global one and register to hierarchy with no refine
- fun new_global
- do
- var g = new MMGlobalClass(self)
- _mmmodule._global_classes.add(g)
- _mmmodule._global_class_by_name[name] = g
- set_global(g)
- end
-
- # Associate this local class to a global one and register to hierarchy
- # the global class for this class
- # refined classes for this class
- fun set_global(g: MMGlobalClass)
- do
- _global = g
- _global.register_local_class(self)
- _mmmodule.register_global_class(self)
- end
-
- # Is there a global propery with a given name
- # TODO: Will disapear when qualified names will be available
- fun has_global_property_by_name(n: Symbol): Bool
- do
- return _properties_by_name.has_key(n) and _properties_by_name[n].length == 1
- end
-
- # Get a global property by its name
- # TODO: Will disapear when qualified names will be available
- fun get_property_by_name(n: Symbol): MMGlobalProperty
- do
- if not has_global_property_by_name(n) then abort
- var props = _properties_by_name[n]
- return props.first
- end
-
- # Get a attribute by its name
- # TODO: Will disapear when qualified names will be available
- fun attribute(a: Symbol): MMGlobalProperty
- do
- return get_property_by_name(a)
- end
-
- # Get a method by its name
- # TODO: Will disapear when qualified names will be available
- fun method(na: Symbol): MMGlobalProperty
- do
- return _properties_by_name[na].first
- end
-
- # Select a method from its name
- # TODO: Will disapear when qualified names will be available
- fun select_method(name: Symbol): MMMethod
- do
- var gp = method(name)
- var res = self[gp]
- assert res isa MMMethod
- return res
- end
-
- # Select an attribute from its name
- # TODO: Will disapear when qualified names will be available
- fun select_attribute(name: Symbol): MMAttribute
- do
- var gp = attribute(name)
- var res = self[gp]
- assert res isa MMAttribute
- return res
- end
-
- # Look in super-classes (by specialization) and return properties with name
- # Beware, global property of results is not intended to be the same
- fun super_methods_named(n: Symbol): Array[MMLocalProperty]
- do
- var classes = new Array[MMLocalClass]
- for c in cshe.greaters do
- if c.has_global_property_by_name(n) then classes.add(c)
- end
- classes = cshe.order.select_smallests(classes)
- var res = new Array[MMLocalProperty]
- for c in classes do
- var g = c.method(n)
- #print "found {c[g].full_name}"
- res.add(c[g])
- end
- return res
- end
-
- # Register a local property and associate it with its global property
- fun register_local_property(p: MMLocalProperty)
- do
- _local_property_by_global[p.global] = p
- if p.local_class == self then
- _local_local_properties.add(p)
- end
- end
-
- # Register a global property and associate it with its name
- fun register_global_property(glob: MMGlobalProperty)
- do
- var prop = glob.intro
- var name = prop.name
- if _properties_by_name.has_key(name) then
- _properties_by_name[name].add(glob)
- else
- _properties_by_name[name] = [glob]
- end
- _global_properties.add(glob)
- register_local_property(prop)
- end
-
- # Does the global property belong to the class?
- fun has_global_property(glob: MMGlobalProperty): Bool
- do
- return _global_properties.has(glob)
- end
-
- # Get a local property by its global property
- fun [](glob: MMGlobalProperty): MMLocalProperty
- do
- return _local_property_by_global[glob]
- end
-
- # The current MMContext
- fun context: MMContext do return mmmodule.context
-
- redef fun to_s
- do
- return _name.to_s
- end
-
- # Comparaison in a total order that superset the class refinement and the class specialisation relations
- fun total_order_compare(b: MMLocalClass): Int
- do
- var a = self
- if a == b then
- return 0
- else if a.mmmodule.mhe < b.mmmodule then
- return 1
- else if b.mmmodule.mhe < a.mmmodule then
- return -1
- end
- var ar = a.cshe.rank
- var br = b.cshe.rank
- if ar > br then
- return 1
- else if br > ar then
- return -1
- else
- return b.name.to_s <=> a.name.to_s
- end
- end
-end
-
-# A global property gather local properties that correspond to a same message
-class MMGlobalProperty
- # The local property for each class that has the global property
-
- # The introducing local property
- readable var _intro: MMLocalProperty
-
- # The local class that introduces the global property
- fun local_class: MMLocalClass
- do
- return intro.local_class
- end
-
- # The property redefinition hierarchy
- readable var _property_hierarchy: PartialOrder[MMLocalProperty] = new PartialOrder[MMLocalProperty]
-
- # Create a new global property introduced by a local one
- protected init(p: MMLocalProperty)
- do
- _intro = p
- add_local_property(p, new Array[MMLocalProperty])
- end
-
- redef fun to_s do return intro.full_name
-
- # Register a new local property
- fun add_local_property(i: MMLocalProperty, sup: Array[MMLocalProperty])
- do
- i._prhe = _property_hierarchy.add(i,sup)
- end
-
- # Is the global property an attribute ?
- fun is_attribute: Bool do return intro isa MMAttribute
-
- # Is the global property a method (or a constructor)?
- fun is_method: Bool do return intro isa MMMethod
-
- # Is the global property a constructor (thus also a method)?
- readable writable var _is_init: Bool = false
-
- # Is the global property a constructor for a given class?
- fun is_init_for(c: MMLocalClass): Bool
- do
- if not is_init then return false
- var sc = intro.local_class
- var res = c.che <= sc and c.global.mixin_of == sc.global.mixin_of
- return res
- end
-
- # Visibility of the property
- # 1 -> public
- # 2 -> protected
- # 3 -> private
- readable writable var _visibility_level: Int = 1 # FIXME: why this should be defined ?
-end
-
-# Local properties are properties defined (explicitely or not) in a local class
-abstract class MMLocalProperty
- # The name of the property
- readable var _name: Symbol
-
- # The local class who own the local property
- readable var _local_class: MMLocalClass
-
- # The global property where belong the local property
- var _global: nullable MMGlobalProperty
-
- fun global: MMGlobalProperty do return _global.as(not null)
- fun is_global_set: Bool do return _global != null
-
- # Redefinition hierarchy of the local property
- var _prhe: nullable PartialOrderElement[MMLocalProperty]
-
- fun prhe: PartialOrderElement[MMLocalProperty] do return _prhe.as(not null)
-
- # The module of the local property
- fun mmmodule: MMModule do return _local_class.mmmodule
-
- # Full expanded name with all qualifications
- fun full_name: String
- do
- if _global == null then
- return "{local_class.mmmodule}::{local_class}::(?::{name})"
- else if global.intro == self then
- return "{local_class.mmmodule}::{local_class}::{name}"
- else
- return "{local_class.mmmodule}::{local_class}::({global.intro.full_name})"
- end
- end
-
- # set the global property for this property
- fun set_global(g: MMGlobalProperty)
- do
- _global = g
- _local_class.register_local_property(self)
- end
-
- # Introduce a new global property for this local one
- fun new_global
- do
- assert _global == null
- var global = new MMGlobalProperty(self)
- _global = global
- _local_class.register_global_property(global)
- end
-
- redef fun to_s do return name.to_s
-
- # Is the concrete property contain a `super' in the body?
- readable writable var _need_super: Bool = false
-
- protected init(n: Symbol, bc: MMLocalClass)
- do
- _name = n
- _local_class = bc
- end
-end
-
-# Attribute local properties
-abstract class MMAttribute
- super MMLocalProperty
-end
-
-class MMExplicitImport
- var local_class : MMLocalClass
- var method : MMMethod
-end
-
-# Method local properties
-abstract class MMMethod
- super MMLocalProperty
- # Is the method defined with intern
- fun is_intern: Bool is abstract
-
- # Is the method abstract
- fun is_abstract: Bool is abstract
-
- # Is the method extern
- fun is_extern: Bool is abstract
-
- # extern name when specified explicitly in nit code
- fun extern_name: nullable String is abstract
-
- # properties explicitly exported to native code
- fun explicit_imports : Set[ MMExplicitImport ] is abstract
-end
-
-# Concrete local classes
-abstract class MMConcreteClass
- super MMLocalClass
-end
-
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2004-2008 Jean Privat <jean@pryen.org>
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Generic classes, generic types and generic formal parameters
-module genericity
-
-intrude import type_formal
-intrude import inheritance
-
-redef class MMLocalClass
- # The pos-th formal type parameter
- fun get_formal(pos: Int): MMTypeFormalParameter
- do
- return formals_types[pos]
- end
-
- # Register a new formal type
- # Called in order during the building of the class
- fun register_formal(f: MMTypeFormalParameter)
- do
- assert f.def_class == self
- assert f.position == _formals_types.length
- _formals_types.add(f)
- end
-
- # All the insanciated types of the class
- var _types: Array[MMTypeGeneric] = new Array[MMTypeGeneric]
-
- # Instanciate a type from the generic class
- fun get_instantiate_type(t: Array[MMType]): MMType
- do
- for g in _types do
- if g.params_equals(t) then return g
- end
- var g = new MMTypeGeneric(self, t)
- _types.add(g)
- return g
- end
-
- # The formal types of the class
- var _formals_types: Array[MMTypeFormalParameter] = new Array[MMTypeFormalParameter]
-
- # Return the definition formal types for the class
- # Import them from the intro class if needed
- private fun formals_types: Array[MMTypeFormalParameter]
- do
- if _formals_types.is_empty then
- assert not self isa MMConcreteClass
- # Because of F-genericity, the import is done in two pass
- # First, get the formal types untyped so that one can recurcively call formals_types
- for i in [0..arity[ do
- var oft = global.intro.get_formal(i)
- var ft = new MMTypeFormalParameter(oft.name, i, self)
- register_formal(ft)
- end
- # Second, assign the bound to the formal type
- for i in [0..arity[ do
- var oft = global.intro.get_formal(i)
- var ft = _formals_types[i]
- ft.bound = oft.bound.for_module(mmmodule)
- end
- end
- return _formals_types
- end
-
- redef fun get_type
- do
- if _base_type_cache == null and is_generic then
- _base_type_cache = get_instantiate_type(formals_types)
- return _base_type_cache.as(not null)
- else
- return super
- end
- end
-
- # Is the class a generic one?
- fun is_generic: Bool do return arity > 0
-end
-
-redef class MMType
- # TODO: IS this useful?
- fun is_generic: Bool is abstract
-end
-
-redef class MMTypeFormal
- redef fun is_generic do return _bound.is_generic
-end
-
-redef class MMTypeSimpleClass
- redef fun is_generic do return false
-end
-
-class MMTypeGeneric
- super MMTypeClass
- # Formal arguments
- readable var _params: Array[MMType]
-
- redef fun is_generic do return true
-
- redef fun is_supertype(t)
- do
- if t.local_class.cshe <= _local_class then
- var u = t.upcast_for(_local_class)
- if u isa MMTypeGeneric then
- return is_subtype(u) # and u.is_subtype(self) # Strong typing is too strong
- end
- end
- return false
- end
-
- redef fun upcast_for(c)
- do
- var t = super
- if t != self then
- t = t.adapt_to(self)
- end
- return t
- end
-
- redef fun for_module(mod)
- do
- var t: MMType = self
- if mmmodule != mod then
- var parms = new Array[MMType]
- for p in _params do
- parms.add(p.for_module(mod))
- end
- var b = _local_class.for_module(mod)
- t = b.get_instantiate_type(parms)
- end
- return t
- end
-
- redef fun adapt_to(r)
- do
- var rv = new Array[MMType]
- for i in _params do
- rv.add(i.adapt_to(r))
- end
- var l = _local_class.get_instantiate_type(rv)
- return l
- end
-
- private fun params_equals(t: Array[MMType]): Bool
- do
- if t.length != _params.length then
- return false
- end
- for i in [0..t.length[ do
- if _params[i] != t[i] then
- return false
- end
- end
- return true
- end
-
- redef fun to_s
- do
- return "{super}[{_params.join(", ")}]"
- end
-
- # Is self a subtype of t?
- # Require that t.local_class = self.local_class
- private fun is_subtype(t: MMTypeGeneric) : Bool
- do
- for i in [0.._params.length[
- do
- if not t.params[i] < _params[i] then
- return false
- end
- end
- return true
- end
-
- redef fun has_formal
- do
- for p in _params do
- if p.has_formal then return true
- end
- return false
- end
-
- init(c: MMLocalClass, p: Array[MMType])
- do
- super(c)
- _params = p
- end
-end
-
-class MMTypeFormalParameter
- super MMTypeFormal
- # The class where self is defined
- readable var _def_class: MMLocalClass
-
- # The position is self in def_class
- readable var _position: Int
-
- redef fun mmmodule do return _def_class.mmmodule
-
- redef fun for_module(mod)
- do
- var t: MMType = self
- if mmmodule != mod then
- t = mod[_def_class.global].get_formal(position)
- end
- return t
- end
-
- redef fun upcast_for(c) do return _bound.upcast_for(c)
-
- fun bound=(t: MMType)
- do
- assert _bound == null
- _bound = t
- end
-
- redef fun adapt_to(r)
- do
- r = r.direct_type
- var old_r = r.upcast_for(def_class)
- #if not old_r isa MMTypeGeneric then
- # print "adapt {self}'{def_class}'{self.mmmodule} to {r}'{r.mmmodule}"
- # print " old_r = {old_r}'{old_r.mmmodule}"
- #end
- assert old_r isa MMTypeGeneric
- var reduct = old_r.params[position]
- return reduct
- end
-
- init with_bound(n: Symbol, p: Int, intro: MMLocalClass, b: MMType)
- do
- init(n,p,intro)
- _bound = b
- end
-
- init(n: Symbol, p: Int, intro: MMLocalClass)
- do
- super(n, null)
- _position = p
- _def_class = intro
- end
-end
-
-redef class MMTypeNone
- redef fun is_generic do return false
- redef fun for_module(mod) do return self
- redef fun adapt_to(r) do return self
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2004-2008 Jean Privat <jean@pryen.org>
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Compute importation of classes and inheritance of properties
-module inheritance
-
-intrude import static_type
-
-redef class MMContext
- # Method to redefine to handle the property conflicts
- # FIXME: This is just a bad workaround because of a bad design
- fun handle_property_conflict(lc: MMLocalClass, impls2: Array[MMLocalProperty])
- do
- var glob = impls2.first.global
- stderr.write("Fatal error: inherit_local_property error\n")
- print("------- {lc.mmmodule}::{lc} {glob.intro.full_name}")
- for i in impls2 do
- print(" {i.full_name}")
- end
- print("------- {glob.property_hierarchy.first}")
- print("------- {glob.property_hierarchy.to_dot}")
- exit(1)
- end
-end
-
-redef class MMModule
- # The root of the class hierarchy
- fun type_any: MMType
- do
- var c_name = class_by_name(once ("Object".to_symbol))
- return c_name.get_type
- end
-
- # Root of then extern class hierarchy
- fun type_any_extern : MMType
- do
- var c_name = class_by_name(once ("Pointer".to_symbol))
- return c_name.get_type
- end
-
- # Import global classes from supermodules
- fun import_global_classes
- do
- var globals = new HashMap[MMGlobalClass,HashSet[MMLocalClass]]
- assert mhe != null
- for mod in mhe.direct_greaters do
- for glob in mod.global_classes do
- if global_classes.has(glob) then continue
- _global_classes.add(glob)
- _global_class_by_name[glob.name] = glob
- end
- end
-
- end
-
- # Create implicit local classes for global classes that are not refined
- fun import_local_classes
- do
- for g in _global_classes do
- if _local_class_by_global.has_key(g) then continue
- var impl = new MMImplicitLocalClass(self, g)
- end
- end
-end
-
-redef class MMLocalClass
- # List of all parents
- var _direct_parents: Array[MMAncestor] = new Array[MMAncestor]
-
- # Is the class computing super.
- # Used to detect specialization loops.
- var _computing_super: Bool = false
-
- # Compute super classes of a class
- fun compute_super_classes
- do
- if computed_super_classes then
- # do no recompute if allready done
- return
- else if _computing_super then
- stderr.write("Fatal error: Inheritance loop for class {self}\n")
- exit(1)
- end
- _computing_super = true
-
- var supers = new Array[MMLocalClass]
- add_explicit_classes(supers)
- add_super_classes(supers)
- add_default_any_class(supers)
- compute_super_parents(supers)
- var set = new HashSet[MMLocalClass]
- set.add_all(supers)
- var u = set.to_a
- mmmodule.set_supers_class(self,u)
- assert _crhe != null
- assert _cshe != null
- _computing_super = false
- end
-
- # Compute ancestors for a class
- fun compute_ancestors
- do
- assert computed_super_classes
- if computed_ancestors then return
-
- var ancestors = group_ancestors(build_ancestors)
- _ancestors = new HashMap[MMLocalClass, MMAncestor]
-
- for set in ancestors.values do
- if set.length == 1 then
- add_ancestor(set.first)
- else
- var ma = merge_ancestors(set)
- add_ancestor(merge_ancestors(set))
- end
- end
- end
-
- var _are_global_properties_inherited: Bool = false
-
- # Inherit global properties for a class
- private fun inherit_global_properties
- do
- if _are_global_properties_inherited then return
- _are_global_properties_inherited = true
-
- var names = _properties_by_name
- var set = _global_properties
- for c in che.direct_greaters do
- for glob in c.global_properties do
- if set.has(glob) then continue
-
- set.add(glob) # Add the global property
-
- # Do not inherit constructors trough specialization
- #print "{c.mmmodule}::{c} -> {mmmodule}::{self} for {glob.local_property.local_class.mmmodule}::{glob.local_property.local_class}::{glob.local_property} : {glob.is_init}"
- if glob.is_init and glob.intro.local_class.global != global then
- #print "pass"
- continue
- end
-
- # Do not inherit new style attributes
- if glob.intro.name.to_s[0] == '@' then continue
-
- make_visible_an_inherited_global_property(glob)
- end
- end
- end
-
- redef fun global_properties
- do
- if _are_global_properties_inherited then return _global_properties
- assert computed_super_classes
- inherit_global_properties
- return _global_properties
- end
-
- redef fun has_global_property(g)
- do
- # has_global_property can be called during the construction of the class
- # hierarchy to check that a type "X" is not a formal type.
- if not computed_super_classes then return false
-
- var set = _global_properties
- if set.has(g) then return true
- for c in che.direct_greaters do
- if c.has_global_property(g) then
- set.add(g)
- return true
- end
- end
- return false
- end
-
- redef fun has_global_property_by_name(n)
- do
- # has_global_property can be called during the construction of the class
- # hierarchy to check that a type "X" is not a formal type.
- if not computed_super_classes then return false
-
- # Ensure that super-classes are constructed
- compute_super_classes
-
- if _properties_by_name.has_key(n) then
- return _properties_by_name[n].length == 1
- end
- var set = _global_properties
- var nset
- if _properties_by_name.has_key(n) then
- nset = _properties_by_name[n]
- else
- nset = new Array[MMGlobalProperty]
- _properties_by_name[n] = nset
- end
- for c in che.direct_greaters do
- if c.has_global_property_by_name(n) then
- var g = c.get_property_by_name(n)
- if not set.has(g) then set.add(g)
- if g.is_init and g.intro.local_class.global != global then continue
- if g.intro.name.to_s.first == '@' then continue # inherited new style attibutes are invisible
- if nset.has(g) then continue
- nset.add(g)
- end
- end
- return nset.length == 1
- end
-
- # Make the name of a global property meaningful in the class
- fun make_visible_an_inherited_global_property(glob: MMGlobalProperty)
- do
- var names = _properties_by_name
- var gname = glob.intro.name
- var conf_set: Array[MMGlobalProperty]
- if names.has_key(gname) then
- conf_set = names[gname]
- else
- conf_set = new Array[MMGlobalProperty]
- names[gname] = conf_set
- end
- conf_set.add(glob)
- end
-
- # Add super stype of this current local class
- fun add_direct_parent(p: MMAncestor)
- do
- _direct_parents.add(p)
- end
-
- # Are super-class already computed?
- fun computed_super_classes: Bool
- do
- return _crhe != null and _cshe != null
- end
-
- # Are ancestors already computed
- fun computed_ancestors: Bool
- do
- return _ancestors != null
- end
-
- # Get the ancestor for a given class
- # TODO: is this useful?
- private fun ancestor_for(c: MMLocalClass): MMAncestor
- do
- assert ancestors != null
-
- if _ancestors.has_key(c) then
- return _ancestors[c]
- end
- var a = c.for_module(mmmodule)
- assert cshe <= a
- var ra: MMAncestor
- if _ancestors.has_key(a) then
- ra = _ancestors[a]
- else if c.global == _global then
- ra = new MMRefineAncestor(self,c)
- else
- ra = new MMSpecAncestor(get_type,c.get_type)
- end
- _ancestors[c] = ra
- return ra
- end
-
- redef fun [](glob)
- do
- if _local_property_by_global.has_key(glob) then
- return _local_property_by_global[glob]
- else if has_global_property(glob) then
- return inherit_local_property(glob)
- else if not computed_super_classes then
- compute_super_classes
- computed_ancestors
- inherit_global_properties
- assert has_global_property(glob)
- return inherit_local_property(glob)
- else
- abort
- end
- end
-
- # Add default super class in direct parent and in super classes if this is not the Object class
- # Default super class is Pointer for extern types
- private fun add_default_any_class(supers: Array[MMLocalClass])
- do
- if name != once ("Object".to_symbol) then
- var has_no_top = false
- if supers.is_empty then
- has_no_top = true
- else if global.is_extern then
- has_no_top = true
- for s in supers do
- if s.global.is_extern then
- has_no_top = false
- break
- end
- end
- end
-
- if has_no_top then
- var top_type
- if name != once ("Pointer".to_symbol) and global.is_extern then
- top_type = mmmodule.type_any_extern
- else
- top_type = mmmodule.type_any
- end
- supers.add(top_type.local_class)
- var default = new MMDefaultAncestor(self, top_type)
- add_direct_parent(default)
- end
- end
- end
-
- # Adding inherited class from previous refinement of self
- private fun add_super_classes(supers: Array[MMLocalClass])
- do
- assert _crhe != null
- for ref in _crhe.direct_greaters do
- for sup in ref.cshe.direct_greaters do
- var cla = sup.for_module(_mmmodule)
- supers.add(cla)
- end
- end
- end
-
- # Add self parents of this local class
- private fun add_explicit_classes(supers: Array[MMLocalClass])
- do
- for p in _direct_parents do
- supers.add(p.local_class)
- end
- end
-
- # Ensure all super parents are computed
- private fun compute_super_parents(supers: Array[MMLocalClass])
- do
- for p in supers do
- p.compute_super_classes
- end
- end
-
- # compute all ancestors for a class (multiple)
- private fun build_ancestors: Array[MMAncestor]
- do
- var all_ancestors = new Array[MMAncestor]
- # Refined classes are ancestors
- assert _crhe != null
- for p in _crhe.direct_greaters do
- assert p != self
- var anc = new MMRefineAncestor(self, p)
- anc.add_in(all_ancestors)
- end
- for anc in _direct_parents do
- assert anc.local_class != self
- anc.add_in(all_ancestors)
- end
- return all_ancestors
- end
-
- # Build an ancestor map indexed by LocalClass
- private fun group_ancestors(all: Array[MMAncestor]): Map[MMLocalClass, Set[MMAncestor]]
- do
- #print "process {self}"
- var map = new HashMap[MMLocalClass, Set[MMAncestor]]
- for a in all do
- var c = a.local_class
- #print "ancestor is"
- #print " {c}"
- var set: Set[MMAncestor]
- c.compute_ancestors
- if map.has_key(c) then
- set = map[c]
- else
- set = new HashSet[MMAncestor]
- map[c] = set
- end
- set.add(a)
- end
- return map
- end
-
- # Remove duplicate ancestors and merge if compatible, in the other case do an error
- private fun merge_ancestors(set: Set[MMAncestor]): MMAncestor
- do
- var marks = new HashSet[MMAncestor]
- var res = new Array[MMAncestor]
- for t in set do
- var it = set.iterator
- var search = true
- while it.is_ok and search do
-
- var a = t == it.item
- a = marks.has(it.item)
- a = it.item.stype < t.stype
-
- if not(t == it.item or marks.has(it.item)) and
- (it.item.stype < t.stype) then
- marks.add(t)
- search = false
- end
- it.next
- end
- if not marks.has(t) then
- res.add(t)
- end
- end
-
- if res.length > 1 then
- stderr.write("Fatal error: Incompatibles ancestors for {self.name}: {res.join(", ")}\n")
- exit(1)
- end
- return res.first
- end
-
- # Inherit a local property
- # Is lazily called
- # FIXME: dont crash lazily
- private fun inherit_local_property(glob: MMGlobalProperty): MMLocalProperty
- do
- assert not _local_property_by_global.has_key(glob)
-
- var impl: MMLocalProperty
-
- var ghier = glob.property_hierarchy
- var supers = che.direct_greaters
- if ghier.length == 1 then
- # Unredefined property
- impl = glob.intro
- else if supers.length == 1 then
- # Single inheritance
- impl = supers.first[glob]
- else
- # Hard multiple inheritance
- # First compute the set of bottom properties
- var impls = new ArraySet[MMLocalProperty]
- for sc in supers do
- if sc.has_global_property(glob) then impls.add(sc[glob])
- end
- # Second, extract most specific
- var impls2 = ghier.select_smallests(impls)
- # Conflict case (FIXME)
- if impls2.length != 1 then
- self.mmmodule.context.handle_property_conflict(self, impls2)
- end
- impl = impls2.first
- end
-
- # FIXME: Why these 3 lines ?
- #var ac = ancestor_for(impl.local_class)
- #ac.local_class.inherit_global_properties
- #var a = ac.stype
- #assert a.local_class != self
-
- # Register the local property
- _local_property_by_global[glob] = impl
-
- return impl
- end
-end
-
-redef class MMLocalProperty
- # Attach self to a global property
- fun inherit_global(g: MMGlobalProperty)
- do
- set_global(g)
- var impls = new Array[MMLocalProperty]
- for sc in local_class.che.direct_greaters do
- if not sc.has_global_property(g) then continue
- impls.add(sc[g])
- end
- g.add_local_property(self, impls)
- end
-end
-
-redef class MMAncestor
- # Add this ancestor and it's super one in tab
- private fun add_in(tab: Array[MMAncestor])
- do
- tab.add(self)
- stype.local_class.compute_ancestors
- for anc in stype.local_class.ancestors.values do
- var aaa = anc.stype.for_module(stype.mmmodule)
- var a = aaa.adapt_to(stype).for_module(inheriter.mmmodule)
- if a.local_class != inheriter.local_class then
- var it = tab.iterator
- var b = true
- while it.is_ok and b do
- b = not ( it.item.inheriter == inheriter and it.item.stype == a)
- it.next
- end
- if b then
- tab.add(new MMSpecAncestor(inheriter,a))
- end
- end
- end
- end
-end
-
-##########################################
-
-# A local class that is a pure importation of an other local class
-class MMImplicitLocalClass
- super MMLocalClass
- init(mod: MMModule, g: MMGlobalClass)
- do
- var cla = g.intro
- super(mod, cla.name, cla.arity)
- set_global(g)
- end
-end
-
-class MMRefineAncestor
- super MMAncestor
- redef readable var _local_class: MMLocalClass
-
- init(b: MMLocalClass, a: MMLocalClass)
- do
- _local_class = a
- inheriter = b.get_type
- stype = _local_class.get_type
- end
-end
-
-
-class MMSpecAncestor
- super MMAncestor
- redef fun local_class do return stype.local_class
-
- init(inheriter: MMType, stype: MMType)
- do
- _inheriter = inheriter
- _stype = stype
- end
-end
-
-class MMDefaultAncestor
- super MMAncestor
- redef fun local_class do return stype.local_class
-
- init(b: MMLocalClass, anc: MMType)
- do
- inheriter = b.get_type
- stype = anc
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# NIT metamodel bottom module (aka metamodel featuring all extensions)
-module metamodel
-
-import genericity
-import vararg
-import virtualtype
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2004-2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Partial ordered sets (ie hierarchies)
-module partial_order
-
-# Handles partial ordered sets (ie hierarchies)
-# Thez are built by adding new element at the bottom of the hierarchy
-class PartialOrder[E: Object]
- super Collection[E]
- # Elements
- var _elements: Map[E, PartialOrderElement[E]]
-
- # Elements
- var _elements_list: Array[E]
-
- # The roots of the hierarchy are elements without greaters
- readable var _roots: Array[E]
-
-# Collection
-
- redef fun is_empty do return _elements.is_empty
-
- redef fun length do return _elements.length
-
- redef fun first do return _elements_list.first
-
- redef fun has(e) do return _elements.has_key(e)
-
- redef fun has_only(e) do return _elements.length == 1 and _elements.values.first == e
-
- redef fun count(e)
- do
- if has(e) then
- return 1
- else
- return 0
- end
- end
-
- redef fun iterator do return _elements_list.iterator
-
-# Access
-
- # Return the element associed with the item
- fun [](e: E): PartialOrderElement[E]
- do
- return _elements[e]
- end
-
- # Return a dot representation
- fun to_dot: String
- do
- var s = new Buffer
- s.append(to_dot_header)
- for e in _elements.values do
- s.append(to_dot_node(e.value))
- for d in e.direct_greaters do
- s.append(to_dot_edge(e.value, d))
- end
- end
- s.append("}\n")
- return s.to_s
- end
-
- # Called to display the header
- protected fun to_dot_header: String
- do
- return "digraph G \{\ngraph [rankdir=BT];\n"
- end
-
- # Called to display a node
- protected fun to_dot_node(e: E): String
- do
- return "\"{e}\";\n"
- end
-
- # Called to draw an edge between `e1' and `e2' when `e1' < `e2'
- protected fun to_dot_edge(e1: E, e2: E): String
- do
- return "\"{e1}\" -> \"{e2}\";\n"
- end
-
- # Get an array consisting of only minimal elements
- fun select_smallests(c: nullable Collection[E]): Array[E]
- do
- if c == null then return new Array[E]
- assert has_all(c)
- var res = new Array[E].with_capacity(c.length)
- var tmp = new Array[E].with_capacity(c.length)
- for e in c do
- # try to add `e' to the set of smallests
- var r = add_to_smallests(e, res, tmp)
- if r then
- # `tmp' contains the new smallests
- # therefore swap
- var t = tmp
- tmp = res
- res = t
- end
- end
- return res
- end
-
- # Add a new element inferior of some others
- fun add(e: E, supers: nullable Collection[E]): PartialOrderElement[E]
- do
- assert not has(e)
- assert supers == null or has_all(supers)
- var directs = select_smallests(supers)
- var poe = new_poe(e, directs)
- _elements[e] = poe
- _elements_list.add(e)
- if supers == null or supers.is_empty then
- _roots.add(e)
- end
- return poe
- end
-
- # factory for partial order elements
- protected fun new_poe(e: E, directs: Array[E]): PartialOrderElement[E]
- do
- return new PartialOrderElement[E](self, e, directs)
- end
-
- protected fun add_to_smallests(e: E, from: Array[E], to: Array[E]): Bool
- # an element `e'
- # some others elements `from' incomparable two by two
- # Return false if `from' < e
- # else return false and
- # fill `to' with smallests incomparable elements (including `e')
- do
- to.clear
- var poe = self[e]
- for i in from do
- if poe > i then
- return false
- end
- if not poe < i then
- to.add(i)
- end
- end
- to.add(e)
- return true
- end
-
- protected fun compute_smallers_for(poe: PartialOrderElement[E], set: Set[E])
- do
- var e = poe.value
- for s in _elements.values do
- if s < e then
- set.add(s.value)
- end
- end
- end
-
- init
- do
- _elements = new HashMap[E, PartialOrderElement[E]]
- _elements_list = new Array[E]
- _roots = new Array[E]
- end
-end
-
-class PartialOrderElement[E: Object]
- # The partial order where belong self
- readable var _order: PartialOrder[E]
-
- # The value handled by self
- readable var _value: E
-
- # Current rank in the hierarchy
- # Roots have 0
- # Sons of roots have 1
- # Etc.
- readable var _rank: Int
-
- # Elements that are direclty greater than self
- readable var _direct_greaters: Array[E]
-
- # Elements that are direclty smallers than self
- readable var _direct_smallers: Array[E]
-
- # Elements that are strictly greater than self
- readable var _greaters: Set[E]
-
- # Cached result of greaters_and_self
- var _greaters_and_self_cache: nullable Array[E]
-
- # Elements that are self or greater than self
- fun greaters_and_self: Collection[E]
- do
- if _greaters_and_self_cache == null then
- _greaters_and_self_cache = _greaters.to_a
- _greaters_and_self_cache.add(_value)
- end
- return _greaters_and_self_cache.as(not null)
- end
-
- # Cached value of _order.length to validade smallers_cache
- var _smallers_last_length: Int = 0
-
- # Cached result of smallers
- var _smallers_cache: Set[E]
-
- # Elements that are strictly smaller than self
- fun smallers: Collection[E]
- do
- if _smallers_last_length < _order.length then
- _order.compute_smallers_for(self, _smallers_cache)
- _smallers_last_length = _order.length
- end
- return _smallers_cache
- end
-
- # Cached result of linear_extension
- var _linear_extension_cache: nullable Array[E]
-
- # Return a linear extension of self
- # FIXME: Uses the C++ algo that is not good!
- fun linear_extension: Array[E]
- do
- if _linear_extension_cache == null then
- var res = new Array[E]
- var res2 = new Array[E]
- res.add(value)
- for s in direct_greaters do
- var sl = order[s].linear_extension
- res2.clear
- for e in res do
- if not sl.has(e) then res2.add(e)
- end
- res2.append(sl)
-
- var tmp = res
- res = res2
- res2 = tmp
- end
- _linear_extension_cache = res
- end
- return _linear_extension_cache.as(not null)
- end
-
- # Cached result of reverse_linear_extension
- var _reverse_linear_extension_cache: nullable Array[E]
-
- # Return a reverse linear extension of self
- # FIXME: Uses the C++ algo that is not good!
- fun reverse_linear_extension: Array[E]
- do
- if _reverse_linear_extension_cache == null then
- var res = new HashSet[E]
- for s in direct_greaters do
- var sl = order[s].linear_extension
- res.add_all(sl)
- end
- res.add(value)
- _linear_extension_cache = res.to_a
- end
- return _linear_extension_cache.as(not null)
- end
-
- # Is value < o according to order?
- fun <(o: E): Bool
- do
- return _greaters.has(o)
- end
-
- # Is value <= o according to order?
- fun <=(o: E): Bool
- do
- return _value == o or _greaters.has(o)
- end
-
- # Is value > o according to order?
- fun >(o: E): Bool
- do
- return _order[o] < _value
- end
-
- # Is value >= o according to order?
- fun >=(o: E): Bool
- do
- return _value == o or _order[o] < _value
- end
-
- protected fun register_direct_smallers(e: E)
- do
- _direct_smallers.add(e)
- end
-
- protected init(o: PartialOrder[E], e: E, directs: Array[E])
- do
- _order = o
- _value = e
- _direct_greaters = directs
- _direct_smallers = new Array[E]
-
- _greaters = new HashSet[E]
- _smallers_cache = new HashSet[E]
-
- var g = _greaters
- var r = 0
- for ee in directs do
- g.add(ee)
- var poee = _order[ee]
- if poee.rank >= r then
- r = poee.rank + 1
- end
- poee.register_direct_smallers(e)
- for eee in poee.greaters do
- g.add(eee)
- end
- end
- _rank = r
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2004-2008 Jean Privat <jean@pryen.org>
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Static types and property signatures
-module static_type
-
-intrude import abstractmetamodel
-
-redef class MMLocalClass
- # Cached result of get_type
- var _base_type_cache: nullable MMType
-
- # Return the type of self for this class
- fun get_type: MMType
- do
- if _base_type_cache == null then _base_type_cache = new MMTypeSimpleClass(self)
- return _base_type_cache.as(not null)
- end
-
- # Register a new ancestor
- protected fun add_ancestor(a: MMAncestor)
- do
- assert not _ancestors.has_key(a.local_class)
- assert a.local_class != self
- _ancestors[a.local_class] = a
- end
-
- # Array of ancestor that associate each superclass with the corresponding ancestor
- readable var _ancestors: nullable Map[MMLocalClass, MMAncestor]
-
- # The ancestor type for a given superclass
- fun ancestor(c: MMLocalClass): MMType
- do
- return _ancestors[c].stype
- end
-end
-
-redef class MMLocalProperty
- # The signature of the property (where it is declared)
- readable writable var _signature: nullable MMSignature
-
- var _signatures_cache: HashMap[MMType, MMSignature] = new HashMap[MMType, MMSignature]
-
- # Return the adapted signature of self for a receiver of type t
- fun signature_for(t: MMType): MMSignature do
- if t == local_class.get_type then return signature.as(not null)
-
- if _signatures_cache.has_key(t) then return _signatures_cache[t]
-
- var res = signature.adaptation_to(t)
- _signatures_cache[t] = res
- return res
- end
-end
-
-class MMParam
- var mmtype: MMType
- var name: Symbol writable
-
- init ( t : MMType, n : Symbol )
- do
- mmtype = t
- name = n
- end
-
- redef fun to_s do return "{name}: {mmtype}"
-end
-
-# Signature for local properties
-class MMSignature
- # The type of the reveiver
- readable var _recv: MMType
-
- # The parameter types
- readable var _params: Array[MMParam]
-
- # The return type
- readable var _return_type: nullable MMType
-
- # The closure parameters
- readable var _closures: Array[MMClosure] = new Array[MMClosure]
-
- # Return the closure named 'name'. Null if no such a closure exists.
- fun closure_named(name: Symbol): nullable MMClosure
- do
- for c in _closures do
- if c.name == name then return c
- end
- return null
- end
-
- # Number of parameters
- fun arity: Int
- do
- return _params.length
- end
-
- # Is self a valid subtype of an other signature
- fun <(s: MMSignature): Bool
- do
- if self == s then
- return true
- end
- assert _recv.mmmodule == s.recv.mmmodule
- var rt = _return_type
- var srt = s.return_type
- if arity != s.arity or (rt == null) != (srt == null) then return false
- if rt != null and not rt < srt.as(not null) then
- return false
- end
-
- for i in [0..arity[ do
- if not s[i] < self[i] then
- return false
- end
- end
-
- if closures.length != s.closures.length then return false
- for i in [0..closures.length[ do
- if not s.closures[i] < closures[i] then return false
- end
- return true
- end
-
- # The type of the i-th parameter
- fun [](i: Int): MMType
- do
- assert _params.length > i
- return _params[i].mmtype
- end
-
- redef fun to_s
- do
- var s = new Buffer
- if _params.length > 0 then
- var tmp: String
- var a = new Array[String].with_capacity(_params.length)
- for i in [0.._params.length[ do
- var p = _params[i]
- a.add(p.to_s)
- end
- s.append("({a.join(", ")})")
- end
- var rt = _return_type
- if rt != null then s.append(": {rt}")
- return s.to_s
- end
-
- # Adapt the signature to a different receiver
- fun adaptation_to(r: MMType): MMSignature
- do
- if _recv == r then
- return self
- end
- var mod = r.mmmodule
- var p = new Array[MMParam]
- for i in _params do
- var new_type = i.mmtype.for_module(mod).adapt_to(r)
- var new_param
- if new_type == i.mmtype then
- new_param = i
- else
- new_param = new MMParam( new_type, i.name )
- end
-
- p.add( new_param )
- end
- var rv = _return_type
- if rv != null then
- rv = rv.for_module(mod).adapt_to(r)
- end
- var res = new MMSignature(p,rv,r)
- for clos in _closures do
- res.closures.add(clos.adaptation_to(r))
- end
- return res
- end
-
- var _not_for_self_cache: nullable MMSignature = null
-
- # Return a type approximation if the reveiver is not self
- # Useful for virtual types
- fun not_for_self: MMSignature
- do
- if _not_for_self_cache != null then return _not_for_self_cache.as(not null)
-
- var need_for_self = false
- var p = new Array[MMParam]
- for i in _params do
- var new_type = i.mmtype.not_for_self
- var new_param
- if i.mmtype == new_type then
- new_param = i
- else
- need_for_self = true
- new_param = new MMParam( new_type, i.name )
- end
-
- p.add( new_param )
- end
-
- var rv = _return_type
- if rv != null then
- rv = rv.not_for_self
- if rv != _return_type then need_for_self = true
- end
-
- var clos = new Array[MMClosure]
- for c in _closures do
- var c2 = c.not_for_self
- if c2 != c then need_for_self = true
- clos.add(c2)
- end
-
- var res: MMSignature
- if need_for_self then
- res = new MMSignature(p, rv, _recv)
- res.closures.add_all(clos)
- else
- res = self
- end
-
- _not_for_self_cache = res
- return res
- end
-
- init(params: Array[MMParam], return_type: nullable MMType, r: MMType)
- do
- _params = params
- _return_type = return_type
- _recv = r
- end
-end
-
-
-redef class MMExplicitImport
- var signature : MMSignature
-
- redef init( local_class : MMLocalClass, meth : MMMethod )
- do
- super
- signature = meth.signature.adaptation_to( local_class.get_type )
- end
-end
-
-# A closure in a signature
-class MMClosure
- # The name of the closure (without the !)
- readable var _name: Symbol
-
- # The signature of the closure
- readable var _signature: MMSignature
-
- # Is the closure a brek one
- # aka is defined with the break keyword thus does not return
- readable var _is_break: Bool
-
- # Is the closure optional?
- # ie is there a default definition
- readable var _is_optional: Bool
-
- # Adapt the signature to a different receiver
- fun adaptation_to(r: MMType): MMClosure
- do
- return new MMClosure(_name, _signature.adaptation_to(r), _is_break, _is_optional)
- end
-
- init(name: Symbol, s: MMSignature, is_break: Bool, is_optional: Bool)
- do
- _name = name
- _signature = s
- _is_break = is_break
- _is_optional = is_optional
- end
-
- fun not_for_self: MMClosure
- do
- var sig = _signature.not_for_self
- if sig != _signature then
- return new MMClosure(_name, sig, _is_break, _is_optional)
- else
- return self
- end
- end
-
- fun <(c: MMClosure): Bool
- do
- if c.is_optional and not is_optional then return false
- if not c.is_break and is_break then return false
- return c.signature < signature
- end
-end
-
-# Inheritance relation between two types
-abstract class MMAncestor
- # The inherited type
- writable var _stype: nullable MMType = null
-
- # The inherited type
- fun stype: MMType do return _stype.as(not null)
-
- # The inheriter (heir) type
- writable var _inheriter: nullable MMType = null
-
- # The inheriter (heir) type
- fun inheriter: MMType do return _inheriter.as(not null)
-
- fun is_reffinement: Bool do
- return stype.mmmodule != stype.mmmodule
- end
-
- fun is_specialisation: Bool do
- return stype.local_class.global != inheriter.local_class.global
- end
-
- # The inherited class
- fun local_class: MMLocalClass is abstract
-
- redef fun to_s
- do
- if _stype == null then
- return local_class.to_s
- else
- return stype.to_s
- end
- end
-end
-
-# A static type
-# Note that static type is related to a specific module
-abstract class MMType
- # The module where self makes sence
- fun mmmodule: MMModule is abstract
-
- # The local class that self direclty or indirectly refers to
- fun local_class: MMLocalClass is abstract
-
- # Is the type a valid one
- # For instance, circular dependency on formal types is invalid
- fun is_valid: Bool do return true
-
- # Is self a valid subtype of t
- fun <(t : MMType): Bool is abstract
-
- # Is self a valid supertype of t
- # This method must be only called within definition of < if
- # a double dispatch is needed
- fun is_supertype(t: MMType): Bool is abstract
-
- # Adapt self to another module
- fun for_module(mod: MMModule): MMType is abstract
-
- # Get the type adapted to another receiver type
- # Useful for formal types
- fun adapt_to(recv: MMType): MMType is abstract
-
- # Adapt self to another local class context
- # Useful for genericity
- # 'c' Must be a super-class of self
- # Example:
- # class A[E]
- # class B[F] super A[F]
- # class C[G] super B[String]
- # class D super C[Float]
- # 'A[Int]'.upcast_for('A') -> 'A[Int]'
- # 'A[Int]'.upcast_for('B') -> abort
- # 'B[Int]'.upcast_for('B') -> 'B[Int]'
- # 'B[Int]'.upcast_for('A') -> 'A[Int]'
- # 'B[Int]'.upcast_for('C') -> abort
- # 'C[Int]'.upcast_for('C') -> 'C[Int]'
- # 'C[Int]'.upcast_for('B') -> 'B[String]'
- # 'C[Int]'.upcast_for('A') -> 'A[String]'
- # 'D'.upcast_for('D') -> 'D'
- # 'D'.upcast_for('C') -> 'C[Float]'
- # 'D'.upcast_for('B') -> 'C[String]'
- # 'D'.upcast_for('A') -> 'A[String]'
- fun upcast_for(c: MMLocalClass): MMType is abstract
-
- # Return a type approximation if the reveiver is not self
- # Useful for virtual types
- fun not_for_self: MMType do return self
-
- # The nullable version of self (if needed)
- var _as_nullable_cache: nullable MMType = null
-
- # IS the type can accept null?
- fun is_nullable: Bool do return false
-
- # Return the nullable version of the type
- # Noop if already nullable
- fun as_nullable: MMType do
- var cache = _as_nullable_cache
- if cache != null then return cache
- var res = new MMNullableType(self)
- _as_nullable_cache = res
- return res
- end
-
- # Return the not null version of the type
- # Noop if already not null
- fun as_notnull: MMType do return self
-end
-
-class MMNullableType
- super MMType
- readable var _base_type: MMType
- redef fun is_valid do return _base_type.is_valid
- redef fun is_nullable: Bool do return true
- redef fun as_notnull do return _base_type
- redef fun as_nullable do return self
- init(t: MMType) do _base_type = t
-
- redef fun mmmodule do return _base_type.mmmodule
-
- redef fun local_class do return _base_type.local_class
-
- redef fun <(t)
- do
- return t isa MMNullableType and _base_type < t.as_notnull
- end
-
- redef fun to_s
- do
- return "nullable {_base_type}"
- end
-
- redef fun is_supertype(t)
- do
- return _base_type.is_supertype(t)
- end
-
- redef fun for_module(mod)
- do
- return _base_type.for_module(mod).as_nullable
- end
-
- redef fun adapt_to(recv)
- do
- return _base_type.adapt_to(recv).as_nullable
- end
-
- redef fun upcast_for(c)
- do
- return _base_type.upcast_for(c)
- end
-
- redef fun not_for_self
- do
- return _base_type.not_for_self.as_nullable
- end
-end
-
-abstract class MMTypeClass
- super MMType
- redef readable var _local_class: MMLocalClass
- redef fun mmmodule do return _local_class.mmmodule end
- redef fun <(t) do return t.is_supertype(self)
-
- redef fun to_s
- do
- return _local_class.to_s
- end
-
- redef fun upcast_for(c)
- do
- var t: MMType = self
- if _local_class != c then
- t = _local_class.ancestor(c)
- end
- return t
- end
-
- init(c : MMLocalClass)
- do
- _local_class = c
- end
-end
-
-class MMTypeSimpleClass
- super MMTypeClass
- redef fun is_supertype(t)
- do
- return t.local_class.cshe <= _local_class
- end
-
- redef fun for_module(mod)
- do
- var t: MMType = self
- if mmmodule != mod then
- t = _local_class.for_module(mod).get_type
- end
- return t
- end
-
- redef fun adapt_to(recv) do return self
-
- init(c: MMLocalClass)
- do
- super(c)
- end
-end
-
-# The type of null
-class MMTypeNone
- super MMType
- redef readable var _mmmodule: MMModule
- redef fun is_nullable: Bool do return true
- redef fun <(t) do return t isa MMTypeNone or t isa MMNullableType
- redef fun to_s do return "null"
- redef fun is_supertype(t) do return false
- redef fun local_class do abort
- redef fun upcast_for(c) do abort
- redef fun as_nullable do return self
- redef fun as_notnull do abort
-
- private init(m: MMModule) do _mmmodule = m
-end
-
-redef class MMModule
- # The type of null
- readable var _type_none: MMTypeNone = new MMTypeNone(self)
-
- # The type of bool
- fun type_bool: MMType
- do
- return class_by_name(once ("Bool".to_symbol)).get_type
- end
-end
-
-# Explicitly imported cast
-class MMImportedCast
- readable var _from : MMType
- readable var _to : MMType
-
- fun is_about_nullable_only : Bool
- do
- return ( _from.is_nullable and _to.as_nullable == _from ) or
- ( _to.is_nullable and _from.as_nullable == _to )
- end
-
- fun is_not_null_to_nullable : Bool
- do
- return not _from.is_nullable and _to.is_nullable
- end
-
- fun is_nullable_to_not_null : Bool
- do
- return _from.is_nullable and not _to.is_nullable
- end
-
- redef fun == ( o )
- do
- return o isa MMImportedCast and
- o.from == from and o.to == to
- end
-end
-
-# Method local properties
-redef class MMMethod
- # casts explicitely imported to be available from native implementation
- fun explicit_casts : Set[ MMImportedCast ] is abstract
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2004-2008 Jean Privat <jean@pryen.org>
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Formal types
-module type_formal
-
-import inheritance
-
-redef class MMType
- # The type a indirect type refers to
- fun direct_type: MMType do return self
-
- fun has_formal: Bool do return false
-end
-
-# Formal types are named indirect types
-abstract class MMTypeFormal
- super MMType
- redef fun is_valid do return _bound != null and _bound.is_valid
-
- # The name of the type
- readable var _name: Symbol
-
- # The type refered
- fun bound: MMType do return _bound.as(not null)
- var _bound: nullable MMType
-
- redef fun <(t) do return t == self or t.is_supertype(bound)
- redef fun is_supertype(t) do return _bound.is_supertype(t)
- redef fun is_nullable do return _bound.is_nullable
- redef fun direct_type do return _bound.direct_type
- redef fun local_class do return _bound.local_class
- redef fun has_formal do return true
-
- redef fun to_s do return _name.to_s
-
- protected init(name: Symbol, bound: nullable MMType)
- do
- _name = name
- _bound = bound
- end
-end
-
-redef class MMNullableType
- redef fun has_formal do return self.base_type.has_formal
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2004-2008 Jean Privat <jean@pryen.org>
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Handle multiple number of parameters in signatures
-module vararg
-
-import genericity
-
-redef class MMSignature
- # Position of the vararg parameter. -1 in no vararg parameter
- readable writable var _vararg_rank: Int
-
- # Is there a vararg parameter in the signature?
- fun has_vararg: Bool
- do
- return _vararg_rank >= 0
- end
-
- redef fun adaptation_to(r)
- do
- var s = super(r)
- s.vararg_rank = _vararg_rank
- return s
- end
-
- redef fun not_for_self
- do
- var s = super
- s.vararg_rank = _vararg_rank
- return s
- end
-
- redef init(params, return_type, r)
- do
- super
- _vararg_rank = -1
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Virtual type (property of type type)
-module virtualtype
-
-import type_formal
-
-redef class MMGlobalProperty
- # Is self a virtual type
- fun is_virtual_type: Bool do return intro isa MMTypeProperty
-end
-
-# Virtual type properties
-abstract class MMTypeProperty
- super MMLocalProperty
- # The virtual static type associated
- fun stype_for(recv: MMType): nullable MMVirtualType
- do
- var prop = recv.local_class[global]
- assert prop isa MMTypeProperty
- return prop.real_stype_for(recv)
- end
-
- # Cached results of stype
- var _stypes_cache: HashMap[MMType, MMVirtualType] = new HashMap[MMType, MMVirtualType]
-
- private fun real_stype_for(recv: MMType): nullable MMVirtualType
- do
- # If the signature is not build: Circular definition
- if signature == null then return null
-
- if _stypes_cache.has_key(recv) then return _stypes_cache[recv]
-
- var res = new MMVirtualType(self, recv)
- _stypes_cache[recv] = res
-
- return res
- end
-end
-
-class MMVirtualType
- super MMTypeFormal
- # The property associed
- readable var _property: MMTypeProperty
-
- # The receiver type
- readable var _recv: MMType
-
- protected init(p: MMTypeProperty, recv: MMType)
- do
- super(p.name, p.signature_for(recv).return_type)
- _property = p
- _recv = recv
- end
-
- redef fun mmmodule do return _recv.mmmodule
-
- redef fun for_module(mod)
- do
- if mod == mmmodule then return self
- return adapt_to(recv.for_module(mod))
- end
-
- redef fun not_for_self
- do
- return bound.not_for_self
- end
-
- redef fun adapt_to(recv)
- do
- return property.stype_for(recv).as(not null)
- end
-end
-
-redef class MMLocalClass
- fun virtual_type(s: Symbol): MMGlobalProperty
- do
- var prop = get_property_by_name(s)
- if prop.is_virtual_type then
- return prop
- end
- abort
- end
-
- # Select a virtual type property by its name
- fun select_virtual_type(name: Symbol): MMTypeProperty
- do
- var gp = virtual_type(name)
- var res = self[gp]
- assert res isa MMTypeProperty
- return res
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-# Copyright 2008 Jean Privat <jean@pryen.org>
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This module is used to load a metamodel
-module mmloader
-
-import metamodel
-import opts
-import toolcontext
-
-redef class ToolContext
- super MMContext
-
- # Paths where to locate modules files
- readable var _paths: Array[String] = new Array[String]
-
- # List of module loaders
- var _loaders: Array[ModuleLoader] = new Array[ModuleLoader]
-
- # Option --path
- readable var _opt_path: OptionArray = new OptionArray("Set include path for loaders (may be used more than once)", "-I", "--path")
-
- # Option --only-metamodel
- readable var _opt_only_metamodel: OptionBool = new OptionBool("Stop after meta-model processing", "--only-metamodel")
-
- # Option --only-parse
- readable var _opt_only_parse: OptionBool = new OptionBool("Only proceed to parse step of loaders", "--only-parse")
-
- redef init
- do
- super
- option_context.add_option(opt_path, opt_only_parse, opt_only_metamodel)
- end
-
- # Parse and process the options given on the command line
- redef fun process_options
- do
- super
-
- # Setup the paths value
- paths.append(opt_path.value)
-
- var path_env = "NIT_PATH".environ
- if not path_env.is_empty then
- paths.append(path_env.split_with(':'))
- end
-
- path_env = "NIT_DIR".environ
- if not path_env.is_empty then
- var libname = "{path_env}/lib"
- if libname.file_exists then paths.add(libname)
- end
-
- var libname = "{sys.program_name.dirname}/../lib"
- if libname.file_exists then paths.add(libname.simplify_path)
- end
-
- # Load and process a module in a directory (or a parent directory).
- # If the module is already loaded, just return it without further processing.
- # If no module is found, just return null without complaining.
- private fun try_to_load(module_name: Symbol, dir: MMDirectory): nullable MMModule
- do
- # Look in the module directory
- for m in dir.modules.values do
- if m.name == module_name then return m
- end
-
- # print "try to load {module_name} in {dir.name} {_loaders.length}"
-
- for l in _loaders do
- var dir2 = l.try_to_load_dir(module_name, dir)
- if dir2 != null then
- var m = try_to_load(module_name, dir2)
- if m != null then
- dir2.owner = m
- dir.add_module(m)
- return m
- end
- end
-
- if l.can_handle(module_name, dir) then
- var full_name = dir.full_name_for(module_name)
- if _processing_modules.has(full_name) then
- # FIXME: Generate better error
- fatal_error(null, "Error: Dependency loop for module {full_name}")
- end
- _processing_modules.add(full_name)
- var m = l.load_and_process_module(self, module_name, dir)
- _processing_modules.remove(full_name)
- #if m != null then print "loaded {m.name} in {m.directory.name} -> {m.full_name} ({m.full_name.object_id.to_hex})"
- dir.add_module(m)
- return m
- end
- end
- return null
- end
-
- # List of module currently processed.
- # Used to prevent dependence loops.
- var _processing_modules: HashSet[Symbol] = new HashSet[Symbol]
-
- # Locate, load and analysis a module (and its supermodules) from its file name.
- # If the module is already loaded, just return it without further processing.
- # Beware, the files are automatically considered root of their directory.
- fun get_module_from_filename(filename: String): MMModule
- do
- var path = filename.dirname
- var module_name = filename.basename(".nit").to_symbol
-
- var dir = directory_for(path)
-
- if module_name.to_s == filename then
- # It's just a modulename
- # look for it in the path directory "."
- var m = try_to_load(module_name, dir)
- if m != null then return m
-
- # Else look for it in the path
- return get_module(module_name, null)
- end
-
- if not filename.file_exists then
- fatal_error(null, "Error: File {filename} not found.")
- abort
- end
-
- # Try to load the module where mentionned
- var m = try_to_load(module_name, dir)
- if m != null then return m
-
- fatal_error(null, "Error: {filename} is not a NIT source module.")
- abort
- end
-
- # Locate, load and analysis a module (and its supermodules).
- # If the module is already loaded, just return it without further processing.
- fun get_module(module_name: Symbol, from: nullable MMModule): MMModule
- do
- if from != null then
- var dir: nullable MMDirectory = from.directory
- while dir != null do
- var m = try_to_load(module_name, dir)
- if m != null then return m
- dir = dir.parent
- end
- end
-
- for p in paths do
- var m = try_to_load(module_name, directory_for(p))
- if m != null then return m
- end
- # FIXME: Generate better error
- fatal_error(null, "Error: No ressource found for module {module_name}.")
- abort
- end
-
- # Return the module directory associated with a given path
- private fun directory_for(path: String): MMDirectory
- do
- if _path_dirs.has_key(path) then return _path_dirs[path]
- var dir = new MMDirectory(path.to_symbol, path, null)
- _path_dirs[path] = dir
- return dir
- end
-
- # Association bwtween plain path and module directories
- var _path_dirs: Map[String, MMDirectory] = new HashMap[String, MMDirectory]
-
- # Register a new module loader
- fun register_loader(ml: ModuleLoader) do _loaders.add(ml)
-end
-
-# A load handler know how to load a specific module type
-interface ModuleLoader
- # Type of module loaded by the loader
- type MODULE: MMModule
-
- # Extension that the loadhandler accepts
- fun file_type: String is abstract
-
- # Try to load a new module directory
- fun try_to_load_dir(dirname: Symbol, parent_dir: MMDirectory): nullable MMDirectory
- do
- var fname = "{parent_dir.path}/{dirname}"
- if not fname.file_exists then return null
-
- var dir = new MMDirectory(parent_dir.full_name_for(dirname), fname, parent_dir)
- return dir
- end
-
- # Can the loadhandler load a given module?
- # Return the file found
- fun can_handle(module_name: Symbol, dir: MMDirectory): Bool
- do
- var fname = "{dir.path}/{module_name}.{file_type}"
- if fname.file_exists then return true
- return false
- end
-
- # Load the module and process it
- # filename is the result of can_handle
- fun load_and_process_module(context: ToolContext, module_name: Symbol, dir: MMDirectory): MODULE
- do
- var filename = "{dir.path}/{module_name}.{file_type}".simplify_path
- var m = load_module(context, module_name, dir, filename)
- if not context.opt_only_parse.value then process_metamodel(context, m)
- return m
- end
-
- # Load an parse the module
- private fun load_module(context: ToolContext, module_name: Symbol, dir: MMDirectory, filename: String): MODULE
- do
- var file: IFStream
- if filename == "-" then
- file = stdin
- else
- file = new IFStream.open(filename.to_s)
- end
-
- if file.eof then
- context.fatal_error(null, "Error: Problem in opening file {filename}")
- end
- var m = parse_file(context, file, filename, module_name, dir)
- if file != stdin then file.close
- return m
- end
-
- # Parse the file to load a module
- protected fun parse_file(context: ToolContext, file: IFStream, filename: String, module_name: Symbol, dir: MMDirectory): MODULE is abstract
-
- # Process a parsed module
- protected fun process_metamodel(context: ToolContext, mod: MODULE) is abstract
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Nit compiler main module
-module nitc
-
-import abstracttool
-import analysis
-import global
-import program
-import compiling
-import syntax
-import native_interface
-import separate_options
-import ffi
-
-# The main class of the nitcompiler program
-class NitCompiler
- super AbstractCompiler
- readable var _opt_output: OptionString = new OptionString("Output file", "-o", "--output")
- readable var _opt_boost: OptionBool = new OptionBool("Optimize compilation", "-O", "--boost")
- readable var _opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
- readable var _opt_cc_no_link: OptionBool = new OptionBool("Do not invoke C linker", "--cc-no-link")
- readable var _opt_clibdir: OptionString = new OptionString("NIT C library directory", "--clibdir")
- readable var _opt_bindir: OptionString = new OptionString("NIT tools directory", "--bindir")
- readable var _opt_compdir: OptionString = new OptionString("Intermediate compilation directory", "--compdir")
- readable var _opt_extension_prefix: OptionString = new OptionString("Append prefix to file extension", "-p", "--extension-prefix")
- readable var _opt_output_format: OptionEnum = new OptionEnum(["none", "C", "icode"], "The type of code we want to be generated", 1, "--output-format")
-
- init
- do
- super("nitc")
- option_context.add_option(opt_output, opt_boost, opt_no_cc, opt_cc_no_link, opt_cc_libs, opt_cc_lib_paths, opt_cc_include_paths, opt_clibdir, opt_bindir, opt_compdir, opt_extension_prefix, opt_output_format)
- end
-
- redef fun process_options
- do
- super
- output_file = opt_output.value
- boost = opt_boost.value
- no_cc = opt_no_cc.value
- cc_link = not opt_cc_no_link.value
- cc_lib_paths.add_all( opt_cc_lib_paths.value )
- cc_libs.add_all( opt_cc_libs.value )
- cc_include_paths.add_all( opt_cc_include_paths.value )
- var ext = opt_extension_prefix.value
- if ext != null then ext_prefix = ext else ext_prefix = ""
- compdir = opt_compdir.value
- if compdir == null then
- var dir = "NIT_COMPDIR".environ
- if not dir.is_empty then
- compdir = dir
- end
- if compdir == null then
- compdir = ".nit_compile"
- end
- end
- compdir += ext_prefix
- compdir = compdir.simplify_path
-
- clibdir = opt_clibdir.value
- if clibdir == null then
- var dir = "NIT_DIR".environ
- if dir.is_empty then
- dir = "{sys.program_name.dirname}/../clib"
- if dir.file_exists then clibdir = dir
- else
- dir = "{dir}/clib"
- if dir.file_exists then clibdir = dir
- end
- if clibdir == null then
- fatal_error(null, "Error: Cannot locate NIT C library directory. Uses --clibdir or envvar NIT_DIR.")
- end
- end
- clibdir = clibdir.simplify_path
-
- bindir = opt_bindir.value
- if bindir == null then
- var dir = "NIT_DIR".environ
- if dir.is_empty then
- dir = "{sys.program_name.dirname}/../bin"
- if dir.file_exists then bindir = dir
- else
- dir = "{dir}/bin"
- if dir.file_exists then bindir = dir
- end
- if bindir == null then
- fatal_error(null, "Error: Cannot locate NIT tools directory. Uses --bindir or envvar NIT_DIR.")
- end
- end
- bindir = bindir.simplify_path
- end
-
- redef fun perform_work(mods)
- do
- for mod in mods do
- var p = new Program(mod, self)
- p.output_format = opt_output_format.value_name
- p.compute_main_method
- p.generate_allocation_iroutines
- if global then p.do_global_analysis
- p.do_table_computation
- p.compile_prog
- end
- end
-end
-
-redef class ToolContext
- redef init do super # Avoid local property conflict
-end
-
-
-var c = new NitCompiler
-c.exec_cmd_line
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2011 Alexis Laferrière <alexis.laf@xymus.net>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Stub generator for modules using the native interface
-module nits
-
-import parser
-import syntax
-import metamodel
-import analysis
-import abstracttool
-import native_interface
-
-class NitStubGenerator
- super AbstractCompiler
-
- var opt_in_place: OptionBool = new OptionBool("Generate stub files as .nit.[ch] instead of .stub.nit.[ch]", "-i", "--in-place")
-
- init
- do
- super( "nits" )
-
- option_context.add_option(opt_in_place)
- end
-
- redef fun perform_work(mods)
- do
- for mod in mods do
- assert mod isa MMSrcModule
-
- if mod.is_extern_hybrid then
- var visitor = new StubVisitor( opt_in_place.value )
-
- # actually compile stub
- mod.generate_stub( visitor )
-
- # write to file
- var mod_base_path = "{mod.directory.path}/{mod.name}"
- visitor.write_to_files( mod_base_path )
- end
- end
- end
-end
-
-
-redef class MMModule
- fun generate_stub( v: StubVisitor )
- do
- ### header comments
- var module_info = "/*\n\tExtern implementation of Nit module {name}\n"
- v.header_head.add( module_info )
- v.body_head.add( module_info )
-
- # instructions to rename
- if not v.in_place then
- v.header_head.add( "\tFile initially created by nits to implement extern methods body\n" )
- v.body_head.add( "\tFile initially created by nits to customize type of extern classes\n" )
- end
-
- v.header_head.add( "*/\n\n" )
- v.body_head.add( "*/\n\n" )
- ### end of header comments
-
- # header file guard
- var guard = "{name.to_s.to_upper}_NIT_H"
- v.header_head.add( "#ifndef {guard}\n#define {guard}\n\n" )
-
- ### imports
- # import standard .nit.h file
- v.body_head.add( "#include \"{name}.nit.h\"\n" )
-
- # import autogenerated frontier header file
- v.header_head.add( "#include <{name}._nitni.h>\n\n" )
- ### end of import
-
- for local_class in local_classes do
- ### extern methods
- for prop in local_class.local_local_properties do
- # if defined of redefined in this module
- # and is extern
- if prop.mmmodule == self and
- prop isa MMSrcMethod and prop.is_extern then
- prop.generate_stub( v )
- end
- end
-
- ### extern classes
- # if class is extern and defined here first
- if local_class.global.intro == local_class and
- local_class.global.is_extern then
- local_class.generate_stub( v )
- end
- end
-
- # header file guard close
- v.header_decl.add( "\n#endif\n" )
- end
-end
-
-redef class MMSrcMethod
- fun generate_stub( v: StubVisitor )
- do
- ### documentation to guide the user
- var helper_documentation = new Writer
-
- # title comment
- helper_documentation.add( "C implementation of {full_name}" )
-
- # TODO add nitdoc comment
-
- # explicit extern calls signatures in comment
- var imported_signatures = new Array[String]
- for extern_call in explicit_imports do
- var meth = extern_call.method
- imported_signatures.add( "\t{meth.friendly_csignature(extern_call.local_class)} for {meth.full_name}" )
- end
-
- # explicit extern casts
- for cast in explicit_casts do
- if not ( cast.is_about_nullable_only and
- cast.is_not_null_to_nullable ) then
- imported_signatures.add( "\t{cast.is_a_friendly_csignature} to check if a {cast.from} is a {cast.to}" )
- end
-
- imported_signatures.add( "\t{cast.as_friendly_csignature} to cast from {cast.from} to {cast.to}" )
- end
-
- # explicit extern super
- if need_super then
- imported_signatures.add( "\t{friendly_super_csignature} to call super" )
- end
-
- if imported_signatures.length > 0 then
- helper_documentation.add("\n\nImported methods signatures:\n" )
- helper_documentation.add_all( imported_signatures, "\n")
- end
-
- v.body_impl.add( "\n/*\n" )
- v.body_impl.append( helper_documentation )
- v.body_impl.add( "\n*/\n" )
- ### end of documentation
-
- ### extern method implementation
- v.header_decl.add( "{impl_csignature};\n" )
-
- v.body_impl.add( "{impl_csignature}\n\{\n\}\n" )
- ### end of implementation
- end
-end
-
-redef class MMLocalClass
- fun generate_stub( v: StubVisitor )
- do
- v.header_head.add( "#define {get_type.friendly_extern_name} void*\n" )
- end
-end
-
-class StubVisitor
- # comments, imports (auto and custom from inline), types
- var header_head : Writer = new Writer
-
- # implementation declaration for extern methods
- var header_decl : Writer = new Writer
-
- # comments, imports and custom code from inline
- var body_head : Writer = new Writer
-
- # implementation body of extern methods
- var body_impl : Writer = new Writer
-
- # generates final .nit.[ch] instead of stub files .sub.nit.[ch]
- var in_place : Bool
-
- init ( in_place : Bool ) do self.in_place = in_place
-
- # write stubs to disk
- fun write_to_files( base_path : String )
- do
- var mid_ext = if in_place then ".nit" else ".stub.nit"
-
- var stream = new OFStream.open( "{base_path}{mid_ext}.h" )
- header_head.write_to_stream( stream )
- header_decl.write_to_stream( stream )
- stream.close
-
- stream = new OFStream.open( "{base_path}{mid_ext}.c" )
- body_head.write_to_stream( stream )
- body_impl.write_to_stream( stream )
- stream.close
- end
-end
-
-var nits = new NitStubGenerator
-nits.exec_cmd_line
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Common things for NIT compilation and C generation
-module primitive_info
-
-#FIXME Split this module into 2: one in metamodel and one in compiling
-
-import metamodel
-
-redef class MMLocalClass
- # extern type of extern classes
- fun extern_c_type : String is abstract
-
- # Cached primitive_info result
- var _primitive_info_cache: nullable PrimitiveInfo = null
-
- # If primitive_info result cached?
- var _primitive_info_b: Bool = false
-
- # Return the primitive information of the class.
- # Return null if the class is not primitive
- fun primitive_info: nullable PrimitiveInfo
- do
- if _primitive_info_b == true then return _primitive_info_cache
-
- var ctypes = once primitive_ctypes
- if ctypes.has_key(name) then
- _primitive_info_cache = ctypes[name]
- _primitive_info_b = true
- return _primitive_info_cache
- end
- if global.is_extern then
- var pi = new PrimitiveInfo( name, false, extern_c_type )
- _primitive_info_cache = pi
- _primitive_info_b = true
- return _primitive_info_cache
- end
- var i = ctypes.iterator
- while i.is_ok do
- var n = i.key
- if mmmodule.has_global_class_named(n) then
- var c = mmmodule.class_by_name(n)
- if cshe < c then
- _primitive_info_cache = i.item
- _primitive_info_b = true
- return _primitive_info_cache
- end
- end
- i.next
- end
- _primitive_info_b = true
- return null
- end
-
- # Static information of primitive types
- private fun primitive_ctypes: HashMap[Symbol, PrimitiveInfo]
- do
- var res = new HashMap[Symbol, PrimitiveInfo]
- var pnames = ["Int", "Char", "Bool", "Float", "NativeString", "Pointer"]
- var tagged = [true, true, true, false, false, false]
- var cnames = ["bigint", "char", "int", "float", "char *", "void *"]
- for i in [0..pnames.length[ do
- var n = pnames[i].to_symbol
- var pi = new PrimitiveInfo(n, tagged[i], cnames[i])
- res[n] = pi
- end
- return res
- end
-end
-
-# Information about a primitive class
-class PrimitiveInfo
- # The name of the class
- readable var _name: Symbol
-
- # Is the class tagged (aka not boxed)
- readable var _tagged: Bool
-
- # The corresponding c type for the primitive value
- readable var _cname: String
-
- private init(n: Symbol, t: Bool, c: String)
- do
- _name = n
- _tagged = t
- _cname = c
- end
-end
-
-redef class MMType
- # The corresponding c type
- fun cname: String
- do
- var pi = local_class.primitive_info
- if pi == null then
- return "val_t"
- else
- return pi.cname
- end
- end
-
- # Is the type tagged?
- fun is_tagged: Bool
- do
- if is_nullable then return false
- var pi = local_class.primitive_info
- return pi != null and pi.tagged
- end
-
- # The default c value for uninitialized types.
- # Return "null" for non primitive types and something more specific for primitive types
- fun default_cvalue: String
- do
- var pi = local_class.primitive_info
- if pi != null and pi.tagged then
- return "TAG_{local_class.name}(({pi.cname})0)"
- else
- return "NIT_NULL"
- end
- end
-
- # Box (or tag) a primitive value
- # Is identity if not primitive
- fun boxtype(s: String): String
- do
- var pi = local_class.primitive_info
- if pi == null or is_nullable then
- return s
- else if pi.tagged then
- return "TAG_{local_class.name}({s})"
- else
- return "BOX_{local_class.name}({s})"
- end
- end
-
- # Unbox (or untag) a primitive value
- # Is identity if not primitive
- fun unboxtype(s: String): String
- do
- var pi = local_class.primitive_info
- if pi == null or is_nullable then
- return s
- else if pi.tagged then
- return "UNTAG_{local_class.name}({s})"
- else
- return "UNBOX_{local_class.name}({s})"
- end
- end
-end
-
-redef class MMMethod
- fun default_extern_name : String
- do
- return "{friendly_extern_name(local_class)}___impl"
- end
-
- # Friendly name for this method. Is mainly the class name followed by the
- # function name. It is prefixed with "new" for a constructor.
- fun friendly_extern_name( local_class : MMLocalClass ) : String
- do
- if not global.is_init then
- var native_fun_name : String
- var method_name = name.to_s
- if method_name == "+" then
- native_fun_name = "_plus" # add
- else if method_name == "-" then
- native_fun_name = "_minus" # sub
- else if method_name == "*" then
- native_fun_name = "_star" # multi
- else if method_name == "/" then
- native_fun_name = "_slash" # div
- else if method_name == "%" then
- native_fun_name = "_percent" # mod
- else if method_name == "[]" then
- native_fun_name = "_index" # brackets
- else if method_name == "[]=" then
- native_fun_name = "_index_assign" # brackets
- else if method_name == "==" then
- native_fun_name = "_equal" # eq
- else if method_name == "<" then
- native_fun_name = "_less" # lt
- else if method_name == ">" then
- native_fun_name = "_greater" # gt
- else if method_name == "<=" then
- native_fun_name = "_less_or_equal" # greater_or_equal
- else if method_name == ">=" then
- native_fun_name = "_ge" # smaller_or_equal
- else if method_name == "!=" then
- native_fun_name = "_not_equal" # bang
- else if method_name == ">>" then
- native_fun_name = "_right"
- else if method_name == "<<" then
- native_fun_name = "_left"
- else if method_name == "<=>" then
- native_fun_name = "_starship"
- else if method_name[ method_name.length-1 ] == '=' then
- native_fun_name = "{method_name.substring(0,method_name.length-1)}__assign"
- else
- native_fun_name = method_name
- end
-
- return "{local_class.name}_{native_fun_name}"
- else
- if name == once "init".to_symbol then
- return "new_{local_class.name}"
- else
- return "new_{local_class.name}_{name}"
- end
- end
- end
-end
-
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Common things to represent a Nit program/library
-module program
-
-import metamodel
-import icode
-private import primitive_info
-import mmloader
-
-redef class ToolContext
- readable writable var _global: Bool = false
- writable var _use_SFT_optimization: Bool = true
-
- # We can say that we are using SFT optimization *only* when we are
- # doing global compilation and we enabled the command line option
- fun use_SFT_optimization: Bool do return global and _use_SFT_optimization
-end
-
-# Instances of this class represent a program/library that will
-# be analyzed/compiled by nitc
-class Program
- # This is the ToolContext associated with this Program
- # It contains (amongts other things) the command line options
- readable var _tc: ToolContext
-
- # This module is the 'main' module, the one where we find the 'main' method
- readable var _main_module: MMModule
-
- # This method is the entry point of this program
- # There might be no entry point (if in fact we are compiling a library)
- readable var _main_method: nullable MMMethod = null
-
- # This is the class that contains the main method.
- # Would be null if there is no main method
- readable var _main_class: nullable MMLocalClass = null
-
- # This method will ensure that all the metamodel is computed before we
- # start using all the classes
- private fun finish_processing_classes do
- var classes = new Array[MMLocalClass]
- for c in main_module.local_classes do
- c.compute_super_classes
- classes.add(c)
- end
-
- for c in classes do
- c.compute_ancestors
- end
- end
-
- fun compute_main_method do
- # Check for the 'Sys' class
- var sysname = once "Sys".to_symbol
- if not main_module.has_global_class_named(sysname) then return
- var sys = main_module.class_by_name(sysname)
-
- # Check for 'Sys::main' method
- var entryname = once "main".to_symbol
- if not sys.has_global_property_by_name(entryname) then return
-
- _main_method = sys.select_method(entryname)
- _main_class = sys
- end
-
- # Generation of allocation function of this class
- fun generate_allocation_iroutines
- do
- for c in main_module.local_classes do
- if c.global.is_abstract or c.global.is_interface then continue
- var pi = c.primitive_info
- if pi == null then
- do
- # Generate INIT_ATTRIBUTES routine
- var iself = new IRegister(c.get_type)
- var iselfa = [iself]
- var iroutine = new IRoutine(iselfa, null)
- var icb = new ICodeBuilder(main_module, iroutine)
-
- for sc in c.che.linear_extension.reversed do
- for g in sc.global_properties do
- if g.local_class != sc then continue
- if not g.intro isa MMAttribute then continue
- var p = c[g]
- var t = p.signature.return_type
- if p isa MMAttribute and t != null then
- var ir = p.iroutine
- if ir == null then continue
- # FIXME: Not compatible with sep compilation
- var e = icb.inline_routine(ir, iselfa, null).as(not null)
- icb.stmt(new IAttrWrite(p, iself, e))
- end
- end
- end
-
- c.init_var_iroutine = iroutine
- end
- do
- # Compile CHECKNAME
- var iself = new IRegister(c.get_type)
- var iselfa = [iself]
- var iroutine = new IRoutine(iselfa, null)
- var icb = new ICodeBuilder(main_module, iroutine)
- for g in c.global_properties do
- if not g.intro isa MMAttribute then continue
- var p = c[g]
- var t = p.signature.return_type
- if p isa MMAttribute and t != null and not t.is_nullable then
- icb.add_attr_check(p, iself)
- end
- end
-
- c.checknew_iroutine = iroutine
- end
-
- for g in c.global_properties do
- # FIXME skip invisible constructors
- if not g.is_init_for(c) then continue
- var p = c[g]
- assert p isa MMMethod
-
- var iself = new IRegister(c.get_type)
- var iparams = new Array[IRegister]
- for i in [0..p.signature.arity[ do iparams.add(new IRegister(p.signature[i]))
- var iroutine = new IRoutine(iparams, iself)
- iroutine.location = p.iroutine.location
- var icb = new ICodeBuilder(main_module, iroutine)
-
- var inew = new IAllocateInstance(c.get_type)
- inew.result = iself
- icb.stmt(inew)
- var iargs = [iself]
- iargs.add_all(iparams)
-
- icb.stmt(new IInitAttributes(c.get_type, iself))
- icb.stmt(new IStaticCall(p, iargs))
- icb.stmt(new ICheckInstance(c.get_type, iself))
-
- c.new_instance_iroutine[p] = iroutine
- end
- end
- end
- end
-
- # This function will call the attached block for each IRoutines
- # in this program
- fun with_each_iroutines
- !action(i: IRoutine, m: MMModule)
- do
- for m in main_module.mhe.greaters_and_self do
- for c in m.local_classes do
- var iroutine: nullable IRoutine = null
-
- # Process methods and attributes initialization
- for p in c.local_local_properties do
- if p isa MMAttribute then
- iroutine = p.iroutine
- else if p isa MMMethod then
- iroutine = p.iroutine
- end
- if iroutine == null then continue
- action(iroutine, m)
- end
-
- # Process class-specific iroutines
- iroutine = c.init_var_iroutine
- if iroutine != null then
- action(iroutine, m)
- end
- iroutine = c.checknew_iroutine
- if iroutine != null then
- action(iroutine, m)
- end
- for i in c.new_instance_iroutine.values do
- action(i, m)
- end
- end
- end
- end
-
- # This function will call the attached block for each MMMethods
- # in this program
- fun with_each_methods
- !action(m: MMMethod)
- do
- for m in main_module.mhe.greaters_and_self do
- for c in m.local_classes do
- # Process methods and attributes initialization
- for p in c.local_local_properties do
- if p isa MMMethod then
- action(p)
- end
- end
- end
- end
- end
-
- # This function will call the attached block for each live local classes
- # in this program
- fun with_each_live_local_classes
- !action(m: MMLocalClass)
- do
- for c in main_module.local_classes do
- action(c)
- end
- end
-
- init(m: MMModule, toolcontext: ToolContext) do
- _main_module = m
- _tc = toolcontext
- finish_processing_classes
- end
-end
-
-redef class MMLocalClass
- # IRoutine for the initialization of the default attributes (called by IInitAttributes)
- readable writable var _init_var_iroutine: nullable IRoutine = null
- # IRoutine to validate the instance after initialization (called by ICheckInstance)
- readable writable var _checknew_iroutine: nullable IRoutine = null
- # IRoutines to call to create a new valid instance (memory allocated, object initialized and validated)
- # These iroutines will call: IAllocateInstance, IInitAttributes, some init function and ICheckInstance
- # These routines will be called by INew
- readable var _new_instance_iroutine: HashMap[MMMethod, IRoutine] = new HashMap[MMMethod, IRoutine]
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2012 <alexis.laf@xymus.net>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Manages foreign code blocks using the Nit FFI
-module extern_inline
-
-intrude import mmbuilder
-import syntax_base
-import primitive_info
-
-# Extern code segment
-class ExternCode
- var language : nullable String
- var code : String
- var location : nullable Location
-end
-
-redef class MMModule
- # extern code blocks in module
- var extern_code_blocks : Set[ExternCode] = new HashSet[ExternCode]
- redef var uses_ffi : Bool = false
-end
-
-redef class MMLocalClass
- # extern equivalent of class
- var extern_type : nullable ExternCode = null
-end
-
-redef class MMMethod
- # extern code bodu of extern method
- var extern_implementation : nullable ExternCode = null
-end
-
-redef class TExternCodeSegment
- # removes `{ and `} and return code of interest
- fun code : String do
- return text.substring( 2, text.length-4 )
- end
-end
-
-redef class AExternCodeBlock
- fun to_extern_code : ExternCode
- do
- var language
- if n_in_language == null then
- language = null
- else
- var text = n_in_language.n_string.text
- language = text.substring( 1, text.length-2 )
- end
- return new ExternCode( language, n_extern_code_segment.code, n_extern_code_segment.location )
- end
-end
-
-redef class AExternPropdef
- redef fun accept_property_verifier(v)
- do
- super
-
- # Extern method implementation
- var n_extern_code_block = self.n_extern_code_block
- if n_extern_code_block != null then
- if not method.is_extern then
- v.error( n_extern_code_block,
- "Cannot implement the non extern method {method.full_name} with extern code" )
- else
- method.extern_implementation = n_extern_code_block.to_extern_code
- method.mmmodule.uses_ffi = true
- end
- end
- end
-end
-
-redef class AStdClassdef
- redef fun accept_property_verifier(v)
- do
- super
-
- # Extern type of extern classes
- var extern_code_block = self.n_extern_code_block
- if extern_code_block != null then
- if not local_class.global.is_extern then
- v.error( extern_code_block,
- "Cannot define an extern equivalent in the non extern class {local_class.name}" )
- else
- local_class.extern_type = extern_code_block.to_extern_code
- local_class.mmmodule.uses_ffi = true
- end
- end
- end
-end
-
-redef class MMSrcModule
- # Syntax analysis and MM construction for the module
- # Require that supermodules are processed
- redef fun do_mmbuilder(tc: ToolContext)
- do
- super
-
- # extern code blocks
- if not node.n_extern_code_blocks.is_empty then uses_ffi = true
- for n_extern_code_block in node.n_extern_code_blocks do
- extern_code_blocks.add( n_extern_code_block.to_extern_code )
- end
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2012-2013 Alexis Laferrière <alexis.laf@xymus.net>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This module manages the C type of extern classes
-module extern_type_inheritance
-
-import primitive_info
-intrude import extern_inline
-
-redef class MMLocalClass
- redef fun extern_c_type : String
- do
- return extern_type.code
- end
-
- fun extern_type_origin : MMLocalClass
- do
- extern_type
- return extern_type_origin_cache.as(not null)
- end
-
- # where was the extern type explicitly declared
- private var extern_type_origin_cache : nullable MMLocalClass = null
-
- # extern type of an extern class
- private var extern_type_cache : nullable ExternCode = null
-
- redef fun extern_type
- do
- assert global.is_extern
-
- if extern_type_cache != null then return extern_type_cache
-
- if global.intro != self then
- if global.intro.extern_type_cache != null then
- extern_type_cache = global.intro.extern_type_cache
- extern_type_origin_cache = self
- return extern_type_cache
- end
- end
-
- if name == once "Pointer".to_symbol then
- extern_type_cache = new ExternCode( "C", "void*", null )
- extern_type_origin_cache = self
- return extern_type_cache
- end
-
- # find all extern types in direct parents
- var extern_types = new HashSet[MMLocalClass]
- var local_class = self # global.class_refinement_hierarchy .first
- for c in local_class.cshe.direct_greaters do
- if c.global.is_extern then
- extern_types.add( c.extern_type_origin )
- end
- end
- #end
-
- if extern_types.length > 1 then
- stderr.write("Error: Extern class {mmmodule}::{name} has ambiguous extern type, found in super classes: \n")
- for c in extern_types do stderr.write( "{c.extern_type.code} from {c}\n" )
- exit(1)
- else if extern_types.length == 1 then
- var source = extern_types.first
- extern_type_cache = source.extern_type
- extern_type_origin_cache = source
- else
- # Extern class has unknown extern type. This should never happen.
- abort
- end
-
- return extern_type_cache
- end
-
- redef fun extern_type=( v )
- do
- extern_type_cache = v
- extern_type_origin_cache = self
- end
-end
-
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008-2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Analysis control flow and variable visibility in property bodies, statements and expressions
-module flow
-
-import syntax_base
-
-#################################################################
-
-# All-in-one context for flow control.
-# It features:
-# * reachability
-# * set/unset variable
-# * adaptive type of variable
-# FlowContextes are imutables, new contexts are created:
-# * as an empty root context
-# * as the adaptation of a existing context (see methods sub_*)
-# * as the merge of existing contexts
-abstract class FlowContext
- # Display an error localised on `n' if the variable `v' is not set
- fun check_is_set(n: ANode, v: Variable)
- do
- if v.must_be_set and not is_set(v) then
- _visitor.error(n, "Error: variable '{v}' is possibly unset.")
- end
- end
-
- # The effective static type of a given variable
- # May be different from the declaration static type
- fun stype(v: Variable): nullable MMType
- do
- return v.stype
- end
-
- # Return a context where the variable is marked as set
- fun sub_setvariable(v: Variable): FlowContext
- do
- var ctx = new SubFlowContext.with_prev(self, node)
- ctx._set_variables.add(v)
- return ctx
- end
-
- # Return a context where unreash == true
- fun sub_unreash(node: ANode): FlowContext
- do
- var ctx = new SubFlowContext.with_prev(self, node)
- ctx._unreash = true
- return ctx
- end
-
- # Return a context where v is casted as t
- fun sub_with(node: ANode, v: Variable, t: MMType): FlowContext
- do
- return new CastFlowContext(self, node, v, t)
- end
-
- # Merge various alternative contexts (all must be reashable)
- # Note that self can belong to alternatives
- # Base is self
- fun merge(node: ANode, alternatives: Array[FlowContext]): FlowContext
- do
- for a in alternatives do assert not a.unreash
- if alternatives.length == 1 then return alternatives.first
- return new MergeFlowContext(self, node, alternatives)
- end
-
- # Merge only context that are reachable
- # Used for if/then/else merges
- # Base is self
- fun merge_reash(node: ANode, alt1, alt2: FlowContext): FlowContext
- do
- if alt1.unreash then
- if alt2.unreash then
- return self.sub_unreash(node)
- else
- var t = alt2
- alt2 = alt1
- alt1 = t
- end
- end
-
- if alt2.unreash or alt1 == alt2 then
- return alt1
- #if alt1 == self then
- # return self
- #else
- # return merge(node, [alt1])
- #end
- else
- return merge(node, [alt1, alt2])
- end
- end
-
- # The visitor of the context (used to display error)
- var _visitor: AbsSyntaxVisitor
-
- # The syntax node that introduced the context
- readable var _node: ANode
-
- init(visitor: AbsSyntaxVisitor, node: ANode)
- do
- _visitor = visitor
- _node = node
- end
-
- # Is a control flow break met? (return, break, continue)
- readable var _unreash: Bool = false
-
- # Is a control flow already broken?
- # Used to avoid repeating the same error message
- readable writable var _already_unreash: Bool = false
-
- # Set of variable that are set (assigned)
- readable var _set_variables: HashSet[Variable] = new HashSet[Variable]
-
- # Is a variable set?
- fun is_set(v: Variable): Bool
- do
- return _set_variables.has(v)
- end
-end
-
-# Root of a variable context hierarchy
-class RootFlowContext
- super FlowContext
- init(visitor: AbsSyntaxVisitor, node: ANode)
- do
- super(visitor, node)
- end
-end
-
-# Contexts that are an evolution of a single previous context
-class SubFlowContext
- super FlowContext
- readable var _prev: FlowContext
-
- redef fun is_set(v)
- do
- return _set_variables.has(v) or _prev.is_set(v)
- end
-
- redef fun stype(v)
- do
- return prev.stype(v)
- end
-
- init with_prev(p: FlowContext, node: ANode)
- do
- init(p._visitor, node)
- _prev = p
- end
-end
-
-# A variable context where a variable got a type adptation
-class CastFlowContext
- super SubFlowContext
- # The casted variable
- var _variable: Variable
-
- # The new static type of the variable
- var _stype: nullable MMType
-
- redef fun stype(v)
- do
- if v == _variable then
- return _stype
- else
- return prev.stype(v)
- end
- end
-
- init(p: FlowContext, node: ANode, v: Variable, s: nullable MMType)
- do
- with_prev(p, node)
- _variable = v
- _stype = s
- end
-end
-
-# Context that resulting from the combinaisons of other contexts.
-# Most of the merge computation are done lasily.
-class MergeFlowContext
- super FlowContext
- var _base: FlowContext
- var _alts: Array[FlowContext]
-
- # Updated static type of variables
- var _stypes: Map[Variable, nullable MMType] = new HashMap[Variable, nullable MMType]
-
- init(base: FlowContext, node: ANode, alts: Array[FlowContext])
- do
- super(base._visitor, node)
- _alts = alts
- _base = base
- end
-
- redef fun stype(v)
- do
- if _stypes.has_key(v) then
- return _stypes[v]
- else
- var s = merge_stype(v)
- _stypes[v] = s
- return s
- end
- end
-
- private fun merge_stype(v: Variable): nullable MMType
- do
- var candidate: nullable MMType = null
- var is_nullable = false
- var same_candidate: nullable MMType = _alts.first.stype(v)
- for ctx in _alts do
- var t = ctx.stype(v)
- if t == null then
- return null
- end
- if t != same_candidate then
- same_candidate = null
- end
- if t isa MMTypeNone then
- is_nullable = true
- continue
- end
- if t isa MMNullableType then
- is_nullable = true
- t = t.as_notnull
- end
- if candidate == null or candidate < t then
- candidate = t
- end
- end
- if same_candidate != null then
- return same_candidate
- end
- if is_nullable then
- if candidate == null then
- return _visitor.type_none
- else
- candidate = candidate.as_nullable
- end
- end
- if candidate == null then
- return _base.stype(v)
- else
- for ctx in _alts do
- var t = ctx.stype(v)
- if not t < candidate then
- return _base.stype(v)
- end
- end
- end
- return candidate
- end
-
- redef fun is_set(v)
- do
- if _set_variables.has(v) then
- return true
- else
- for ctx in _alts do
- if not ctx.is_set(v) then
- return false
- end
- end
- _set_variables.add(v)
- return true
- end
- end
-end
-
-
-redef class Variable
- # Is the variable must be set before being used ?
- fun must_be_set: Bool do return false
-end
-
-redef class VarVariable
- redef fun must_be_set do return true
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2009 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Things needed by typing.nit to generate intermediate code from AST
-module icode_generation
-
-import icode
-import syntax_base
-private import typing
-private import primitive_info
-
-# An AST2ICode context stores the currently built icode informations
-class A2IContext
- super ICodeBuilder
- redef fun stmt(s: ICode)
- do
- if _current_node != null then
- current_location = _current_node.location
- else if visitor.current_node != null then
- current_location = visitor.current_node.location
- end
- super
- end
-
- # Prepare a new array of length item
- fun add_new_array(stype: MMType, length: Int): IRegister
- do
- var prop = visitor.get_method(stype, once "with_capacity".to_symbol)
- var ni = expr(new IIntValue(length.to_s), visitor.type_int)
- return expr(new INew(stype, prop, [ni]), stype)
- end
-
- # Add an array add
- fun add_call_array_add(recv, item: IRegister)
- do
- var stype = recv.stype
- var prop = visitor.get_method(stype, once "add".to_symbol)
- stmt(new ICall(prop, [recv, item]))
- end
-
- # Get the iregister associated with a variable
- # Or assign one if none exists
- fun variable(v: Variable): IRegister
- do
- if _variables.has_key(v) then
- return _variables[v]
- else
- var reg = new_register(v.stype.as(not null))
- _variables[v] = reg
- return reg
- end
- end
-
- # Current registered variable
- var _variables: HashMap[Variable, IRegister] = new HashMap[Variable, IRegister]
-
- # Current registered closurevariables
- readable var _closurevariables: HashMap[ClosureVariable, IClosureDecl] = new HashMap[ClosureVariable, IClosureDecl]
-
- # The current syntax visitor
- readable var _visitor: AbsSyntaxVisitor
-
- # Where a nit return must branch
- readable writable var _return_seq: nullable ISeq
-
- # Register where a functionnal nit return must store its value
- readable writable var _return_value: nullable IRegister
-
- # The method associated to the iroutine (if any)
- readable var _method: nullable MMMethod
-
- # The register of self (if any)
- var selfreg: nullable IRegister writable
-
- init(visitor: AbsSyntaxVisitor, r: IRoutine, m: nullable MMMethod)
- do
- super(visitor.mmmodule, r)
- _visitor = visitor
- _return_seq = r.body
- _return_value = r.result
- _method = m
- end
-
- # Insert implicit super init calls
- fun invoke_super_init_calls_after(start_prop: nullable MMMethod)
- do
- var p = method
- assert p isa MMSrcMethod
- var n = p.node
- assert n isa AConcreteInitPropdef
-
- if n.super_init_calls.is_empty then return
- var i = 0
- var j = 0
- if start_prop != null then
- while n.super_init_calls[i] != start_prop do
- i += 1
- end
- i += 1
-
- while n.explicit_super_init_calls[j] != start_prop do
- j += 1
- end
- j += 1
- end
- var stop_prop: nullable MMMethod = null
- if j < n.explicit_super_init_calls.length then
- stop_prop = n.explicit_super_init_calls[j]
- end
- var l = n.super_init_calls.length
- while i < l do
- var sp = n.super_init_calls[i]
- if sp == stop_prop then break
- var cargs = new Array[IRegister]
- if sp.signature.arity == 0 then
- cargs.add(iroutine.params.first)
- else
- for va in iroutine.params do
- cargs.add(va)
- end
- end
- stmt(new ICall(sp, cargs))
- i += 1
- end
- end
-
- # The current AExpr
- var _current_node: nullable AExpr = null
-
- # Generate icode in the current sequence from a statement
- fun generate_stmt(n: nullable AExpr)
- do
- if n == null then return
- var old = _current_node
- _current_node = n
- n.generate_icode(self)
- _current_node = old
- end
-
- # Generate icode in the current sequence from an expression
- fun generate_expr(n: AExpr): IRegister
- do
- var old = _current_node
- _current_node = n
- var reg = n.generate_icode(self).as(not null)
- _current_node = old
- return reg
- end
-end
-
-redef class EscapableBlock
- # Where a nit break must branch
- readable writable var _break_seq: nullable ISeq
-
- # Where a nit continue must branch
- readable writable var _continue_seq: nullable ISeq
-
- # Register where a functionnal nit break must store its value
- readable writable var _break_value: nullable IRegister
-
- # Register where a functionnal nit continue must store its value
- readable writable var _continue_value: nullable IRegister
-end
-
-redef class MMSrcModule
- # Generate icode for method bodies
- fun generate_icode(tc: ToolContext)
- do
- var v = new A2IVisitor(tc, self)
- for c in src_local_classes.values do
- for p in c.src_local_properties.values do
- if p isa MMSrcMethod then
- p.generate_iroutine(v)
- else if p isa MMSrcAttribute then
- p.generate_iroutine(v)
- end
- end
- end
- end
-end
-
-redef class MMSrcAttribute
- redef readable writable var _iroutine: nullable IRoutine
-
- # Generate the initialization iroutine
- fun generate_iroutine(visitor: A2IVisitor)
- do
- if node.n_expr != null then
- var iroutine = signature.generate_empty_iroutine
- iroutine.location = node.location
- var v = new A2IContext(visitor, iroutine, null)
- visitor.icode_ctx = v
- visitor.enter_visit(node)
- visitor.icode_ctx = null
- _iroutine = iroutine
- end
- end
-end
-
-redef class MMSrcMethod
- redef readable writable var _iroutine: nullable IRoutine
-
- # Generate the body iroutine
- fun generate_iroutine(visitor: A2IVisitor)
- do
- var iroutine = signature.generate_empty_iroutine
- if node != null then
- iroutine.location = node.location
- end
- var v = new A2IContext(visitor, iroutine, self)
- visitor.icode_ctx = v
- inner_generate_iroutine(v)
- visitor.icode_ctx = null
- _iroutine = iroutine
- end
-
- # Generate the body iroutine (specific part)
- fun inner_generate_iroutine(v: A2IContext) is abstract
-end
-
-redef class MMReadImplementationMethod
- redef fun inner_generate_iroutine(v)
- do
- var e = v.add_attr_read(node.prop, v.iroutine.params.first)
- v.add_return_value(e)
- end
-end
-
-redef class MMWriteImplementationMethod
- redef fun inner_generate_iroutine(v)
- do
- var params = v.iroutine.params
- v.stmt(new IAttrWrite(node.prop, params[0], params[1]))
- end
-end
-
-redef class MMMethSrcMethod
- redef fun inner_generate_iroutine(v)
- do
- v.visitor.enter_visit(node)
- end
-end
-
-redef class MMImplicitInit
- redef fun inner_generate_iroutine(v)
- do
- var params = v.iroutine.params
- var f = params.length - unassigned_attributes.length
- var recv = params.first
- for sp in super_inits do
- assert sp isa MMMethod
- var args_recv = [recv]
- if sp == super_init then
- var args = new Array[IRegister].with_capacity(f)
- args.add(recv)
- for i in [1..f[ do
- args.add(params[i])
- end
- v.stmt(new ICall(sp, args))
- else
- v.stmt(new ICall(sp, args_recv))
- end
- end
- for i in [f..params.length[ do
- var attribute = unassigned_attributes[i-f]
- v.stmt(new IAttrWrite(attribute, recv, params[i]))
- end
- end
-end
-
-class A2IVisitor
- super AbsSyntaxVisitor
- writable var _icode_ctx: nullable A2IContext
- fun icode_ctx: A2IContext do return _icode_ctx.as(not null)
- redef fun visit(n) do n.accept_icode_generation(self)
- init(tc, m) do super
-end
-
-
-###############################################################################
-
-redef class ANode
- fun accept_icode_generation(v: A2IVisitor) do accept_abs_syntax_visitor(v) end
-end
-
-redef class AAttrPropdef
- redef fun accept_icode_generation(vv)
- do
- var v = vv.icode_ctx
- v.stmt(new IMove(v.variable(self_var), v.iroutine.params.first))
- super
- var ne = n_expr
- if ne != null then
- v.stmt(new IMove(v.iroutine.result.as(not null), v.generate_expr(ne)))
- end
- end
-end
-
-redef class AMethPropdef
- redef fun accept_icode_generation(vv)
- do
- super
- fill_iroutine(vv.icode_ctx, method)
- end
-
- # Compile the method body common preambule (before specific body stuff if any)
- fun fill_iroutine(v: A2IContext, method: MMSrcMethod) is abstract
-end
-
-redef class ASignature
- fun fill_iroutine_parameters(v: A2IContext, orig_sig: MMSignature, params: Sequence[IRegister], closdecls: nullable Sequence[IClosureDecl])
- do
- for ap in n_params do
- var reg = v.variable(ap.variable)
- var orig_type = orig_sig[ap.position]
- var apst = ap.variable.stype.as(not null)
- if not orig_type < apst then
- v.add_type_cast(params[ap.position], apst)
- end
- v.stmt(new IMove(reg, params[ap.position]))
- end
- for i in [0..n_closure_decls.length[ do
- var wd = n_closure_decls[i]
- v.closurevariables[wd.variable] = closdecls[i]
- end
- end
-end
-
-redef class AClosureDecl
- redef fun accept_icode_generation(vv)
- do
- var v = vv.icode_ctx
- var iclos = variable.closure.signature.generate_empty_iclosuredef(v)
- var old_seq = v.seq
- v.seq = iclos.body
- escapable.continue_seq = iclos.body
- escapable.continue_value = iclos.result
- escapable.break_seq = v.return_seq
- escapable.break_value = v.return_value
- n_signature.fill_iroutine_parameters(v, variable.closure.signature, iclos.params, null)
-
- if n_expr != null then
- v.generate_stmt(n_expr)
- v.iroutine.closure_decls[position].default = iclos
-
- # Add a final break in case of break block witout value
- if variable.closure.is_break and v.return_value == null then
- v.add_escape(v.return_seq.as(not null))
- end
- end
- v.seq = old_seq
- end
-end
-
-redef class AConcreteMethPropdef
- redef fun fill_iroutine(v, method)
- do
- var params = v.iroutine.params.to_a
- var selfreg = v.variable(self_var)
- v.stmt(new IMove(selfreg, params[0]))
- v.selfreg = selfreg
- params.shift
-
- var orig_meth: MMLocalProperty = method.global.intro
- var orig_sig = orig_meth.signature_for(method.signature.recv)
- if n_signature != null then
- n_signature.fill_iroutine_parameters(v, orig_sig, params, v.iroutine.closure_decls)
- end
-
- if self isa AConcreteInitPropdef then
- v.invoke_super_init_calls_after(null)
- end
-
- if n_block != null then
- v.generate_stmt(n_block)
- end
- v.selfreg = null
- end
-end
-
-redef class AExternPropdef
- redef fun fill_iroutine(v, method)
- do
- # add all explicit extern calls for this extern method
- for explicit_import in method.as(MMMethSrcMethod).explicit_imports
- do
- var prop = explicit_import.method
- var ic : IAbsCall
- if prop.is_init then
- ic = new INew(prop.signature.recv, prop, new List[IRegister])
- else
- ic = new ICall(prop, new List[IRegister])
- end
- ic.is_explicit_from_extern = true
- v.stmt(ic)
- end
- end
-end
-
-redef class AExternInitPropdef
- redef fun fill_iroutine(v, method)
- do
- var params = v.iroutine.params
- var sig = method.signature
- assert params.length == sig.arity + 1
- var rtype = sig.recv # sig.return_type
- v.add_assignment(new IRegister(rtype), v.expr(new INative(method, params), rtype))
-
- super
- end
-end
-
-redef class ADeferredMethPropdef
- redef fun fill_iroutine(v, method)
- do
- v.add_abort("Deferred method called")
- end
-end
-
-redef class AExternMethPropdef
- redef fun fill_iroutine(v, method)
- do
- var params = v.iroutine.params
- var rtype = method.signature.return_type
- if rtype != null then
- v.add_return_value(v.expr(new INative(method, params), rtype))
- else
- v.stmt(new INative(method, params))
- end
-
- super
- end
-end
-
-redef class AInternMethPropdef
- redef fun fill_iroutine(v, method)
- do
- var params = v.iroutine.params
- var rtype = method.signature.return_type
- if rtype != null then
- v.add_return_value(v.expr(new INative(method, params), rtype))
- else
- v.stmt(new INative(method, params))
- end
- end
-end
-
-###############################################################################
-
-redef class AExpr
- redef fun accept_icode_generation(v) do end
-
- # Generate icode sequence in the current A2IContext
- # This method should not be called direclty: use generate_expr and generate_stmt from A2IContext instead
- protected fun generate_icode(v: A2IContext): nullable IRegister is abstract
-end
-
-redef class AVardeclExpr
- redef fun generate_icode(v)
- do
- var reg = v.variable(variable)
- var ne = n_expr
- if ne != null then
- v.add_assignment(reg, v.generate_expr(ne))
- end
- return null
- end
-end
-
-redef class ABlockExpr
- redef fun generate_icode(v)
- do
- for ne in n_expr do v.generate_stmt(ne)
- return null
- end
-end
-
-redef class ADoExpr
- redef fun generate_icode(v)
- do
- var seq_old = v.seq
- var seq = new ISeq
- v.stmt(seq)
- escapable.break_seq = seq
- v.seq = seq
-
- v.generate_stmt(n_block)
-
- v.seq = seq_old
- return null
- end
-end
-
-redef class AReturnExpr
- redef fun generate_icode(v)
- do
- var ne = n_expr
- if ne != null then
- v.add_assignment(v.return_value.as(not null), v.generate_expr(ne))
- end
- v.add_escape(v.return_seq.as(not null))
- return null
- end
-end
-
-redef class ABreakExpr
- redef fun generate_icode(v)
- do
- var ne = n_expr
- if ne != null then
- v.add_assignment(escapable.break_value.as(not null), v.generate_expr(ne))
- end
- v.add_escape(escapable.break_seq.as(not null))
- return null
- end
-end
-
-redef class AContinueExpr
- redef fun generate_icode(v)
- do
- var ne = n_expr
- if ne != null then
- v.add_assignment(escapable.continue_value.as(not null), v.generate_expr(ne))
- end
- v.add_escape(escapable.continue_seq.as(not null))
- return null
- end
-end
-
-redef class AAbortExpr
- redef fun generate_icode(v)
- do
- v.add_abort("Aborted")
- return null
- end
-end
-
-redef class AIfExpr
- redef fun generate_icode(v)
- do
- var iif = new IIf(v.generate_expr(n_expr))
- v.stmt(iif)
- var seq_old = v.seq
-
- if n_then != null then
- v.seq = iif.then_seq
- v.generate_stmt(n_then)
- end
-
- if n_else != null then
- v.seq = iif.else_seq
- v.generate_stmt(n_else)
- end
-
- v.seq = seq_old
- return null
- end
-end
-
-redef class AWhileExpr
- redef fun generate_icode(v)
- do
- var seq_old = v.seq
- var iloop = new ILoop
- v.stmt(iloop)
- escapable.break_seq = iloop
- v.seq = iloop
-
- # Process condition
- var iif = new IIf(v.generate_expr(n_expr))
- v.stmt(iif)
-
- # Process inside (condition is true)
- if n_block != null then
- v.seq = iif.then_seq
- escapable.continue_seq = iif.then_seq
- v.generate_stmt(n_block)
- end
-
- # Process escape (condition is false)
- v.seq = iif.else_seq
- v.add_escape(iloop)
-
- v.seq = seq_old
- return null
- end
-end
-
-redef class ALoopExpr
- redef fun generate_icode(v)
- do
- var seq_old = v.seq
- var iloop = new ILoop
- v.stmt(iloop)
- escapable.break_seq = iloop
- v.seq = iloop
-
- # Process inside
- if n_block != null then
- var seq = new ISeq
- v.stmt(seq)
- v.seq = seq
- escapable.continue_seq = seq
- v.generate_stmt(n_block)
- end
-
- v.seq = seq_old
- return null
- end
-end
-
-redef class AForExpr
- redef fun generate_icode(v)
- do
- var ne = n_expr
- var expr_type = ne.stype
- var tint = v.visitor.type_int
- var meth # The method that call the closure
- var args # The arguments of meth
-
- if ne isa ARangeExpr and expr_type == v.visitor.type_range(tint) then
- # Shortcut. No Range[Int] object allocated.
- # 'for x in [y..z] do' become 'y.enumerate_to(z) !each(x) do'
- # 'for x in [y..z[ do' become 'y.enumerate_before(z) !each(x) do'
- # And both methods may be inlined
- args = [v.generate_expr(ne.n_expr), v.generate_expr(ne.n_expr2)]
- if ne isa ACrangeExpr then
- meth = v.visitor.get_method(tint, once "enumerate_to".to_symbol)
- else
- assert ne isa AOrangeExpr
- meth = v.visitor.get_method(tint, once "enumerate_before".to_symbol)
- end
- else
- # Standard way.
- # 'for x in e do' become 'e.iterate !each(x) do'
- # Some iterate methods may be inlined (eg. the Array one)
- meth = v.visitor.get_method(expr_type, once "iterate".to_symbol)
- args = [v.generate_expr(n_expr)]
- end
-
- # Build closure
- var iclos = meth.signature.closures.first.signature.generate_empty_iclosuredef(v)
- var old_seq = v.seq
-
- var seq = new ISeq
- v.stmt(seq)
- v.seq = seq
- escapable.break_seq = seq
- escapable.break_value = null
-
- v.seq = iclos.body
- escapable.continue_seq = iclos.body
- escapable.continue_value = null
- for i in [0..variables.length[ do
- v.stmt(new IMove(v.variable(variables[i]), iclos.params[i]))
- end
- v.generate_stmt(n_block)
-
- # Call closure
- v.seq = seq
- v.add_call(meth, args, [iclos])
-
- v.seq = old_seq
- return null
- end
-end
-
-redef class AAssertExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- var iif = new IIf(e)
- v.stmt(iif)
- var seq_old = v.seq
- v.seq = iif.else_seq
- v.generate_stmt(n_else)
- var id = n_id
- if id == null then
- v.add_abort("Assert failed")
- else
- v.add_abort("Assert '%s' failed", id.text.to_s)
- end
- v.seq = seq_old
- return null
- end
-end
-
-redef class AVarExpr
- redef fun generate_icode(v)
- do
- return v.variable(variable)
- end
-end
-
-redef class AVarAssignExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_value)
- v.add_assignment(v.variable(variable), e)
- return null
- end
-end
-
-redef class AVarReassignExpr
- redef fun generate_icode(v)
- do
- var e1 = v.variable(variable)
- var e2 = v.generate_expr(n_value)
- var e3 = v.expr(new ICall(assign_method, [e1, e2]), assign_method.signature.return_type.as(not null))
- v.add_assignment(e1, e3)
- return null
- end
-end
-
-redef class ASelfExpr
- redef fun generate_icode(v)
- do
- return v.variable(variable)
- end
-end
-
-redef class AIfexprExpr
- redef fun generate_icode(v)
- do
- # Process condition
- var iif = new IIf(v.generate_expr(n_expr))
- v.stmt(iif)
- var seq_old = v.seq
-
- # Prepare result
- var reg = v.new_register(stype)
-
- # Process 'then'
- v.seq = iif.then_seq
- v.add_assignment(reg, v.generate_expr(n_then))
-
- # Process 'else'
- v.seq = iif.else_seq
- v.add_assignment(reg, v.generate_expr(n_else))
-
- v.seq = seq_old
- return reg
- end
-end
-
-redef class AEeExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- var e2 = v.generate_expr(n_expr2)
- return v.expr(new IIs(e, e2), stype)
- end
-end
-
-redef class AOrExpr
- redef fun generate_icode(v)
- do
- # Prepare result
- var reg = v.new_register(stype)
-
- # Process left operand (in a if/then)
- var iif = new IIf(v.generate_expr(n_expr))
- v.stmt(iif)
- var seq_old = v.seq
- v.seq = iif.then_seq
- v.add_assignment(reg, v.lit_true_reg)
-
- # Process right operand (in the else)
- v.seq = iif.else_seq
- v.add_assignment(reg, v.generate_expr(n_expr2))
-
- v.seq = seq_old
- return reg
- end
-end
-
-redef class AImpliesExpr
- redef fun generate_icode(v)
- do
- # Prepare result
- var reg = v.new_register(stype)
-
- # Process left operand (in a if/then)
- var iif = new IIf(v.generate_expr(n_expr))
- v.stmt(iif)
- var seq_old = v.seq
- v.seq = iif.else_seq
- v.add_assignment(reg, v.lit_true_reg)
-
- # Process right operand (in the else)
- v.seq = iif.then_seq
- v.add_assignment(reg, v.generate_expr(n_expr2))
-
- v.seq = seq_old
- return reg
- end
-end
-
-redef class AAndExpr
- redef fun generate_icode(v)
- do
- # Prepare result
- var reg = v.new_register(stype)
-
- # Process left operand (in a if/else)
- var iif = new IIf(v.generate_expr(n_expr))
- v.stmt(iif)
- var seq_old = v.seq
- v.seq = iif.else_seq
- v.add_assignment(reg, v.lit_false_reg)
-
- # Process right operand (in the then)
- v.seq = iif.then_seq
- v.add_assignment(reg, v.generate_expr(n_expr2))
-
- v.seq = seq_old
- return reg
- end
-end
-
-redef class ANotExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- return v.expr(new INot(e), stype)
- end
-end
-
-redef class AOrElseExpr
- redef fun generate_icode(v)
- do
- # Compute left operand
- var e = v.generate_expr(n_expr)
-
- # Prepare result
- var reg = v.new_register(stype)
-
- # Compare left and null
- var n = v.lit_null_reg
- var c = v.expr(new IIs(e, n), v.mmmodule.type_bool)
- var iif = new IIf(c)
- v.stmt(iif)
- var old_seq = v.seq
-
- # if equal, result = right opr
- v.seq = iif.then_seq
- v.add_assignment(reg, v.generate_expr(n_expr2))
-
- # else, result = left operand
- v.seq = iif.else_seq
- v.add_assignment(reg, e)
-
- v.seq = old_seq
-
- return reg
- end
-end
-
-redef class AIsaExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- return v.expr(new ITypeCheck(v.selfreg.as(not null), e, n_type.stype), stype)
- end
-end
-
-redef class AAsCastExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- v.add_type_cast(e, stype)
- return e
- end
-end
-
-redef class AAsNotnullExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- v.add_type_cast(e, stype)
- return e
- end
-end
-
-redef class ATrueExpr
- redef fun generate_icode(v)
- do
- return v.lit_true_reg
- end
-end
-
-redef class AFalseExpr
- redef fun generate_icode(v)
- do
- return v.lit_false_reg
- end
-end
-
-redef class AIntExpr
- redef fun generate_icode(v)
- do
- return v.expr(new IIntValue(n_number.text), stype)
- end
-end
-
-redef class AFloatExpr
- redef fun generate_icode(v)
- do
- return v.expr(new IFloatValue(n_float.text), stype)
- end
-end
-
-redef class ACharExpr
- redef fun generate_icode(v)
- do
- return v.expr(new ICharValue(n_char.text), stype)
- end
-end
-
-redef class AStringFormExpr
- redef fun generate_icode(v)
- do
- compute_string_infos
- var old_seq = v.seq
- var ionce = new IOnce
- var reg = v.expr(ionce, stype)
- v.seq = ionce.body
- var native_type = v.visitor.type_nativestring
- var ns = v.expr(new IStringValue(_cstring.as(not null)), native_type)
- var ni = v.expr(new IIntValue(_cstring_length.to_s), v.visitor.type_int)
- var prop = v.visitor.get_method(native_type, once "to_s_with_length".to_symbol)
- var e = v.expr(new ICall(prop, [ns, ni]), stype)
- v.add_assignment(reg, e)
- v.seq = old_seq
- return reg
- end
-
- # The raw string value
- protected fun string_text: String is abstract
-
- # The string in a C native format
- protected var _cstring: nullable String
-
- # The string length in bytes
- protected var _cstring_length: nullable Int
-
- # Compute _cstring and _cstring_length using string_text
- protected fun compute_string_infos
- do
- var str = n_string.text
- if str.length >= 6 and str[0] == str[1] then
- str = str.substring(3, str.length - 6)
- else
- str = str.substring(1, str.length - 2)
- end
- var res = new Buffer
- var len = 0
- var i = 0
- while i < str.length do
- var c = str[i]
- if c == '\\' then
- i = i + 1
- var c2 = str[i]
- if c2 != '{' and c2 != '}' then
- res.add(c)
- end
- c = c2
- else if c == '"' then
- res.add('\\')
- else if c == '\n' then
- res.add('\\')
- c = 'n'
- end
- len = len + 1
- res.add(c)
- i = i + 1
- end
- _cstring = res.to_s
- _cstring_length = len
- end
-end
-
-redef class AStringExpr
- redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AStartStringExpr
- redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AMidStringExpr
- redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AEndStringExpr
- redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-
-redef class ASuperstringExpr
- redef fun generate_icode(v)
- do
- var array = v.add_new_array(atype, n_exprs.length)
- var prop_to_s = v.visitor.get_method(v.visitor.type_object, once "to_s".to_symbol)
- for ne in n_exprs do
- var e = v.generate_expr(ne)
- if ne.stype != stype then
- e = v.expr(new ICall(prop_to_s, [e]), stype)
- end
- v.add_call_array_add(array, e)
- end
- return v.expr(new ICall(prop_to_s, [array]), stype)
- end
-end
-
-redef class ANullExpr
- redef fun generate_icode(v)
- do
- return v.lit_null_reg
- end
-end
-
-redef class AArrayExpr
- redef fun generate_icode(v)
- do
- var nes = n_exprs.n_exprs
- var recv = v.add_new_array(stype, nes.length)
- for ne in nes do
- var e = v.generate_expr(ne)
- v.add_call_array_add(recv, e)
- end
- return recv
- end
-end
-
-redef class ACrangeExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- var e2 = v.generate_expr(n_expr2)
- var prop = v.visitor.get_method(stype, once "init".to_symbol)
- return v.expr(new INew(stype, prop, [e, e2]), stype)
- end
-end
-
-redef class AOrangeExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- var e2 = v.generate_expr(n_expr2)
- var prop = v.visitor.get_method(stype, once "without_last".to_symbol)
- return v.expr(new INew(stype, prop, [e, e2]), stype)
- end
-end
-
-redef class ASuperExpr
- redef fun generate_icode(v)
- do
- var arity = v.iroutine.params.length - 1
- if init_in_superclass != null then
- arity = init_in_superclass.signature.arity
- end
- var args = new Array[IRegister].with_capacity(arity + 1)
- args.add(v.iroutine.params[0])
- var nas = n_args.n_exprs
- if nas.length != arity then
- for i in [0..arity[ do
- args.add(v.iroutine.params[i + 1])
- end
- else
- for na in nas do
- args.add(v.generate_expr(na))
- end
- end
- var p = init_in_superclass
- if p != null then
- var rtype = p.signature.return_type
- if rtype != null then
- return v.expr(new ICall(p, args), rtype)
- else
- v.stmt(new ICall(p, args))
- return null
- end
- else
- p = prop
- var rtype = p.signature.return_type
- if rtype == null then
- v.stmt(new ISuper(p, args))
- return null
- else
- return v.expr(new ISuper(p, args), rtype)
- end
- end
- end
-end
-
-redef class AAttrExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- if n_expr.stype.is_nullable then v.add_null_reciever_check(e)
- return v.add_attr_read(prop, e)
- end
-end
-
-redef class AAttrAssignExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- if n_expr.stype.is_nullable then v.add_null_reciever_check(e)
- var e2 = v.generate_expr(n_value)
- v.stmt(new IAttrWrite(prop, e, e2))
- return null
- end
-end
-redef class AAttrReassignExpr
- redef fun generate_icode(v)
- do
- var e1 = v.generate_expr(n_expr)
- if n_expr.stype.is_nullable then v.add_null_reciever_check(e1)
- var e2 = v.expr(new IAttrRead(prop, e1), attr_type)
- var e3 = v.generate_expr(n_value)
- var e4 = v.expr(new ICall(assign_method, [e2, e3]), attr_type)
- v.stmt(new IAttrWrite(prop, e1, e4))
- return null
- end
-end
-
-redef class AIssetAttrExpr
- redef fun generate_icode(v)
- do
- var e = v.generate_expr(n_expr)
- if n_expr.stype.is_nullable then v.add_null_reciever_check(e)
- return v.expr(new IAttrIsset(prop, e), stype)
- end
-end
-
-redef class AAbsAbsSendExpr
- # Compile each argument and add them to the array
- fun generate_icode_for_arguments_in(v: A2IContext, args: Array[IRegister], signature: MMSignature)
- do
- var par_arity = signature.arity
- var par_vararg = signature.vararg_rank
- var raw_args = raw_arguments
- var raw_arity = raw_args.length
- var arg_idx = 0
- for par_idx in [0..par_arity[ do
- var a: AExpr
- var par_type = signature[par_idx]
- if par_idx == par_vararg then
- var arr = v.add_new_array(v.visitor.type_array(par_type), raw_arity-par_arity)
- for i in [0..(raw_arity-par_arity)] do
- a = raw_args[arg_idx]
- v.add_call_array_add(arr, v.generate_expr(a))
- arg_idx = arg_idx + 1
- end
- args.add(arr)
- else
- a = raw_args[arg_idx]
- args.add(v.generate_expr(a))
- arg_idx = arg_idx + 1
- end
- end
- end
-end
-
-redef class ASendExpr
- redef fun generate_icode(v)
- do
- var recv = v.generate_expr(n_expr)
- var args = new Array[IRegister]
- args.add(recv)
- var prop = prop
- generate_icode_for_arguments_in(v, args, prop.signature.as(not null))
- var r: nullable IRegister = null # The full result of the send (raw call + breaks)
- var r2: nullable IRegister # The raw result of the call
-
- # Prepare closures
- var seq_old = v.seq
- var closcns: nullable Array[nullable IClosureDef] = null
- if not prop_signature.closures.is_empty then
- var rtype = prop_signature.return_type
- if rtype != null then
- r = v.new_register(rtype)
- end
- var seq = new ISeq
- v.stmt(seq)
- v.seq = seq
- closcns = new Array[nullable IClosureDef]
- var cdarity = 0
- if closure_defs != null then cdarity = closure_defs.length
- var closure_defs = closure_defs
- for mmc in prop_signature.closures do
- var found = false
- var name = mmc.name
- if closure_defs != null then
- for cd in closure_defs do
- if cd.n_id.to_symbol != name then continue
- assert found == false
- found = true
- cd.escapable.break_seq = seq
- cd.escapable.break_value = r
- var cn = cd.generate_iclosuredef(v)
- closcns.add(cn)
- end
- end
- if not found then
- closcns.add(null)
- end
- end
- end
-
- r2 = v.add_call(prop, args, closcns)
-
- # Closure work
- if not prop_signature.closures.is_empty then
- if r != null and r2 != null then v.add_assignment(r, r2)
- v.seq = seq_old
- else
- r = r2
- end
-
- if prop.global.is_init then
- v.invoke_super_init_calls_after(prop)
- end
- return r
- end
-end
-
-redef class ASendReassignExpr
- redef fun generate_icode(v)
- do
- var recv = v.generate_expr(n_expr)
- if n_expr.stype.is_nullable then v.add_null_reciever_check(recv)
- var args = new Array[IRegister]
- args.add(recv)
- generate_icode_for_arguments_in(v, args, read_prop.signature.as(not null))
-
- var e2 = v.expr(new ICall(read_prop, args), read_prop.signature.return_type.as(not null))
- var e3 = v.generate_expr(n_value)
- var e4 = v.expr(new ICall(assign_method, [e2, e3]), assign_method.signature.return_type.as(not null))
- var args2 = args.to_a
- args2.add(e4)
- v.stmt(new ICall(prop, args2))
- return null
- end
-end
-
-redef class ANewExpr
- redef fun generate_icode(v)
- do
- var args = new Array[IRegister]
- generate_icode_for_arguments_in(v, args, prop.signature.as(not null))
- return v.expr(new INew(stype, prop, args), stype)
- end
-end
-
-redef class AProxyExpr
- redef fun generate_icode(v)
- do
- return v.generate_expr(n_expr)
- end
-end
-
-redef class AOnceExpr
- redef fun generate_icode(v)
- do
- var ionce = new IOnce
- var reg = v.expr(ionce, stype)
- var old_seq = v.seq
- v.seq = ionce.body
-
- var e = v.generate_expr(n_expr)
- v.add_assignment(reg, e)
-
- v.seq = old_seq
- return reg
- end
-end
-
-
-redef class AClosureDef
- var _iclosure_def: nullable IClosureDef
-
- fun generate_iclosuredef(v: A2IContext): IClosureDef
- do
- # Prepare signature
- var args = new Array[IRegister]
- var sig = closure.signature
- for i in [0..sig.arity[ do
- args.add(v.new_register(sig[i]))
- end
- var ret: nullable IRegister = null
- var rtype = sig.return_type
- if rtype != null then
- ret = v.new_register(rtype)
- end
-
- var iclos = new IClosureDef(args, ret)
- iclos.location = location
-
- # Prepare env
- var seq_old = v.seq
- v.seq = iclos.body
- escapable.continue_seq = iclos.body
- escapable.continue_value = iclos.result
-
- # Assign parameters
- for i in [0..variables.length[ do
- var res = v.variable(variables[i])
- v.add_assignment(res, iclos.params[i])
- end
-
- v.generate_stmt(n_expr)
-
- # Add a final break in case of break block witout value
- if closure.is_break and escapable.break_value == null then
- v.add_escape(escapable.break_seq.as(not null))
- end
-
- v.seq = seq_old
- _iclosure_def = iclos
- return iclos
- end
-end
-
-redef class AClosureCallExpr
- redef fun generate_icode(v)
- do
- # Geneate arguments
- var args = new Array[IRegister]
- generate_icode_for_arguments_in(v, args, variable.closure.signature)
-
- # Prepare icall
- var closdecl = v.closurevariables[variable]
- var icall = new IClosCall(closdecl, args)
- var seq_old = v.seq
-
- # Fill break of ical
- if n_closure_defs.length == 1 then do
- var iseq = new ISeq
- icall.break_seq = iseq
- v.seq = iseq
- v.generate_stmt(n_closure_defs.first.n_expr)
- v.seq = seq_old
- end
-
- # Prepare in case of default block
- var iif: nullable IIf = null # The iif of default block
- var closdecl_default = closdecl.default # The default (if any)
- if closdecl_default != null then
- iif = new IIf(v.expr(new IHasClos(closdecl), v.visitor.type_bool))
- v.stmt(iif)
- v.seq = iif.then_seq
- end
-
- # Add the icall
- var r2: nullable IRegister = null # the result of the icall
- var rtype = variable.closure.signature.return_type
- if rtype == null then
- v.stmt(icall)
- else
- r2 = v.expr(icall, rtype)
- end
-
- # Process the case of default block
- var r: nullable IRegister = null # the real result
- if closdecl_default != null then
- assert iif != null
- if r2 != null then
- assert rtype != null
- r = v.new_register(rtype)
- v.add_assignment(r, r2)
- end
- v.seq = iif.else_seq
- var r3 = v.inline_routine(closdecl_default, args, null)
- if r != null then
- assert r3 != null
- v.add_assignment(r, r3)
- end
- v.seq = seq_old
- else
- r = r2
- end
- return r
- end
-end
-
-redef class ADebugTypeExpr
- redef fun generate_icode(v)
- do
- # Do nothing.
- return null
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Build MM entity from NIT AST and check conformance of these entities.
-# This module introduce specific MM class (MMSrcXXX) that specialize the abstract one from metamodel
-#
-module mmbuilder
-
-import syntax_base
-private import primitive_info
-
-redef class ToolContext
- redef fun handle_property_conflict(lc, impls)
- do
- var location: nullable Location = null
- if lc isa MMSrcLocalClass then
- var node = lc.node
- if node != null then node.location
- end
- #if location == null then location = lc.mmmodule.location
- var clas = new Array[MMLocalClass]
- for i in impls do
- clas.add(i.local_class)
- end
- self.fatal_error(location, "Property inheritance conflict in class {lc} for `{impls.first.name}': conflicting properties are defined in {clas.join(", ")}")
- end
-end
-
-# Class specialization hierarchy sorter
-private class CSHSorter
- super AbstractSorter[MMLocalClass]
- redef fun compare(a, b)
- do
- return a.cshe.rank <=> b.cshe.rank
- end
-
- init do end
-end
-
-redef class MMSrcModule
- # Syntax analysis and MM construction for the module
- # Require that supermodules are processed
- fun do_mmbuilder(tc: ToolContext)
- do
- # Import global classes
- import_global_classes
-
- # Create local classes and attach them to global classes
- var mmbv = new ClassBuilderVisitor(tc, self)
- mmbv.enter_visit(node)
- tc.check_errors
-
- if mhe.direct_greaters.is_empty then
- process_default_classes(tc)
- end
-
- # Import unrefined local classes and attach them to global classes
- import_local_classes
-
- # Resolve classes in super clauses
- var mmbv1 = new ClassSpecializationBuilderVisitor(tc, self)
- mmbv1.enter_visit(node)
- tc.check_errors
-
- # Compute specialization relation
- for c in local_classes do
- if visibility_for(c.global.intro.mmmodule) < c.global.visibility_level then
- continue
- end
- c.compute_super_classes
- end
- tc.check_errors
-
- # Class that we will process now are those in the hierarchy
- # Its mean all the visible classes and their super-classes
- # Note that leaves invisible classes are not in the 'classes' set
- var classes = class_specialization_hierarchy.to_a
-
- # Prepare class list to process the following in a right order
- var sorter = once new CSHSorter
- sorter.sort(classes)
-
- # Compute class ancestors types
- var mmbv1b = new ClassAncestorBuilder(tc, self)
- for c in classes do
- c.accept_class_visitor(mmbv1b)
- tc.check_errors
- c.compute_ancestors
- end
-
- # Check class conformity
- var mmbv1c = new ClassVerifierVisitor(tc, self)
- for c in classes do
- c.accept_class_visitor(mmbv1c)
- end
- tc.check_errors
-
- # Property inhritance and introduction
- var mmbv2 = new PropertyBuilderVisitor(tc, self)
- for c in classes do
- # Global property introduction and redefinition
- c.accept_class_visitor(mmbv2)
-
- # Default and inherited constructor if needed
- if c isa MMSrcLocalClass and c.global.intro == c and not c.global.is_enum and not c.global.is_extern and not c.global.is_interface then
- c.process_default_constructors(mmbv2)
- end
-
- # Note that inherited unredefined property are processed on demand latter
- end
- tc.check_errors
-
- # Property signature analysis and inheritance conformance
- var mmbv3 = new PropertyVerifierVisitor(tc, self)
- for c in classes do
- c.accept_properties_visitor(mmbv3)
- end
-
- tc.check_errors
- end
-
- # Create some primitive default classes if they do not exists
- fun process_default_classes(tc: ToolContext)
- do
- var name = once ("Object".to_symbol)
- if not has_global_class_named(name) then
- var c = new MMSrcLocalClass(self, name, null, 0)
- c.new_global
- src_local_classes[name] = c
- end
- name = once ("Bool".to_symbol)
- if not has_global_class_named(name) then
- var c = new MMSrcLocalClass(self, name, null, 0)
- c.new_global
- src_local_classes[name] = c
- end
- end
-end
-
-redef class MMLocalClass
- # Accept a class visitor (on class nodes)
- private fun accept_class_visitor(v: AbsSyntaxVisitor)
- do
- end
-
- # Accept a class visitor (on class properties)
- private fun accept_properties_visitor(v: AbsSyntaxVisitor)
- do
- end
-end
-
-redef class MMSrcLocalClass
- redef fun accept_class_visitor(v)
- do
- var n = node
- while n != null do
- v.enter_visit(n)
- n = n.next_node
- end
- end
-
- # Accept a class visitor (on class properties)
- redef fun accept_properties_visitor(v)
- do
- var n = node
- while n != null do
- v.enter_visit(n)
- n = n.next_node
- end
-
- for p in src_local_properties.values do
- p.accept_property_visitor(v)
- end
- end
-
- # Introduce or inherit default constructors
- private fun process_default_constructors(v: PropertyBuilderVisitor)
- do
- # Is there already a constructor ?
- for gp in global_properties do
- if gp.is_init then
- # Return if explicit constructor in the class
- if gp.intro.local_class == self then return
- end
- end
-
- # Collect visible constructors in super stateful classes
- var super_inits = new ArraySet[MMLocalProperty]
- var super_constructors = new ArraySet[MMGlobalProperty]
- for sc in che.direct_greaters do
- if sc.global.is_enum and not sc.global.is_extern or sc.global.is_interface then continue
- for gp in sc.global_properties do
- if not gp.is_init then continue
- super_constructors.add(gp)
- end
- var initname = once ("init".to_symbol)
- if sc.has_global_property_by_name(initname) then
- var gp = sc.get_property_by_name(initname)
- super_inits.add(self[gp])
- end
- end
-
- # Collect unassigned attributes
- var unassigned_attributes = new Array[MMSrcAttribute]
- for a in src_local_properties.values do
- if a isa MMSrcAttribute then
- var n = a.node
- if n.n_expr == null then unassigned_attributes.add(a)
- end
- end
-
- if not super_constructors.is_empty then
- # Select most specific classes introducing inheritable constructors
- # Mixin classes are skipped
- var supers = new Array[MMLocalClass]
- for gp in super_constructors do
- var sc = gp.local_class
- if supers.has(sc) then continue
- if not sc.global.is_mixin then
- supers.add(sc)
- end
- end
- supers = che.order.select_smallests(supers)
-
- # A mixin class can only have 0 or 1 most specific non-mixin superclass
- var superclass: nullable MMLocalClass = null # This most specific non-mixin superclass (if any)
-
- if supers.length > 1 then
- v.error(node, "Error: Explicit constructor required in {self} since multiple inheritance of constructor is forbiden. Conflicting classes are {supers.join(", ")}. Costructors are {super_constructors.join(", ")}.")
- return
- else if supers.length == 1 then
- superclass = supers.first
- end
-
- for gp in super_constructors do
- # Inherit constructors : the one of the non-mixin super class or all from the all mixin super-classes
- if superclass == null or gp.local_class == superclass then
- make_visible_an_inherited_global_property(gp)
- end
- end
- global.mixin_of = superclass.as(not null).global # FIXME Dear! this should break!
- else
- # v.error(nodes.first, "Error, constructor required in {self} since no anonimous init found in {sc}.")
-
- # unassigned attributes, then implicit consructors are generated
- var p = new MMImplicitInit(self, unassigned_attributes, super_inits.to_a)
- add_src_local_property(v, p)
- #print("Create implicit init {p} in {self} from {super_inits.join(", ")} + {unassigned_attributes.length} args")
- end
- end
-
- # Add a source property
- # Register it to the class and attach it to global property
- private fun add_src_local_property(v: PropertyBuilderVisitor, prop: MMLocalProperty)
- do
- var pname = prop.name
- # Check double definition in the same class
- if src_local_properties.has_key(pname) then
- v.error(prop.node, "Error: A property {pname} is already defined in class {name}.")
- return
- end
- src_local_properties[pname] = prop
-
- # Intro or redefinition ?
- if has_global_property_by_name(pname) then
- var g = get_property_by_name(pname)
- prop.inherit_global(g)
- end
-
- if not prop.is_global_set then
- prop.new_global
- prop.global.is_init = prop.is_init
- end
- end
-end
-
-redef class MMLocalProperty
- private fun accept_property_visitor(v: AbsSyntaxVisitor)
- do
- end
-end
-
-redef class MMImplicitInit
- redef readable var _super_init: nullable MMLocalProperty = null
- redef fun accept_property_visitor(v)
- do
- var base: nullable MMLocalProperty = null
- for p in super_inits do
- if p.signature.arity > 0 then
- if base == null then
- base = p
- else
- v.error(null, "Error: explicit constructor needed in {local_class} since both super-constructor {base.full_name} and {p.full_name} have paramters")
- return
- end
- end
- end
- _super_init = base
-
- var params = new Array[MMParam]
- if base != null then
- var sig = base.signature
- for i in [0..sig.arity[ do
- params.add(sig.params[i])
- end
- end
- for a in unassigned_attributes do
- var sig = a.signature
- if sig == null then return # Broken attribute definition
- var name = a.name
- if name.to_s.first == '_' or name.to_s.first == '@' then
- name = a.to_s.substring_from(1).to_symbol
- end
- params.add(new MMParam(sig.return_type.as(not null), name))
- end
- signature = new MMSignature(params, null, local_class.get_type)
- end
-end
-
-
-# Concrete NIT class specialization relation
-class MMSrcAncestor
- super MMAncestor
- redef readable var _local_class: MMLocalClass
-
- init(c: MMLocalClass)
- do
- _local_class = c
- end
-end
-
-###############################################################################
-
-# A pass visitor for syntax analysis.
-# * Build the classes and attach them to global classes
-# * Collect generic formal parameters.
-private class ClassBuilderVisitor
- super AbsSyntaxVisitor
- # Current class arity
- readable writable var _local_class_arity: Int = 0
-
- # Current class formal parameters
- readable writable var _formals: nullable Map[Symbol, MMTypeFormalParameter]
-
- redef fun visit(n) do n.accept_class_builder(self)
- init(tc, m) do super
-end
-
-# Another pass visitor for syntax analysis.
-# * Build ancertors (with only class informations not the type one)
-private class ClassSpecializationBuilderVisitor
- super AbsSyntaxVisitor
- redef fun visit(n) do n.accept_class_specialization_builder(self)
- init(tc, m) do super
-end
-
-# Another pass visitor for syntax analysis.
-# * Compute types in ancestors
-private class ClassAncestorBuilder
- super AbsSyntaxVisitor
- redef fun visit(n) do n.accept_class_ancestor_builder(self)
- init(tc, m) do super
-end
-
-# Another pass visitor for syntax analysis.
-# * Checks classes in regard to superclasses
-private class ClassVerifierVisitor
- super AbsSyntaxVisitor
- redef fun visit(n) do n.accept_class_verifier(self)
- init(tc, m) do super
-end
-
-
-# Another pass visitor for syntax analysis.
-# * Build propertie names
-# * Build local properties and attache them to global properties
-# * Attach bound to formal types
-private class PropertyBuilderVisitor
- super AbsSyntaxVisitor
- redef fun visit(n) do n.accept_property_builder(self)
- init(tc, m) do super
-end
-
-# Another pass pass visitor for syntax analysis.
-# * Check property conformance
-private class PropertyVerifierVisitor
- super AbsSyntaxVisitor
-
- # The signature currently build
- readable writable var _signature_builder: SignatureBuilder
-
- redef fun visit(n) do n.accept_property_verifier(self)
-
- init(tc, m)
- do
- super
- _signature_builder = new SignatureBuilder
- end
-end
-
-# Information about a signature currently build
-private class SignatureBuilder
- # Current visited parameter types
- readable writable var _params: Array[AParam] = new Array[AParam]
-
- # Visited parameters without type information added
- readable writable var _untyped_params: Array[AParam] = new Array[AParam]
-
- # Position of the current star parameter
- readable writable var _vararg_rank: Int = -1
-
- # Current closure declarations
- readable writable var _closure_decls: Array[AClosureDecl] = new Array[AClosureDecl]
-
- # True is a problen occured durring building
- readable writable var _has_error_occured: Bool = false
-
- # Current signature
- readable writable var _signature: nullable MMSignature = null
-end
-
-###############################################################################
-
-redef class ANode
- private fun accept_class_builder(v: ClassBuilderVisitor) do accept_abs_syntax_visitor(v)
- private fun accept_class_specialization_builder(v: ClassSpecializationBuilderVisitor) do accept_abs_syntax_visitor(v)
- private fun accept_class_ancestor_builder(v: ClassAncestorBuilder) do accept_abs_syntax_visitor(v)
- private fun accept_class_verifier(v: ClassVerifierVisitor) do accept_abs_syntax_visitor(v)
- private fun accept_property_builder(v: PropertyBuilderVisitor) do accept_abs_syntax_visitor(v)
- private fun accept_property_verifier(v: PropertyVerifierVisitor) do accept_abs_syntax_visitor(v)
-end
-
-redef class AModule
- # Import supermodules and compute visibility
- fun import_super_modules(tc: ToolContext, mod: MMSrcModule)
- do
- # Import super-modules
- var supers = new Array[MMModule]
- var no_import: nullable AImport = null
- for i in n_imports do
- var n = i.module_name
- if n != null then
- var m = tc.get_module(n, mod)
- supers.add(m)
- mod.add_super_module(m, i.visibility_level)
- else
- no_import = i
- end
- end
- if no_import != null then
- if not supers.is_empty then
- tc.error(no_import.location, "Error: Top modules cannot import other modules.")
- end
- else if supers.is_empty then
- var stdname = once "standard".to_symbol
- var m = tc.get_module(stdname, mod)
- supers.add(m)
- mod.add_super_module(m, 1)
- end
-
- tc.add_module(mod, supers)
- end
-end
-
-redef class AModuledecl
- redef fun accept_class_builder(v)
- do
- if n_name.n_id.to_symbol != v.mmmodule.name then
- v.error(n_name.n_id, "Error: Module name mismatch between {v.mmmodule.name} and {n_name.n_id.to_symbol}")
- end
- end
-end
-
-redef class AImport
- # Imported module name (or null)
- fun module_name: nullable Symbol is abstract
-
- # Visibility level (intrude/public/private)
- fun visibility_level: Int is abstract
-end
-redef class AStdImport
- redef fun module_name
- do
- return n_name.n_id.to_symbol
- end
- redef fun visibility_level
- do
- return n_visibility.level
- end
-end
-redef class ANoImport
- redef fun module_name
- do
- return null
- end
-end
-
-redef class AVisibility
- # Visibility level
- fun level: Int is abstract
-end
-redef class APublicVisibility
- redef fun level do return 1
-end
-redef class AProtectedVisibility
- redef fun level do return 2
-end
-redef class APrivateVisibility
- redef fun level do return 3
-end
-redef class AIntrudeVisibility
- redef fun level do return 0
-end
-
-
-redef class AClassdef
- redef fun local_class: MMSrcLocalClass do return _local_class.as(not null)
- var _local_class: nullable MMSrcLocalClass
-
- # Name of the class
- fun name: Symbol is abstract
-
- # Number of formal parameters
- fun arity: Int do return 0
-
- # Visibility of the class
- fun visibility_level: Int do return 1
-
- redef fun accept_class_builder(v)
- do
- var local_class: MMSrcLocalClass
- var mod = v.mmmodule
- var local_classes = mod.src_local_classes
- if local_classes.has_key(name) then
- local_class = local_classes[name]
- _local_class = local_class
- if self isa AStdClassdef then
- # If we are not a special implicit class then rant
- v.error(self, "Error: A class {name} is already defined at line {local_class.node.location.line_start}.")
- return
- end
- # Add the new node after the last node
- var n = local_class.node
- while n.next_node != null do n = n.next_node
- n.next_node = self
- else
- local_class = new MMSrcLocalClass(mod, name, self, arity)
- _local_class = local_class
- local_classes[name] = local_class
- if not mod.has_global_class_named(name) then
- build_class_introduction(v)
- else
- var glob = mod.global_class_named(name)
- build_class_refinement(v, glob)
- end
-
- end
- v.local_class_arity = 0
- v.formals = local_class.formal_dict
-
- #####
- super
- #####
-
- v.formals = null
- end
-
- fun build_class_introduction(v: AbsSyntaxVisitor)
- do
- local_class.new_global
- var glob = local_class.global
-
- glob.visibility_level = visibility_level
- if self isa AStdClassdef then
- if n_kwredef != null then
- v.error(self, "Redef error: No class {name} is imported. Remove the redef keyword to define a new class.")
- return
- end
- glob.is_interface = n_classkind.is_interface
- glob.is_abstract = n_classkind.is_abstract
- glob.is_enum = n_classkind.is_enum
- end
- end
-
- fun build_class_refinement(v: AbsSyntaxVisitor, glob: MMGlobalClass)
- do
- local_class.set_global(glob)
-
- glob.check_visibility(v, self, v.mmmodule)
- if self isa AStdClassdef and n_kwredef == null then
- v.error(self, "Redef error: {name} is an imported class. Add the redef keyword to refine it.")
- return
- end
-
- if glob.intro.arity != _local_class.arity then
- v.error(self, "Redef error: Formal parameter arity missmatch; got {_local_class.arity}, expected {glob.intro.arity}.")
- end
-
- if self isa AStdClassdef and (not glob.is_interface and n_classkind.is_interface or
- not glob.is_abstract and n_classkind.is_abstract or
- not glob.is_enum and n_classkind.is_enum)
- then
- v.error(self, "Redef error: cannot change kind of class {name}.")
- end
- end
-
- redef fun accept_class_verifier(v)
- do
- super
- var glob = _local_class.global
- for c in _local_class.cshe.direct_greaters do
- var cg = c.global
- if glob.is_interface then
- if cg.is_enum then
- v.error(self, "Special error: Interface {name} try to specialise enum class {c.name}.")
- else if not cg.is_interface then
- v.error(self, "Special error: Interface {name} try to specialise class {c.name}.")
- end
- else if glob.is_enum then
- if not cg.is_interface and not cg.is_enum then
- v.error(self, "Special error: Enum class {name} try to specialise class {c.name}.")
- end
- else
- if cg.is_enum then
- v.error(self, "Special error: Class {name} try to specialise enum class {c.name}.")
- end
- end
-
- end
- end
-
- redef fun accept_abs_syntax_visitor(v)
- do
- v.local_class = _local_class
- super
- v.local_class = null
- end
-end
-
-redef class AClasskind
- fun is_interface: Bool do return false
- fun is_enum: Bool do return false
- fun is_abstract: Bool do return false
- fun is_extern : Bool do return false
-end
-
-redef class AInterfaceClasskind
- redef fun is_interface do return true
-end
-redef class AEnumClasskind
- redef fun is_enum do return true
-end
-redef class AExternClasskind
- redef fun is_extern do return true
-end
-redef class AAbstractClasskind
- redef fun is_abstract do return true
-end
-
-redef class AStdClassdef
- redef fun name
- do
- return n_id.to_symbol
- end
- redef fun arity
- do
- return n_formaldefs.length
- end
- redef fun accept_class_specialization_builder(v)
- do
- super
-
- var glob = local_class.global
- if glob.intro == local_class then
- glob.is_interface = n_classkind.is_interface
- glob.is_abstract = n_classkind.is_abstract
- glob.is_enum = n_classkind.is_enum
- glob.is_extern = n_classkind.is_extern
- glob.visibility_level = visibility_level
- end
- end
- redef fun accept_class_verifier(v)
- do
- super
- var glob = _local_class.global
- if glob.intro == _local_class then
- # Intro
- if n_kwredef != null then
- v.error(self, "Redef error: No class {name} is imported. Remove the redef keyword to define a new class.")
- end
- if glob.is_extern then
- glob.mmmodule.is_extern_hybrid = true
- end
-
- for c in _local_class.cshe.direct_greaters do
- var cg = c.global
- if glob.is_interface then
- if cg.is_enum then
- v.error(self, "Special error: Interface {name} try to specialise enum class {c.name}.")
- else if not cg.is_interface then
- v.error(self, "Special error: Interface {name} try to specialise class {c.name}.")
- end
- else if glob.is_enum then
- if not cg.is_interface and not cg.is_enum then
- v.error(self, "Special error: Enum class {name} try to specialise class {c.name}.")
- end
- else if glob.is_extern then
- if not cg.is_interface and not cg.is_extern then
- v.error(self, "Special error: Extern class {name} try to specialise class {c.name}.")
- end
- else
- if cg.is_enum then
- v.error(self, "Special error: Class {name} try to specialise enum class {c.name}.")
- else if cg.is_extern then
- v.error(self, "Special error: Class {name} try to specialise extern class {c.name}.")
- end
- end
-
- end
- return
- end
-
- # Redef
-
- glob.check_visibility(v, self, v.mmmodule)
- if n_kwredef == null then
- v.error(self, "Redef error: {name} is an imported class. Add the redef keyword to refine it.")
- return
- end
-
- if glob.intro.arity != _local_class.arity then
- v.error(self, "Redef error: Formal parameter arity mismatch; got {_local_class.arity}, expected {glob.intro.arity}.")
- end
-
- if
- not glob.is_interface and n_classkind.is_interface or
- not glob.is_abstract and n_classkind.is_abstract or
- not glob.is_enum and n_classkind.is_enum or
- not glob.is_extern and n_classkind.is_extern
- then
- v.error(self, "Redef error: cannot change kind of class {name}.")
- end
- end
-
- redef fun visibility_level
- do
- return n_visibility.level
- end
-end
-
-redef class AMainClassdef
- redef fun name
- do
- return once "Sys".to_symbol
- end
-end
-
-redef class ATopClassdef
- redef fun name
- do
- return once "Object".to_symbol
- end
-end
-
-redef class AFormaldef
- # The associated formal generic parameter (MM entity)
- var _formal: nullable MMTypeFormalParameter
-
- redef fun accept_class_builder(v)
- do
- var name = n_id.to_symbol
- var formal_type = new MMTypeFormalParameter(name, v.local_class_arity, v.local_class)
- _formal = formal_type
- v.local_class_arity = v.local_class_arity + 1
- v.local_class.register_formal(formal_type)
- v.formals[name] = formal_type
- super
- end
-
- redef fun accept_class_verifier(v)
- do
- super
- var c = v.local_class
- var o = c.global.intro
- if c == o then
- if n_type == null then
- _formal.bound = v.mmmodule.type_any.as_nullable
- else
- var stype = n_type.get_stype(v)
- if stype == null then return
- _formal.bound = stype
- end
- else
- var ob = o.get_formal(_formal.position).bound.for_module(v.mmmodule)
- if n_type == null then
- _formal.bound = ob
- else
- var stype = n_type.get_stype(v)
- if stype == null then return
- _formal.bound = stype
- if _formal.bound != ob then
- v.error(self, "Redef error: Cannot change formal parameter type of class {c}; got {_formal.bound}, expected {ob}.")
- end
- end
- end
- end
-end
-
-redef class ASuperclass
- readable var _ancestor: nullable MMSrcAncestor
-
- redef fun accept_class_specialization_builder(v)
- do
- super
- var c = n_type.get_local_class(v)
- if c == null then return
- var ancestor = new MMSrcAncestor(c)
- _ancestor = ancestor
- v.local_class.add_direct_parent(ancestor)
- end
-
- redef fun accept_class_ancestor_builder(v)
- do
- super
- _ancestor.stype = n_type.get_unchecked_stype(v)
- _ancestor.inheriter = v.local_class.get_type
- end
-
- redef fun accept_class_verifier(v)
- do
- super
- n_type.check_conform(v)
- end
-end
-
-redef class APropdef
- # Process and check properties of the property.
- # * Distinguish inits and methods
- # * Inherit or check visibility.
- # * Check redef errors.
- # * Check forbiden attribute definitions.
- # * Check signature conformance.
- private fun process_and_check(v: PropertyVerifierVisitor, prop: MMLocalProperty, has_redef: Bool, visibility_level: Int)
- do
- if prop.global.intro == prop then
- do_and_check_intro(v, prop, has_redef, visibility_level)
- else
- do_and_check_redef(v, prop, has_redef, visibility_level)
- end
- end
-
- # The part of process_and_check when prop is an introduction
- private fun do_and_check_intro(v: PropertyVerifierVisitor, prop: MMLocalProperty, has_redef: Bool, visibility_level: Int)
- do
- var glob = prop.global
- var gbc = prop.local_class.global
- if v.local_class.global.visibility_level >= 3 then
- # Method of private classes are private
- visibility_level = 3
- end
- glob.visibility_level = visibility_level
- if has_redef then
- v.error(self, "Error: No property {prop.local_class}::{prop} is inherited. Remove the redef keyword to define a new property.")
- end
- if glob.is_attribute then
- if gbc.is_interface then
- v.error(self, "Error: Attempt to define attribute {prop} in the interface {prop.local_class}.")
- else if gbc.is_enum then
- v.error(self, "Error: Attempt to define attribute {prop} in the enum class {prop.local_class}.")
- else if gbc.is_extern then
- v.error(self, "Error: Attempt to define attribute {prop} in the extern class {prop.local_class}.")
- end
- else if glob.is_init then
- if gbc.is_interface then
- v.error(self, "Error: Attempt to define a constructor {prop} in the class {prop.local_class}.")
- else if gbc.is_enum then
- v.error(self, "Error: Attempt to define a constructor {prop} in the enum {prop.local_class}.")
- end
-
- # ok in extern
- end
- if prop.signature == null then
- if glob.is_init then
- var supers = prop.local_class.super_methods_named(prop.name)
- inherit_signature(v, prop, supers)
- end
- if prop.signature != null or v.signature_builder.has_error_occured then
- # ok
- else if not v.signature_builder.untyped_params.is_empty then
- v.error(v.signature_builder.untyped_params.first, "Error: Untyped parameter.")
- else
- prop.signature = new MMSignature(new Array[MMParam], null, v.local_class.get_type)
- for clos in v.signature_builder.closure_decls do
- prop.signature.closures.add(clos.variable.closure)
- end
- end
- end
- end
-
- private fun inherit_signature(v: PropertyVerifierVisitor, prop: MMLocalProperty, supers: Array[MMLocalProperty])
- do
- var s = prop.signature
- for ip in supers do
- var isig = ip.signature.adaptation_to(v.local_class.get_type)
-
- if s == null then
- if v.signature_builder.params.length != isig.arity then
- return
- end
- for p in v.signature_builder.params do
- var t = isig[p.position]
- p.stype = t
- if p.position == isig.vararg_rank then
- t = v.type_array(t)
- end
- p.variable.stype = t
- end
-
- s = isig
- prop.signature = s
- end
- end
- end
-
- # The part of process_and_check when prop is a redefinition
- private fun do_and_check_redef(v: PropertyVerifierVisitor, prop: MMLocalProperty, has_redef: Bool, visibility_level: Int)
- do
- var is_init = self isa AInitPropdef
- var glob = prop.global
-
- if not has_redef then
- v.error(self, "Redef error: {prop.local_class}::{prop} is an inherited property. To redefine it, add the redef keyword.")
- return
- end
- if glob.is_init and not is_init then
- v.error(self, "Redef error: A method {prop.local_class}::{prop} cannot redefine a constructor.")
- else if not glob.is_init and is_init then
- v.error(self, "Redef error: A constructor {prop.local_class}::{prop} cannot redefine a method.")
- end
-
- var s = prop.signature
- #print "process {prop.local_class.mmmodule}::{prop.local_class}::{prop} from global {prop.global.local_property.local_class.mmmodule}::{prop.global.local_property.local_class}::{prop.global.local_property}"
- for i in prop.prhe.direct_greaters do
- var ip = i.local_class[prop.global]
- var isig = i.signature
- if isig == null then break # previous signature is invalid
- isig = isig.adaptation_to(v.local_class.get_type)
-
- if s == null then
- #print "{prop.full_name} inherits signature from {ip.full_name}"
- if v.signature_builder.params.length != isig.arity then
- v.error(self, "Redef error: {prop.local_class}::{prop} redefines {ip.local_class}::{ip} with {isig.arity} parameter(s).")
- return
- end
- if v.signature_builder.closure_decls.length != isig.closures.length then
- v.error(self, "Redef error: {prop.local_class}::{prop} redefines {ip.local_class}::{ip} with {isig.arity} closure(s).")
- return
- end
- for p in v.signature_builder.params do
- var t = isig[p.position]
- p.stype = t
- if p.position == isig.vararg_rank then
- t = v.type_array(t)
- end
- p.variable.stype = t
-
- isig.params[p.position].name = p.variable.name
- end
- s = isig
- prop.signature = s
- #print "s is null"
- end
-
- var nberr = v.tc.error_count
- #print "Check {prop.local_class}::{prop}{s} vs {ip.local_class}::{ip}{isig}"
- #print "s={s.object_id} isig={isig.object_id} isigorig={i.signature.object_id}"
-
- #print "orig signature: {i.signature.recv} . {i.signature}"
- #print "inh signature: {isig.recv} . {isig}"
- #print "redef signature: {s.recv} . {s}"
-
- if s.arity != isig.arity then
- v.error(self, "Redef error: {prop.local_class}::{prop} redefines {ip.local_class}::{ip} with {isig.arity} parameter(s).")
- else if s.arity > 0 then
- if self isa AMethPropdef then
- # A standard method
- for j in [0..s.arity[ do
- if s[j] != isig[j] then
- v.error(n_signature.n_params[j], "Redef error: Expected {isig[j]}, as in {ip.local_class}::{ip}.")
- end
- end
- else if self isa AAttrPropdef then
- # A write accessor
- if s[0] != isig[0] then
- v.error(n_type, "Redef error: Expected {isig[0]}, as in the parameter of {ip.local_class}::{ip}.")
- end
-
- else
- abort #
- end
- end
-
- var srt = s.return_type
- var isrt = isig.return_type
- if srt == null and isrt != null then
- v.error(self, "Redef error: The procedure {prop.local_class}::{prop} redefines the function {ip.local_class}::{ip}.")
- else if srt != null and isrt == null then
- v.error(self, "Redef error: The function {prop.local_class}::{prop} redefines the procedure {ip.local_class}::{ip}.")
- else if srt != null and isrt != null and not srt < isrt then
- var n: nullable ANode = null
- if self isa AMethPropdef then
- n = self.n_signature.n_type
- else if self isa AAttrPropdef then
- n = self.n_type
- else if self isa ATypePropdef then
- n = self.n_type
- end
- v.error(n, "Redef error: Expected {isrt}, as in {ip.local_class}::{ip}.")
- else if not s < isig and nberr == v.tc.error_count then
- # Systematic fallback for conformance check
- v.error(self, "Redef error: Incompatible redefinition of {ip.local_class}::{ip} with {prop.local_class}::{prop}")
- else if srt != null and isrt != null and srt != isrt and prop isa MMAttribute then
- v.error(self, "Redef error: Expected {isrt}, as in {ip.local_class}::{ip}.")
- end
- end
-
- if visibility_level != 1 and glob.visibility_level != visibility_level then
- v.error(self, "Redef error: {prop.local_class}::{prop} redefinition cannot change visibility.")
- end
- glob.check_visibility(v, self, v.mmmodule, true)
- end
-end
-
-redef class AAttrPropdef
- redef readable var _readmethod: nullable MMSrcMethod
- redef readable var _writemethod: nullable MMSrcMethod
- var _prop: nullable MMSrcAttribute
- redef fun prop do return _prop.as(not null)
-
- redef fun accept_property_builder(v)
- do
- super
- var name: Symbol
- if n_id != null then
- name = n_id.to_symbol
- else
- name = ("@" + n_id2.text).to_symbol
- end
- var lc = v.local_class
- var prop = new MMSrcAttribute(name, lc, self)
- _prop = prop
- v.local_class.add_src_local_property(v, prop)
-
- if n_readable != null or n_id == null then
- if n_id != null then
- name = n_id.text.substring_from(1).to_symbol
- else
- name = n_id2.to_symbol
- end
- var readmethod = new MMReadImplementationMethod(name, lc, self)
- _readmethod = readmethod
- v.local_class.add_src_local_property(v, readmethod)
- end
- if n_writable != null or n_id == null then
- if n_id != null then
- name = (n_id.text.substring_from(1) + "=").to_symbol
- else
- name = (n_id2.text + "=").to_symbol
- end
- var writemethod = new MMWriteImplementationMethod(name, lc, self)
- _writemethod = writemethod
- v.local_class.add_src_local_property(v, writemethod)
- end
- end
-
- redef fun accept_property_verifier(v)
- do
- super
- var t: nullable MMType = null
- if n_type != null then
- var t0 = n_type.get_stype(v)
- if t0 != null then t = t0 else return
- else if n_expr != null then
- t = n_expr.get_easy_stype(v)
- end
-
- if t == null then
- v.error(self, "Not yet implemented: Attribute definition {prop.local_class}::{prop} requires an explicit type.")
- return
- end
-
- var prop = prop
- var signature = new MMSignature(new Array[MMParam], t, v.local_class.get_type)
- prop.signature = signature
- var visibility_level = n_visibility.level
- process_and_check(v, prop, n_id != null and n_kwredef != null, visibility_level)
- if n_readable != null or n_id == null then
- var m = _readmethod.as(not null)
- m.signature = signature
- process_and_check(v, m, (n_readable != null and n_readable.n_kwredef != null) or (n_id == null and n_kwredef != null), visibility_level)
- if n_type != null then n_type.check_visibility(v, m)
- end
- if n_writable != null or n_id == null then
- var m = _writemethod.as(not null)
- m.signature = new MMSignature(new Array[MMParam].with_items(new MMParam(t, once "value".to_symbol)), null, v.local_class.get_type)
- var vl = visibility_level
- if n_id == null then
- if n_writable == null then vl = 3 else vl = n_writable.n_visibility.level # write accessor has a specific visibility
- end
- process_and_check(v, m, n_writable != null and n_writable.n_kwredef != null, vl)
- if n_type != null then n_type.check_visibility(v, m)
- end
- end
-
- redef fun accept_abs_syntax_visitor(v)
- do
- v.local_property = _prop
- super
- v.local_property = null
- end
-end
-
-redef class AMethPropdef
- # Name of the method
- readable var _name: nullable Symbol
-
- var _method: nullable MMMethSrcMethod
- redef fun method do return _method.as(not null)
-
- redef fun accept_property_builder(v)
- do
- super
- var name: Symbol
- if n_methid == null then
- if self isa AInitPropdef then
- name = once "init".to_symbol
- else
- name = once "main".to_symbol
- end
- else
- name = n_methid.name.as(not null)
- # FIXME: Add the 'unary' keyword
- if n_methid.name == (once "-".to_symbol) then
- var ns = n_signature
- if ns != null and ns.n_params.length == 0 then
- name = once "unary -".to_symbol
- end
- end
- end
- _name = name
- var prop = new MMMethSrcMethod(name, v.local_class, self)
- _method = prop
- v.local_class.add_src_local_property(v, prop)
- end
-
- redef fun accept_property_verifier(v)
- do
- v.signature_builder = new SignatureBuilder
- super
-
- if v.signature_builder.has_error_occured then return
-
- if v.signature_builder.signature == null then
- #_method.signature = new MMSignature(new Array[MMType], null, v.local_class.get_type)
- else
- method.signature = v.signature_builder.signature.as(not null)
- end
- var visibility_level = 1
- if n_visibility != null and n_visibility.level > 1 then
- visibility_level = n_visibility.level
- end
- process_and_check(v, method, n_kwredef != null, visibility_level)
- if n_signature != null then n_signature.check_visibility(v, method)
- end
-
- redef fun accept_abs_syntax_visitor(v)
- do
- v.local_property = _method
- super
- v.local_property = null
- end
-end
-
-redef class AMainMethPropdef
- redef fun process_and_check(v, prop, has_redef, visibility_level)
- do
- prop.global.visibility_level = visibility_level
- prop.signature = new MMSignature(new Array[MMParam], null, v.local_class.get_type)
- # Disable all checks for main
- end
-end
-
-redef class AExternPropdef
- redef fun accept_property_verifier(v)
- do
- super # Compute signature
- var ename: String
- if n_extern != null then
- ename = n_extern.text
- ename = ename.substring(1, ename.length-2)
- else
- ename = method.default_extern_name
- end
- method.extern_name = ename
- end
-end
-
-redef class ATypePropdef
- redef fun prop do return _prop.as(not null)
- var _prop: nullable MMSrcTypeProperty
-
- redef fun accept_property_builder(v)
- do
- super
- var name = n_id.to_symbol
- var prop = new MMSrcTypeProperty(name, v.local_class, self)
- _prop = prop
- v.local_class.add_src_local_property(v, prop)
- end
-
- redef fun accept_property_verifier(v)
- do
- super
- var signature = new MMSignature(new Array[MMParam], n_type.get_stype(v), v.local_class.get_type)
- prop.signature = signature
- var visibility_level = n_visibility.level
- process_and_check(v, prop, n_kwredef != null, visibility_level)
- end
-
- redef fun accept_abs_syntax_visitor(v)
- do
- v.local_property = _prop
- super
- v.local_property = null
- end
-end
-
-# Visitor used to build a full method name from multiple tokens
-private class MethidAccumulator
- super Visitor
- readable var _name: Buffer = new Buffer
- redef fun visit(n)
- do
- if n isa Token then
- _name.append(n.text)
- else
- n.visit_all(self)
- end
- end
-end
-
-redef class AMethid
- redef readable var _name: nullable Symbol
-
- redef fun accept_property_builder(v)
- do
- var accumulator = new MethidAccumulator
- accumulator.enter_visit(self)
- _name = accumulator.name.to_s.to_symbol
- super
- end
-end
-
-redef class ASignature
- redef fun accept_property_verifier(v)
- do
- super
- if v.signature_builder.has_error_occured then
- return
- else if not v.signature_builder.untyped_params.is_empty then
- if v.signature_builder.untyped_params.first != v.signature_builder.params.first or n_type != null then
- v.error(v.signature_builder.untyped_params.first, "Syntax error: untyped parameter.")
- return
- end
- else if not v.signature_builder.params.is_empty or n_type != null then
- var pars = new Array[MMParam]
- for p in v.signature_builder.params do
- pars.add( new MMParam( p.stype.as(not null), p.n_id.to_symbol ) )
- end
- var ret: nullable MMType = null
- if n_type != null then
- ret = n_type.get_stype(v)
- if ret == null then
- v.signature_builder.has_error_occured = true
- return
- end
- end
- v.signature_builder.signature = new MMSignature(pars, ret, v.local_class.get_type)
- if v.signature_builder.vararg_rank >= 0 then
- v.signature_builder.signature.vararg_rank = v.signature_builder.vararg_rank
- end
- for clos in v.signature_builder.closure_decls do
- v.signature_builder.signature.closures.add(clos.variable.closure)
- end
- end
- end
-
- # Check that visibilities of types in the signature are compatible with the visibility of the property.
- fun check_visibility(v: AbsSyntaxVisitor, p: MMLocalProperty)
- do
- if p.global.visibility_level >= 3 then return
- for n in n_params do
- if n.n_type != null then n.n_type.check_visibility(v, p)
- end
- if n_type != null then n_type.check_visibility(v, p)
- end
-end
-
-redef class AParam
- redef readable var _position: Int = 0
-
- redef fun variable: ParamVariable do return _variable.as(not null)
- var _variable: nullable ParamVariable
-
- # The type of the parameter in signature
- readable writable var _stype: nullable MMType
-
- redef fun accept_property_verifier(v)
- do
- super
- _position = v.signature_builder.params.length
- _variable = new ParamVariable(n_id.to_symbol, n_id)
- v.signature_builder.params.add(self)
- v.signature_builder.untyped_params.add(self)
- if n_type != null then
- var stype = n_type.get_stype(v)
- if stype == null then
- v.signature_builder.has_error_occured = true
- return
- end
- for p in v.signature_builder.untyped_params do
- p.stype = stype
- if is_vararg then
- if v.signature_builder.vararg_rank == -1 then
- v.signature_builder.vararg_rank = p.position
- else
- v.error(self, "Error: A vararg parameter is already defined.")
- end
- stype = v.type_array(stype)
- end
- p.variable.stype = stype
- end
- v.signature_builder.untyped_params.clear
- end
- end
-
- fun is_vararg: Bool do return n_dotdotdot != null
-end
-
-redef class AClosureDecl
- redef readable var _position: Int = 0
-
- redef fun variable: ClosureVariable do return _variable.as(not null)
- var _variable: nullable ClosureVariable
-
- redef fun accept_property_verifier(v)
- do
- var old_signature_builder = v.signature_builder
- v.signature_builder = new SignatureBuilder
- super
- if v.signature_builder.has_error_occured then
- return
- end
- var sig = v.signature_builder.signature
- if sig == null then
- sig = new MMSignature(new Array[MMParam], null, v.local_class.get_type)
- end
- if sig.return_type != null and n_kwbreak != null then
- v.error(self, "Syntax Error: A break block cannot have a return value.")
- end
-
- # Add the finalizer to the closure signature
- var finalize_sig = new MMSignature(new Array[MMParam], null, v.mmmodule.type_any) # FIXME should be no receiver
- var finalizer_clos = new MMClosure(once ("break".to_symbol), finalize_sig, false, true)
- sig.closures.add(finalizer_clos)
-
- var name = n_id.to_symbol
- var clos = new MMClosure(name, sig, n_kwbreak != null, n_expr != null)
- for c in old_signature_builder.closure_decls do
- if c.n_id.to_symbol == name then
- v.error(n_id, "Error: A closure '!{name}' already defined at {c.n_id.location.relative_to(n_id.location)}.")
- return
- end
- end
- v.signature_builder = old_signature_builder
- _position = old_signature_builder.closure_decls.length
- old_signature_builder.closure_decls.add(self)
- _variable = new ClosureVariable(n_id.to_symbol, n_id, clos)
- end
-end
-
-redef class AType
- # Check that visibilities of types in the signature are compatible with the visibility of the property.
- private fun check_visibility(v: AbsSyntaxVisitor, p: MMLocalProperty)
- do
- if p.global.visibility_level >= 3 then return
- var t = get_stype(v)
- if t == null then return
- var bc = t.local_class
- if bc.global.visibility_level >= 3 then
- v.error(self, "Access error: Class {bc} is private and cannot be used in the signature of the non-private property {p}.")
- end
- for n in n_types do
- n.check_visibility(v, p)
- end
- end
-end
-
-redef class AExpr
- redef fun accept_class_builder(v) do end
- redef fun accept_property_builder(v) do end
- redef fun accept_property_verifier(v) do end
-
- private fun get_easy_stype(v:PropertyVerifierVisitor) : nullable MMType do return null
-end
-
-redef class ABoolExpr
- redef fun get_easy_stype(v) do return v.type_bool
-end
-
-redef class AStringExpr
- redef fun get_easy_stype(v) do return v.type_string
-end
-
-redef class ACharExpr
- redef fun get_easy_stype(v) do return v.type_char
-end
-
-redef class AIntExpr
- redef fun get_easy_stype(v) do return v.type_int
-end
-
-redef class AFloatExpr
- redef fun get_easy_stype(v) do return v.type_float
-end
-
-redef class ANewExpr
- redef fun get_easy_stype(v) do return n_type.get_stype(v)
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Manage nested escapable blocks (while, for and closure) and escape statements (break and continue)
-module scope
-
-import syntax_base
-import flow
-
-# All-in-one context for scoped things.
-# It features:
-# * variable and labels visibility
-# * control for 'break' and 'continue'
-# This class manage itself the entree and the exit of scopes:
-# * use push or push_escapable to enter in a new scope block
-# * use pop to remove the last scope block (do not foget do pop!)
-# Warning: ScopeContext are created empty: you must perform a first push to register variables
-class ScopeContext
- # Stack of scope blocks
- var _stack: Array[ScopeBlock] = new Array[ScopeBlock]
-
- # Known variables
- # (all variables, even out of scope ones)
- # Used for debuging
- var _variables: Array[Variable] = new Array[Variable]
-
- # Return the variable associated with a name
- fun [](n: Symbol): nullable Variable
- do
- var i = _stack.length - 1
- while i >= 0 do
- var b = _stack[i]
- var va = b.get_variable(n)
- if va != null then return va
- i -= 1
- end
- return null
- end
-
- # Register a variable with its name in the current scope
- # Display an error if name conflict
- fun add_variable(v: Variable)
- do
- var old_var = self[v.name]
- if old_var != null then
- _visitor.error(v.decl, "Error: '{v}' already defined at {old_var.decl.location.relative_to(v.decl.location)}.")
- end
- _stack.last.add_variable(v)
- _variables.add(v)
- end
-
- # Push a simple unlabeled variable scope block
- fun push(node: ANode)
- do
- var block = new ScopeBlock(node)
- _stack.push(block)
- end
-
- # Push a specific escapable block
- # Display error message if there is a problem with the label
- fun push_escapable(block: EscapableBlock, n_label: nullable ALabel)
- do
- _stack.push(block)
- if n_label != null then
- var lab = n_label.n_id.to_symbol
- var i = _stack.length - 1
- while i >= 0 do
- var b = _stack[i]
- if b isa EscapableBlock and b.lab == lab then
- visitor.error(n_label, "Syntax error: label {lab} already defined at {b.lab_location.relative_to(n_label.location)}.")
- return
- end
- i -= 1
- end
- block._lab = lab
- block._lab_location = n_label.location
- end
- end
-
- # Return the last stacked block that accepts unlabelled break/continue
- fun head: nullable EscapableBlock
- do
- var i = _stack.length - 1
- while i >= 0 do
- var h = _stack[i]
- if h isa EscapableBlock and not (h isa BreakOnlyEscapableBlock) then return h
- i -= 1
- end
- return null
- end
-
- # Return the block associed to a label
- # Output an error end return null if the label is not known
- fun get_by_label(nl: ALabel): nullable EscapableBlock
- do
- var i = _stack.length - 1
- var lab = nl.n_id.to_symbol
- while i >= 0 do
- var b = _stack[i]
- if b isa EscapableBlock and b.lab == lab then return b
- i -= 1
- end
- visitor.error(nl, "Syntax error: invalid label {lab}.")
- return null
- end
-
- # Remove the last block (the last stacked)
- fun pop
- do
- var n = _stack.pop
- end
-
- readable var _visitor: AbsSyntaxVisitor
- init (v: AbsSyntaxVisitor)
- do
- _visitor = v
- end
-end
-
-###############################################################################
-
-# A single scope block. Thez are stacked in a ScopeContext
-# This block is used only to store local variables
-class ScopeBlock
- # The syntax node of the block
- readable var _node: ANode
-
- # List of local variables of the block
- # Lazily constructed since many blocks does not have local variables
- var _dico: nullable HashMap[Symbol, Variable] = null
-
- private fun add_variable(v: Variable)
- do
- var dico = _dico
- if dico == null then
- dico = new HashMap[Symbol, Variable]
- _dico = dico
- end
- dico[v.name] = v
- end
-
- private fun get_variable(n: Symbol): nullable Variable
- do
- var dico = _dico
- if dico == null then return null
- if not dico.has_key(n) then return null
- return dico[n]
- end
-
- init(node: ANode)
- do
- _node = node
- end
-end
-
-# A escapable block correspond to a scope block where break and/or continue can by used.
-# EscapableBlocks can also be labeled.
-# 'for' and 'while' use this class
-# labeled 'do' uses the BreakOnlyEscapableBlock subclass
-# closures uses the EscapableClosure subclass
-class EscapableBlock
- super ScopeBlock
- # The label of the block (if any)
- # Set by the push in EscapableContext
- readable var _lab: nullable Symbol
-
- # The location of the label (if any)
- readable var _lab_location: nullable Location
-
- # Is self a break closure ?
- fun is_break_block: Bool do return false
-
- # Collected expressions used in breaks.
- # null if break does not accept values.
- # break_list is used to store expressions used in break statments and perform type checks latter
- fun break_list: nullable Array[AExpr] do return null
-
- # The static type required by the continue statement (if any)
- fun continue_stype: nullable MMType do return null
-
- # Alternatives flow contexts for breaks
- readable var _break_flow_contexts: Array[FlowContext] = new Array[FlowContext]
-
- init(node: ANode)
- do
- super(node)
- end
-end
-
-# specific EscapableBlock where only labelled break can be used
-class BreakOnlyEscapableBlock
- super EscapableBlock
- redef fun is_break_block: Bool do return true
-
- init(node: ANode) do super
-end
-
-# specific EscapableBlock for closures
-class EscapableClosure
- super EscapableBlock
- # The associated closure
- readable var _closure: MMClosure
-
- redef fun is_break_block do return _closure.is_break
-
- redef readable var _break_list: nullable Array[AExpr]
-
- redef fun continue_stype do return _closure.signature.return_type
-
- init(node: ANode, closure: MMClosure, break_list: nullable Array[AExpr])
- do
- super(node)
- _closure = closure
- _break_list = break_list
- end
-end
-
-###############################################################################
-
-abstract class AEscapeExpr
- super ALabelable
- # The associated escapable block
- readable var _escapable: nullable EscapableBlock
-
- # The name of the keyword
- fun kwname: String is abstract
-
- # Compute, set and return the associated escapable block
- fun compute_escapable_block(lctx: ScopeContext): nullable EscapableBlock
- do
- var block: nullable EscapableBlock
- var nl = n_label
- if nl != null then
- block = lctx.get_by_label(nl)
- else
- block = lctx.head
- if block == null then
- lctx.visitor.error(self, "Syntax Error: '{kwname}' statment outside block.")
- end
- end
- _escapable = block
- return block
- end
-end
-
-redef class AContinueExpr
- super AEscapeExpr
- redef fun kwname do return "continue"
-end
-
-redef class ABreakExpr
- super AEscapeExpr
- redef fun kwname do return "break"
-end
-
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Full syntax analysis of NIT AST.
-# Detect syntax and some metamodel errors.
-module syntax
-
-import mmloader
-import mmbuilder
-import typing
-import icode_generation
-import extern_inline
-import extern_type_inheritance
-
-# Loader of nit source files
-class SrcModuleLoader
- super ModuleLoader
- redef type MODULE: MMSrcModule
-
- redef fun file_type do return "nit"
-
- redef fun parse_file(context, file, filename, name, dir)
- do
- var name_is_valid = name.to_s.length > 0 and name.to_s[0].is_lower
- for char in name.to_s do if not char.is_digit and not char.is_letter and not char == '_'
- then
- name_is_valid = false
- break
- end
- if not name_is_valid then
- context.error( null, "{filename}: Error module name \"{name}\", must start with a lower case letter and contain only letters, digits and '_'." )
- end
-
- var source = new SourceFile(filename, file)
- var lexer = new Lexer(source)
- var parser = new Parser(lexer)
- var node_tree = parser.parse
- if node_tree.n_base == null then
- var err = node_tree.n_eof
- assert err isa AError
- context.fatal_error(err.location, err.message)
- end
- var node_module = node_tree.n_base
- assert node_module != null
- var module_loc = new Location.with_file(source)
- var mod = new MMSrcModule(context, node_module, dir, name, module_loc)
- return mod
- end
-
- redef fun process_metamodel(context, mod)
- do
- mod.process_supermodules(context)
- context.info("Syntax analysis for module: {mod.name}", 2)
- mod.process_syntax(context)
- end
-
- init do end
-end
-
-redef class MMSrcModule
- # Loading and syntax analysis of super modules
- private fun process_supermodules(tc: ToolContext)
- do
- node.import_super_modules(tc, self)
- end
-
- # Syntax analysis and MM construction for the module
- # Require than supermodules are processed
- private fun process_syntax(tc: ToolContext)
- do
- do_mmbuilder(tc)
- tc.check_errors
-
- do_typing(tc)
- tc.check_errors
-
- generate_icode(tc)
- tc.check_errors
-
- if not tc.keep_ast then clear_ast
- end
-end
-
-redef class ToolContext
- # Should the AST be preserved in source modules after syntax processing?
- # Default is false.
- readable writable var _keep_ast: Bool = false
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Common syntax structures for syntax analysis of NIT AST.
-module syntax_base
-
-import parser
-import mmloader
-
-# Concrete NIT source module
-class MMSrcModule
- super MMModule
- # A source module can locate AST nodes of related MM entities
- # Once a source module AST is no more needed, _nodes is set to null
- # See ToolContext::keep_ast property in syntax.nit for details
- var _nodes: nullable HashMap[Object, nullable ANode] = new HashMap[Object, nullable ANode]
-
- # Release the AST
- fun clear_ast do _nodes = null
-
- # The related AST node
- fun node: AModule do return nodes(self).as(AModule)
-
- # Concrete NIT source local classs by name
- readable var _src_local_classes: Map[Symbol, MMSrcLocalClass]
-
- init(c: MMContext, source: AModule, dir: MMDirectory, name: Symbol, loc: Location)
- do
- super(name, dir, c, loc)
- nodes(self) = source
- _src_local_classes = new HashMap[Symbol, MMSrcLocalClass]
- end
-
- redef fun nodes(o: Object): nullable ANode
- do
- if _nodes != null and _nodes.has_key(o) then return _nodes[o] else return null
- end
- redef fun nodes=(o: Object, n: nullable ANode)
- do
- assert not _nodes.has_key(o)
- _nodes[o] = n
- end
-end
-
-redef class MMModule
- # The AST node of some entity
- private fun nodes(o: Object): nullable ANode do return null
- # The AST node of some entity
- private fun nodes=(o: Object, n: nullable ANode) do abort
-end
-
-redef class MMGlobalClass
- # Check that a module can access a class
- fun check_visibility(v: AbsSyntaxVisitor, n: ANode, cm: MMSrcModule): Bool do
- var pm = intro.mmmodule
- assert pm isa MMSrcModule
- var vpm = cm.visibility_for(pm)
- if vpm == 3 then
- return true
- else if vpm == 0 then
- v.error(n, "Visibility error: Class {self} comes from the hidden module {pm}.") # TODO: should not occur
- return false
- else if visibility_level >= 3 then
- v.error(n, "Visibility error: Class {self} is private.")
- return false
- end
- return true
- end
-end
-
-# Concrete NIT source local classes
-class MMSrcLocalClass
- super MMConcreteClass
- # The first related AST node (if any)
- fun node: nullable AClassdef do return mmmodule.nodes(self).as(nullable AClassdef)
-
- # Concrete NIT source generic formal parameter by name
- readable var _formal_dict: Map[Symbol, MMTypeFormalParameter] = new HashMap[Symbol, MMTypeFormalParameter]
-
- # Concrete NIT source properties by name
- readable var _src_local_properties: Map[Symbol, MMLocalProperty]
-
- init(mod: MMSrcModule, n: Symbol, cla: nullable AClassdef, a: Int)
- do
- super(mod, n, a)
- mod.nodes(self) = cla
- _src_local_properties = new HashMap[Symbol, MMLocalProperty]
- end
-end
-
-redef class MMGlobalProperty
- # Check that a module can access a property
- fun check_visibility(v: AbsSyntaxVisitor, n: ANode, cm: MMSrcModule, allows_protected: Bool): Bool do
- var pm = local_class.mmmodule
- assert pm isa MMSrcModule
- var vpm = cm.visibility_for(pm)
- if vpm == 3 then
- return true
- else if vpm == 0 then
- # TODO: should not occurs
- v.error(n, "Visibility error: Property {self} comes from the hidden module {pm}.")
- return false
- else if visibility_level >= 3 then
- v.error(n, "Visibility error: Property {self} is private.")
- return false
- else if visibility_level >= 2 and not allows_protected then
- v.error(n, "Visibility error: Property {self} is protected and can only acceded by self.")
- return false
- end
- return true
- end
-end
-
-redef class MMLocalProperty
- # The attached node (if any)
- fun node: nullable ANode do return null
-
- # Is the concrete method defined as init
- fun is_init: Bool do return false
-end
-
-# Concrete NIT source attribute
-class MMSrcAttribute
- super MMAttribute
- redef fun node: nullable AAttrPropdef do return mmmodule.nodes(self).as(nullable AAttrPropdef)
- init(name: Symbol, cla: MMLocalClass, n: AAttrPropdef)
- do
- super(name, cla)
- cla.mmmodule.nodes(self) = n
- end
-end
-
-# Concrete NIT source method
-abstract class MMSrcMethod
- super MMMethod
- redef fun is_intern do return false
- redef fun is_extern do return false
- redef fun is_abstract do return false
- redef fun extern_name do return null
-end
-
-# Concrete NIT source method for an automatic accesor
-abstract class MMAttrImplementationMethod
- super MMSrcMethod
- redef fun node: nullable AAttrPropdef do return mmmodule.nodes(self).as(nullable AAttrPropdef)
- init(name: Symbol, cla: MMLocalClass, n: AAttrPropdef)
- do
- super(name, cla)
- cla.mmmodule.nodes(self) = n
- end
-end
-
-# Concrete NIT source method for an automatic read accesor
-class MMReadImplementationMethod
- super MMAttrImplementationMethod
- init(name: Symbol, cla: MMLocalClass, n: AAttrPropdef)
- do
- super(name, cla, n)
- end
-end
-
-# Concrete NIT source method for an automatic write accesor
-class MMWriteImplementationMethod
- super MMAttrImplementationMethod
- init(name: Symbol, cla: MMLocalClass, n: AAttrPropdef)
- do
- super(name, cla, n)
- end
-end
-
-# Concrete NIT source method for an explicit method
-class MMMethSrcMethod
- super MMSrcMethod
- redef readable var _is_init: Bool
- redef readable var _is_intern: Bool
- redef readable var _is_extern: Bool
- redef readable var _is_abstract: Bool
- redef readable writable var _extern_name: nullable String # Will be computed during MMBuilder
- redef readable var _explicit_casts : Set[MMImportedCast] = new HashSet[MMImportedCast]
- redef readable var _explicit_imports : Set[MMExplicitImport] = new HashSet[MMExplicitImport]
- redef fun node: nullable AMethPropdef do return mmmodule.nodes(self).as(nullable AMethPropdef)
- init(name: Symbol, cla: MMLocalClass, n: nullable AMethPropdef)
- do
- super(name, cla)
- cla.mmmodule.nodes(self) = n
- _is_init = node isa AInitPropdef
- _is_intern = node isa AInternMethPropdef
- _is_extern = node isa AExternPropdef
- _is_abstract = node isa ADeferredMethPropdef
- _extern_name = null
-
- if is_extern then
- mmmodule.is_extern_hybrid = true
- end
- end
-end
-
-# Concrete NIT source virtual type
-class MMSrcTypeProperty
- super MMLocalProperty
- super MMTypeProperty
- init(name: Symbol, cla: MMLocalClass, n: ATypePropdef)
- do
- super(name, cla)
- end
-end
-
-# Concrete NIT implicit constructor
-class MMImplicitInit
- super MMMethSrcMethod
- fun super_init: nullable MMLocalProperty is abstract
- redef fun is_init do return true
- readable var _unassigned_attributes: Array[MMSrcAttribute]
- readable var _super_inits: Array[MMLocalProperty]
- init(cla: MMLocalClass, unassigned_attributes: Array[MMSrcAttribute], super_inits: Array[MMLocalProperty])
- do
- super(once "init".to_symbol, cla, null)
- _unassigned_attributes = unassigned_attributes
- _super_inits = super_inits
- end
-end
-
-# Local variables
-abstract class Variable
- # Name of the variable
- readable var _name: Symbol
-
- # Declaration AST node
- readable var _decl: nullable ANode
-
- # Static type
- readable writable var _stype: nullable MMType
-
- redef fun to_s do return _name.to_s
-
- fun kind: String is abstract
-
- init(n: Symbol, d: nullable ANode)
- do
- _name = n
- _decl = d
- end
-end
-
-# Variable declared with 'var'
-class VarVariable
- super Variable
- redef fun kind do return once "variable"
- init(n: Symbol, d: ANode) do super
-end
-
-# Parameter of method (declared in signature)
-class ParamVariable
- super Variable
- redef fun kind do return once "parameter"
- init(n: Symbol, d: nullable ANode) do super
-end
-
-# Automatic variable (like in the 'for' statement)
-class AutoVariable
- super Variable
- redef fun kind do return once "automatic variable"
- init(n: Symbol, d: ANode) do super
-end
-
-# False variable corresponding to closures declared in signatures
-# Lives in the same namespace than variables
-class ClosureVariable
- super Variable
- redef fun kind do return once "closure"
-
- # The signature of the closure
- readable var _closure: MMClosure
-
- init(n: Symbol, d: ANode, c: MMClosure)
- do
- super(n, d)
- _closure = c
- end
-end
-
-###############################################################################
-
-# Visitor used during the syntax analysis
-abstract class AbsSyntaxVisitor
- super Visitor
- fun get_type_by_name(clsname: Symbol): MMType
- do
- if not _mmmodule.has_global_class_named(clsname) then _tc.fatal_error(_mmmodule.location, "Missing necessary class: \"{clsname}\"")
- var cls = _mmmodule.class_by_name(clsname)
- return cls.get_type
- end
-
- fun get_instantiated_type_by_name(clsname: Symbol, vtype: Array[MMType]): MMType
- do
- if not _mmmodule.has_global_class_named(clsname) then _tc.fatal_error(_mmmodule.location, "Missing necessary class: \"{clsname}\"")
- var cls = _mmmodule.class_by_name(clsname)
- return cls.get_instantiate_type(vtype)
- end
-
- # The root type Object
- fun type_object: MMType
- do
- return get_type_by_name(once ("Object".to_symbol))
- end
-
- # The primitive type Bool
- fun type_bool: MMType
- do
- return get_type_by_name(once ("Bool".to_symbol))
- end
-
- # The primitive type Int
- fun type_int: MMType
- do
- return get_type_by_name(once ("Int".to_symbol))
- end
-
- # The primitive type Float
- fun type_float: MMType
- do
- return get_type_by_name(once ("Float".to_symbol))
- end
-
- # The primitive type Char
- fun type_char: MMType
- do
- return get_type_by_name(once ("Char".to_symbol))
- end
-
- # The primitive type String
- fun type_string: MMType
- do
- return get_type_by_name(once ("String".to_symbol))
- end
-
- # The primitive type NativeString
- fun type_nativestring: MMType
- do
- return get_type_by_name(once ("NativeString".to_symbol))
- end
-
- # The primitive type Array[?]
- fun type_array(stype: MMType): MMType
- do
- return get_instantiated_type_by_name(once ("Array".to_symbol), [stype])
- end
-
- # The primitive type Discrete
- fun type_discrete: MMType
- do
- return get_type_by_name(once ("Discrete".to_symbol))
- end
-
- # The primitive type Range[?]
- fun type_range(stype: MMType): MMType
- do
- return get_instantiated_type_by_name(once ("Range".to_symbol), [stype])
- end
-
- # The primitive type of null
- fun type_none: MMType
- do
- return _mmmodule.type_none
- end
-
- fun get_method(recv: MMType, name: Symbol): MMMethod
- do
- if not recv.local_class.has_global_property_by_name(name) then
- fatal_error(current_node, "Fatal Error: {recv} must have a property named {name}.")
- end
- return recv.local_class.select_method(name)
- end
-
- # The current module
- readable var _mmmodule: MMSrcModule
-
- # The current class
- fun local_class: MMSrcLocalClass do return _local_class.as(not null)
- writable var _local_class: nullable MMSrcLocalClass
-
- # The current property
- fun local_property: MMLocalProperty do return _local_property.as(not null)
- writable var _local_property: nullable MMLocalProperty
-
- # The current tool configuration/status
- readable var _tc: ToolContext
-
- # Display an error for a given syntax node
- fun error(n: nullable ANode, s: String)
- do
- _tc.error(if n == null then null else n.hot_location, s)
- end
-
- # Add an error, show errors and quit
- fun fatal_error(n: nullable ANode, s: String)
- do
- _tc.fatal_error(if n == null then null else n.hot_location, s)
- end
-
- # Display a warning for a given syntax node
- fun warning(n: nullable ANode, s: String)
- do
- _tc.warning(if n == null then null else n.hot_location, s)
- end
-
- # Check conformity and display error
- fun check_conform(n: ANode, subtype: nullable MMType, stype: nullable MMType): Bool
- do
- if stype == null or subtype == null then
- return false
- end
- if subtype < stype then
- return true
- end
- error(n, "Type error: expected {stype}, got {subtype}")
- return false
- end
-
- # Check that an expression has a static type and that
- # Display an error and return false if n is a statement
- # Require that the static type of n is known
- fun check_expr(n: AExpr): Bool
- do
- if not n.is_typed then
- if tc.error_count == 0 then
- print("{n.location} not typed but not error")
- abort
- end
- # An error occured in a sub node,
- # sillently cascade fail
- return false
- else if n.is_statement then
- error(n, "Type error: expected expression.")
- return false
- end
- return true
- end
-
- # Combine check_conform and check_expr
- fun check_conform_expr(n: AExpr, stype: nullable MMType): Bool
- do
- if stype == null then return false
- if check_expr(n) then return check_conform(n, n.stype, stype) else return false
- end
-
- # Check conformance between multiple expressions and a static type
- # Conformance is granted if among them there is a most general type
- # Return the most general type if a conformance is found
- # Display an error and return null if no conformance is found
- # The only allowed combinaison is with the nullable marker
- # @param stype is a possible additional type (without node)
- # Examples:
- # Int, Int, Object => return Object
- # Int, Float => display error, return null
- # nullable Int, Object => return nullable Object
- fun check_conform_multiexpr(stype: nullable MMType, nodes: Collection[AExpr]): nullable MMType
- do
- var node: nullable AExpr = null # candidate node
- for n in nodes do
- if not check_expr(n) then return null
- var ntype = n.stype
- if stype != null and stype.is_nullable != ntype.is_nullable then
- # nullable combinaison: if one of them is nulable, considers that both are
- stype = stype.as_nullable
- ntype = ntype.as_nullable
- end
- if stype == null or stype < ntype then
- stype = ntype
- node = n
- end
- end
- assert stype != null
- for n in nodes do
- if not n.stype < stype then
- if node == null then
- error(n, "Type error: no most general type. Got {n.stype} and {stype}.")
- else
- error(n, "Type error: no most general type. Got {n.stype} and {stype} at {node.location.relative_to(n.location)}.")
- end
- return null
- end
- end
- return stype
- end
-
- protected init(tc: ToolContext, mmmodule: MMSrcModule)
- do
- _tc = tc
- _mmmodule = mmmodule
- end
-end
-
-###############################################################################
-
-redef class ANode
- protected fun accept_abs_syntax_visitor(v: AbsSyntaxVisitor) do visit_all(v)
-end
-
-redef class Token
- var _symbol_cache: nullable Symbol
-
- # Symbol associated with the text
- # Lazily computed
- fun to_symbol: Symbol
- do
- var s = _symbol_cache
- if s == null then
- s = text.to_symbol
- _symbol_cache = s
- end
- return s
- end
-end
-
-redef class AClassdef
- # Associated class (MM entity)
- fun local_class: MMSrcLocalClass is abstract
-
- # Next AClassdef of the same class (if any)
- readable writable var _next_node: nullable AClassdef = null
-end
-
-redef class APropdef
- # Associated 'self' variable
- fun self_var: ParamVariable is abstract
-end
-
-redef class AAttrPropdef
- # Associated attribute (MM entity)
- fun prop: MMSrcAttribute is abstract
-
- # Associated read accessor (MM entity)
- fun readmethod: nullable MMSrcMethod is abstract
-
- # Associated write accessor (MM entity)
- fun writemethod: nullable MMSrcMethod is abstract
-end
-
-redef class AConcreteInitPropdef
- readable var _super_init_calls: Array[MMMethod] = new Array[MMMethod]
- readable var _explicit_super_init_calls: Array[MMMethod] = new Array[MMMethod]
-end
-
-redef class AMethPropdef
- # Associated method (MM entity)
- fun method: MMMethSrcMethod is abstract
-end
-
-redef class ATypePropdef
- # Associated formal type (MM entity)
- fun prop: MMSrcTypeProperty is abstract
-end
-
-redef class AParam
- # Position in the signature
- fun position: Int is abstract
-
- # Associated local variable
- fun variable: ParamVariable is abstract
-end
-
-redef class AClosureDecl
- # Position in the signature
- fun position: Int is abstract
-
- # Associated closure variable
- fun variable: ClosureVariable is abstract
-end
-
-redef class AType
- # Is the node correcly typed
- # Return false if typed was not yet computed or
- # if an error occured during the typing computation
- fun is_typed: Bool is abstract
-
- # Return corresponding static type. (require is_typed)
- fun stype: MMType is abstract
-
- var _stype_cache: nullable MMType = null
- var _stype_cached: Bool = false
-
- # Retrieve the local class corresponding to the type.
- # Display an error and return null if there is no class
- # Display an error and return null if the type is not class based (formal one)
- fun get_local_class(v: AbsSyntaxVisitor): nullable MMLocalClass
- do
- var name = n_id.to_symbol
- var mod = v.mmmodule
- var cla = v.local_class
-
- if cla.formal_dict.has_key(name) or cla.has_global_property_by_name(name) then
- v.error(n_id, "Type error: {name} is a formal type")
- _stype_cached = true
- return null
- end
-
- if not mod.has_global_class_named(name) then
- v.error(n_id, "Type error: class {name} not found in module {mod}.")
- _stype_cached = true
- return null
- end
-
- var local_class = mod.class_by_name(name)
- local_class.global.check_visibility(v, self, mod)
- return local_class
- end
-
- # Retrieve corresponding static type.
- # Display an error and return null if there is a problem
- # But do not performs any subtype check.
- # get_unchecked_stype should be called to check that the static type is fully valid
- fun get_unchecked_stype(v: AbsSyntaxVisitor): nullable MMType
- do
- if _stype_cached then return _stype_cache
- _stype_cached = true
-
- var name = n_id.to_symbol
- var mod = v.mmmodule
- var cla = v.local_class
- var t: nullable MMType
-
- if cla.formal_dict.has_key(name) then
- if n_types.length > 0 then
- v.error(self, "Type error: formal type {name} cannot have formal parameters.")
- return null
- end
- t = cla.formal_dict[name]
- if n_kwnullable != null then t = t.as_nullable
- _stype_cache = t
- return t
- end
-
- if cla.has_global_property_by_name(name) then
- if n_types.length > 0 then
- v.error(self, "Type error: formal type {name} cannot have formal parameters.")
- return null
- end
- t = cla.get_type.local_class.select_virtual_type(name).stype_for(cla.get_type)
- if t == null then
- v.error(self, "Type error: circular definition in formal type {name}.")
- return null
- end
- if n_kwnullable != null then t = t.as_nullable
- _stype_cache = t
- return t
- end
-
- var local_class = get_local_class(v)
- if local_class == null then return null
-
- var arity = n_types.length
- if local_class.arity != arity then
- if arity == 0 then
- v.error(self, "Type error: '{local_class}' is a generic class.")
- else if local_class.arity == 0 then
- v.error(self, "Type error: '{local_class}' is not a generic class.")
- else
- v.error(self, "Type error: '{local_class}' has {local_class.arity} parameters ({arity} are provided).")
- end
- return null
- end
-
- if arity > 0 then
- var tab = new Array[MMType]
- for p in n_types do
- var t2 = p.get_unchecked_stype(v)
- if t2 == null then return null
- tab.add(t2)
- end
- t = local_class.get_instantiate_type(tab)
- else
- t = local_class.get_type
- end
- if n_kwnullable != null then t = t.as_nullable
- _stype_cache = t
- return t
- end
-
- # Retrieve corresponding static type.
- # Display an error and return null if there is a problem
- fun get_stype(v: AbsSyntaxVisitor): nullable MMType
- do
- var t = get_unchecked_stype(v)
- if t == null then return null
- if not t.is_valid then return null
- check_conform(v)
- return t
- end
-
- # Check that a static definition type is conform with regard to formal types
- # Useful with get_unchecked_stype
- # Remember that conformance check need that ancestors are totaly computed
- fun check_conform(v: AbsSyntaxVisitor)
- do
- var st = get_unchecked_stype(v)
- if st == null then return
- var local_class = st.local_class
- var arity = n_types.length
- if arity > 0 then
- for i in [0..arity[ do
- var p = n_types[i]
- var pt = p.get_stype(v)
- var b = local_class.get_formal(i)
- if not b.is_valid then return
- var bt = b.bound
- bt = bt.adapt_to(st) # We need to abapt because of F-genericity
- v.check_conform(p, pt, bt)
- end
- end
- end
-end
-
-redef class AExpr
- # Is the expression node correcly typed
- # Return false if typed was not yet computed or
- # if an error occured during the typing computation
- fun is_typed: Bool is abstract
-
- # Is the expression node a statement? (ie has no return value)
- # require: is_typed
- fun is_statement: Bool is abstract
-
- # The static type of the expression
- # require: is_typed and not is_statement
- fun stype: MMType is abstract
-end
-
-abstract class AAbsAbsSendExpr
- super AExpr
- # The signature of the called property (require is_typed)
- fun prop_signature: MMSignature is abstract
-
- # The raw arguments used (without vararg transformation) (require is_typed)
- fun raw_arguments: Array[AExpr] is abstract
-end
-
-abstract class AAbsSendExpr
- super AAbsAbsSendExpr
- # The invoked method (require is_typed)
- fun prop: MMMethod is abstract
-
- # The return type (if any) (once computed)
- fun return_type: nullable MMType is abstract
-end
-
-abstract class ASuperInitCall
- super AAbsSendExpr
-end
-
-redef class ASuperExpr
- super ASuperInitCall
- fun init_in_superclass: nullable MMMethod is abstract
-end
-
-redef class ANewExpr
- super AAbsSendExpr
-end
-
-redef class ASendExpr
- super ASuperInitCall
- # Closure definitions
- fun closure_defs: nullable Array[AClosureDef] is abstract
-end
-
-redef class AReassignFormExpr
- # Method used through the reassigment operator (require is_typed)
- fun assign_method: MMMethod is abstract
-end
-
-abstract class ASendReassignExpr
- super ASendExpr
- super AReassignFormExpr
- # The invoked method used to read (require is_typed)
- # prop is the method used to write
- fun read_prop: MMMethod is abstract
-end
-
-redef class ACallReassignExpr
- super ASendReassignExpr
-end
-
-redef class ABraReassignExpr
- super ASendReassignExpr
-end
-
-redef class AAttrFormExpr
- # Attribute accessed (require is_typed)
- fun prop: MMAttribute is abstract
-
- # Attribute type of the acceded attribute (require is_typed)
- fun attr_type: MMType is abstract
-end
-
-redef class ASuperstringExpr
- fun atype: MMType is abstract
-end
-
-redef class AVardeclExpr
- # Assiociated local variable
- fun variable: VarVariable is abstract
- #readable writable var _variable: nullable VarVariable
-end
-
-redef class AForExpr
- # Associated automatic local variable
- fun variables: Array[AutoVariable] is abstract
-end
-
-redef class ASelfExpr
- # Associated local variable
- fun variable: ParamVariable is abstract
-end
-
-redef class AVarFormExpr
- # Associated local variable
- fun variable: Variable is abstract
-end
-
-redef class AClosureCallExpr
- super AAbsAbsSendExpr
- # Associated closure variable
- fun variable: ClosureVariable is abstract
-end
-
-redef class AClosureDef
- # Associated closure
- fun closure: MMClosure is abstract
-
- # Automatic variables
- readable writable var _variables: nullable Array[AutoVariable]
-end
-
-redef class AMethid
- # Name of method
- fun name: nullable Symbol is abstract
-end
-
-redef class AExprs
- # Return an array made of each expr
- fun to_a: Array[AExpr] do return self.n_exprs.to_a
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Analysis property bodies, statements and expressions
-module typing
-
-import syntax_base
-import flow
-import scope
-
-redef class MMSrcModule
- # Walk trough the module and type statments and expressions
- # Require than supermodules are processed
- fun do_typing(tc: ToolContext)
- do
- var tv = new TypingVisitor(tc, self)
- tv.enter_visit(node)
- end
-end
-
-# Typing visitor
-# * Associate local variables to nodes
-# * Distinguish method call and local variable access
-# * Resolve call and attribute access
-# * Check type conformance
-private class TypingVisitor
- super AbsSyntaxVisitor
- redef fun visit(n)
- do
- n.accept_typing(self)
- end
-
- # Current knowledge about scoped things (variable, labels, etc.)
- readable var _scope_ctx: ScopeContext = new ScopeContext(self)
-
- # Current knowledge about control flow
- fun flow_ctx: FlowContext do return _flow_ctx.as(not null)
- writable var _flow_ctx: nullable FlowContext
-
- # Mark a local variable as set
- fun mark_is_set(va: Variable)
- do
- if flow_ctx.is_set(va) then return
- flow_ctx = flow_ctx.sub_setvariable(va)
- end
-
- # Mark the flow context as unreashable
- fun mark_unreash(n: ANode)
- do
- flow_ctx = flow_ctx.sub_unreash(n)
- end
-
- # Enter in an expression as inside a new local variable scope
- fun enter_visit_block(node: nullable AExpr)
- do
- if node == null then return
- scope_ctx.push(node)
- enter_visit(node)
- scope_ctx.pop
- end
-
- # Non-bypassable knowledge about variables names and types
- fun base_flow_ctx: FlowContext do return _base_flow_ctx.as(not null)
- writable var _base_flow_ctx: nullable FlowContext
-
- # The current reciever
- fun self_var: ParamVariable do return _self_var.as(not null)
- writable var _self_var: nullable ParamVariable
-
- # Block of the current method
- readable writable var _top_block: nullable AExpr
-
- # List of explicit invocation of constructors of super-classes
- readable writable var _explicit_super_init_calls: nullable Array[MMMethod]
-
- # Is a other constructor of the same class invoked
- readable writable var _explicit_other_init_call: Bool = false
-
- # Make the if_true_flow_ctx of the expression effective
- private fun use_if_true_flow_ctx(e: AExpr)
- do
- var ctx = e.if_true_flow_ctx
- if ctx != null then flow_ctx = ctx
- end
-
- # Make the if_false_flow_ctx of the expression effective
- private fun use_if_false_flow_ctx(e: AExpr)
- do
- var ctx = e.if_false_flow_ctx
- if ctx != null then flow_ctx = ctx
- end
-
- # Are we inside a default closure definition ?
- readable writable var _is_default_closure_definition: Bool = false
-
- # Number of nested once
- readable writable var _once_count: Int = 0
-
- init(tc, mod) do super
-
- private fun get_default_constructor_for(n: ANode, c: MMLocalClass, prop: MMSrcMethod): nullable MMMethod
- do
- var v = self
- #var prop = v.local_property
- #assert prop isa MMMethod
- var candidates = new Array[MMMethod]
- var false_candidates = new Array[MMMethod]
- var parity = prop.signature.arity
- for g in c.global_properties do
- if not g.is_init_for(c) then continue
- var gp = c[g]
- var gps = gp.signature_for(c.get_type)
- assert gp isa MMSrcMethod
- var garity = gps.arity
- if gp.name == prop.name then
- if garity == 0 or (parity == garity and prop.signature < gps) then
- return gp
- else
- false_candidates.add(gp)
- end
- else if garity == 0 and gp.name == once ("init".to_symbol) then
- candidates.add(gp)
- false_candidates.add(gp)
- else
- false_candidates.add(gp)
- end
- end
- if candidates.length == 1 then
- return candidates.first
- else if candidates.length > 0 then
- var a = new Array[String]
- for p in candidates do
- a.add("{p.full_name}{p.signature.as(not null)}")
- end
- v.error(n, "Error: Conflicting default constructor to call for {c}: {a.join(", ")}.")
- return null
- else if false_candidates.length > 0 then
- var a = new Array[String]
- for p in false_candidates do
- a.add("{p.full_name}{p.signature.as(not null)}")
- end
- v.error(n, "Error: there is no available compatible constructor in {c}. Discarded candidates are {a.join(", ")}.")
- return null
- else
- v.error(n, "Error: there is no available compatible constructor in {c}.")
- return null
- end
- end
-end
-
-
-###############################################################################
-
-redef class ANode
- private fun accept_typing(v: TypingVisitor)
- do
- accept_abs_syntax_visitor(v)
- after_typing(v)
- end
- private fun after_typing(v: TypingVisitor) do end
-end
-
-redef class AClassdef
- redef fun accept_typing(v)
- do
- v.self_var = new ParamVariable("self".to_symbol, self)
- v.self_var.stype = local_class.get_type
- super
- end
-end
-
-redef class APropdef
- redef fun self_var do return _self_var.as(not null)
- var _self_var: nullable ParamVariable
-end
-
-redef class AAttrPropdef
- redef fun accept_typing(v)
- do
- v.flow_ctx = new RootFlowContext(v, self)
- v.base_flow_ctx = v.flow_ctx
-
- v.scope_ctx.push(self)
- _self_var = v.self_var
- super
- if n_expr != null then
- v.check_conform_expr(n_expr.as(not null), prop.signature.return_type.as(not null))
- end
- v.scope_ctx.pop
- end
-end
-
-redef class AMethPropdef
- redef fun accept_typing(v)
- do
- v.flow_ctx = new RootFlowContext(v, self)
- v.base_flow_ctx = v.flow_ctx
-
- v.scope_ctx.push(self)
- _self_var = v.self_var
- super
- v.scope_ctx.pop
- end
-end
-
-redef class AConcreteMethPropdef
- redef fun after_typing(v)
- do
- super
- if not v.flow_ctx.unreash and method.signature.return_type != null then
- v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).")
- end
- end
-end
-
-redef class AConcreteInitPropdef
- redef fun accept_typing(v)
- do
- v.top_block = n_block
- v.explicit_super_init_calls = explicit_super_init_calls
- v.explicit_other_init_call = false
- super
- end
-
- redef fun after_typing(v)
- do
- super
- if v.explicit_other_init_call or method.global.intro != method then
- # TODO: something?
- else
- var i = 0
- var l = explicit_super_init_calls.length
- var cur_m: nullable MMMethod = null
- var cur_c: nullable MMLocalClass = null
- if i < l then
- cur_m = explicit_super_init_calls[i]
- cur_c = cur_m.global.intro.local_class.for_module(v.mmmodule)
- end
- var j = 0
- while j < v.local_class.cshe.direct_greaters.length do
- var c = v.local_class.cshe.direct_greaters[j]
- if c.global.is_interface or c.global.is_enum or c.global.is_extern or c.global.is_mixin then
- j += 1
- else if cur_c != null and (c.cshe <= cur_c or cur_c.global.is_mixin) then
- if c == cur_c then j += 1
- super_init_calls.add(cur_m.as(not null))
- i += 1
- if i < l then
- cur_m = explicit_super_init_calls[i]
- cur_c = cur_m.global.intro.local_class.for_module(v.mmmodule)
- else
- cur_m = null
- cur_c = null
- end
- else
- var p = v.get_default_constructor_for(self, c, method)
- if p != null then
- super_init_calls.add(p)
- end
- j += 1
- end
- end
- end
- end
-end
-
-redef class AExternInitPropdef
- redef fun accept_typing(v)
- do
- v.explicit_other_init_call = false
- super
- end
- redef fun after_typing(v)
- do
- super
- end
-end
-
-redef class ASignature
- redef fun after_typing(v)
- do
- if self.n_opar != null and self.n_params.is_empty then
- v.warning(self, "Warning: superfluous parentheses.")
- end
- end
-end
-
-redef class AParam
- redef fun after_typing(v)
- do
- v.scope_ctx.add_variable(variable)
- end
-end
-
-redef class AClosureDecl
- # The corresponding escapable object
- readable var _escapable: nullable EscapableBlock
-
- redef fun accept_typing(v)
- do
- # Register the closure for ClosureCallExpr
- v.scope_ctx.add_variable(variable)
-
- var old_flow_ctx = v.flow_ctx
- var old_base_flow_ctx = v.base_flow_ctx
- v.base_flow_ctx = v.flow_ctx
-
- var blist: nullable Array[AExpr] = null
- var t = v.local_property.signature.return_type
- if t != null then blist = new Array[AExpr]
- var escapable = new EscapableClosure(self, variable.closure, blist)
- _escapable = escapable
- v.scope_ctx.push_escapable(escapable, null)
-
- v.is_default_closure_definition = true
-
- super
-
- v.is_default_closure_definition = false
-
- if n_expr != null then
- if v.flow_ctx.unreash == false then
- if variable.closure.signature.return_type != null then
- v.error(self, "Control error: Reached end of block (a 'continue' with a value was expected).")
- else if variable.closure.is_break and escapable.break_list != null then
- v.error(self, "Control error: Reached end of break block (a 'break' with a value was expected).")
- end
- end
- end
- if blist != null then for x in blist do
- v.check_conform_expr(x, t)
- end
-
- v.flow_ctx = old_flow_ctx
- v.base_flow_ctx = old_base_flow_ctx
- v.scope_ctx.pop
- end
-end
-
-redef class AType
- redef fun stype: MMType do return _stype.as(not null)
- redef fun is_typed: Bool do return _stype != null
- var _stype: nullable MMType
-
- redef fun after_typing(v)
- do
- _stype = get_stype(v)
- end
-end
-
-redef class AExpr
- redef readable var _is_typed: Bool = false
- redef fun is_statement: Bool do return _stype == null
- redef fun stype
- do
- if not is_typed then
- print "{location}: not is_typed"
- abort
- end
- if is_statement then
- print "{location}: is_statement"
- abort
- end
- return _stype.as(not null)
- end
- var _stype: nullable MMType
-
- redef fun after_typing(v)
- do
- # Default behavior is to be happy
- _is_typed = true
- end
-
- # Is the expression the implicit receiver
- fun is_implicit_self: Bool do return false
-
- # Is the expression the current receiver (implicit or explicit)
- fun is_self: Bool do return false
-
- # The variable accessed is any
- fun its_variable: nullable Variable do return null
-
- # The control flow information if current boolean expression is true
- readable private var _if_true_flow_ctx: nullable FlowContext
-
- # The control flow information if current boolean expression is false
- readable private var _if_false_flow_ctx: nullable FlowContext
-
- # Wharn in case of superfluous parentheses
- private fun warn_parentheses(v: AbsSyntaxVisitor)
- do
- end
-end
-
-redef class AParExpr
- redef fun warn_parentheses(v)
- do
- v.warning(self, "Warning: superfluous parentheses.")
- end
-end
-
-redef class AParExprs
- redef fun after_typing(v)
- do
- if n_exprs.is_empty then
- v.warning(self, "Warning: superfluous parentheses.")
- end
- end
-end
-
-redef class AVardeclExpr
- var _variable: nullable VarVariable
- redef fun variable do return _variable.as(not null)
-
- redef fun after_typing(v)
- do
- var va = new VarVariable(n_id.to_symbol, n_id)
- _variable = va
- v.scope_ctx.add_variable(va)
- var ne = n_expr
- if ne != null then v.mark_is_set(va)
-
- if n_type != null then
- if not n_type.is_typed then return
- va.stype = n_type.stype
- if ne != null then
- v.check_conform_expr(ne, va.stype)
- end
- else if ne != null then
- if not v.check_expr(ne) then return
- var st = ne.stype
- if st isa MMTypeNone then
- va.stype = v.type_object.as_nullable
- v.flow_ctx = v.flow_ctx.sub_with(self, va, st)
- else
- va.stype = st
- end
- else
- va.stype = v.type_object.as_nullable
- end
- _is_typed = true
- end
-end
-
-redef class ABlockExpr
- redef fun accept_typing(v)
- do
- for e in n_expr do
- if not v.flow_ctx.unreash then
- v.enter_visit(e)
- else if not v.flow_ctx.already_unreash then
- v.flow_ctx.already_unreash = true
- v.error(e, "Error: unreachable statement.")
- end
- end
-
- _is_typed = true
- end
-end
-
-redef class AReturnExpr
- redef fun after_typing(v)
- do
- v.mark_unreash(self)
- var t = v.local_property.signature.return_type
-
- if v.is_default_closure_definition then
- v.error(self, "Error: 'return' invalid in default closure definitions. Use 'continue' or 'break'.")
- return
- end
-
- var e = n_expr
- if e == null and t != null then
- v.error(self, "Error: Return without value in a function.")
- else if e != null and t == null then
- v.error(self, "Error: Return with value in a procedure.")
- else if e != null and t != null then
- v.check_conform_expr(e, t)
- end
- if e != null then
- e.warn_parentheses(v)
- end
- _is_typed = true
- end
-end
-
-redef class AContinueExpr
- redef fun after_typing(v)
- do
- v.mark_unreash(self)
- var esc = compute_escapable_block(v.scope_ctx)
- if esc == null then return
-
- if esc.is_break_block then
- v.error(self, "Error: cannot 'continue', only 'break'.")
- return
- end
-
- var t = esc.continue_stype
- var e = n_expr
- if e == null and t != null then
- v.error(self, "Error: continue with a value required in this block.")
- else if e != null and t == null then
- v.error(self, "Error: continue without value required in this block.")
- else if e != null and t != null then
- v.check_conform_expr(e, t)
- end
- if e != null then
- e.warn_parentheses(v)
- end
- _is_typed = true
- end
-end
-
-redef class ABreakExpr
- redef fun after_typing(v)
- do
- var old_flow_ctx = v.flow_ctx
- v.mark_unreash(self)
- var esc = compute_escapable_block(v.scope_ctx)
- if esc == null then return
-
- esc.break_flow_contexts.add(old_flow_ctx)
-
- var bl = esc.break_list
- var e = n_expr
- if e == null and bl != null then
- v.error(self, "Error: break with a value required in this block.")
- else if e != null and bl == null then
- v.error(self, "Error: break without value required in this block.")
- else if e != null and bl != null then
- # Typing check can only be done later
- bl.add(e)
- end
- if e != null then
- e.warn_parentheses(v)
- end
- _is_typed = true
- end
-end
-
-redef class AAbortExpr
- redef fun after_typing(v)
- do
- v.mark_unreash(self)
- _is_typed = true
- end
-end
-
-# An abstract control structure with feature escapable block
-abstract class AAbsControl
- super AExpr
- # The corresponding escapable block
- readable var _escapable: nullable EscapableBlock
-
- # Enter and process a control structure
- private fun process_control(v: TypingVisitor, escapable: EscapableBlock, n_label: nullable ALabel, is_loop: Bool)
- do
- # Register the escapable block
- _escapable = escapable
- v.scope_ctx.push_escapable(escapable, n_label)
-
- # Save an prepare the contextes
- var old_flow_ctx = v.flow_ctx
- var old_base_flow_ctx = v.base_flow_ctx
- if is_loop then v.base_flow_ctx = v.flow_ctx
-
- # Do the main processing
- process_control_inside(v)
-
- # Add the end of the block as an exit context
- if not v.flow_ctx.unreash then
- escapable.break_flow_contexts.add(v.flow_ctx)
- end
-
- # Merge all exit contexts
- if escapable.break_flow_contexts.is_empty then
- v.flow_ctx = old_flow_ctx
- v.mark_unreash(self)
- else
- v.flow_ctx = old_base_flow_ctx.merge(self, escapable.break_flow_contexts)
- end
-
- if is_loop then v.base_flow_ctx = old_base_flow_ctx
- v.scope_ctx.pop
- _is_typed = true
- end
-
- # What to do inside the control block?
- private fun process_control_inside(v: TypingVisitor) is abstract
-end
-
-redef class ADoExpr
- super AAbsControl
- redef fun accept_typing(v)
- do
- process_control(v, new BreakOnlyEscapableBlock(self), n_label, false)
- end
-
- redef fun process_control_inside(v)
- do
- v.enter_visit_block(n_block)
- end
-end
-
-redef class AIfExpr
- redef fun accept_typing(v)
- do
- v.enter_visit(n_expr)
- v.check_conform_expr(n_expr, v.type_bool)
-
- n_expr.warn_parentheses(v)
-
- # Prepare 'then' context
- var old_flow_ctx = v.flow_ctx
- v.use_if_true_flow_ctx(n_expr)
-
- # Process the 'then'
- v.enter_visit_block(n_then)
-
- # Remember what appened in the 'then'
- var then_flow_ctx = v.flow_ctx
-
- # Prepare 'else' context
- v.flow_ctx = old_flow_ctx
- v.use_if_false_flow_ctx(n_expr)
-
- # Process the 'else'
- v.enter_visit_block(n_else)
-
- # Merge 'then' and 'else' contexts
- v.flow_ctx = v.base_flow_ctx.merge_reash(self, then_flow_ctx, v.flow_ctx)
- _is_typed = true
- end
-end
-
-redef class AWhileExpr
- super AAbsControl
- redef fun accept_typing(v)
- do
- process_control(v, new EscapableBlock(self), n_label, true)
- end
-
- redef fun process_control_inside(v)
- do
- var old_flow_ctx = v.flow_ctx
-
- # Process condition
- v.enter_visit(n_expr)
- v.check_conform_expr(n_expr, v.type_bool)
-
- if n_expr isa ATrueExpr then
- v.warning(self, "Warning: use 'loop' instead of 'while true do'.")
- else
- n_expr.warn_parentheses(v)
- end
-
- # Prepare inside context (assert cond)
- v.use_if_true_flow_ctx(n_expr)
-
- # Process inside
- v.enter_visit_block(n_block)
-
- # Compute outside context (assert !cond + all breaks)
- v.flow_ctx = old_flow_ctx
- v.use_if_false_flow_ctx(n_expr)
- escapable.break_flow_contexts.add(v.flow_ctx)
- end
-end
-
-redef class ALoopExpr
- super AAbsControl
- redef fun accept_typing(v)
- do
- process_control(v, new EscapableBlock(self), n_label, true)
- end
-
- redef fun process_control_inside(v)
- do
- # Process inside
- v.enter_visit_block(n_block)
-
- # Never automatically reach after the loop
- v.mark_unreash(self)
- end
-end
-
-redef class AForExpr
- super AAbsControl
- var _variables: nullable Array[AutoVariable]
- redef fun variables do return _variables.as(not null)
-
- redef fun accept_typing(v)
- do
- process_control(v, new EscapableBlock(self), n_label, true)
- end
-
- redef fun process_control_inside(v)
- do
- v.scope_ctx.push(self)
- var old_flow_ctx = v.flow_ctx
-
- do_typing(v)
-
- # Process inside
- v.enter_visit_block(n_block)
-
- # end == begin of the loop
- v.flow_ctx = old_flow_ctx
- v.scope_ctx.pop
- end
-
- private fun do_typing(v: TypingVisitor)
- do
- # Create the automatic variables
- var vas = new Array[AutoVariable]
- for n_id in n_ids do
- var va = new AutoVariable(n_id.to_symbol, n_id)
- v.scope_ctx.add_variable(va)
- vas.add(va)
- end
- _variables = vas
-
- # Process reciever
- v.enter_visit(n_expr)
- if not v.check_expr(n_expr) then return
- var expr_type = n_expr.stype
-
- if expr_type.is_nullable then
- v.error(n_expr, "Type error: 'for' on a nullable expression.")
- return
- end
- n_expr.warn_parentheses(v)
-
- # Get iterate
- var iterate_name = once "iterate".to_symbol
- if not expr_type.local_class.has_global_property_by_name(iterate_name) then
- v.error(n_expr, "Type error: Expected a type with an 'iterate' method. Found {expr_type}.")
- return
- end
- var prop = expr_type.local_class.select_method(iterate_name)
- prop.global.check_visibility(v, self, v.mmmodule, n_expr.is_self)
- var psig = prop.signature_for(expr_type)
- if not n_expr.is_self then psig = psig.not_for_self
- if psig.arity != 0 then
- v.error(self, "Error: 'iterate' incompatible with 'for': require no arguments.")
- return
- else if psig.closures.length != 1 then
- v.error(self, "Error: 'iterate' incompatible with 'for': require one closure.")
- return
- end
- psig = psig.closures.first.signature
- if psig.return_type != null then
- v.error(self, "Error: 'iterate' incompatible with 'for': require one procedural closure.")
- return
- end
- if vas.length != psig.arity then
- if psig.arity == 1 then
- v.error(self, "Error: Expected {psig.arity} variable {psig}, found {vas.length}.")
- else
- v.error(self, "Error: Expected {psig.arity} variables {psig}, found {vas.length}.")
- end
- return
- end
-
- # Type the automatic variables
- for i in [0..vas.length[ do
- vas[i].stype = psig[i]
- end
- end
-end
-
-redef class AAssertExpr
- redef fun accept_typing(v)
- do
- # Process condition
- v.enter_visit(n_expr)
- v.check_conform_expr(n_expr, v.type_bool)
- n_expr.warn_parentheses(v)
-
- # Process optional 'else' part
- if n_else != null then
- var old_flow_ctx = v.flow_ctx
- v.use_if_false_flow_ctx(n_expr)
- v.enter_visit(n_else)
- v.flow_ctx = old_flow_ctx
- end
-
- # Prepare outside
- v.use_if_true_flow_ctx(n_expr)
- _is_typed = true
- end
-end
-
-redef class AVarFormExpr
- var _variable: nullable Variable
- redef fun variable do return _variable.as(not null)
-end
-
-redef class AVarExpr
- redef fun its_variable do return variable
-
- redef fun after_typing(v)
- do
- v.flow_ctx.check_is_set(self, variable)
- _stype = v.flow_ctx.stype(variable)
- _is_typed = _stype != null
- end
-end
-
-redef class AVarAssignExpr
- redef fun after_typing(v)
- do
- v.mark_is_set(variable)
-
- # Check the base type
- var btype = v.base_flow_ctx.stype(variable)
- if not v.check_expr(n_value) then return
- if btype != null and not v.check_conform_expr(n_value, btype) then return
-
- # Always cast
- v.flow_ctx = v.flow_ctx.sub_with(self, variable, n_value.stype)
-
- _is_typed = true
- end
-end
-
-redef class AReassignFormExpr
- # Compute and check method used through the reassigment operator
- # On success return the static type of the result of the reassigment operator
- # Else display an error and return null
- private fun do_rvalue_typing(v: TypingVisitor, type_lvalue: nullable MMType): nullable MMType
- do
- if type_lvalue == null then
- return null
- end
- var name = n_assign_op.method_name
- if type_lvalue isa MMTypeNone then
- v.error(self, "Error: Method '{name}' call on 'null'.")
- return null
- end
- var lc = type_lvalue.local_class
- if not lc.has_global_property_by_name(name) then
- v.error(self, "Error: Method '{name}' doesn't exists in {type_lvalue}.")
- return null
- end
- var prop = lc.select_method(name)
- prop.global.check_visibility(v, self, v.mmmodule, false)
- var psig = prop.signature_for(type_lvalue)
- _assign_method = prop
- if not v.check_conform_expr(n_value, psig[0].not_for_self) then return null
- return psig.return_type.not_for_self
- end
-
- redef fun assign_method do return _assign_method.as(not null)
- var _assign_method: nullable MMMethod
-end
-
-redef class AVarReassignExpr
- redef fun after_typing(v)
- do
- v.flow_ctx.check_is_set(self, variable)
- v.mark_is_set(variable)
- var t = v.flow_ctx.stype(variable)
- var t2 = do_rvalue_typing(v, t)
- if t2 == null then return
-
- # Check the base type
- var btype = v.base_flow_ctx.stype(variable)
- if not v.check_expr(n_value) then return
- if btype != null and not v.check_conform(n_value, t2, btype) then return
-
- # Always cast
- v.flow_ctx = v.flow_ctx.sub_with(self, variable, t2)
-
- _is_typed = true
- end
-end
-
-redef class AAssignOp
- fun method_name: Symbol is abstract
-end
-redef class APlusAssignOp
- redef fun method_name do return once "+".to_symbol
-end
-redef class AMinusAssignOp
- redef fun method_name do return once "-".to_symbol
-end
-
-redef class ASelfExpr
- var _variable: nullable ParamVariable
- redef fun variable do return _variable.as(not null)
-
- redef fun its_variable do return variable
-
- redef fun after_typing(v)
- do
- _variable = v.self_var
- _stype = v.flow_ctx.stype(variable)
- _is_typed = true
- end
-
- redef fun is_self do return true
-end
-
-redef class AImplicitSelfExpr
- redef fun is_implicit_self do return true
-end
-
-redef class AIfexprExpr
- redef fun accept_typing(v)
- do
- var old_flow_ctx = v.flow_ctx
-
- # Process condition
- v.enter_visit(n_expr)
- v.check_conform_expr(n_expr, v.type_bool)
-
- # Prepare 'then' context
- v.use_if_true_flow_ctx(n_expr)
-
- # Process 'then'
- v.enter_visit_block(n_then)
-
- # Remember what appened in the 'then'
- var then_flow_ctx = v.flow_ctx
-
- # Prepare 'else' context
- v.flow_ctx = old_flow_ctx
- v.use_if_false_flow_ctx(n_expr)
-
- # Process 'else'
- v.enter_visit_block(n_else)
-
- # Merge 'then' and 'else' contexts
- v.flow_ctx = v.base_flow_ctx.merge_reash(self, then_flow_ctx, v.flow_ctx)
-
- var stype = v.check_conform_multiexpr(null, [n_then, n_else])
- if stype == null then return
-
- _stype = stype
- _is_typed = true
- end
-end
-
-redef class ABoolExpr
- redef fun after_typing(v)
- do
- _stype = v.type_bool
- _is_typed = true
- end
-end
-
-redef class AOrExpr
- redef fun accept_typing(v)
- do
- var old_flow_ctx = v.flow_ctx
- var stype = v.type_bool
- _stype = stype
-
- # Process left operand
- v.enter_visit(n_expr)
-
- # Prepare right operand context
- v.use_if_false_flow_ctx(n_expr)
-
- # Process right operand
- v.enter_visit(n_expr2)
- if n_expr2.if_false_flow_ctx != null then
- _if_false_flow_ctx = n_expr2.if_false_flow_ctx
- else
- _if_false_flow_ctx = v.flow_ctx
- end
-
- v.flow_ctx = old_flow_ctx
-
- v.check_conform_expr(n_expr, stype)
- v.check_conform_expr(n_expr2, stype)
- _stype = stype
- _is_typed = true
- end
-end
-
-redef class AImpliesExpr
- redef fun accept_typing(v)
- do
- var old_flow_ctx = v.flow_ctx
- var stype = v.type_bool
- _stype = stype
-
- # Process left operand
- v.enter_visit(n_expr)
-
- # Prepare right operand context
- v.use_if_true_flow_ctx(n_expr)
-
- # Process right operand
- v.enter_visit(n_expr2)
- if n_expr2.if_false_flow_ctx != null then
- _if_false_flow_ctx = n_expr2.if_false_flow_ctx
- else
- _if_false_flow_ctx = v.flow_ctx
- end
-
- v.flow_ctx = old_flow_ctx
-
- v.check_conform_expr(n_expr, stype)
- v.check_conform_expr(n_expr2, stype)
- _stype = stype
- _is_typed = true
- end
-end
-
-redef class AAndExpr
- redef fun accept_typing(v)
- do
- var old_flow_ctx = v.flow_ctx
- var stype = v.type_bool
-
- # Process left operand
- v.enter_visit(n_expr)
-
- # Prepare right operand context
- v.use_if_true_flow_ctx(n_expr)
-
- # Process right operand
- v.enter_visit(n_expr2)
- if n_expr2.if_true_flow_ctx != null then
- _if_true_flow_ctx = n_expr2.if_true_flow_ctx
- else
- _if_true_flow_ctx = v.flow_ctx
- end
-
- v.flow_ctx = old_flow_ctx
-
- v.check_conform_expr(n_expr, stype)
- v.check_conform_expr(n_expr2, stype)
- _stype = stype
- _is_typed = true
- end
-end
-
-redef class ANotExpr
- redef fun after_typing(v)
- do
- v.check_conform_expr(n_expr, v.type_bool)
-
- # Invert if_true/if_false information
- _if_false_flow_ctx = n_expr._if_true_flow_ctx
- _if_true_flow_ctx = n_expr._if_false_flow_ctx
-
- _stype = v.type_bool
- _is_typed = true
- end
-end
-
-redef class AOrElseExpr
- redef fun after_typing(v)
- do
- var old_flow_ctx = v.flow_ctx
-
- # Process left operand
- v.enter_visit(n_expr)
- v.check_expr(n_expr)
-
- # Consider the type of the left operand
- var t = n_expr.stype
- if not t.is_nullable then
- v.warning(n_expr, "Warning: left operand of a 'or else' is not a nullable type.")
- else
- t = t.as_notnull
- end
-
- # Prepare the else context : ie the first expression is null
- var variable = n_expr.its_variable
- if variable != null then
- v.flow_ctx.sub_with(self, variable, v.type_none)
- end
-
- # Process right operand
- v.enter_visit(n_expr2)
- v.check_expr(n_expr)
-
- # Restore the context
- v.flow_ctx = old_flow_ctx
-
- # Merge the types
- var stype = v.check_conform_multiexpr(t, [n_expr2])
- if stype == null then return
-
- _stype = stype
- _is_typed = true
- end
-end
-
-redef class AIntExpr
- redef fun after_typing(v)
- do
- _stype = v.type_int
- _is_typed = true
- end
-end
-
-redef class AFloatExpr
- redef fun after_typing(v)
- do
- _stype = v.type_float
- _is_typed = true
- end
-end
-
-redef class ACharExpr
- redef fun after_typing(v)
- do
- _stype = v.type_char
- _is_typed = true
- end
-end
-
-redef class AStringFormExpr
- redef fun after_typing(v)
- do
- _stype = v.type_string
- _is_typed = true
- end
-end
-
-redef class ASuperstringExpr
- redef fun atype do return _atype.as(not null)
- var _atype: nullable MMType
- redef fun after_typing(v)
- do
- var otype = v.type_object
- var stype = v.type_string
- _stype = stype
- for e in n_exprs do v.check_conform_expr(e, otype)
- var atype = v.type_array(stype)
- _atype = atype
- _is_typed = true
- end
-end
-
-redef class ANullExpr
- redef fun after_typing(v)
- do
- _stype = v.type_none
- _is_typed = true
- end
-end
-
-redef class AArrayExpr
- redef fun after_typing(v)
- do
- var stype = v.check_conform_multiexpr(null, n_exprs.n_exprs)
- if stype != null then do_typing(v, stype)
- end
-
- private fun do_typing(v: TypingVisitor, element_type: MMType)
- do
- _stype = v.type_array(element_type)
- _is_typed = true
- end
-end
-
-redef class ARangeExpr
- redef fun after_typing(v)
- do
- if not v.check_expr(n_expr) or not v.check_expr(n_expr2) then return
- var ntype = n_expr.stype
- var ntype2 = n_expr2.stype
- if ntype < ntype2 then
- ntype = ntype2
- else if not ntype2 < ntype then
- v.error(self, "Type error: {ntype} incompatible with {ntype2}.")
- return
- end
- var dtype = v.type_discrete
- if not v.check_conform_expr(n_expr, dtype) or not v.check_conform_expr(n_expr2, dtype) then return
- _stype = v.type_range(ntype)
- _is_typed = true
- end
-end
-
-redef class ASuperExpr
- redef readable var _init_in_superclass: nullable MMMethod
- redef fun compute_raw_arguments do return n_args.to_a
- redef fun after_typing(v)
- do
- var precs: Array[MMLocalProperty] = v.local_property.prhe.direct_greaters
- if not precs.is_empty then
- v.local_property.need_super = true
- else if v.local_property.global.is_init then
- var base_precs = v.local_class.super_methods_named(v.local_property.name)
- for p in base_precs do
- if not p.global.is_init then
- v.error(self, "Error: {p.local_class}::{p} is not a constructor.")
- else
- precs.add(v.local_class[p.global])
- end
- end
- if precs.is_empty then
- v.error(self, "Error: No contructor named {v.local_property.name} in superclasses.")
- return
- else if precs.length > 1 then
- v.error(self, "Error: Conflicting contructors named {v.local_property.name} in superclasses: {precs.join(", ")}.")
- return
- end
- var p = base_precs.first
- assert p isa MMMethod
- _init_in_superclass = p
- register_super_init_call(v, p)
- if n_args.n_exprs.length > 0 then
- var signature = get_signature(v, v.self_var.stype.as(not null), p, true)
- process_signature(v, signature, p.name, compute_raw_arguments)
- end
- else
- v.error(self, "Error: No super method to call for {v.local_property}.")
- return
- end
-
- if precs.first.signature_for(v.self_var.stype.as(not null)).return_type != null then
- var stypes = new Array[MMType]
- var stype: nullable MMType = null
- for prop in precs do
- assert prop isa MMMethod
- var t = prop.signature_for(v.self_var.stype.as(not null)).return_type.for_module(v.mmmodule).adapt_to(v.local_property.signature.recv)
- stypes.add(t)
- if stype == null or stype < t then
- stype = t
- end
- end
- for t in stypes do
- v.check_conform(self, t, stype.as(not null))
- end
- _stype = stype
- end
- var p = v.local_property
- assert p isa MMSrcMethod
- _prop = p
- _is_typed = true
- end
-end
-
-redef class AExternCall
- fun target_class_name : nullable Symbol do return null
- fun target_method_name : Symbol is abstract
-
- redef fun after_typing(v)
- do
- var target_class_name = self.target_class_name
- var target_method_name = self.target_method_name
-
- var target_class : MMLocalClass
- var target_method : MMMethod
-
- # find class
- # self.target_class_name can be redef'd by sub-classes
- if target_class_name == null then
- target_class = v.local_property.local_class
- else
- if v.local_property.mmmodule.has_global_class_named( target_class_name ) then
- var global_class = v.local_property.mmmodule.global_class_named( target_class_name )
- target_class = v.local_property.mmmodule[ global_class ]
- else
- v.error( self, "Error: class {target_class_name.to_s}, not found." )
- return
- end
- end
-
- if target_class.has_global_property_by_name( target_method_name ) then
- var global_property = target_class.get_property_by_name( target_method_name )
-
- var target_property = target_class[global_property]
-
- if target_property isa MMMethod then
- target_method = target_property
- else
- v.error( self, "Error: property {target_method_name.to_s} is not a method." )
- return
- end
- else
- v.error( self, "Error: property {target_method_name.to_s} not found in target class." )
- return
- end
-
- var explicit_import = new MMExplicitImport( target_class, target_method )
- v.local_property.as(MMSrcMethod).explicit_imports.add( explicit_import )
- end
-end
-
-redef class ALocalPropExternCall
- redef fun target_class_name do return null
- redef fun target_method_name do return n_methid.name.as(not null)
-end
-
-redef class ASuperExternCall
- redef fun after_typing(v)
- do
- var precs: Array[MMLocalProperty] = v.local_property.prhe.direct_greaters
- if not precs.is_empty then
- v.local_property.need_super = true
- else
- v.error(self, "Error: No super method to call for {v.local_property}.")
- return
- end
- end
-end
-
-redef class AFullPropExternCall
- redef fun target_class_name do return n_classid.to_symbol
- redef fun target_method_name do return n_methid.name.as(not null)
-end
-
-redef class AInitPropExternCall
- redef fun target_class_name do return n_classid.to_symbol
- redef fun target_method_name do return "init".to_symbol
-end
-
-redef class ACastExternCall
- fun from_type : MMType is abstract
- fun to_type : MMType is abstract
-
- redef fun after_typing(v)
- do
- if from_type == to_type
- then
- v.error( self, "Attepting to cast from and to the same type." )
- end
-
- var cast = new MMImportedCast( from_type, to_type )
- var m = v.local_property
- assert m isa MMMethod
- m.explicit_casts.add( cast )
- end
-end
-
-redef class ACastAsExternCall
- redef fun from_type do return n_from_type.stype
- redef fun to_type do return n_to_type.stype
-end
-
-redef class AAsNullableExternCall
- redef fun from_type do return n_type.stype
- redef fun to_type do return n_type.stype.as_nullable
-end
-
-redef class AAsNotNullableExternCall
- redef fun from_type
- do
- var t = n_type.stype
- if t.is_nullable
- then
- return t
- else
- return t.as_nullable
- end
- end
- redef fun to_type do return n_type.stype.as_notnull
-end
-
-redef class AAttrFormExpr
- redef fun prop do return _prop.as(not null)
- var _prop: nullable MMAttribute
-
- redef fun attr_type do return _attr_type.as(not null)
- var _attr_type: nullable MMType
-
- # Compute the attribute accessed
- private fun do_typing(v: TypingVisitor)
- do
- if not v.check_expr(n_expr) then return
- var type_recv = n_expr.stype
- var name = n_id.to_symbol
- if type_recv isa MMTypeNone then
- v.error(self, "Error: Attribute '{name}' access on 'null'.")
- return
- end
- var lc = type_recv.local_class
- if not lc.has_global_property_by_name(name) then
- v.error(self, "Error: Attribute {name} doesn't exists in {type_recv}.")
- return
- end
- var prop = lc.select_attribute(name)
- if v.mmmodule.visibility_for(prop.global.local_class.mmmodule) < 3 then
- v.error(self, "Error: Attribute {name} from {prop.global.local_class.mmmodule} is invisible in {v.mmmodule}")
- end
- _prop = prop
- var at = prop.signature_for(type_recv).return_type
- if not n_expr.is_self then at = at.not_for_self
- _attr_type = at
- end
-end
-
-redef class AAttrExpr
- redef fun after_typing(v)
- do
- do_typing(v)
- if _prop == null then return
- _stype = attr_type
- _is_typed = true
- end
-end
-
-redef class AAttrAssignExpr
- redef fun after_typing(v)
- do
- do_typing(v)
- if _prop == null then return
- if not v.check_conform_expr(n_value, attr_type) then return
- _is_typed = true
- end
-end
-
-redef class AAttrReassignExpr
- redef fun after_typing(v)
- do
- do_typing(v)
- if _prop == null then return
- var t = do_rvalue_typing(v, attr_type)
- if t == null then return
- v.check_conform(self, t, n_value.stype)
- _is_typed = true
- end
-end
-
-redef class AIssetAttrExpr
- redef fun after_typing(v)
- do
- do_typing(v)
- if _prop == null then return
- if attr_type.is_nullable then
- v.error(self, "Error: isset on a nullable attribute.")
- end
- _stype = v.type_bool
- _is_typed = true
- end
-end
-
-redef class AAbsAbsSendExpr
- # The signature of the called property
- redef fun prop_signature do return _prop_signature.as(not null)
- var _prop_signature: nullable MMSignature
-
- # Raw arguments used (without vararg transformation)
- redef fun raw_arguments: Array[AExpr]
- do
- var res = _raw_arguments_cache
- if res != null then
- return res
- else
- res = compute_raw_arguments
- if res == null then res = new Array[AExpr]
- _raw_arguments_cache = res
- return res
- end
- end
-
- var _raw_arguments_cache: nullable Array[AExpr] = null
-
- fun compute_raw_arguments: nullable Array[AExpr]
- do
- print "{location} no compute_raw_arguments"
- return null
- end
-
- # Check the conformity of a set of arguments `raw_args' to a signature.
- private fun process_signature(v: TypingVisitor, psig: MMSignature, name: Symbol, raw_args: nullable Array[AExpr]): Bool
- do
- var par_vararg = psig.vararg_rank
- var par_arity = psig.arity
- var raw_arity: Int
- if raw_args == null then raw_arity = 0 else raw_arity = raw_args.length
- if par_arity > raw_arity or (par_arity != raw_arity and par_vararg == -1) then
- v.error(self, "Error: arity mismatch; prototype is '{name}{psig}'.")
- return false
- end
- var arg_idx = 0
- for par_idx in [0..par_arity[ do
- var a: AExpr
- var par_type = psig[par_idx]
- if par_idx == par_vararg then
- for i in [0..(raw_arity-par_arity)] do
- a = raw_args[arg_idx]
- v.check_conform_expr(a, par_type)
- arg_idx = arg_idx + 1
- end
- else
- a = raw_args[arg_idx]
- v.check_conform_expr(a, par_type)
- arg_idx = arg_idx + 1
- end
- end
- return true
- end
-
- # Check the conformity of a set of defined closures
- private fun process_closures(v: TypingVisitor, psig: MMSignature, name: Symbol, cd: nullable Array[AClosureDef]): nullable MMType
- do
- var t = psig.return_type
- var cs = psig.closures # Declared closures
- var min_arity = 0
- for c in cs do
- if not c.is_optional then min_arity += 1
- end
- var arity = 0
- if cd != null then arity = cd.length
- if cs.length > 0 then
- if arity == 0 and min_arity > 0 then
- v.error(self, "Error: {name} requires {cs.length} blocks.")
- else if arity > cs.length or arity < min_arity then
- v.error(self, "Error: {name} requires {cs.length} blocks, {cd.length} found.")
- else
- # Initialize the break list if a value is required for breaks (ie. if the method is a function)
- var break_list: nullable Array[ABreakExpr] = null
- if t != null then break_list = new Array[ABreakExpr]
-
- # The n_label, is any in only set on the last decl
- var n_label = if arity > 0 then cd[arity-1].n_label else null
-
- # Process each closure definition
- for i in [0..arity[ do
- var cdi = cd[i]
- var cni = cdi.n_id.to_symbol
- var csi = psig.closure_named(cni)
- if csi != null then
- var esc = new EscapableClosure(cdi, csi, break_list)
- v.scope_ctx.push_escapable(esc, n_label)
- cdi.accept_typing2(v, esc)
- v.scope_ctx.pop
- else if cs.length == 1 then
- v.error(cdi.n_id, "Error: no closure named '!{cni}' in {name}; only closure is !{cs.first.name}.")
- else
- var a = new Array[String]
- for c in cs do
- a.add("!{c.name}")
- end
- v.error(cdi.n_id, "Error: no closure named '!{cni}' in {name}; only closures are {a.join(",")}.")
- end
- end
-
- # Check break type conformity
- if break_list != null then
- t = v.check_conform_multiexpr(t, break_list)
- end
- end
- else if arity != 0 then
- v.error(self, "Error: {name} does not require blocks.")
- end
- return t
- end
-end
-
-redef class AAbsSendExpr
- # Compute the called global property
- private fun do_typing(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, recv_is_self: Bool, name: Symbol, raw_args: nullable Array[AExpr], closure_defs: nullable Array[AClosureDef])
- do
- var prop = get_property(v, type_recv, is_implicit_self, name)
- if prop == null then return
- var sig = get_signature(v, type_recv, prop, recv_is_self)
- if not process_signature(v, sig, prop.name, raw_args) then return
- var rtype = process_closures(v, sig, prop.name, closure_defs)
- if rtype == null and sig.return_type != null then return
- _prop = prop
- _prop_signature = sig
- _return_type = rtype
- end
-
- private fun get_property(v: TypingVisitor, type_recv: MMType, is_implicit_self: Bool, name: Symbol): nullable MMMethod
- do
- if type_recv isa MMTypeNone then
- if name == (once "==".to_symbol) or name == (once "!=".to_symbol) then
- # Special case on != and == that are allowed for 'null'
- type_recv = v.type_object.as_nullable
- else
- v.error(self, "Error: Method '{name}' call on 'null'.")
- return null
- end
- end
- var lc = type_recv.local_class
- var prop: nullable MMMethod = null
- if lc.has_global_property_by_name(name) then prop = lc.select_method(name)
- if prop == null then
- var props = lc.super_methods_named(name)
- if props.length > 1 then
- v.error(self, "Error: Ambigous method name '{name}' for {props.join(", ")}. Use explicit designation.")
- return null
- else if props.length == 1 then
- var p = lc[props.first.global]
- assert p isa MMMethod
- prop = p
- end
-
- end
- if prop == null then
- if is_implicit_self then
- v.error(self, "Error: Method or variable '{name}' unknown in {type_recv}.")
- else
- v.error(self, "Error: Method '{name}' doesn't exists in {type_recv}.")
- end
- return null
- end
- return prop
- end
-
- # Get the signature for a local property and a receiver
- private fun get_signature(v: TypingVisitor, type_recv: MMType, prop: MMMethod, recv_is_self: Bool): MMSignature
- do
- prop.global.check_visibility(v, self, v.mmmodule, recv_is_self)
- var psig = prop.signature_for(type_recv)
- if not recv_is_self then psig = psig.not_for_self
- return psig
- end
-
- # The invoked method (once computed)
- redef fun prop do return _prop.as(not null)
- var _prop: nullable MMMethod
-
- # The return type (if any) (once computed)
- redef readable var _return_type: nullable MMType
-end
-
-# A possible call of constructor in a super class
-# Could be an explicit call or with the 'super' keyword
-redef class ASuperInitCall
- private fun register_super_init_call(v: TypingVisitor, property: MMMethod)
- do
- if parent != v.top_block and self != v.top_block then
- v.error(self, "Error: Constructor invocation {property} must not be in nested block.")
- end
- var cla = v.mmmodule[property.global.intro.local_class.global]
- var prev_class: nullable MMLocalClass = null
- var esic = v.explicit_super_init_calls.as(not null)
- if not esic.is_empty then
- prev_class = esic.last.global.intro.local_class
- end
- var order = v.local_class.cshe.reverse_linear_extension
- if cla == v.local_class then
- v.explicit_other_init_call = true
- else if not order.has(cla) then
- v.error(self, "Error: Constructor of class {cla} must be one in {order.join(", ")}.")
- else if cla == prev_class then
- v.error(self, "Error: Only one super constructor invocation of class {cla} is allowed.")
- else
- for c in order do
- if c == prev_class then
- prev_class = null
- else if c == cla then
- esic.add(property)
- break
- end
- end
- end
- end
-
-end
-
-redef class ANewExpr
- redef fun compute_raw_arguments do return n_args.to_a
- redef fun after_typing(v)
- do
- if not n_type.is_typed then return
- var t = n_type.stype
- if t.local_class.global.is_abstract then
- v.error(self, "Error: try to instantiate abstract class {t.local_class}.")
- return
- end
- if t.is_nullable then
- v.error(self, "Type error: cannot instantiate the nullable type {t}.")
- end
- var name: Symbol
- if n_id == null then
- name = once "init".to_symbol
- else
- name = n_id.to_symbol
- end
-
- do_typing(v, t, false, false, name, raw_arguments, null)
- if _prop == null then return
-
- if not prop.global.is_init then
- v.error(self, "Error: {prop} is not a constructor.")
- return
- end
- if not prop.global.is_init_for(t.local_class) then
- v.error(self, "Error: {prop} is not a constructor in {t.local_class}.")
- return
- end
- _stype = t
- _is_typed = true
- end
-end
-
-
-redef class ASendExpr
- # Name of the invoked property
- fun name: Symbol is abstract
-
- # Closure definitions
- redef fun closure_defs: nullable Array[AClosureDef] do return null
-
- redef fun after_typing(v)
- do
- do_all_typing(v)
- end
-
- private fun do_all_typing(v: TypingVisitor)
- do
- if not v.check_expr(n_expr) then return
- do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, name, raw_arguments, closure_defs)
- if _prop == null then return
- var prop = _prop.as(not null)
-
- if prop.global.is_init then
- if not v.local_property.global.is_init then
- v.error(self, "Error: try to invoke constructor {prop} in a method.")
- else if not n_expr.is_self then
- v.error(self, "Error: constructor {prop} is not invoken on 'self'.")
- else
- register_super_init_call(v, prop)
- end
- end
-
- _stype = return_type
- _is_typed = true
- end
-end
-
-redef class ASendReassignExpr
- redef fun read_prop do return _read_prop.as(not null)
- var _read_prop: nullable MMMethod
- redef fun do_all_typing(v)
- do
- if not v.check_expr(n_expr) then return
- var raw_args = raw_arguments
- do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, name, raw_args, null)
- var prop = _prop
- if prop == null then return
- if prop.global.is_init then
- if not v.local_property.global.is_init then
- v.error(self, "Error: try to invoke constructor {prop} in a method.")
- else if not n_expr.is_self then
- v.error(self, "Error: constructor {prop} is not invoken on 'self'.")
- end
- end
- var t = prop.signature_for(n_expr.stype).return_type.as(not null)
- if not n_expr.is_self then t = t.not_for_self
-
- var t2 = do_rvalue_typing(v, t)
- if t2 == null then return
- v.check_conform(self, t2, n_value.stype)
-
- _read_prop = prop
- raw_args = raw_args.to_a
- raw_args.add(n_value)
-
- do_typing(v, n_expr.stype, n_expr.is_implicit_self, n_expr.is_self, "{name}=".to_symbol, raw_args, null)
- if prop.global.is_init then
- if not v.local_property.global.is_init then
- v.error(self, "Error: try to invoke constructor {prop} in a method.")
- else if not n_expr.is_self then
- v.error(self, "Error: constructor {prop} is not invoken on 'self'.")
- end
- end
-
- _is_typed = true
- end
-end
-
-redef class ABinopExpr
- redef fun compute_raw_arguments do return [n_expr2]
-end
-redef class AEqExpr
- redef fun name do return once "==".to_symbol
- redef fun after_typing(v)
- do
- super
- if not n_expr.is_typed or not n_expr2.is_typed then return
- if n_expr.stype isa MMTypeNone and not n_expr2.stype.is_nullable or
- n_expr2.stype isa MMTypeNone and not n_expr.stype.is_nullable then
- v.warning(self, "Warning: comparaison between null and a non nullable value.")
- end
-
- if n_expr.stype isa MMTypeNone then
- if n_expr2.stype isa MMTypeNone then
- v.warning(self, "Warning: comparaison between two null values.")
- else
- try_to_isa(v, n_expr2)
- end
- else if n_expr2.stype isa MMTypeNone then
- try_to_isa(v, n_expr)
- end
- end
-
- private fun try_to_isa(v: TypingVisitor, n: AExpr)
- do
- var variable = n.its_variable
- if variable != null and n.stype isa MMNullableType then
- _if_false_flow_ctx = v.flow_ctx.sub_with(self, variable, n.stype.as_notnull)
- _if_true_flow_ctx = v.flow_ctx.sub_with(self, variable, v.type_none)
- end
- end
-end
-redef class ANeExpr
- redef fun name do return once "!=".to_symbol
- redef fun after_typing(v)
- do
- super
- if not n_expr.is_typed or not n_expr2.is_typed then return
- if n_expr.stype isa MMTypeNone and not n_expr2.stype.is_nullable or
- n_expr2.stype isa MMTypeNone and not n_expr.stype.is_nullable then
- v.warning(self, "Warning: comparaison between null and a non nullable value.")
- end
-
- if n_expr.stype isa MMTypeNone then
- if n_expr2.stype isa MMTypeNone then
- v.warning(self, "Warning: comparaison between two null values.")
- else
- try_to_isa(v, n_expr2)
- end
- else if n_expr2.stype isa MMTypeNone then
- try_to_isa(v, n_expr)
- end
- end
-
- private fun try_to_isa(v: TypingVisitor, n: AExpr)
- do
- var variable = n.its_variable
- if variable != null and n.stype isa MMNullableType then
- _if_true_flow_ctx = v.flow_ctx.sub_with(self, variable, n.stype.as_notnull)
- _if_false_flow_ctx = v.flow_ctx.sub_with(self, variable, v.type_none)
- end
- end
-end
-redef class ALtExpr
- redef fun name do return once "<".to_symbol
-end
-redef class ALeExpr
- redef fun name do return once "<=".to_symbol
-end
-redef class ALlExpr
- redef fun name do return once "<<".to_symbol
-end
-redef class AGtExpr
- redef fun name do return once ">".to_symbol
-end
-redef class AGeExpr
- redef fun name do return once ">=".to_symbol
-end
-redef class AGgExpr
- redef fun name do return once ">>".to_symbol
-end
-redef class APlusExpr
- redef fun name do return once "+".to_symbol
-end
-redef class AMinusExpr
- redef fun name do return once "-".to_symbol
-end
-redef class AStarshipExpr
- redef fun name do return once "<=>".to_symbol
-end
-redef class AStarExpr
- redef fun name do return once "*".to_symbol
-end
-redef class ASlashExpr
- redef fun name do return once "/".to_symbol
-end
-redef class APercentExpr
- redef fun name do return once "%".to_symbol
-end
-
-redef class AUminusExpr
- redef fun name do return once "unary -".to_symbol
- redef fun compute_raw_arguments do return null
-end
-
-redef class ACallFormExpr
- redef fun after_typing(v)
- do
- if n_expr.is_implicit_self then
- var name = n_id.to_symbol
- var variable = v.scope_ctx[name]
- if variable != null then
- var n: AExpr
- if variable isa ClosureVariable then
- n = new AClosureCallExpr.init_aclosurecallexpr(n_id, n_args, n_closure_defs)
- n._variable = variable
- else
- if not n_args.n_exprs.is_empty or n_args isa AParExprs then
- v.error(self, "Error: {name} is variable, not a function.")
- return
- end
- n = variable_create(variable)
- n._variable = variable
- end
- replace_with(n)
- n.after_typing(v)
- return
- end
- end
-
- super
- end
-
- redef fun closure_defs
- do
- if n_closure_defs.is_empty then
- return null
- else
- return n_closure_defs.to_a
- end
- end
-
- # Create a variable acces corresponding to the call form
- fun variable_create(variable: Variable): AVarFormExpr is abstract
-end
-
-redef class ACallExpr
- redef fun variable_create(variable)
- do
- return new AVarExpr.init_avarexpr(n_id)
- end
-
- redef fun name do return n_id.to_symbol
- redef fun compute_raw_arguments do return n_args.to_a
-end
-
-redef class ACallAssignExpr
- redef fun variable_create(variable)
- do
- return new AVarAssignExpr.init_avarassignexpr(n_id, n_assign, n_value)
- end
-
- redef fun name do return (n_id.text + "=").to_symbol
- redef fun compute_raw_arguments do
- var res = n_args.to_a
- res.add(n_value)
- return res
- end
-end
-
-redef class ACallReassignExpr
- redef fun variable_create(variable)
- do
- return new AVarReassignExpr.init_avarreassignexpr(n_id, n_assign_op, n_value)
- end
-
- redef fun name do return n_id.to_symbol
- redef fun compute_raw_arguments do return n_args.to_a
-end
-
-redef class ABraExpr
- redef fun name do return once "[]".to_symbol
- redef fun compute_raw_arguments do return n_args.to_a
- redef fun closure_defs
- do
- if n_closure_defs.is_empty then
- return null
- else
- return n_closure_defs.to_a
- end
- end
-end
-
-redef class ABraAssignExpr
- redef fun name do return once "[]=".to_symbol
- redef fun compute_raw_arguments do
- var res = n_args.to_a
- res.add(n_value)
- return res
- end
-end
-
-redef class ABraReassignExpr
- redef fun name do return once "[]".to_symbol
- redef fun compute_raw_arguments do return n_args.to_a
-end
-
-redef class AInitExpr
- redef fun name do return once "init".to_symbol
- redef fun compute_raw_arguments do return n_args.to_a
-end
-
-redef class AClosureCallExpr
- var _variable: nullable ClosureVariable
- redef fun variable do return _variable.as(not null)
- redef fun compute_raw_arguments do return n_args.to_a
-
- redef fun after_typing(v)
- do
- var va = variable
- if va.closure.is_break then v.mark_unreash(self)
- var sig = va.closure.signature
- var s = process_signature(v, sig, n_id.to_symbol, compute_raw_arguments)
- if not n_closure_defs.is_empty then
- process_closures(v, sig, n_id.to_symbol, n_closure_defs.to_a)
- end
- if not s then return
- _prop_signature = sig
- _stype = sig.return_type
- _is_typed = true
- end
-end
-
-redef class AClosureId
- fun to_symbol: Symbol is abstract
-end
-redef class ASimpleClosureId
- redef fun to_symbol: Symbol do return n_id.to_symbol
-end
-redef class ABreakClosureId
- redef fun to_symbol: Symbol do return n_kwbreak.to_symbol
-end
-
-redef class AClosureDef
- var _closure: nullable MMClosure
- redef fun closure do return _closure.as(not null)
-
- # The corresponding escapable object
- readable var _escapable: nullable EscapableBlock
-
- var _accept_typing2: Bool = false
- redef fun accept_typing(v)
- do
- # Typing is deferred, wait accept_typing2(v)
- if _accept_typing2 then super
- end
-
- private fun accept_typing2(v: TypingVisitor, esc: EscapableClosure)
- do
- _escapable = esc
-
- var sig = esc.closure.signature
- if sig.arity != n_ids.length then
- v.error(self, "Error: {sig.arity} automatic variable names expected, {n_ids.length} found.")
- return
- end
-
- _closure = esc.closure
-
- v.scope_ctx.push(self)
- var old_flow_ctx = v.flow_ctx
- var old_base_flow_ctx = v.base_flow_ctx
- v.base_flow_ctx = v.flow_ctx
- variables = new Array[AutoVariable]
- for i in [0..n_ids.length[ do
- var va = new AutoVariable(n_ids[i].to_symbol, n_ids[i])
- variables.add(va)
- va.stype = sig[i]
- v.scope_ctx.add_variable(va)
- end
-
- _accept_typing2 = true
- accept_typing(v)
-
- if v.flow_ctx.unreash == false then
- if closure.signature.return_type != null then
- v.error(self, "Control error: Reached end of block (a 'continue' with a value was expected).")
- else if closure.is_break and esc.break_list != null then
- v.error(self, "Control error: Reached end of break block (a 'break' with a value was expected).")
- end
- end
- v.flow_ctx = old_flow_ctx
- v.base_flow_ctx = old_base_flow_ctx
- v.scope_ctx.pop
- end
-end
-
-abstract class ATypeCheckExpr
- super AExpr
- private fun check_expr_cast(v: TypingVisitor, n_expr: AExpr, n_type: AType)
- do
- if not v.check_expr(n_expr) then return
- if not n_type.is_typed then return
- var etype = n_expr.stype
- var ttype = n_type.stype
- if etype == ttype then
- v.warning(self, "Warning: Expression is already a {ttype}.")
- else if etype < ttype then
- if not ttype.has_formal and not etype.has_formal then
- # the old metamodel is not that great with formal types
- v.warning(self, "Warning: Expression is already a {ttype} since it is a {etype}.")
- end
- else if etype isa MMTypeNone then
- # ttype is not nullable because of prevous test
- v.warning(self, "Warning: Expression is null therefore cannot be a {ttype}.")
- else if etype.is_nullable and etype.as_notnull == ttype then
- if ttype isa MMTypeFormal and ttype.bound.is_nullable then
- # No warning in this case since with
- # type T: nullable A
- # var x: nullable T
- # 'x.as(not null)' != 'x.as(T)'
- # 'x != null' != 'x isa T'
- else if self isa AIsaExpr then
- v.warning(self, "Warning: Prefer '!= null'.")
- else
- v.warning(self, "Warning: Prefer '.as(not null)'.")
- end
- end
- end
-end
-
-redef class AIsaExpr
- super ATypeCheckExpr
- redef fun after_typing(v)
- do
- check_expr_cast(v, n_expr, n_type)
- if not n_type.is_typed then return
- var variable = n_expr.its_variable
- if variable != null then
- _if_true_flow_ctx = v.flow_ctx.sub_with(self, variable, n_type.stype)
- end
- _stype = v.type_bool
- _is_typed = true
- end
-end
-
-redef class AAsCastExpr
- super ATypeCheckExpr
- redef fun after_typing(v)
- do
- check_expr_cast(v, n_expr, n_type)
- if not n_type.is_typed then return
- _stype = n_type.stype
- _is_typed = _stype != null
- end
-end
-
-redef class AAsNotnullExpr
- redef fun after_typing(v)
- do
- if not v.check_expr(n_expr) then return
- var t = n_expr.stype
- if t isa MMTypeNone then
- v.error(n_expr, "Type error: 'as(not null)' on 'null' value.")
- return
- else if not t.is_nullable then
- v.warning(n_expr, "Warning: 'as(not null)' on non nullable type.")
- end
- _stype = n_expr.stype.as_notnull
- _is_typed = true
- end
-end
-
-redef class AProxyExpr
- redef fun after_typing(v)
- do
- if not n_expr.is_typed then return
- _is_typed = true
- if n_expr.is_statement then return
- _stype = n_expr.stype
- _if_true_flow_ctx = n_expr._if_true_flow_ctx
- _if_false_flow_ctx = n_expr._if_false_flow_ctx
- end
-
- redef fun is_self do return n_expr.is_self
-
- redef fun its_variable do return n_expr.its_variable
-end
-
-redef class AOnceExpr
- redef fun accept_typing(v)
- do
- if v.once_count > 0 then
- v.warning(self, "Useless once in a once expression.")
- end
- v.once_count = v.once_count + 1
-
- super
-
- v.once_count = v.once_count - 1
- end
-end
-
-redef class ADebugTypeExpr
- redef fun after_typing(v)
- do
- if not v.check_expr(n_expr) then return
- if not n_type.is_typed then return
- var etype = n_expr.stype
- var ttype = n_type.stype
- if etype != ttype then
- v.warning(self, "Warning: Expression is a {etype}, expected {ttype}.")
- end
- end
-end
+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2006-2008 Floréal Morandat <morandat@lirmm.fr>
-# Copyright 2008 Jean Privat <jean@pryen.org>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Various functions for NIT tools
-module utils
-
-import symbol
-
-# Mangle an array of symbol using only alphanums and underscores
-fun cmangle(symbols: Symbol...): String
-do
- var table = once cmangle_table
- var res = new Buffer
- for sym in symbols do
- if not res.is_empty then
- res.add('_')
- res.add('_')
- res.add('_')
- end
- var underscore = false
- var normal = true
- var s = sym.to_s
- for c in s do
- if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') or (c >= '0' and c <= '9') then
- res.add(c)
- underscore = false
- normal = true
- else if c == '_' and not underscore then
- res.add(c)
- underscore = true
- normal = true
- else if table.has_key(c) then
- if normal then
- res.add('_')
- res.add('_')
- end
- res.append(table[c])
- normal = false
- underscore = false
- end
- end
- end
- return res.to_s
-end
-
-# Build the table that associates character to mangle to string
-private fun cmangle_table: HashMap[Char, String]
-do
- var res = new HashMap[Char, String]
- res['+'] = "plus"
- res['-'] = "minus"
- res['*'] = "star"
- res['/'] = "slash"
- res['%'] = "percent"
- res['['] = "bra"
- res['='] = "eq"
- res['<'] = "l"
- res['>'] = "g"
- res['!'] = "n"
- res['_'] = "u"
- res['@'] = "at"
- return res
-end
-