Skip to content

Commit

Permalink
Generate inlines and structs for methods (not constants)
Browse files Browse the repository at this point in the history
Summary: Generate objcpp inlines and struct part not methods (not getConstants!)

Reviewed By: RSNara

Differential Revision: D16494709

fbshipit-source-id: 44ecf6be5031112bf47c44392e375709622ae83b
  • Loading branch information
osdnk authored and facebook-github-bot committed Jul 26, 2019
1 parent c5ea18f commit fe5b179
Show file tree
Hide file tree
Showing 5 changed files with 539 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/

'use strict';

import type {ObjectParamTypeAnnotation} from '../../../CodegenSchema';
import {flatObjects, capitalizeFirstLetter} from './Utils';

const template = `
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <RCTTypeSafety/RCTConvertHelpers.h>
::_STRUCTS_::
::_INLINES_::
`;

const structTemplate = `
namespace JS {
namespace Native::_MODULE_NAME_:: {
struct Spec::_STRUCT_NAME_:: {
::_STRUCT_PROPERTIES_::
Spec::_STRUCT_NAME_::(NSDictionary *const v) : _v(v) {}
private:
NSDictionary *_v;
};
}
}
@interface RCTCxxConvert (Native::_MODULE_NAME_::_Spec::_STRUCT_NAME_::)
+ (RCTManagedPointer *)JS_Native::_MODULE_NAME_::_Spec::_STRUCT_NAME_:::(id)json;
@end
`;

const inlineTemplate = `
inline ::_RETURN_TYPE_:: *JS::Native::_MODULE_NAME_::::Spec::_STRUCT_NAME_::::::_PROPERTY_NAME_::() const
{
id const p = _v[@"a"];
return ::_RETURN_VALUE_::;
}
`;

function getInlineMethodSignature(
property: ObjectParamTypeAnnotation,
name: string,
): string {
const {typeAnnotation} = property;
switch (typeAnnotation.type) {
case 'StringTypeAnnotation':
return `NSString *${property.name}() const;`;
case 'NumberTypeAnnotation':
case 'FloatTypeAnnotation':
case 'Int32TypeAnnotation':
return `double ${property.name}() const;`;
case 'BooleanTypeAnnotation':
return `bool ${property.name}() const;`;
case 'ObjectTypeAnnotation':
return `JS::Native::_MODULE_NAME_::::Spec${name}${capitalizeFirstLetter(
property.name,
)} ${property.name}() const;`;
case 'GenericObjectTypeAnnotation':
case 'AnyTypeAnnotation':
return `id<NSObject> ${property.name}() const;`;
case 'FunctionTypeAnnotation':
case 'ArrayTypeAnnotation':
default:
throw new Error(`Unknown prop type, found: ${typeAnnotation.type}"`);
}
}

function getInlineMethodImplementation(
property: ObjectParamTypeAnnotation,
name: string,
): string {
const {typeAnnotation} = property;
switch (typeAnnotation.type) {
case 'StringTypeAnnotation':
return inlineTemplate
.replace(/::_RETURN_TYPE_::/, 'NSString')
.replace(/::_RETURN_VALUE_::/, 'RCTBridgingToString(p)');
case 'NumberTypeAnnotation':
case 'FloatTypeAnnotation':
case 'Int32TypeAnnotation':
return inlineTemplate
.replace(/::_RETURN_TYPE_::/, 'double')
.replace(/::_RETURN_VALUE_::/, 'RCTBridgingToDouble(p)');
case 'BooleanTypeAnnotation':
return inlineTemplate
.replace(/::_RETURN_TYPE_::/, 'bool')
.replace(/::_RETURN_VALUE_::/, 'RCTBridgingToBool(p)');
case 'GenericObjectTypeAnnotation':
case 'AnyTypeAnnotation':
return inlineTemplate
.replace(/::_RETURN_TYPE_::/, 'id<NSObject>')
.replace(/::_RETURN_VALUE_::/, 'p');
case 'ObjectTypeAnnotation':
return inlineTemplate
.replace(
/::_RETURN_TYPE_::/,
`JS::Native::_MODULE_NAME_::::Spec${name}${capitalizeFirstLetter(
property.name,
)}`,
)
.replace(
/::_RETURN_VALUE_::/,
`JS::Native::_MODULE_NAME_::::Spec${name}${capitalizeFirstLetter(
property.name,
)}(p)`,
);
case 'FunctionTypeAnnotation':
case 'ArrayTypeAnnotation':
default:
throw new Error(`Unknown prop type, found: ${typeAnnotation.type}"`);
}
}

