src/object-spec-code-utils.ts (266 lines of code) (raw):

/** * Copyright (c) 2016-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import * as Maybe from './maybe'; import * as ObjC from './objc'; import * as ObjCTypeUtils from './objc-type-utils'; import * as StringUtils from './string-utils'; import * as ObjectSpec from './object-spec'; function allocationAndInvocationCanBeOneForAttribute( typeName: string, attributes: ObjectSpec.Attribute[], ): boolean { return attributes.length == 0; } function allocationPartOfConstructorInvocationForTypeName( typeName: string, ): string { return typeName + ' alloc'; } function invocationPartForAttribute( valueGenerator: (attribute: ObjectSpec.Attribute) => string, attribute: ObjectSpec.Attribute, ): string { return attribute.name + ':' + valueGenerator(attribute); } function invocationPartOfConstructorInvocationForAttributes( attributes: ObjectSpec.Attribute[], valueGenerator: (attribute: ObjectSpec.Attribute) => string, ): string { if (attributes.length > 0) { return ( 'initWith' + StringUtils.capitalize( attributes .map(invocationPartForAttribute.bind(null, valueGenerator)) .join(' '), ) ); } else { return 'init'; } } export function shouldUseObjectBehavior(objectSpec: ObjectSpec.Type): boolean { return objectSpec.includes.indexOf('LSObjectBehavior') != -1; } export function typeForSpec(valueType: ObjectSpec.Type): ObjC.Type { return { name: valueType.typeName, reference: valueType.typeName + ' *', }; } export function isAttributeMutable( objectSpec: ObjectSpec.Type, attribute: ObjectSpec.Attribute, ): boolean { return ( shouldUseObjectBehavior(objectSpec) && 'ls-mutable' in attribute.annotations ); } export function isTypeMutable(objectSpec: ObjectSpec.Type): boolean { return objectSpec.attributes.some((attr) => isAttributeMutable(objectSpec, attr), ); } export function methodInvocationForConstructor( objectType: ObjectSpec.Type, valueGenerator: (attribute: ObjectSpec.Attribute) => string, ): string { if ( allocationAndInvocationCanBeOneForAttribute( objectType.typeName, objectType.attributes, ) ) { return '[' + objectType.typeName + ' new]'; } const allocationPart = allocationPartOfConstructorInvocationForTypeName( objectType.typeName, ); const invocationPart = invocationPartOfConstructorInvocationForAttributes( objectType.attributes, valueGenerator, ); return '[[' + allocationPart + '] ' + invocationPart + ']'; } export function ivarForAttribute(attribute: ObjectSpec.BaseAttribute): string { return '_' + attribute.name; } function typeForUnderlyingType(underlyingType: string): ObjC.Type { return { name: underlyingType, reference: underlyingType === 'NSObject' ? 'NSObject*' : underlyingType, }; } function propertyModifierForCopyable( supportsValueSemantics: boolean, ): ObjC.PropertyModifier { if (supportsValueSemantics) { return ObjC.PropertyModifier.Copy(); } else { return ObjC.PropertyModifier.Assign(); } } export function computeTypeOfAttribute( attribute: ObjectSpec.BaseAttribute, ): ObjC.Type { return Maybe.match( typeForUnderlyingType, function (): ObjC.Type { return { name: attribute.type.name, reference: attribute.type.reference, }; }, attribute.type.underlyingType, ); } export function computeOriginalTypeOfAttribute( attribute: ObjectSpec.BaseAttribute, ): ObjC.Type { return { name: attribute.type.name, reference: attribute.type.reference, }; } export function propertyOwnershipModifierForAttribute( supportsValueSemantics: boolean, attribute: ObjectSpec.BaseAttribute, ): ObjC.PropertyModifier | undefined { const type = computeTypeOfAttribute(attribute); if (type === null) { return ObjC.PropertyModifier.Assign(); } return ObjCTypeUtils.matchType( { id: function () { return Maybe.match( function (protocol) { return ObjC.PropertyModifier.Assign(); }, function () { return propertyModifierForCopyable(supportsValueSemantics); }, attribute.type.conformingProtocol, ); }, NSObject: function () { return propertyModifierForCopyable(supportsValueSemantics); }, BOOL: function () { return ObjC.PropertyModifier.Assign(); }, NSInteger: function () { return ObjC.PropertyModifier.Assign(); }, NSUInteger: function () { return ObjC.PropertyModifier.Assign(); }, double: function () { return ObjC.PropertyModifier.Assign(); }, float: function () { return ObjC.PropertyModifier.Assign(); }, CGFloat: function () { return ObjC.PropertyModifier.Assign(); }, NSTimeInterval: function () { return ObjC.PropertyModifier.Assign(); }, uintptr_t: function () { return ObjC.PropertyModifier.Assign(); }, uint32_t: function () { return ObjC.PropertyModifier.Assign(); }, uint64_t: function () { return ObjC.PropertyModifier.Assign(); }, int32_t: function () { return ObjC.PropertyModifier.Assign(); }, int64_t: function () { return ObjC.PropertyModifier.Assign(); }, SEL: function () { return ObjC.PropertyModifier.Assign(); }, NSRange: function () { return ObjC.PropertyModifier.Assign(); }, CGRect: function () { return ObjC.PropertyModifier.Assign(); }, CGPoint: function () { return ObjC.PropertyModifier.Assign(); }, CGSize: function () { return ObjC.PropertyModifier.Assign(); }, UIEdgeInsets: function () { return ObjC.PropertyModifier.Assign(); }, Class: function () { return ObjC.PropertyModifier.UnsafeUnretained(); }, dispatch_block_t: function () { return propertyModifierForCopyable(supportsValueSemantics); }, unmatchedType: function () { return undefined; }, }, type, ); } export function shouldCopyIncomingValueForAttribute( supportsValueSemantics: boolean, attribute: ObjectSpec.BaseAttribute, ): boolean { if (attribute.annotations['copy'] !== undefined) { return true; } const modifier = propertyOwnershipModifierForAttribute( supportsValueSemantics, attribute, ); if (modifier == null) { return false; } return modifier.match( function assign() { return false; }, function atomic() { return false; }, function copy() { return true; }, function nonatomic() { return false; }, function nonnull() { return false; }, function nullable() { return false; }, function readonly() { return false; }, function readwrite() { return false; }, function setter() { return false; }, function strong() { return false; }, function weak() { return false; }, function unsafeUnretained() { return false; }, ); }