def externalize(info): """Records dependencies on foreign definitions. Each CodeSwitch package has a list of other packages it depends on. This list goes beyond recording the name and version of each required package: it records the definitions that are actually used. These are "extern" definitions (marked with the `EXTERN` flag), and they can be referred to directly by the package like normal definitions. During compilation, we may refer to "foreign" definitions which are actually in other packages (not in the dependency list). This externalization step detects these foreign definitions, creates "extern" stubs, and records them. """ for useInfo in info.iterUseInfo(): defn = useInfo.defnInfo.irDefn if isinstance(defn, ir.Package): info.package.ensureDependency(defn) elif isinstance(defn, ir.IrTopDefn): externalizeDefn(info, defn) elif isinstance(defn, ir.Field): externalizeDefn(info, defn.definingClass) info.package.findOrAddName(defn.name) def externalizeInheritedMethods(objDefn): for m in objDefn.methods: externalizeDefn(info, m) each(externalizeInheritedMethods, info.package.classes) each(externalizeInheritedMethods, info.package.traits) for t in info.iterType(): externalizeType(info, t) for d in info.iterStdExternInfo(): externalizeDefn(info, d)
def externalizeType(self, ty): if isinstance(ty, ir_types.ClassType): self.externalizeDefn(ty.clas) each(self.externalizeType, ty.typeArguments) elif isinstance(ty, ir_types.VariableType) and \ ty.typeParameter.isForeign(): self.externalizeDefn(ty.typeParameter) elif isinstance(ty, ir_types.ExistentialType): each(self.externalizeDefn, ty.variables) self.externalizeType(ty.ty)
def externalizeMethod(self, method, dep): self.package.addName(method.name) id = ids.DefnId(ids.TARGET_PACKAGE_ID, ids.DefnId.FUNCTION, len(dep.externMethods)) externFlags = method.flags | frozenset([flags.EXTERN]) externMethod = ir.Function(method.name, id, astDefn=method.astDefn, returnType=method.returnType, parameterTypes=method.parameterTypes, flags=externFlags) dep.externMethods.append(externMethod) self.externalizeType(method.returnType) externMethod.typeParameters = map(self.externalizeDefn, method.typeParameters) each(self.externalizeType, method.parameterTypes) return externMethod
def __repr__(self): waiters = ', '.join(each(self.waiters).name) return '<{} {}{}>'.format( type(self).__name__, self.now, ' ({})'.format(waiters) if waiters else '', )
def buildSubtypeGraph(info): """Builds a directed graph of definitions and their subtype relations. Each class, trait, and type parameter defined in the package being compiled is a node in this graph. Edges go from classes and traits to their base classes and traits. Type parameters also have edges from their lower bounds and to their upper bounds. """ subtypeGraph = Graph() def addTypeDefn(irTypeDefn): subtypeGraph.addVertex(irTypeDefn.id) if isinstance(irTypeDefn, ir.ObjectTypeDefn): for supertype in irTypeDefn.supertypes: if supertype.isNullable(): raise InheritanceException.fromDefn( irTypeDefn, "cannot inherit nullable type") if supertype.clas is getNothingClass(): raise InheritanceException.fromDefn( irTypeDefn, "cannot inherit Nothing") supertypeId = getIdForType(supertype) if irTypeDefn.id is supertypeId: raise InheritanceException.fromDefn( irTypeDefn, "cannot inherit from itself") if not supertype.clas.isLocal(): NonLocalObjectTypeDefnScope.ensureForDefn( supertype.clas, info) subtypeGraph.addEdge(irTypeDefn.id, supertypeId) elif isinstance(irTypeDefn, ir.TypeParameter): upperBoundId = getIdForType(irTypeDefn.upperBound) if irTypeDefn.id is upperBoundId: raise InheritanceException.fromDefn( irTypeDefn, "cannot be upper bounded by itself") subtypeGraph.addEdge(irTypeDefn.id, upperBoundId) lowerBoundId = getIdForType(irTypeDefn.lowerBound) if irTypeDefn.id is lowerBoundId: raise InheritanceException.fromDefn( irTypeDefn, "cannot be lower bounded by itself") subtypeGraph.addEdge(lowerBoundId, irTypeDefn.id) else: raise NotImplementedError() each(addTypeDefn, info.package.classes) each(addTypeDefn, info.package.traits) each(addTypeDefn, info.package.typeParameters) return subtypeGraph
def buildSubtypeGraph(info): """Builds a directed graph of definitions and their subtype relations. Each class, trait, and type parameter defined in the package being compiled is a node in this graph. Edges go from classes and traits to their base classes and traits. Type parameters also have edges from their lower bounds and to their upper bounds. """ subtypeGraph = Graph() def addTypeDefn(irTypeDefn): subtypeGraph.addVertex(irTypeDefn.id) if isinstance(irTypeDefn, ir.ObjectTypeDefn): for supertype in irTypeDefn.supertypes: if supertype.isNullable(): raise InheritanceException.fromDefn(irTypeDefn, "cannot inherit nullable type") if supertype.clas is getNothingClass(): raise InheritanceException.fromDefn(irTypeDefn, "cannot inherit Nothing") supertypeId = getIdForType(supertype) if irTypeDefn.id is supertypeId: raise InheritanceException.fromDefn(irTypeDefn, "cannot inherit from itself") if not supertype.clas.isLocal(): NonLocalObjectTypeDefnScope.ensureForDefn(supertype.clas, info) subtypeGraph.addEdge(irTypeDefn.id, supertypeId) elif isinstance(irTypeDefn, ir.TypeParameter): upperBoundId = getIdForType(irTypeDefn.upperBound) if irTypeDefn.id is upperBoundId: raise InheritanceException.fromDefn(irTypeDefn, "cannot be upper bounded by itself") subtypeGraph.addEdge(irTypeDefn.id, upperBoundId) lowerBoundId = getIdForType(irTypeDefn.lowerBound) if irTypeDefn.id is lowerBoundId: raise InheritanceException.fromDefn(irTypeDefn, "cannot be lower bounded by itself") subtypeGraph.addEdge(lowerBoundId, irTypeDefn.id) else: raise NotImplementedError() each(addTypeDefn, info.package.classes) each(addTypeDefn, info.package.traits) each(addTypeDefn, info.package.typeParameters) return subtypeGraph
def step(self, by=None): if by is None: by = self.Time threads = self.timeline.get(depth=by) self.now = threads[0].current_event.now each(threads).resume()
def finished(self): if not self.started: return False return all(each(self.threads).finished)
def _writeTypeFlags(self, flags): utils.each(self._write, flags)
def externalizeDefn(info, defn): if not defn.isForeign(): return defn assert flags.PUBLIC in defn.flags id = defn.id # Make sure we have a dependency on the package that contains this definition. info.package.ensureDependency( info.packageLoader.getPackageById(id.packageId)) # Find the list we're going to put our external description in. info.package.findOrAddName(defn.name) externFlags = defn.flags | frozenset([flags.EXTERN]) dep = info.package.dependencies[id.packageId.index] if isinstance(defn, ir.Global): externDefns = dep.externGlobals elif isinstance(defn, ir.Function): externDefns = dep.externFunctions elif isinstance(defn, ir.Class): externDefns = dep.externClasses elif isinstance(defn, ir.Trait): externDefns = dep.externTraits else: raise NotImplementedError() # Base case: we already externalized this definition, so re-use that. if id.externIndex is not None: return externDefns[id.externIndex] # Recursive case: add a placeholder definition so recursive calls can find it. # Externalize anything else the definition depends on. id.externIndex = len(externDefns) if isinstance(defn, ir.Global): externDefn = ir.Global(defn.name, id, astDefn=defn.astDefn, type=defn.type, flags=externFlags) externDefns.append(externDefn) externalizeType(info, defn.type) elif isinstance(defn, ir.Function): externDefn = ir.Function(defn.name, id, astDefn=defn.astDefn, typeParameters=defn.typeParameters, returnType=defn.returnType, parameterTypes=defn.parameterTypes, flags=externFlags) externDefns.append(externDefn) externalizeType(info, defn.returnType) for p in defn.typeParameters: externalizeTypeParameter(info, p) for t in defn.parameterTypes: externalizeType(info, t) elif isinstance(defn, ir.Class): externDefn = ir.Class(defn.name, id, astDefn=defn.astDefn, typeParameters=defn.typeParameters, supertypes=defn.supertypes, elementType=defn.elementType, flags=externFlags) externDefns.append(externDefn) for p in defn.typeParameters: externalizeTypeParameter(info, p) for t in defn.supertypes: externalizeType(info, t) externDefn.constructors = [ externalizeMethod(info, ctor, dep) for ctor in defn.constructors if flags.PUBLIC in ctor.flags ] externDefn.fields = [ f for f in defn.fields if flags.PUBLIC in f.flags or flags.ARRAY in f.flags ] each(info.package.findOrAddName, (f.name for f in externDefn.fields)) for f in externDefn.fields: externalizeType(info, f.type) externDefn.methods = [ externalizeMethod(info, m, dep) for m in defn.methods if flags.PUBLIC in m.flags ] if externDefn.elementType is not None: externalizeType(info, externDefn.elementType) elif isinstance(defn, ir.Trait): externDefn = ir.Trait(defn.name, id, astDefn=defn.astDefn, typeParameters=defn.typeParameters, supertypes=defn.supertypes, flags=externFlags) externDefns.append(externDefn) for p in defn.typeParameters: externalizeTypeParameter(info, p) for t in defn.supertypes: externalizeType(info, t) externDefn.methods = [ externalizeMethod(info, m, dep) for m in defn.methods if flags.PUBLIC in m.flags ] else: raise NotImplementedError return externDefn
def isSubtypeOfRules_(self, other, subEnv): left = self right = other # Basic rules that apply to all types. if left.isEquivalent(right): return True if left is AnyType or right is NoType: return False if right is AnyType or left is NoType: return True # Primitive types. if left.isPrimitive() or right.isPrimitive(): return False assert left.isObject() and right.isObject() # Existential types. if isinstance(right, ExistentialType): utils.each(subEnv.addVariable, right.variables) return left.isSubtypeOf_(right.ty, subEnv) if subEnv.isExistentialVar(right): return subEnv.trySubstitute(left, right) if isinstance(left, ExistentialType): return left.ty.isSubtypeOf_(right, subEnv) # A nullable type cannot be a subtype of a non-nullable type. Note that existential # types cannot be nullable (but their inner types can). if left.isNullable() and not right.isNullable(): return False # Variable types. if isinstance(left, VariableType) and isinstance(right, VariableType): # If both are variable types, try to find a common bound. leftBound = left rightFirstBound = right while isinstance(leftBound, VariableType): rightBound = rightFirstBound while isinstance(rightBound, VariableType): if leftBound.typeParameter is rightBound.typeParameter and \ (rightBound.isNullable() or not leftBound.isNullable()): return True rightBound = rightBound.lowerBound() leftBound = leftBound.upperBound() while isinstance(left, VariableType): left = left.upperBound() while isinstance(right, VariableType): right = right.lowerBound() # Class types. assert isinstance(left, ClassType) and isinstance(right, ClassType) # Special cases for `Nothing`. if left.clas is builtins.getNothingClass(): return True if right.clas is builtins.getNothingClass(): return False # Check that left is derived from right, and substitute for the same definition. leftBase = left.clas.findBaseType(right.clas) if leftBase is None: return False left = leftBase.substitute(left.clas.typeParameters, left.typeArguments) # Compare type arguments, based on variance. for lty, rty, tp in \ zip(left.typeArguments, right.typeArguments, left.clas.typeParameters): # This could be more compact, but it's better for debugging to have the cases split. variance = tp.variance() if variance is flags.COVARIANT: if not lty.isSubtypeOf_(rty, subEnv): return False elif variance is flags.CONTRAVARIANT: if not rty.isSubtypeOf_(lty, subEnv): return False else: assert variance is INVARIANT if not (subEnv.isExistentialVar(rty) and subEnv.trySubstitute(lty, rty)) and \ not lty.isEquivalent(rty): return False return True
def externalizeDefn(info, defn): if not defn.isForeign(): return defn assert flags.PUBLIC in defn.flags id = defn.id # Make sure we have a dependency on the package that contains this definition. info.package.ensureDependency(info.packageLoader.getPackageById(id.packageId)) # Find the list we're going to put our external description in. info.package.findOrAddName(defn.name) externFlags = defn.flags | frozenset([flags.EXTERN]) dep = info.package.dependencies[id.packageId.index] if isinstance(defn, ir.Global): externDefns = dep.externGlobals elif isinstance(defn, ir.Function): externDefns = dep.externFunctions elif isinstance(defn, ir.Class): externDefns = dep.externClasses elif isinstance(defn, ir.Trait): externDefns = dep.externTraits else: raise NotImplementedError() # Base case: we already externalized this definition, so re-use that. if id.externIndex is not None: return externDefns[id.externIndex] # Recursive case: add a placeholder definition so recursive calls can find it. # Externalize anything else the definition depends on. id.externIndex = len(externDefns) if isinstance(defn, ir.Global): externDefn = ir.Global(defn.name, id, astDefn=defn.astDefn, type=defn.type, flags=externFlags) externDefns.append(externDefn) externalizeType(info, defn.type) elif isinstance(defn, ir.Function): externDefn = ir.Function(defn.name, id, astDefn=defn.astDefn, typeParameters=defn.typeParameters, returnType=defn.returnType, parameterTypes=defn.parameterTypes, flags=externFlags) externDefns.append(externDefn) externalizeType(info, defn.returnType) for p in defn.typeParameters: externalizeTypeParameter(info, p) for t in defn.parameterTypes: externalizeType(info, t) elif isinstance(defn, ir.Class): externDefn = ir.Class(defn.name, id, astDefn=defn.astDefn, typeParameters=defn.typeParameters, supertypes=defn.supertypes, elementType=defn.elementType, flags=externFlags) externDefns.append(externDefn) for p in defn.typeParameters: externalizeTypeParameter(info, p) for t in defn.supertypes: externalizeType(info, t) externDefn.constructors = [externalizeMethod(info, ctor, dep) for ctor in defn.constructors if flags.PUBLIC in ctor.flags] externDefn.fields = [f for f in defn.fields if flags.PUBLIC in f.flags or flags.ARRAY in f.flags] each(info.package.findOrAddName, (f.name for f in externDefn.fields)) for f in externDefn.fields: externalizeType(info, f.type) externDefn.methods = [externalizeMethod(info, m, dep) for m in defn.methods if flags.PUBLIC in m.flags] if externDefn.elementType is not None: externalizeType(info, externDefn.elementType) elif isinstance(defn, ir.Trait): externDefn = ir.Trait(defn.name, id, astDefn=defn.astDefn, typeParameters=defn.typeParameters, supertypes=defn.supertypes, flags=externFlags) externDefns.append(externDefn) for p in defn.typeParameters: externalizeTypeParameter(info, p) for t in defn.supertypes: externalizeType(info, t) externDefn.methods = [externalizeMethod(info, m, dep) for m in defn.methods if flags.PUBLIC in m.flags] else: raise NotImplementedError return externDefn
def start(self): each(self.threads).start() self.started = True
def lubRules_(self, other, stack, subEnv): # We need to be able to detect infinite recursion in order to ensure termination. # Consider the case below: # class A[+T] # class B <: A[B] # class C <: A[C] # Suppose we want to find B lub C. # The correct answer is A[B lub C] = A[A[B lub C]] = A[A[A[B lub C]]] ... # Since we have no way to correctly express the least upper bound in that case, we # settle for returning a close upper bound: A[Object]. left = self right = other if (left, right) in stack: if left.isObject() and right.isObject(): return getRootClassType() else: return AnyType # Basic rules for all types. if left.isEquivalent(right): return left if left is AnyType or right is AnyType: return AnyType if left is NoType: return right if right is NoType: return left # Primitive types. if left.isPrimitive() or right.isPrimitive(): return AnyType # Existential types (always recursive). if isinstance(left, ExistentialType) or isinstance(right, ExistentialType): stack.append((left, right)) # Add the type parameters from both existential types to the substitution # environment. These will be treated a little differently than normal # type parameters. if isinstance(left, ExistentialType): leftInnerType = left.ty utils.each(subEnv.addVariable, left.variables) else: leftInnerType = left if isinstance(right, ExistentialType): rightInnerType = right.ty utils.each(subEnv.addVariable, right.variables) else: rightInnerType = right # Find the lub of the inner types. innerLubType = leftInnerType.lub_(rightInnerType, stack, subEnv) stack.pop() # Find which variables are still being used in the lub. If some of the variables # from `left` and `right` are still present, return an existential with those. # Note that unused variables can be removed from an existential type. For example: # forsome [X] Option[String] == Option[String] # We must be careful the returned type has variables in a deterministic order. # Technically, the order shouldn't matter, but it's infeasible to test whether two # types are equivalent with arbitrary ordered variables, since they cannot easily # be sorted. return ExistentialType.close(subEnv.getVariables(), innerLubType) # If either side is nullable, the result is nullable. if self.isNullable() or other.isNullable(): combinedFlags = frozenset([NULLABLE_TYPE_FLAG]) else: combinedFlags = frozenset() leftWithFlags = left.withFlags(combinedFlags) rightWithFlags = right.withFlags(combinedFlags) # Rules for subtypes. if leftWithFlags.isSubtypeOf_(rightWithFlags, subEnv): return rightWithFlags if rightWithFlags.isSubtypeOf_(leftWithFlags, subEnv): return leftWithFlags # Variable types. if isinstance(left, VariableType) and isinstance(right, VariableType): sharedBound = left.typeParameter.findCommonUpperBound(right.typeParameter) if sharedBound is not None: return VariableType(sharedBound, combinedFlags) # TODO: beyond this point, we can't find an accurate least upper bound because of the # presence of multiple shared traits. We will find a common base class instead, which # may be less specific. When union types are supported, we should just return # left | right. while isinstance(left, VariableType): left = left.upperBound() while isinstance(right, VariableType): right = right.upperBound() assert isinstance(left, ClassType) and isinstance(right, ClassType) # Find a common base class. We don't assume that there is a single root class # (even though there is), so this can fail. baseClass = left.clas.findCommonBaseClass(right.clas) while baseClass is not None: left = left.substituteForBase(baseClass) right = right.substituteForBase(baseClass) # We need to combine the type arguments, according to the variance of the # corresponding type parameters. This is not necessarily possible. If we get # stuck, we'll try again with the superclass. leftArgs = left.typeArguments rightArgs = right.typeArguments combinedArgs = [] combineSuccess = True for param, leftArg, rightArg in zip(baseClass.typeParameters, leftArgs, rightArgs): variance = param.variance() if variance is INVARIANT: if leftArg == rightArg: combined = leftArg elif subEnv.isExistentialVar(leftArg) and \ subEnv.trySubstitute(rightArg, leftArg): combined = leftArg elif subEnv.isExistentialVar(rightArg) and \ subEnv.trySubstitute(leftArg, rightArg): combined = rightArg else: combined = AnyType else: stack.append((self, other)) if variance is flags.COVARIANT: combined = leftArg.lub_(rightArg, stack, subEnv) else: assert variance is flags.CONTRAVARIANT combined = leftArg.glb_(rightArg, stack) stack.pop() if combined is AnyType: combineSuccess = False break combinedArgs.append(combined) if combineSuccess: return ClassType(baseClass, tuple(combinedArgs), combinedFlags) baseClass = baseClass.superclass() # If we get here, then we ran out of superclasses. Fall through. return AnyType
def externalizeDefn(self, defn): if not defn.isForeign(): return defn id = defn.id # Make sure we have a dependency on the package that contains this definition. self.package.ensureDependency(self.packageLoader.getPackageById(id.packageId)) # Find the list we're going to put our external description in. self.package.addName(defn.name) externFlags = defn.flags | frozenset([flags.EXTERN]) dep = self.package.dependencies[id.packageId.index] if isinstance(defn, ir.Global): externDefns = dep.externGlobals elif isinstance(defn, ir.Function): externDefns = dep.externFunctions elif isinstance(defn, ir.Class): externDefns = dep.externClasses elif isinstance(defn, ir.TypeParameter): externDefns = dep.externTypeParameters else: raise NotImplementedError # Base case: we already externalized this definition, so re-use that. if id.externIndex is not None: return externDefns[id.externIndex] # Recursive case: add a placeholder definition so recursive calls can find it. # Externalize anything else the definition depends on. id.externIndex = len(externDefns) if isinstance(defn, ir.Global): externDefn = ir.Global(defn.name, id, defn.astDefn, defn.type, externFlags) externDefns.append(externDefn) self.externalizeType(defn.type) elif isinstance(defn, ir.Function): externDefn = ir.Function(defn.name, id, astDefn=defn.astDefn, returnType=defn.returnType, parameterTypes=defn.parameterTypes, flags=externFlags) externDefns.append(externDefn) self.externalizeType(defn.returnType) externDefn.typeParameters = [self.externalizeDefn(param) for param in defn.typeParameters] each(self.externalizeType, defn.parameterTypes) if defn.definingClass is not None: externDefn.definingclass = self.externalizeDefn(defn.definingClass) elif isinstance(defn, ir.Class): externDefn = ir.Class(defn.name, id, astDefn=defn.astDefn, supertypes=defn.supertypes, fields=defn.fields, elementType=defn.elementType, flags=externFlags) externDefns.append(externDefn) externDefn.typeParameters = [self.externalizeDefn(param) for param in defn.typeParameters] each(self.externalizeType, defn.supertypes) externDefn.constructors = [self.externalizeMethod(ctor, dep) for ctor in defn.constructors] each(self.package.addName, (f.name for f in defn.fields)) each(self.externalizeType, (f.type for f in defn.fields)) externDefn.methods = [self.externalizeMethod(m, dep) for m in defn.methods] if externDefn.elementType is not None: self.externalizeType(externDefn.elementType) elif isinstance(defn, ir.TypeParameter): externDefn = ir.TypeParameter(defn.name, id, astDefn=defn.astDefn, upperBound=defn.upperBound, lowerBound=defn.lowerBound, flags=externFlags) externDefns.append(externDefn) self.externalizeType(externDefn.upperBound) self.externalizeType(externDefn.lowerBound) else: raise NotImplementedError return externDefn
def add(name, paths): snapshot.set(name) for path in each([p for p in paths]): logger.info("Adding file %s to snapshot %s" % (path, name)) Parse.parser(path)
def addName(self, name): assert isinstance(name, Name) each(self.findOrAddString, name.components)