react-native-macos/React/CoreModules/RCTPlatform.mm

95 строки
2.6 KiB
Plaintext
Исходник Обычный вид История

/*
* 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 "RCTPlatform.h"
#import <UIKit/UIKit.h>
#import <FBReactNativeSpec/FBReactNativeSpec.h>
#import <React/RCTUtils.h>
#import <React/RCTVersion.h>
#import "CoreModulesPlugins.h"
using namespace facebook::react;
static NSString *interfaceIdiom(UIUserInterfaceIdiom idiom)
{
switch (idiom) {
case UIUserInterfaceIdiomPhone:
return @"phone";
case UIUserInterfaceIdiomPad:
return @"pad";
case UIUserInterfaceIdiomTV:
return @"tv";
case UIUserInterfaceIdiomCarPlay:
return @"carplay";
default:
return @"unknown";
}
}
@interface RCTPlatform () <NativePlatformConstantsIOSSpec>
@end
@implementation RCTPlatform
RCT_EXPORT_MODULE(PlatformConstants)
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
// TODO: Use the generated struct return type.
- (ModuleConstants<JS::NativePlatformConstantsIOS::Constants>)constantsToExport
Start using getConstants Summary: TurboModules depend on a getConstants method. Existing ObjectiveC modules do not have this method. Therefore, I moved the contents of `constantsToExport` to `getConstants` and then had `constantsToExports` call `getConstants`. facebook Since all NativeModules will eventually need to be migrated to the TurboModule system, I didn't restrict this to just the NativeModules in Marketplace. ``` const fs = require('fs'); if (process.argv.length < 3) { throw new Error('Expected a file containing a list of native modules as the third param'); } function read(filename) { return fs.readFileSync(filename, 'utf8'); } const nativeModuleFilenames = read(process.argv[2]).split('\n').filter(Boolean); nativeModuleFilenames.forEach((fileName) => { if (fileName.endsWith('.h')) { return; } const absPath = `${process.env.HOME}/${fileName}`; const fileSource = read(absPath); if (/(\n|^)-\s*\((.+)\)getConstants/.test(fileSource)) { return; } const constantsToExportRegex = /(\n|^)-\s*\((.+)\)constantsToExport/; const result = constantsToExportRegex.exec(fileSource); if (result == null) { throw new Error(`Didn't find a constantsToExport function inside NativeModule ${fileName}`); } const returnType = result[2]; const newFileSource = fileSource.replace( constantsToExportRegex, '$1- ($2)constantsToExport\n' + '{\n' + ` return ${returnType.includes('ModuleConstants') ? '($2)' : ''}[self getConstants];\n` + '}\n' + '\n' + '- ($2)getConstants' ); fs.writeFileSync(absPath, newFileSource); }); ``` ``` > xbgs -l ')constantsToExport' ``` Reviewed By: fkgozali Differential Revision: D13951197 fbshipit-source-id: 394a319d42aff466c56a3d748e17c335307a8f47
2019-02-05 04:41:36 +03:00
{
return (ModuleConstants<JS::NativePlatformConstantsIOS::Constants>)[self getConstants];
Start using getConstants Summary: TurboModules depend on a getConstants method. Existing ObjectiveC modules do not have this method. Therefore, I moved the contents of `constantsToExport` to `getConstants` and then had `constantsToExports` call `getConstants`. facebook Since all NativeModules will eventually need to be migrated to the TurboModule system, I didn't restrict this to just the NativeModules in Marketplace. ``` const fs = require('fs'); if (process.argv.length < 3) { throw new Error('Expected a file containing a list of native modules as the third param'); } function read(filename) { return fs.readFileSync(filename, 'utf8'); } const nativeModuleFilenames = read(process.argv[2]).split('\n').filter(Boolean); nativeModuleFilenames.forEach((fileName) => { if (fileName.endsWith('.h')) { return; } const absPath = `${process.env.HOME}/${fileName}`; const fileSource = read(absPath); if (/(\n|^)-\s*\((.+)\)getConstants/.test(fileSource)) { return; } const constantsToExportRegex = /(\n|^)-\s*\((.+)\)constantsToExport/; const result = constantsToExportRegex.exec(fileSource); if (result == null) { throw new Error(`Didn't find a constantsToExport function inside NativeModule ${fileName}`); } const returnType = result[2]; const newFileSource = fileSource.replace( constantsToExportRegex, '$1- ($2)constantsToExport\n' + '{\n' + ` return ${returnType.includes('ModuleConstants') ? '($2)' : ''}[self getConstants];\n` + '}\n' + '\n' + '- ($2)getConstants' ); fs.writeFileSync(absPath, newFileSource); }); ``` ``` > xbgs -l ')constantsToExport' ``` Reviewed By: fkgozali Differential Revision: D13951197 fbshipit-source-id: 394a319d42aff466c56a3d748e17c335307a8f47
2019-02-05 04:41:36 +03:00
}
- (ModuleConstants<JS::NativePlatformConstantsIOS::Constants>)getConstants
{
Run getConstants method statements on main queue Summary: If a NativeModule requires main queue setup, its `getConstants()` method must be executed on the main thead. The legacy NativeModule infra takes care of this for us. With TurboModules, however, all synchronous methods, including `getConstants()`, execute on the JS thread. Therefore, if a TurboModule requires main queue setup, and exports constants, we must execute its `getConstants()` method body on the main queue explicitly. **Notes:** - The changes in this diff should be a noop when TurboModules is off, because `RCTUnsafeExecuteOnMainQueueSync` synchronously execute its block on the current thread if the current thread is the main thread. - If a NativeModule doens't have the `requiresMainQueueSetup` method, but has the `constantsToExport` method, both NativeModules and TurboModules assume that it requires main queue setup. ## Script ``` const exec = require("../lib/exec"); const abspath = require("../lib/abspath"); const relpath = require("../lib/relpath"); const readFile = (filename) => require("fs").readFileSync(filename, "utf8"); const writeFile = (filename, content) => require("fs").writeFileSync(filename, content); function main() { const tmFiles = exec("cd ~/fbsource && xbgs -n 10000 -l constantsToExport") .split("\n") .filter(Boolean); const filesWithoutConstantsToExport = []; const filesWithConstantsToExportButNotGetConstants = []; const filesExplicitlyNotRequiringMainQueueSetup = []; tmFiles .filter((filename) => { if (filename.includes("microsoft-fork-of-react-native")) { return false; } return /\.mm?$/.test(filename); }) .map(abspath) .forEach((filename) => { const code = readFile(filename); const relFilename = relpath(filename); if (!/constantsToExport\s*{/.test(code)) { filesWithoutConstantsToExport.push(relFilename); return; } if (!/getConstants\s*{/.test(code)) { filesWithConstantsToExportButNotGetConstants.push(relFilename); return; } if (/requiresMainQueueSetup\s*{/.test(code)) { const requiresMainQueueSetupRegex = /requiresMainQueueSetup\s*{\s*return\s+(?<requiresMainQueueSetup>YES|NO)/; const requiresMainQueueSetupRegexMatch = requiresMainQueueSetupRegex.exec( code ); if (!requiresMainQueueSetupRegexMatch) { throw new Error( "Detected requiresMainQueueSetup method in file " + relFilename + " but was unable to parse the method return value" ); } const { requiresMainQueueSetup, } = requiresMainQueueSetupRegexMatch.groups; if (requiresMainQueueSetup == "NO") { filesExplicitlyNotRequiringMainQueueSetup.push(relFilename); return; } } const getConstantsTypeRegex = () => /-\s*\((?<type>.*)\)getConstants\s*{/; const getConstantsTypeRegexMatch = getConstantsTypeRegex().exec(code); if (!getConstantsTypeRegexMatch) { throw new Error( `Failed to parse return type of getConstants method in file ${relFilename}` ); } const getConstantsType = getConstantsTypeRegexMatch.groups.type; const getConstantsBody = code .split(getConstantsTypeRegex())[2] .split("\n}")[0]; const newGetConstantsBody = ` __block ${getConstantsType} constants; RCTUnsafeExecuteOnMainQueueSync(^{${getConstantsBody .replace(/\n/g, "\n ") .replace(/_bridge/g, "self->_bridge") .replace(/return /g, "constants = ")} }); return constants; `; writeFile( filename, code .replace(getConstantsBody, newGetConstantsBody) .replace("#import", "#import <React/RCTUtils.h>\n#import") ); }); console.log("Files without constantsToExport: "); filesWithoutConstantsToExport.forEach((file) => console.log(file)); console.log(); console.log("Files with constantsToExport but no getConstants: "); filesWithConstantsToExportButNotGetConstants.forEach((file) => console.log(file) ); console.log(); console.log("Files with requiresMainQueueSetup = NO: "); filesExplicitlyNotRequiringMainQueueSetup.forEach((file) => console.log(file) ); } if (!module.parent) { main(); } ``` Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D21797048 fbshipit-source-id: a822a858fecdbe976e6197f8339e509dc7cd917f
2020-06-03 08:58:04 +03:00
__block ModuleConstants<JS::NativePlatformConstantsIOS::Constants> constants;
RCTUnsafeExecuteOnMainQueueSync(^{
UIDevice *device = [UIDevice currentDevice];
auto versions = RCTGetReactNativeVersion();
constants = typedConstants<JS::NativePlatformConstantsIOS::Constants>({
.forceTouchAvailable = RCTForceTouchAvailable() ? true : false,
.osVersion = [device systemVersion],
.systemName = [device systemName],
.interfaceIdiom = interfaceIdiom([device userInterfaceIdiom]),
.isTesting = RCTRunningInTestEnvironment() ? true : false,
.reactNativeVersion = JS::NativePlatformConstantsIOS::ConstantsReactNativeVersion::Builder(
{.minor = [versions[@"minor"] doubleValue],
.major = [versions[@"major"] doubleValue],
.patch = [versions[@"patch"] doubleValue],
.prerelease = [versions[@"prerelease"] isKindOfClass:[NSNull class]]
? folly::Optional<double>{}
: [versions[@"prerelease"] doubleValue]}),
});
});
Run getConstants method statements on main queue Summary: If a NativeModule requires main queue setup, its `getConstants()` method must be executed on the main thead. The legacy NativeModule infra takes care of this for us. With TurboModules, however, all synchronous methods, including `getConstants()`, execute on the JS thread. Therefore, if a TurboModule requires main queue setup, and exports constants, we must execute its `getConstants()` method body on the main queue explicitly. **Notes:** - The changes in this diff should be a noop when TurboModules is off, because `RCTUnsafeExecuteOnMainQueueSync` synchronously execute its block on the current thread if the current thread is the main thread. - If a NativeModule doens't have the `requiresMainQueueSetup` method, but has the `constantsToExport` method, both NativeModules and TurboModules assume that it requires main queue setup. ## Script ``` const exec = require("../lib/exec"); const abspath = require("../lib/abspath"); const relpath = require("../lib/relpath"); const readFile = (filename) => require("fs").readFileSync(filename, "utf8"); const writeFile = (filename, content) => require("fs").writeFileSync(filename, content); function main() { const tmFiles = exec("cd ~/fbsource && xbgs -n 10000 -l constantsToExport") .split("\n") .filter(Boolean); const filesWithoutConstantsToExport = []; const filesWithConstantsToExportButNotGetConstants = []; const filesExplicitlyNotRequiringMainQueueSetup = []; tmFiles .filter((filename) => { if (filename.includes("microsoft-fork-of-react-native")) { return false; } return /\.mm?$/.test(filename); }) .map(abspath) .forEach((filename) => { const code = readFile(filename); const relFilename = relpath(filename); if (!/constantsToExport\s*{/.test(code)) { filesWithoutConstantsToExport.push(relFilename); return; } if (!/getConstants\s*{/.test(code)) { filesWithConstantsToExportButNotGetConstants.push(relFilename); return; } if (/requiresMainQueueSetup\s*{/.test(code)) { const requiresMainQueueSetupRegex = /requiresMainQueueSetup\s*{\s*return\s+(?<requiresMainQueueSetup>YES|NO)/; const requiresMainQueueSetupRegexMatch = requiresMainQueueSetupRegex.exec( code ); if (!requiresMainQueueSetupRegexMatch) { throw new Error( "Detected requiresMainQueueSetup method in file " + relFilename + " but was unable to parse the method return value" ); } const { requiresMainQueueSetup, } = requiresMainQueueSetupRegexMatch.groups; if (requiresMainQueueSetup == "NO") { filesExplicitlyNotRequiringMainQueueSetup.push(relFilename); return; } } const getConstantsTypeRegex = () => /-\s*\((?<type>.*)\)getConstants\s*{/; const getConstantsTypeRegexMatch = getConstantsTypeRegex().exec(code); if (!getConstantsTypeRegexMatch) { throw new Error( `Failed to parse return type of getConstants method in file ${relFilename}` ); } const getConstantsType = getConstantsTypeRegexMatch.groups.type; const getConstantsBody = code .split(getConstantsTypeRegex())[2] .split("\n}")[0]; const newGetConstantsBody = ` __block ${getConstantsType} constants; RCTUnsafeExecuteOnMainQueueSync(^{${getConstantsBody .replace(/\n/g, "\n ") .replace(/_bridge/g, "self->_bridge") .replace(/return /g, "constants = ")} }); return constants; `; writeFile( filename, code .replace(getConstantsBody, newGetConstantsBody) .replace("#import", "#import <React/RCTUtils.h>\n#import") ); }); console.log("Files without constantsToExport: "); filesWithoutConstantsToExport.forEach((file) => console.log(file)); console.log(); console.log("Files with constantsToExport but no getConstants: "); filesWithConstantsToExportButNotGetConstants.forEach((file) => console.log(file) ); console.log(); console.log("Files with requiresMainQueueSetup = NO: "); filesExplicitlyNotRequiringMainQueueSetup.forEach((file) => console.log(file) ); } if (!module.parent) { main(); } ``` Changelog: [Internal] Reviewed By: fkgozali Differential Revision: D21797048 fbshipit-source-id: a822a858fecdbe976e6197f8339e509dc7cd917f
2020-06-03 08:58:04 +03:00
return constants;
}
Part 2: Update ObjC++ codegen classes to use ObjCTurboModule::InitParams Summary: ## Summary Please check out D21035209. ## Changes - Codemod all ObjC NativeModule `getTurboModuleWithJsInvoker:nativeInvoker:perfLogger` methods to `getTurboModule:(const ObjCTurboModule::Args)` ## Script ``` var withSpaces = (...args) => args.join('\s*') var regexString = withSpaces( '-', '\(', 'std::shared_ptr', '<', '(?<turboModuleClass>(facebook::react::|react::|::|)TurboModule)', '>', '\)', 'getTurboModuleWithJsInvoker', ':', '\(', 'std::shared_ptr', '<', '(?<fbNamespace>(facebook::react::|react::|::|))CallInvoker', '>', '\)', '(?<jsInvokerInstance>[A-Za-z0-9]+)', 'nativeInvoker', ':', '\(', 'std::shared_ptr', '<', '(facebook::react::|react::|::|)CallInvoker', '>', '\)', '(?<nativeInvokerInstance>[A-Za-z0-9]+)', 'perfLogger', ':', '\(', 'id', '<', 'RCTTurboModulePerformanceLogger', '>', '\)', '(?<perfLoggerInstance>[A-Za-z0-9]+)', '{', 'return', 'std::make_shared', '<', '(?<specName>(facebook::react::|react::|::|)Native[%A-Za-z0-9]+SpecJSI)', '>', '\(', 'self', ',', '\k<jsInvokerInstance>', ',', '\k<nativeInvokerInstance>', ',', '\k<perfLoggerInstance>', '\)', ';', '}', ) var replaceString = `- (std::shared_ptr<$<turboModuleClass>>) getTurboModule:(const $<fbNamespace>ObjCTurboModule::InitParams &)params { return std::make_shared<$<specName>>(params); }` const exec = require('../lib/exec'); const abspath = require('../lib/abspath'); const relpath = require('../lib/relpath'); const readFile = (filename) => require('fs').readFileSync(filename, 'utf8'); const writeFile = (filename, content) => require('fs').writeFileSync(filename, content); function main() { const tmFiles = exec('cd ~/fbsource && xbgs -n 10000 -l getTurboModuleWithJsInvoker:').split('\n').filter(Boolean); tmFiles .filter((filename) => !filename.includes('microsoft-fork-of-react-native')) .map(abspath) .forEach((filename) => { const source = readFile(filename); const newSource = source.replace(new RegExp(regexString, 'g'), replaceString); if (source == newSource) { console.log(relpath(filename)); } writeFile(filename, newSource); }); } if (!module.parent) { main(); } ``` ## Re-generating diff ``` > hg revert -r .^ --all > node index.js # run script ``` Changelog: [iOS][Changed] - Make all ObjC NativeModules create TurboModules using ObjCTurboModule::Args Reviewed By: PeteTheHeat Differential Revision: D21036265 fbshipit-source-id: 404bcc548d1775ef23d793527606d02fe384a0a2
2020-04-17 03:23:39 +03:00
- (std::shared_ptr<TurboModule>)getTurboModule:(const ObjCTurboModule::InitParams &)params
{
Part 2: Update ObjC++ codegen classes to use ObjCTurboModule::InitParams Summary: ## Summary Please check out D21035209. ## Changes - Codemod all ObjC NativeModule `getTurboModuleWithJsInvoker:nativeInvoker:perfLogger` methods to `getTurboModule:(const ObjCTurboModule::Args)` ## Script ``` var withSpaces = (...args) => args.join('\s*') var regexString = withSpaces( '-', '\(', 'std::shared_ptr', '<', '(?<turboModuleClass>(facebook::react::|react::|::|)TurboModule)', '>', '\)', 'getTurboModuleWithJsInvoker', ':', '\(', 'std::shared_ptr', '<', '(?<fbNamespace>(facebook::react::|react::|::|))CallInvoker', '>', '\)', '(?<jsInvokerInstance>[A-Za-z0-9]+)', 'nativeInvoker', ':', '\(', 'std::shared_ptr', '<', '(facebook::react::|react::|::|)CallInvoker', '>', '\)', '(?<nativeInvokerInstance>[A-Za-z0-9]+)', 'perfLogger', ':', '\(', 'id', '<', 'RCTTurboModulePerformanceLogger', '>', '\)', '(?<perfLoggerInstance>[A-Za-z0-9]+)', '{', 'return', 'std::make_shared', '<', '(?<specName>(facebook::react::|react::|::|)Native[%A-Za-z0-9]+SpecJSI)', '>', '\(', 'self', ',', '\k<jsInvokerInstance>', ',', '\k<nativeInvokerInstance>', ',', '\k<perfLoggerInstance>', '\)', ';', '}', ) var replaceString = `- (std::shared_ptr<$<turboModuleClass>>) getTurboModule:(const $<fbNamespace>ObjCTurboModule::InitParams &)params { return std::make_shared<$<specName>>(params); }` const exec = require('../lib/exec'); const abspath = require('../lib/abspath'); const relpath = require('../lib/relpath'); const readFile = (filename) => require('fs').readFileSync(filename, 'utf8'); const writeFile = (filename, content) => require('fs').writeFileSync(filename, content); function main() { const tmFiles = exec('cd ~/fbsource && xbgs -n 10000 -l getTurboModuleWithJsInvoker:').split('\n').filter(Boolean); tmFiles .filter((filename) => !filename.includes('microsoft-fork-of-react-native')) .map(abspath) .forEach((filename) => { const source = readFile(filename); const newSource = source.replace(new RegExp(regexString, 'g'), replaceString); if (source == newSource) { console.log(relpath(filename)); } writeFile(filename, newSource); }); } if (!module.parent) { main(); } ``` ## Re-generating diff ``` > hg revert -r .^ --all > node index.js # run script ``` Changelog: [iOS][Changed] - Make all ObjC NativeModules create TurboModules using ObjCTurboModule::Args Reviewed By: PeteTheHeat Differential Revision: D21036265 fbshipit-source-id: 404bcc548d1775ef23d793527606d02fe384a0a2
2020-04-17 03:23:39 +03:00
return std::make_shared<NativePlatformConstantsIOSSpecJSI>(params);
}
@end
Class RCTPlatformCls(void)
{
return RCTPlatform.class;
}