mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dff572c174 | ||
|
|
af3e1d4a86 | ||
|
|
897a3a5d5e | ||
|
|
0fea3515a1 | ||
|
|
80e8efe8e7 | ||
|
|
d1888650c5 | ||
|
|
f65b9e883a | ||
|
|
aa0b2ad1cd | ||
|
|
0a319978ff | ||
|
|
29e5deb85f | ||
|
|
031feb6942 | ||
|
|
d82138ff91 |
@@ -927,6 +927,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
if (!self.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer != nil && buffer != (id)kCFNull) {
|
||||
_wasBatchActive = YES;
|
||||
[self handleBuffer:buffer];
|
||||
|
||||
@@ -373,6 +373,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init);
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
(void)[self instance];
|
||||
RCTAssert(_methodQueue != nullptr, @"Module %@ has no methodQueue (instance: %@, bridge.valid: %d)",
|
||||
self, _instance, _bridge.valid);
|
||||
return _methodQueue;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
// Throttle progress events so we don't send more that around 60 per second.
|
||||
CFTimeInterval currentTime = CACurrentMediaTime();
|
||||
|
||||
NSUInteger headersContentLength = headers[@"Content-Length"] != nil ? [headers[@"Content-Length"] unsignedIntValue] : 0;
|
||||
NSInteger headersContentLength = headers[@"Content-Length"] != nil ? [headers[@"Content-Length"] integerValue] : 0;
|
||||
if (callback && (currentTime - _lastDownloadProgress > 0.016 || final)) {
|
||||
_lastDownloadProgress = currentTime;
|
||||
callback(headers, @(headersContentLength), @(contentLength));
|
||||
|
||||
@@ -125,13 +125,12 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
[bridge_ partialBatchDidFlush];
|
||||
[bridge_ batchDidComplete];
|
||||
}
|
||||
void incrementPendingJSCalls() override {}
|
||||
void decrementPendingJSCalls() override {}
|
||||
};
|
||||
|
||||
@implementation RCTCxxBridge
|
||||
{
|
||||
BOOL _wasBatchActive;
|
||||
BOOL _didInvalidate;
|
||||
|
||||
NSMutableArray<dispatch_block_t> *_pendingCalls;
|
||||
std::atomic<NSInteger> _pendingCount;
|
||||
@@ -169,7 +168,7 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
|
||||
- (JSGlobalContextRef)jsContextRef
|
||||
{
|
||||
return (JSGlobalContextRef)self->_reactInstance->getJavaScriptContext();
|
||||
return (JSGlobalContextRef)(self->_reactInstance ? self->_reactInstance->getJavaScriptContext() : nullptr);
|
||||
}
|
||||
|
||||
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
|
||||
@@ -204,7 +203,7 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)runJSRunLoop
|
||||
+ (void)runRunLoop
|
||||
{
|
||||
@autoreleasepool {
|
||||
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"-[RCTCxxBridge runJSRunLoop] setup", nil);
|
||||
@@ -267,8 +266,8 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
object:_parentBridge userInfo:@{@"bridge": self}];
|
||||
|
||||
// Set up the JS thread early
|
||||
_jsThread = [[NSThread alloc] initWithTarget:self
|
||||
selector:@selector(runJSRunLoop)
|
||||
_jsThread = [[NSThread alloc] initWithTarget:[self class]
|
||||
selector:@selector(runRunLoop)
|
||||
object:nil];
|
||||
_jsThread.name = RCTJSThreadName;
|
||||
_jsThread.qualityOfService = NSOperationQualityOfServiceUserInteractive;
|
||||
@@ -493,7 +492,7 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
if (_reactInstance) {
|
||||
// This is async, but any calls into JS are blocked by the m_syncReady CV in Instance
|
||||
_reactInstance->initializeBridge(
|
||||
std::unique_ptr<RCTInstanceCallback>(new RCTInstanceCallback(self)),
|
||||
std::make_unique<RCTInstanceCallback>(self),
|
||||
executorFactory,
|
||||
_jsMessageThread,
|
||||
[self _buildModuleRegistry]);
|
||||
@@ -816,6 +815,7 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
}
|
||||
|
||||
RCTFatal(error);
|
||||
|
||||
// RN will stop, but let the rest of the app keep going.
|
||||
return;
|
||||
}
|
||||
@@ -826,27 +826,27 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
|
||||
// Hack: once the bridge is invalidated below, it won't initialize any new native
|
||||
// modules. Initialize the redbox module now so we can still report this error.
|
||||
[self redBox];
|
||||
RCTRedBox *redBox = [self redBox];
|
||||
|
||||
_loading = NO;
|
||||
_valid = NO;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self->_jsMessageThread) {
|
||||
auto thread = self->_jsMessageThread;
|
||||
self->_jsMessageThread->runOnQueue([thread] {
|
||||
thread->quitSynchronous();
|
||||
});
|
||||
self->_jsMessageThread.reset();
|
||||
// Make sure initializeBridge completed
|
||||
self->_jsMessageThread->runOnQueueSync([] {});
|
||||
}
|
||||
|
||||
self->_reactInstance.reset();
|
||||
self->_jsMessageThread.reset();
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
||||
object:self->_parentBridge userInfo:@{@"bridge": self, @"error": error}];
|
||||
|
||||
if ([error userInfo][RCTJSRawStackTraceKey]) {
|
||||
[self.redBox showErrorMessage:[error localizedDescription]
|
||||
withRawStack:[error userInfo][RCTJSRawStackTraceKey]];
|
||||
[redBox showErrorMessage:[error localizedDescription]
|
||||
withRawStack:[error userInfo][RCTJSRawStackTraceKey]];
|
||||
}
|
||||
|
||||
RCTFatal(error);
|
||||
@@ -913,63 +913,68 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
if (!_valid) {
|
||||
if (_didInvalidate) {
|
||||
return;
|
||||
}
|
||||
|
||||
RCTAssertMainQueue();
|
||||
RCTAssert(_reactInstance != nil, @"Can't complete invalidation without a react instance");
|
||||
RCTLogInfo(@"Invalidating %@ (parent: %@, executor: %@)", self, _parentBridge, [self executorClass]);
|
||||
|
||||
_loading = NO;
|
||||
_valid = NO;
|
||||
_didInvalidate = YES;
|
||||
|
||||
if ([RCTBridge currentBridge] == self) {
|
||||
[RCTBridge setCurrentBridge:nil];
|
||||
}
|
||||
|
||||
// Invalidate modules
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
for (RCTModuleData *moduleData in _moduleDataByID) {
|
||||
// Be careful when grabbing an instance here, we don't want to instantiate
|
||||
// any modules just to invalidate them.
|
||||
if (![moduleData hasInstance]) {
|
||||
continue;
|
||||
// Stop JS instance and message thread
|
||||
[self ensureOnJavaScriptThread:^{
|
||||
[self->_displayLink invalidate];
|
||||
self->_displayLink = nil;
|
||||
|
||||
if (RCTProfileIsProfiling()) {
|
||||
RCTProfileUnhookModules(self);
|
||||
}
|
||||
|
||||
if ([moduleData.instance respondsToSelector:@selector(invalidate)]) {
|
||||
dispatch_group_enter(group);
|
||||
[self dispatchBlock:^{
|
||||
[(id<RCTInvalidating>)moduleData.instance invalidate];
|
||||
dispatch_group_leave(group);
|
||||
} queue:moduleData.methodQueue];
|
||||
// Invalidate modules
|
||||
// We're on the JS thread (which we'll be suspending soon), so no new calls will be made to native modules after
|
||||
// this completes. We must ensure all previous calls were dispatched before deallocating the instance (and module
|
||||
// wrappers) or we may have invalid pointers still in flight.
|
||||
dispatch_group_t moduleInvalidation = dispatch_group_create();
|
||||
for (RCTModuleData *moduleData in self->_moduleDataByID) {
|
||||
// Be careful when grabbing an instance here, we don't want to instantiate
|
||||
// any modules just to invalidate them.
|
||||
if (![moduleData hasInstance]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([moduleData.instance respondsToSelector:@selector(invalidate)]) {
|
||||
dispatch_group_enter(moduleInvalidation);
|
||||
[self dispatchBlock:^{
|
||||
[(id<RCTInvalidating>)moduleData.instance invalidate];
|
||||
dispatch_group_leave(moduleInvalidation);
|
||||
} queue:moduleData.methodQueue];
|
||||
}
|
||||
[moduleData invalidate];
|
||||
}
|
||||
[moduleData invalidate];
|
||||
}
|
||||
|
||||
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
|
||||
[self ensureOnJavaScriptThread:^{
|
||||
[self->_displayLink invalidate];
|
||||
self->_displayLink = nil;
|
||||
if (dispatch_group_wait(moduleInvalidation, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC))) {
|
||||
RCTLogError(@"Timed out waiting for modules to be invalidated");
|
||||
}
|
||||
|
||||
self->_reactInstance.reset();
|
||||
if (self->_jsMessageThread) {
|
||||
self->_jsMessageThread->quitSynchronous();
|
||||
self->_jsMessageThread.reset();
|
||||
}
|
||||
self->_reactInstance.reset();
|
||||
self->_jsMessageThread.reset();
|
||||
|
||||
if (RCTProfileIsProfiling()) {
|
||||
RCTProfileUnhookModules(self);
|
||||
}
|
||||
self->_moduleDataByName = nil;
|
||||
self->_moduleDataByID = nil;
|
||||
self->_moduleClassesByID = nil;
|
||||
self->_pendingCalls = nil;
|
||||
|
||||
self->_moduleDataByName = nil;
|
||||
self->_moduleDataByID = nil;
|
||||
self->_moduleClassesByID = nil;
|
||||
self->_pendingCalls = nil;
|
||||
|
||||
[self->_jsThread cancel];
|
||||
self->_jsThread = nil;
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
}];
|
||||
});
|
||||
[self->_jsThread cancel];
|
||||
self->_jsThread = nil;
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)logMessage:(NSString *)message level:(NSString *)level
|
||||
@@ -1098,7 +1103,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||
*/
|
||||
|
||||
RCTProfileBeginFlowEvent();
|
||||
|
||||
[self _runAfterLoad:^{
|
||||
RCTProfileEndFlowEvent();
|
||||
|
||||
@@ -1189,25 +1193,25 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR
|
||||
if (!_reactInstance) {
|
||||
if (error) {
|
||||
*error = RCTErrorWithMessage(
|
||||
@"Attempt to call sync callFunctionOnModule: on uninitialized bridge");
|
||||
@"callFunctionOnModule was called on uninitialized bridge");
|
||||
}
|
||||
return nil;
|
||||
} else if (self.executorClass) {
|
||||
if (error) {
|
||||
*error = RCTErrorWithMessage(
|
||||
@"sync callFunctionOnModule: can only be used with JSC executor");
|
||||
@"callFunctionOnModule can only be used with JSC executor");
|
||||
}
|
||||
return nil;
|
||||
} else if (!self.valid) {
|
||||
if (error) {
|
||||
*error = RCTErrorWithMessage(
|
||||
@"sync callFunctionOnModule: bridge is no longer valid");
|
||||
@"Bridge is no longer valid");
|
||||
}
|
||||
return nil;
|
||||
} else if (self.loading) {
|
||||
if (error) {
|
||||
*error = RCTErrorWithMessage(
|
||||
@"sync callFunctionOnModule: bridge is still loading");
|
||||
@"Bridge is still loading");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ public:
|
||||
|
||||
void runOnQueue(std::function<void()>&& func) override {
|
||||
dispatch_queue_t queue = moduleData_.methodQueue;
|
||||
RCTAssert(queue != nullptr, @"Module %@ provided invalid queue", moduleData_);
|
||||
dispatch_block_t block = [func=std::move(func)] { func(); };
|
||||
RCTAssert(block != nullptr, @"Invalid block generated in call to %@", moduleData_);
|
||||
if (queue && block) {
|
||||
|
||||
+32
-19
@@ -36,33 +36,46 @@
|
||||
typedef CGFloat RCTFontWeight;
|
||||
static RCTFontWeight weightOfFont(UIFont *font)
|
||||
{
|
||||
static NSDictionary *nameToWeight;
|
||||
static NSArray *fontNames;
|
||||
static NSArray *fontWeights;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
nameToWeight = @{
|
||||
@"normal": @(UIFontWeightRegular),
|
||||
@"bold": @(UIFontWeightBold),
|
||||
@"ultralight": @(UIFontWeightUltraLight),
|
||||
@"thin": @(UIFontWeightThin),
|
||||
@"light": @(UIFontWeightLight),
|
||||
@"regular": @(UIFontWeightRegular),
|
||||
@"medium": @(UIFontWeightMedium),
|
||||
@"semibold": @(UIFontWeightSemibold),
|
||||
@"bold": @(UIFontWeightBold),
|
||||
@"heavy": @(UIFontWeightHeavy),
|
||||
@"black": @(UIFontWeightBlack),
|
||||
};
|
||||
// We use two arrays instead of one map because
|
||||
// the order is important for suffix matching.
|
||||
fontNames = @[
|
||||
@"normal",
|
||||
@"ultralight",
|
||||
@"thin",
|
||||
@"light",
|
||||
@"regular",
|
||||
@"medium",
|
||||
@"semibold",
|
||||
@"bold",
|
||||
@"heavy",
|
||||
@"black"
|
||||
];
|
||||
fontWeights = @[
|
||||
@(UIFontWeightRegular),
|
||||
@(UIFontWeightUltraLight),
|
||||
@(UIFontWeightThin),
|
||||
@(UIFontWeightLight),
|
||||
@(UIFontWeightRegular),
|
||||
@(UIFontWeightMedium),
|
||||
@(UIFontWeightSemibold),
|
||||
@(UIFontWeightBold),
|
||||
@(UIFontWeightHeavy),
|
||||
@(UIFontWeightBlack)
|
||||
];
|
||||
});
|
||||
|
||||
for (NSString *name in nameToWeight) {
|
||||
if ([font.fontName.lowercaseString hasSuffix:name]) {
|
||||
return [nameToWeight[name] doubleValue];
|
||||
for (NSInteger i = 0; i < fontNames.count; i++) {
|
||||
if ([font.fontName.lowercaseString hasSuffix:fontNames[i]]) {
|
||||
return (RCTFontWeight)[fontWeights[i] doubleValue];
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
|
||||
RCTFontWeight weight = [traits[UIFontWeightTrait] doubleValue];
|
||||
return weight;
|
||||
return (RCTFontWeight)[traits[UIFontWeightTrait] doubleValue];
|
||||
}
|
||||
|
||||
static BOOL isItalicFont(UIFont *font)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=1000.0.0-master
|
||||
VERSION_NAME=0.49.0-rc.3
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -27,8 +27,6 @@ public class OnScrollDispatchHelper {
|
||||
|
||||
private long mLastScrollEventTimeMs = -(MIN_EVENT_SEPARATION_MS + 1);
|
||||
|
||||
private static final float THRESHOLD = 0.1f; // Threshold for end fling
|
||||
|
||||
/**
|
||||
* Call from a ScrollView in onScrollChanged, returns true if this onScrollChanged is legit (not a
|
||||
* duplicate) and should be dispatched.
|
||||
@@ -40,11 +38,6 @@ public class OnScrollDispatchHelper {
|
||||
mPrevX != x ||
|
||||
mPrevY != y;
|
||||
|
||||
// Skip the first calculation in each scroll
|
||||
if (Math.abs(mXFlingVelocity) < THRESHOLD && Math.abs(mYFlingVelocity) < THRESHOLD) {
|
||||
shouldDispatch = false;
|
||||
}
|
||||
|
||||
if (eventTime - mLastScrollEventTimeMs != 0) {
|
||||
mXFlingVelocity = (float) (x - mPrevX) / (eventTime - mLastScrollEventTimeMs);
|
||||
mYFlingVelocity = (float) (y - mPrevY) / (eventTime - mLastScrollEventTimeMs);
|
||||
|
||||
@@ -27,9 +27,9 @@ class ModuleRegistry;
|
||||
|
||||
struct InstanceCallback {
|
||||
virtual ~InstanceCallback() {}
|
||||
virtual void onBatchComplete() = 0;
|
||||
virtual void incrementPendingJSCalls() = 0;
|
||||
virtual void decrementPendingJSCalls() = 0;
|
||||
virtual void onBatchComplete() {}
|
||||
virtual void incrementPendingJSCalls() {}
|
||||
virtual void decrementPendingJSCalls() {}
|
||||
};
|
||||
|
||||
class RN_EXPORT Instance {
|
||||
|
||||
+6
-2
@@ -58,7 +58,8 @@ test:
|
||||
- cat <(echo eslint; npm run lint --silent -- --format=json; echo flow; npm run flow --silent -- check --json) | GITHUB_TOKEN="af6ef0d15709bc91d""06a6217a5a826a226fb57b7" CI_USER=$CIRCLE_PROJECT_USERNAME CI_REPO=$CIRCLE_PROJECT_REPONAME PULL_REQUEST_NUMBER=$CIRCLE_PR_NUMBER node bots/code-analysis-bot.js
|
||||
- npm run lint
|
||||
# JS tests for dependencies installed with npm3
|
||||
- npm run flow -- check
|
||||
# Commenting out Flow tests
|
||||
# - npm run flow -- check
|
||||
- npm test -- --maxWorkers=1
|
||||
|
||||
# build app
|
||||
@@ -76,8 +77,11 @@ test:
|
||||
# integration tests
|
||||
# build JS bundle for instrumentation tests
|
||||
- node local-cli/cli.js bundle --max-workers 1 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
|
||||
|
||||
# build test APK
|
||||
- buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
|
||||
# Commented out due to test failures. Please uncomment the next line once these have been fixed. See Issue #15726.
|
||||
# - buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1
|
||||
|
||||
# run installed apk with tests
|
||||
# - node ./scripts/run-android-ci-instrumentation-tests.js --retries 3 --path ./ReactAndroid/src/androidTest/java/com/facebook/react/tests --package com.facebook.react.tests
|
||||
|
||||
|
||||
@@ -157,9 +157,11 @@ Go to the root directory for your project and create a new `package.json` file w
|
||||
Next, you will install the `react` and `react-native` packages. Open a terminal or command prompt, then navigate to the root directory for your project and type the following commands:
|
||||
|
||||
```
|
||||
$ npm install --save react react-native
|
||||
$ npm install --save react@16.0.0-beta.5 react-native
|
||||
```
|
||||
|
||||
> Make sure you use the same React version as specified in the [React Native `package.json` file](https://github.com/facebook/react-native/blob/0.49-stable/package.json). This will only be necessary as long as React Native depends on a pre-release version of React.
|
||||
|
||||
This will create a new `/node_modules` folder in your project's root directory. This folder stores all the JavaScript dependencies required to build your project.
|
||||
|
||||
<block class="objc swift" />
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "1000.0.0",
|
||||
"version": "0.49.0-rc.3",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
@@ -205,4 +205,4 @@
|
||||
"shelljs": "0.6.0",
|
||||
"sinon": "^2.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user