private ObjectType( NominalType nominalType, PersistentMap<String, Property> props, FunctionType fn, boolean isLoose, ObjectKind objectKind) { Preconditions.checkArgument( fn == null || fn.isQmarkFunction() || fn.isLoose() == isLoose, "isLoose: %s, fn: %s", isLoose, fn); Preconditions.checkArgument(FunctionType.isInhabitable(fn)); Preconditions.checkArgument( fn == null || nominalType != null, "Cannot create function %s without nominal type", fn); if (nominalType != null) { Preconditions.checkArgument( !nominalType.isClassy() || !isLoose, "Cannot create loose objectType with nominal type %s", nominalType); Preconditions.checkArgument( fn == null || nominalType.isFunction(), "Cannot create objectType of nominal type %s with function (%s)", nominalType, fn); Preconditions.checkArgument( !nominalType.isFunction() || fn != null, "Cannot create Function instance without a FunctionType"); } this.nominalType = nominalType; this.props = props; this.fn = fn; this.isLoose = isLoose; this.objectKind = objectKind; }
static ObjectType meet(ObjectType obj1, ObjectType obj2) { Preconditions.checkState(areRelatedClasses(obj1.nominalType, obj2.nominalType)); if (obj1 == TOP_OBJECT) { return obj2; } else if (obj2 == TOP_OBJECT) { return obj1; } NominalType resultNomType = NominalType.pickSubclass(obj1.nominalType, obj2.nominalType); FunctionType fn = FunctionType.meet(obj1.fn, obj2.fn); if (!FunctionType.isInhabitable(fn)) { return BOTTOM_OBJECT; } boolean isLoose = obj1.isLoose && obj2.isLoose || fn != null && fn.isLoose(); if (resultNomType != null && resultNomType.isFunction() && fn == null) { fn = obj1.fn == null ? obj2.fn : obj1.fn; isLoose = fn.isLoose(); } PersistentMap<String, Property> props; if (isLoose) { props = joinPropsLoosely(obj1.props, obj2.props); } else { props = meetPropsHelper(false, resultNomType, obj1.props, obj2.props); } if (props == BOTTOM_MAP) { return BOTTOM_OBJECT; } ObjectKind ok = ObjectKind.meet(obj1.objectKind, obj2.objectKind); return new ObjectType(resultNomType, props, fn, isLoose, ok); }
static ObjectType join(ObjectType obj1, ObjectType obj2) { if (obj1 == TOP_OBJECT || obj2 == TOP_OBJECT) { return TOP_OBJECT; } NominalType nom1 = obj1.nominalType; NominalType nom2 = obj2.nominalType; Preconditions.checkState(areRelatedClasses(nom1, nom2)); if (obj1.equals(obj2)) { return obj1; } boolean isLoose = obj1.isLoose || obj2.isLoose; FunctionType fn = FunctionType.join(obj1.fn, obj2.fn); PersistentMap<String, Property> props; if (isLoose) { fn = fn == null ? null : fn.withLoose(); props = joinPropsLoosely(obj1.props, obj2.props); } else { props = joinProps(obj1.props, obj2.props, nom1, nom2); } NominalType nominal = NominalType.pickSuperclass(nom1, nom2); // TODO(blickly): Split TOP_OBJECT from empty object and remove this case if (nominal == null || !nominal.isFunction()) { fn = null; } return ObjectType.makeObjectType( nominal, props, fn, isLoose, ObjectKind.join(obj1.objectKind, obj2.objectKind)); }
ObjectType specialize(ObjectType other) { Preconditions.checkState(areRelatedClasses(this.nominalType, other.nominalType)); if (this == TOP_OBJECT && other.objectKind.isUnrestricted()) { return other; } NominalType resultNomType = NominalType.pickSubclass(this.nominalType, other.nominalType); ObjectKind ok = ObjectKind.meet(this.objectKind, other.objectKind); if (resultNomType != null && resultNomType.isClassy()) { Preconditions.checkState(this.fn == null && other.fn == null); PersistentMap<String, Property> newProps = meetPropsHelper(true, resultNomType, this.props, other.props); if (newProps == BOTTOM_MAP) { return BOTTOM_OBJECT; } return new ObjectType(resultNomType, newProps, null, false, ok); } FunctionType thisFn = this.fn; boolean isLoose = this.isLoose; if (resultNomType != null && resultNomType.isFunction() && this.fn == null) { thisFn = other.fn; isLoose = other.fn.isLoose(); } PersistentMap<String, Property> newProps = meetPropsHelper(true, resultNomType, this.props, other.props); if (newProps == BOTTOM_MAP) { return BOTTOM_OBJECT; } FunctionType newFn = thisFn == null ? null : thisFn.specialize(other.fn); if (!FunctionType.isInhabitable(newFn)) { return BOTTOM_OBJECT; } return new ObjectType(resultNomType, newProps, newFn, isLoose, ok); }