function translateObjectsForStructs(
annotations: $ReadOnlyArray<
$ReadOnly<{|
name: string,
object: $ReadOnly<{|
type: 'ObjectTypeAnnotation',
properties: $ReadOnlyArray<ObjectParamTypeAnnotation>,
|}>,
|}>,
>,
): string {
const flattenObjects = flatObjects(annotations);

const translatedInlineMethods = flattenObjects
.reduce(
(acc, object) =>
acc.concat(
object.properties.map(property =>
getInlineMethodImplementation(property, object.name)
.replace(/::_PROPERTY_NAME_::/g, property.name)
.replace(/::_STRUCT_NAME_::/g, object.name),
),
),
[],
)
.join('\n');

const translatedStructs = flattenObjects
.map(object =>
structTemplate
.replace(
/::_STRUCT_PROPERTIES_::/g,
object.properties
.map(property => getInlineMethodSignature(property, object.name))
.join('\n '),
)
.replace(/::_STRUCT_NAME_::/g, object.name),
)
.join('\n');

return template
.replace(/::_STRUCTS_::/, translatedStructs)
.replace(/::_INLINES_::/, translatedInlineMethods);
}
module.exports = {
translateObjectsForStructs,
capitalizeFirstLetter,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/

'use strict';

import type {ObjectParamTypeAnnotation} from '../../../CodegenSchema';

function capitalizeFirstLetter(string: string): string {
return string.charAt(0).toUpperCase() + string.slice(1);
}

function flatObjects(
annotations: $ReadOnlyArray<
$ReadOnly<{|
name: string,
object: $ReadOnly<{|
type: 'ObjectTypeAnnotation',
properties: $ReadOnlyArray<ObjectParamTypeAnnotation>,
|}>,
|}>,
>,
): $ReadOnlyArray<
$ReadOnly<{|
name: string,
properties: $ReadOnlyArray<ObjectParamTypeAnnotation>,
|}>,
> {
let objectTypesToFlatten: Array<{|
properties: $ReadOnlyArray<ObjectParamTypeAnnotation>,
name: string,
|}> = annotations.map(annotation => ({
name: annotation.name,
properties: annotation.object.properties,
}));

let flattenObjects: Array<{|
properties: $ReadOnlyArray<ObjectParamTypeAnnotation>,
name: string,
|}> = [];

while (objectTypesToFlatten.length !== 0) {
const oldObjectTypesToFlatten = objectTypesToFlatten;
objectTypesToFlatten = [];
flattenObjects = flattenObjects.concat(
oldObjectTypesToFlatten.map(object => {
const {properties} = object;
if (properties !== undefined) {
objectTypesToFlatten = objectTypesToFlatten.concat(
properties.reduce((acc, curr) => {
if (
curr.typeAnnotation.type === 'ObjectTypeAnnotation' &&
curr.typeAnnotation.properties
) {
return acc.concat({
properties: curr.typeAnnotation.properties,
name: object.name + capitalizeFirstLetter(curr.name),
});
}
return acc;
}, []),
);
}
return object;
}),
);
}

return flattenObjects;
}
module.exports = {
flatObjects,
capitalizeFirstLetter,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

'use strict';

import type {ObjectParamTypeAnnotation} from '../../../CodegenSchema.js';
const SIMPLE_STRUCT: $ReadOnlyArray<
$ReadOnly<{|
name: string,
object: $ReadOnly<{|
type: 'ObjectTypeAnnotation',
properties: $ReadOnlyArray<ObjectParamTypeAnnotation>,
|}>,
|}>,
> = [
{
name: 'SampleFuncReturnType',
object: {
type: 'ObjectTypeAnnotation',
properties: [
{
optional: false,
name: 'a',
typeAnnotation: {
type: 'BooleanTypeAnnotation',
},
},
{
optional: false,
name: 'b',
typeAnnotation: {
type: 'NumberTypeAnnotation',
},
},
{
optional: false,
name: 'c',
typeAnnotation: {
type: 'StringTypeAnnotation',
},
},
{
optional: false,
name: 'd',
typeAnnotation: {
type: 'ObjectTypeAnnotation',
properties: [
{
optional: false,
name: 'e',
typeAnnotation: {
type: 'BooleanTypeAnnotation',
},
},
{
optional: false,
name: 'f',
typeAnnotation: {
type: 'NumberTypeAnnotation',
},
},
{
optional: false,
name: 'g',
typeAnnotation: {
type: 'ObjectTypeAnnotation',
properties: [
{
optional: false,
name: 'h',
typeAnnotation: {
type: 'BooleanTypeAnnotation',
},
},
{
optional: false,
name: 'i',
typeAnnotation: {
type: 'NumberTypeAnnotation',
},
},
{
optional: false,
name: 'j',
typeAnnotation: {
type: 'StringTypeAnnotation',
},
},
],
},
},
],
},
},
],
},
},
];
module.exports = {
SIMPLE_STRUCT,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+react_native
* @flow strict-local
* @format
*/

'use strict';

const fixtures = require('../__test_fixtures__/structFixtures.js');
const generator = require('../ObjCppUtils/GenerateStructs.js');

describe('GenerateStructs', () => {
Object.keys(fixtures)
.sort()
.forEach(fixtureName => {
const fixture = fixtures[fixtureName];

it(`can generate fixture ${fixtureName}`, () => {
expect(
generator
.translateObjectsForStructs(fixture)
.replace(/::_MODULE_NAME_::/g, 'SampleTurboModule'),
).toMatchSnapshot();
});
});
});
Loading

0 comments on commit fe5b179

Please sign in to comment.