diff --git a/.circleci/config.yml b/.circleci/config.yml index bcb01f2a8..899f6808e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -135,6 +135,10 @@ commands: name: Test command: | npx detox test << parameters.folder >> --configuration ios.sim.release --cleanup + when: always + + - store_artifacts: + path: ./artifacts # JOBS jobs: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c868686af..3cbbc7560 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,34 @@ - -@RocketChat/ReactNative + - -Closes #ISSUE_NUMBER +## Proposed changes + - +## Issue(s) + + +## How to test or reproduce + + +## Screenshots + +## Types of changes + + + +- [ ] Bugfix (non-breaking change which fixes an issue) +- [ ] Improvement (non-breaking change which improves a current function) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Documentation update (if none of the other choices apply) + +## Checklist + + +- [ ] I have read the [CONTRIBUTING](https://github.com/RocketChat/Rocket.Chat/blob/develop/.github/CONTRIBUTING.md#contributing-to-rocketchat) doc +- [ ] I have signed the [CLA](https://cla-assistant.io/RocketChat/Rocket.Chat.ReactNative) +- [ ] Lint and unit tests pass locally with my changes +- [ ] I have added tests that prove my fix is effective or that my feature works (if applicable) +- [ ] I have added necessary documentation (if applicable) +- [ ] Any dependent changes have been merged and published in downstream modules + +## Further comments + diff --git a/.gitignore b/.gitignore index fee7bf865..18624dd0b 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ buck-out/ coverage +artifacts .vscode/ e2e/docker/rc_test_env/docker-compose.yml e2e/docker/data/db \ No newline at end of file diff --git a/__mocks__/react-native-firebase.js b/__mocks__/@react-native-firebase/analytics.js similarity index 100% rename from __mocks__/react-native-firebase.js rename to __mocks__/@react-native-firebase/analytics.js diff --git a/__mocks__/@react-native-firebase/crashlytics.js b/__mocks__/@react-native-firebase/crashlytics.js new file mode 100644 index 000000000..8e8b4e8db --- /dev/null +++ b/__mocks__/@react-native-firebase/crashlytics.js @@ -0,0 +1,3 @@ +export default { + crashlytics: null +}; diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index e881064aa..4b04cc4a8 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -4994,137 +4994,6 @@ exports[`Storyshots Message list message 1`] = ` `; -exports[`Storyshots RoomItem list roomitem 1`] = ` - - - - Basic - - View - - User - - View - View - - Type - - View - View - View - View - View - - Alerts - - View - View - View - View - View - View - View - View - View - - Last Message - - View - View - View - View - View - View - - -`; - exports[`Storyshots UiKitMessage list uikitmessage 1`] = ` getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); - if (BuildConfig.PLAY_BUILD) { - packages.add(new RNFirebaseCrashlyticsPackage()); - packages.add(new RNFirebaseAnalyticsPackage()); - packages.add(new RNFirebasePerformancePackage()); + if (!BuildConfig.FDROID_BUILD) { packages.add(new RNNotificationsPackage(MainApplication.this)); } packages.add(new KeyboardInputPackage(MainApplication.this)); diff --git a/android/build.gradle b/android/build.gradle index 5ba4f467b..a1544ceaf 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,10 +1,10 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext { - buildToolsVersion = "28.0.3" + buildToolsVersion = "29.0.2" minSdkVersion = 21 - compileSdkVersion = 28 - targetSdkVersion = 28 + compileSdkVersion = 29 + targetSdkVersion = 29 glideVersion = "4.9.0" kotlin_version = "1.3.50" supportLibVersion = "28.0.0" @@ -24,11 +24,10 @@ buildscript { dependencies { if (!isFoss) { classpath 'com.google.gms:google-services:4.2.0' - classpath 'io.fabric.tools:gradle:1.28.1' - classpath 'com.google.firebase:perf-plugin:1.2.1' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0' classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+' } - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:3.5.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/android/gradle.properties b/android/gradle.properties index a7df71591..6a47b0e29 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -27,7 +27,7 @@ android.useAndroidX=true android.enableJetifier=true # Version of flipper SDK to use with React Native -FLIPPER_VERSION=0.33.1 +FLIPPER_VERSION=0.37.0 # App properties VERSIONCODE=999999999 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e8758b6d5..e603f2a51 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists \ No newline at end of file diff --git a/android/gradlew b/android/gradlew index 77564ddb2..645f6ca31 100755 --- a/android/gradlew +++ b/android/gradlew @@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" \ No newline at end of file diff --git a/android/gradlew.bat b/android/gradlew.bat index 7d3e0e477..8d8accd7c 100644 --- a/android/gradlew.bat +++ b/android/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" diff --git a/app/constants/environment.js b/app/constants/environment.js index 15f70a619..099d27d1c 100644 --- a/app/constants/environment.js +++ b/app/constants/environment.js @@ -1,3 +1,3 @@ import RNConfigReader from 'react-native-config-reader'; -export const isGooglePlayBuild = RNConfigReader.PLAY_BUILD; +export const isFDroidBuild = RNConfigReader.FDROID_BUILD; diff --git a/app/containers/LoginServices.js b/app/containers/LoginServices.js index 11ffc6d97..31c8bbaf2 100644 --- a/app/containers/LoginServices.js +++ b/app/containers/LoginServices.js @@ -15,6 +15,7 @@ import OrSeparator from './OrSeparator'; import Touch from '../utils/touch'; import I18n from '../i18n'; import random from '../utils/random'; +import { logEvent, events } from '../utils/log'; import RocketChat from '../lib/rocketchat'; const BUTTON_HEIGHT = 48; @@ -77,6 +78,7 @@ class LoginServices extends React.PureComponent { } onPressFacebook = () => { + logEvent(events.LOGIN_WITH_FACEBOOK); const { services, server } = this.props; const { clientId } = services.facebook; const endpoint = 'https://m.facebook.com/v2.9/dialog/oauth'; @@ -88,6 +90,7 @@ class LoginServices extends React.PureComponent { } onPressGithub = () => { + logEvent(events.LOGIN_WITH_GITHUB); const { services, server } = this.props; const { clientId } = services.github; const endpoint = `https://github.com/login?client_id=${ clientId }&return_to=${ encodeURIComponent('/login/oauth/authorize') }`; @@ -99,6 +102,7 @@ class LoginServices extends React.PureComponent { } onPressGitlab = () => { + logEvent(events.LOGIN_WITH_GITLAB); const { services, server, Gitlab_URL } = this.props; const { clientId } = services.gitlab; const baseURL = Gitlab_URL ? Gitlab_URL.trim().replace(/\/*$/, '') : 'https://gitlab.com'; @@ -111,6 +115,7 @@ class LoginServices extends React.PureComponent { } onPressGoogle = () => { + logEvent(events.LOGIN_WITH_GOOGLE); const { services, server } = this.props; const { clientId } = services.google; const endpoint = 'https://accounts.google.com/o/oauth2/auth'; @@ -122,6 +127,7 @@ class LoginServices extends React.PureComponent { } onPressLinkedin = () => { + logEvent(events.LOGIN_WITH_LINKEDIN); const { services, server } = this.props; const { clientId } = services.linkedin; const endpoint = 'https://www.linkedin.com/oauth/v2/authorization'; @@ -133,6 +139,7 @@ class LoginServices extends React.PureComponent { } onPressMeteor = () => { + logEvent(events.LOGIN_WITH_METEOR); const { services, server } = this.props; const { clientId } = services['meteor-developer']; const endpoint = 'https://www.meteor.com/oauth2/authorize'; @@ -143,6 +150,7 @@ class LoginServices extends React.PureComponent { } onPressTwitter = () => { + logEvent(events.LOGIN_WITH_TWITTER); const { server } = this.props; const state = this.getOAuthState(); const url = `${ server }/_oauth/twitter/?requestTokenAndRedirect=true&state=${ state }`; @@ -150,6 +158,7 @@ class LoginServices extends React.PureComponent { } onPressWordpress = () => { + logEvent(events.LOGIN_WITH_WORDPRESS); const { services, server } = this.props; const { clientId, serverURL } = services.wordpress; const endpoint = `${ serverURL }/oauth/authorize`; @@ -161,6 +170,7 @@ class LoginServices extends React.PureComponent { } onPressCustomOAuth = (loginService) => { + logEvent(events.LOGIN_WITH_CUSTOM_OAUTH); const { server } = this.props; const { serverURL, authorizePath, clientId, scope, service @@ -175,6 +185,7 @@ class LoginServices extends React.PureComponent { } onPressSaml = (loginService) => { + logEvent(events.LOGIN_WITH_SAML); const { server } = this.props; const { clientConfig } = loginService; const { provider } = clientConfig; @@ -184,6 +195,7 @@ class LoginServices extends React.PureComponent { } onPressCas = () => { + logEvent(events.LOGIN_WITH_CAS); const { server, CAS_login_url } = this.props; const ssoToken = random(17); const url = `${ CAS_login_url }?service=${ server }/_cas/${ ssoToken }`; diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js index 813d7d64c..92f74e5de 100644 --- a/app/containers/MessageBox/index.js +++ b/app/containers/MessageBox/index.js @@ -122,6 +122,7 @@ class MessageBox extends Component { command: {} }; this.text = ''; + this.selection = { start: 0, end: 0 }; this.focused = false; // MessageBox Actions @@ -331,6 +332,10 @@ class MessageBox extends Component { this.setInput(text); } + onSelectionChange = (e) => { + this.selection = e.nativeEvent.selection; + } + // eslint-disable-next-line react/sort-comp debouncedOnChangeText = debounce(async(text) => { const { sharing } = this.props; @@ -358,9 +363,9 @@ class MessageBox extends Component { if (!isTextEmpty) { try { - const { start, end } = this.component?.lastNativeSelection; + const { start, end } = this.selection; const cursor = Math.max(start, end); - const lastNativeText = this.component?.lastNativeText || ''; + const lastNativeText = this.text; // matches if text either starts with '/' or have (@,#,:) then it groups whatever comes next of mention type let regexp = /(#|@|:|^\/)([a-z0-9._-]+)$/im; @@ -399,7 +404,7 @@ class MessageBox extends Component { } const { trackingType } = this.state; const msg = this.text; - const { start, end } = this.component?.lastNativeSelection; + const { start, end } = this.selection; const cursor = Math.max(start, end); const regexp = /([a-z0-9._-]+)$/im; const result = msg.substr(0, cursor).replace(regexp, ''); @@ -410,7 +415,8 @@ class MessageBox extends Component { if ((trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) && item.providesPreview) { this.setState({ showCommandPreview: true }); } - this.setInput(text); + const newCursor = cursor + mentionName.length; + this.setInput(text, { start: newCursor, end: newCursor }); this.focus(); requestAnimationFrame(() => this.stopTrackingMention()); } @@ -443,15 +449,11 @@ class MessageBox extends Component { let newText = ''; // if messagebox has an active cursor - if (this.component?.lastNativeSelection) { - const { start, end } = this.component.lastNativeSelection; - const cursor = Math.max(start, end); - newText = `${ text.substr(0, cursor) }${ emoji }${ text.substr(cursor) }`; - } else { - // if messagebox doesn't have a cursor, just append selected emoji - newText = `${ text }${ emoji }`; - } - this.setInput(newText); + const { start, end } = this.selection; + const cursor = Math.max(start, end); + newText = `${ text.substr(0, cursor) }${ emoji }${ text.substr(cursor) }`; + const newCursor = cursor + emoji.length; + this.setInput(newText, { start: newCursor, end: newCursor }); this.setShowSend(true); } @@ -551,11 +553,12 @@ class MessageBox extends Component { this.setState({ commandPreview: [], showCommandPreview: true, command: {} }); } - setInput = (text) => { + setInput = (text, selection) => { this.text = text; - if (this.component && this.component.setNativeProps) { - this.component.setNativeProps({ text }); + if (selection) { + return this.component.setTextAndSelection(text, selection); } + this.component.setNativeProps({ text }); } setShowSend = (showSend) => { @@ -888,6 +891,7 @@ class MessageBox extends Component { blurOnSubmit={false} placeholder={I18n.t('New_Message')} onChangeText={this.onChangeText} + onSelectionChange={this.onSelectionChange} underlineColorAndroid='transparent' defaultValue='' multiline diff --git a/app/containers/TwoFactor/index.js b/app/containers/TwoFactor/index.js index e59ed0c1a..011b5e3de 100644 --- a/app/containers/TwoFactor/index.js +++ b/app/containers/TwoFactor/index.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { View, Text } from 'react-native'; +import { View, Text, InteractionManager } from 'react-native'; import _ from 'lodash'; import PropTypes from 'prop-types'; import { sha256 } from 'js-sha256'; @@ -99,6 +99,7 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }) => { InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())} returnKeyType='send' autoCapitalize='none' onChangeText={setCode} diff --git a/app/containers/TwoFactor/styles.js b/app/containers/TwoFactor/styles.js index 36608408a..1c5c789cb 100644 --- a/app/containers/TwoFactor/styles.js +++ b/app/containers/TwoFactor/styles.js @@ -14,9 +14,10 @@ export default StyleSheet.create({ borderRadius: 4 }, title: { - fontSize: 14, + fontSize: 16, paddingBottom: 8, - ...sharedStyles.textBold + ...sharedStyles.textBold, + ...sharedStyles.textAlignCenter }, subtitle: { fontSize: 14, diff --git a/app/index.js b/app/index.js index d93fa2185..8154208bb 100644 --- a/app/index.js +++ b/app/index.js @@ -36,7 +36,7 @@ import Toast from './containers/Toast'; import InAppNotification from './containers/InAppNotification'; import { ActionSheetProvider } from './containers/ActionSheet'; import debounce from './utils/debounce'; -import { isGooglePlayBuild } from './constants/environment'; +import { isFDroidBuild } from './constants/environment'; RNScreens.enableScreens(); @@ -59,7 +59,7 @@ export default class Root extends React.Component { constructor(props) { super(props); this.init(); - if (isGooglePlayBuild) { + if (!isFDroidBuild) { this.initCrashReport(); } const { width, height, scale } = Dimensions.get('window'); diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index cfa959919..1353b7364 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -845,6 +845,12 @@ const RocketChat = { return other && other.length ? other[0] : me; }, + isRead(item) { + let isUnread = item.archived !== true && item.open === true; // item is not archived and not opened + isUnread = isUnread && (item.unread > 0 || item.alert === true); // either its unread count > 0 or its alert + return !isUnread; + }, + isGroupChat(room) { return (room.uids && room.uids.length > 2) || (room.usernames && room.usernames.length > 2); }, diff --git a/app/notifications/push/index.js b/app/notifications/push/index.js index fc232e06a..7e867801b 100644 --- a/app/notifications/push/index.js +++ b/app/notifications/push/index.js @@ -2,7 +2,7 @@ import EJSON from 'ejson'; import PushNotification from './push'; import store from '../../lib/createStore'; import { deepLinkingOpen } from '../../actions/deepLinking'; -import { isGooglePlayBuild } from '../../constants/environment'; +import { isFDroidBuild } from '../../constants/environment'; export const onNotification = (notification) => { if (notification) { @@ -37,7 +37,7 @@ export const onNotification = (notification) => { export const getDeviceToken = () => PushNotification.getDeviceToken(); export const setBadgeCount = count => PushNotification.setBadgeCount(count); export const initializePushNotifications = () => { - if (isGooglePlayBuild) { + if (!isFDroidBuild) { setBadgeCount(); return PushNotification.configure({ onNotification diff --git a/app/presentation/KeyboardView.js b/app/presentation/KeyboardView.js index 9bd25191e..628213825 100644 --- a/app/presentation/KeyboardView.js +++ b/app/presentation/KeyboardView.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; +import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view'; import scrollPersistTaps from '../utils/scrollPersistTaps'; export default class KeyboardView extends React.PureComponent { diff --git a/app/presentation/RoomItem/index.js b/app/presentation/RoomItem/index.js index 1b3ee93f2..487c22430 100644 --- a/app/presentation/RoomItem/index.js +++ b/app/presentation/RoomItem/index.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { View, Text } from 'react-native'; import { connect } from 'react-redux'; @@ -16,82 +16,79 @@ import { themes } from '../../constants/colors'; export { ROW_HEIGHT }; const attrs = [ - 'name', - 'unread', - 'userMentions', - 'showLastMessage', - 'useRealName', - 'alert', - 'type', 'width', - 'isRead', - 'favorite', 'status', 'connected', 'theme', - 'isFocused' + 'isFocused', + 'forceUpdate', + 'showLastMessage' ]; -const arePropsEqual = (oldProps, newProps) => { - const { _updatedAt: _updatedAtOld } = oldProps; - const { _updatedAt: _updatedAtNew } = newProps; - if (_updatedAtOld && _updatedAtNew && _updatedAtOld.toISOString() !== _updatedAtNew.toISOString()) { - return false; - } - return attrs.every(key => oldProps[key] === newProps[key]); -}; +const arePropsEqual = (oldProps, newProps) => attrs.every(key => oldProps[key] === newProps[key]); const RoomItem = React.memo(({ + item, onPress, width, - favorite, toggleFav, - isRead, - rid, toggleRead, hideChannel, testID, - unread, - userMentions, - name, - _updatedAt, - alert, - type, avatarSize, baseUrl, userId, username, token, id, - prid, showLastMessage, - hideUnreadStatus, - lastMessage, status, - avatar, useRealName, getUserPresence, - isGroupChat, connected, theme, - isFocused + isFocused, + getRoomTitle, + getRoomAvatar, + getIsGroupChat, + getIsRead }) => { + const [, setForceUpdate] = useState(1); + useEffect(() => { - if (connected && type === 'd' && id) { + if (connected && item.t === 'd' && id) { getUserPresence(id); } }, [connected]); - const date = lastMessage && formatDate(lastMessage.ts); + useEffect(() => { + if (item?.observe) { + const observable = item.observe(); + const subscription = observable?.subscribe?.(() => { + setForceUpdate(prevForceUpdate => prevForceUpdate + 1); + }); + + return () => { + subscription?.unsubscribe?.(); + }; + } + }, []); + + const name = getRoomTitle(item); + const avatar = getRoomAvatar(item); + const isGroupChat = getIsGroupChat(item); + const isRead = getIsRead(item); + const _onPress = () => onPress(item); + const date = item.lastMessage?.ts && formatDate(item.lastMessage.ts); let accessibilityLabel = name; - if (unread === 1) { - accessibilityLabel += `, ${ unread } ${ I18n.t('alert') }`; - } else if (unread > 1) { - accessibilityLabel += `, ${ unread } ${ I18n.t('alerts') }`; + if (item.unread === 1) { + accessibilityLabel += `, ${ item.unread } ${ I18n.t('alert') }`; + } else if (item.unread > 1) { + accessibilityLabel += `, ${ item.unread } ${ I18n.t('alerts') }`; } - if (userMentions > 0) { + if (item.userMentions > 0) { accessibilityLabel += `, ${ I18n.t('you_were_mentioned') }`; } @@ -101,16 +98,16 @@ const RoomItem = React.memo(({ return ( @@ -121,7 +118,7 @@ const RoomItem = React.memo(({ {name} - {_updatedAt ? ( + {item.roomUpdatedAt ? ( @@ -203,17 +200,10 @@ const RoomItem = React.memo(({ }, arePropsEqual); RoomItem.propTypes = { - type: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, + item: PropTypes.object.isRequired, baseUrl: PropTypes.string.isRequired, showLastMessage: PropTypes.bool, - _updatedAt: PropTypes.string, - lastMessage: PropTypes.object, - alert: PropTypes.bool, - unread: PropTypes.number, - userMentions: PropTypes.number, id: PropTypes.string, - prid: PropTypes.string, onPress: PropTypes.func, userId: PropTypes.string, username: PropTypes.string, @@ -221,27 +211,29 @@ RoomItem.propTypes = { avatarSize: PropTypes.number, testID: PropTypes.string, width: PropTypes.number, - favorite: PropTypes.bool, - isRead: PropTypes.bool, - rid: PropTypes.string, status: PropTypes.string, toggleFav: PropTypes.func, toggleRead: PropTypes.func, hideChannel: PropTypes.func, - avatar: PropTypes.bool, - hideUnreadStatus: PropTypes.bool, useRealName: PropTypes.bool, getUserPresence: PropTypes.func, connected: PropTypes.bool, - isGroupChat: PropTypes.bool, theme: PropTypes.string, - isFocused: PropTypes.bool + isFocused: PropTypes.bool, + getRoomTitle: PropTypes.func, + getRoomAvatar: PropTypes.func, + getIsGroupChat: PropTypes.func, + getIsRead: PropTypes.func }; RoomItem.defaultProps = { avatarSize: 48, status: 'offline', - getUserPresence: () => {} + getUserPresence: () => {}, + getRoomTitle: () => 'title', + getRoomAvatar: () => '', + getIsGroupChat: () => false, + getIsRead: () => false }; const mapStateToProps = (state, ownProps) => { diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js index 2069cbd07..ffa04040f 100644 --- a/app/sagas/deepLinking.js +++ b/app/sagas/deepLinking.js @@ -5,12 +5,12 @@ import RNUserDefaults from 'rn-user-defaults'; import Navigation from '../lib/Navigation'; import * as types from '../actions/actionsTypes'; -import { selectServerRequest } from '../actions/server'; +import { selectServerRequest, serverInitAdd } from '../actions/server'; import { inviteLinksSetToken, inviteLinksRequest } from '../actions/inviteLinks'; import database from '../lib/database'; import RocketChat from '../lib/rocketchat'; import EventEmitter from '../utils/events'; -import { appStart, ROOT_INSIDE } from '../actions/app'; +import { appStart, ROOT_INSIDE, ROOT_NEW_SERVER } from '../actions/app'; import { localAuthenticate } from '../utils/localAuthentication'; import { goRoom } from '../utils/goRoom'; @@ -106,7 +106,8 @@ const handleOpen = function* handleOpen({ params }) { if (!result.success) { return; } - Navigation.navigate('NewServerView', { previousServer: server }); + yield put(appStart({ root: ROOT_NEW_SERVER })); + yield put(serverInitAdd(server)); yield delay(1000); EventEmitter.emit('NewServer', { server: host }); diff --git a/app/sagas/login.js b/app/sagas/login.js index 587b9b7ba..199952190 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -17,7 +17,7 @@ import { import { roomsRequest } from '../actions/rooms'; import { toMomentLocale } from '../utils/moment'; import RocketChat from '../lib/rocketchat'; -import log from '../utils/log'; +import log, { logEvent, events } from '../utils/log'; import I18n from '../i18n'; import database from '../lib/database'; import EventEmitter from '../utils/events'; @@ -32,6 +32,7 @@ const loginCall = args => RocketChat.login(args); const logoutCall = args => RocketChat.logout(args); const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false }) { + logEvent(events.DEFAULT_LOGIN); try { let result; if (credentials.resume) { @@ -52,6 +53,7 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE if (logoutOnError && (e.data && e.data.message && /you've been logged out by the server/i.test(e.data.message))) { yield put(logout(true)); } else { + logEvent(events.DEFAULT_LOGIN_FAIL); yield put(loginFailure(e)); } } diff --git a/app/stacks/InsideStack.js b/app/stacks/InsideStack.js index e29c39dd9..8a0f2e919 100644 --- a/app/stacks/InsideStack.js +++ b/app/stacks/InsideStack.js @@ -112,7 +112,6 @@ const ChatsStackNavigator = () => { { { } -}); - +export { analytics }; export const loggerConfig = bugsnag.config; export const { leaveBreadcrumb } = bugsnag; +export { events }; let metadata = {}; @@ -20,11 +20,18 @@ export const logServerVersion = (serverVersion) => { }; }; -export const setCurrentScreen = (currentScreen) => { - if (isGooglePlayBuild) { - analytics().setCurrentScreen(currentScreen); +export const logEvent = (eventName, payload) => { + if (!isFDroidBuild) { + analytics().logEvent(eventName, payload); + leaveBreadcrumb(eventName, payload); + } +}; + +export const setCurrentScreen = (currentScreen) => { + if (!isFDroidBuild) { + analytics().setCurrentScreen(currentScreen); + leaveBreadcrumb(currentScreen, { type: 'navigation' }); } - leaveBreadcrumb(currentScreen, { type: 'navigation' }); }; export default (e) => { @@ -36,6 +43,9 @@ export default (e) => { } }; }); + if (!isFDroidBuild) { + crashlytics().recordError(e); + } } else { console.log(e); } diff --git a/app/utils/review.js b/app/utils/review.js index 378cc65eb..76040c3e8 100644 --- a/app/utils/review.js +++ b/app/utils/review.js @@ -5,7 +5,7 @@ import { isIOS } from './deviceInfo'; import I18n from '../i18n'; import { showErrorAlert } from './info'; import { STORE_REVIEW_LINK } from '../constants/links'; -import { isGooglePlayBuild } from '../constants/environment'; +import { isFDroidBuild } from '../constants/environment'; const store = isIOS ? 'App Store' : 'Play Store'; @@ -76,7 +76,7 @@ const tryReview = async() => { // if ask me later was pressed earlier, we can ask for review only after {{numberOfDays}} days // if there's no review and it wasn't dismissed by the user - if (daysBetween(lastReviewDate, new Date()) >= numberOfDays && !doneReview && isGooglePlayBuild) { + if (daysBetween(lastReviewDate, new Date()) >= numberOfDays && !doneReview) { setTimeout(askReview, reviewDelay); } }; @@ -90,7 +90,9 @@ class ReviewApp { } this.positiveEventCount += 1; if (this.positiveEventCount === numberOfPositiveEvent) { - tryReview(); + if (!isFDroidBuild) { + tryReview(); + } } } } diff --git a/app/views/AdminPanelView/index.js b/app/views/AdminPanelView/index.js index 283e2ef74..41005fc93 100644 --- a/app/views/AdminPanelView/index.js +++ b/app/views/AdminPanelView/index.js @@ -31,6 +31,8 @@ class AdminPanelView extends React.Component { {}} source={{ uri: `${ baseUrl }/admin/info?layout=embedded` }} injectedJavaScript={`Meteor.loginWithToken('${ token }', function() { })`} /> diff --git a/app/views/DefaultBrowserView.js b/app/views/DefaultBrowserView.js index 3148952ae..d0b9597ef 100644 --- a/app/views/DefaultBrowserView.js +++ b/app/views/DefaultBrowserView.js @@ -19,11 +19,11 @@ import SafeAreaView from '../containers/SafeAreaView'; const DEFAULT_BROWSERS = [ { - title: I18n.t('In_app'), + title: 'In_app', value: 'inApp' }, { - title: isIOS ? 'Safari' : I18n.t('Browser'), + title: isIOS ? 'Safari' : 'Browser', value: 'systemDefault:' } ]; @@ -137,7 +137,7 @@ class DefaultBrowserView extends React.Component { const { title, value } = item; return ( this.changeDefaultBrowser(value)} testID={`default-browser-view-${ title }`} right={this.isSelected(value) ? this.renderIcon : null} diff --git a/app/views/InviteUsersEditView/index.js b/app/views/InviteUsersEditView/index.js index ee9874298..08ff79040 100644 --- a/app/views/InviteUsersEditView/index.js +++ b/app/views/InviteUsersEditView/index.js @@ -21,9 +21,6 @@ import SafeAreaView from '../../containers/SafeAreaView'; const OPTIONS = { days: [{ - label: I18n.t('Never'), value: 0 - }, - { label: '1', value: 1 }, { @@ -36,9 +33,6 @@ const OPTIONS = { label: '30', value: 30 }], maxUses: [{ - label: I18n.t('No_limit'), value: 0 - }, - { label: '1', value: 1 }, { @@ -91,9 +85,12 @@ class InviteUsersView extends React.Component { navigation.pop(); } - renderPicker = (key) => { + renderPicker = (key, first) => { const { props } = this; const { theme } = props; + const firstEl = [{ + label: I18n.t(first), value: 0 + }]; return ( this.onValueChangePicker(key, value)} - items={OPTIONS[key]} + items={firstEl.concat(OPTIONS[key])} /> ); } @@ -121,13 +118,13 @@ class InviteUsersView extends React.Component { this.renderPicker('days')} + right={() => this.renderPicker('days', 'Never')} theme={theme} /> this.renderPicker('maxUses')} + right={() => this.renderPicker('maxUses', 'No_limit')} theme={theme} /> diff --git a/app/views/LoginView.js b/app/views/LoginView.js index fe7587d9c..9e566b5be 100644 --- a/app/views/LoginView.js +++ b/app/views/LoginView.js @@ -6,7 +6,7 @@ import { import { connect } from 'react-redux'; import equal from 'deep-equal'; -import { analytics } from '../utils/log'; +import { logEvent, events } from '../utils/log'; import sharedStyles from './Styles'; import Button from '../containers/Button'; import I18n from '../i18n'; @@ -17,7 +17,6 @@ import FormContainer, { FormContainerInner } from '../containers/FormContainer'; import TextInput from '../containers/TextInput'; import { loginRequest as loginRequestAction } from '../actions/login'; import LoginServices from '../containers/LoginServices'; -import { isGooglePlayBuild } from '../constants/environment'; const styles = StyleSheet.create({ registerDisabled: { @@ -104,6 +103,7 @@ class LoginView extends React.Component { } forgotPassword = () => { + logEvent(events.FORGOT_PASSWORD); const { navigation, Site_Name } = this.props; navigation.navigate('ForgotPasswordView', { title: Site_Name }); } @@ -122,9 +122,6 @@ class LoginView extends React.Component { const { loginRequest } = this.props; Keyboard.dismiss(); loginRequest({ user, password }); - if (isGooglePlayBuild) { - analytics().logEvent('login'); - } } renderUserForm = () => { diff --git a/app/views/MessagesView/index.js b/app/views/MessagesView/index.js index 1a55b72f3..4e826d3fc 100644 --- a/app/views/MessagesView/index.js +++ b/app/views/MessagesView/index.js @@ -18,10 +18,6 @@ import { withActionSheet } from '../../containers/ActionSheet'; import SafeAreaView from '../../containers/SafeAreaView'; class MessagesView extends React.Component { - static navigationOptions = ({ route }) => ({ - title: I18n.t(route.params?.name) - }); - static propTypes = { user: PropTypes.object, baseUrl: PropTypes.string, @@ -39,6 +35,7 @@ class MessagesView extends React.Component { messages: [], fileLoading: true }; + this.setHeader(); this.rid = props.route.params?.rid; this.t = props.route.params?.t; this.content = this.defineMessagesViewContent(props.route.params?.name); @@ -65,10 +62,16 @@ class MessagesView extends React.Component { if (fileLoading !== nextState.fileLoading) { return true; } - return false; } + setHeader = () => { + const { route, navigation } = this.props; + navigation.setOptions({ + title: I18n.t(route.params?.name) + }); + } + navToRoomInfo = (navParam) => { const { navigation, user } = this.props; if (navParam.rid === user.id) { diff --git a/app/views/ModalBlockView.js b/app/views/ModalBlockView.js index 57b415fdc..e88d30885 100644 --- a/app/views/ModalBlockView.js +++ b/app/views/ModalBlockView.js @@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native'; import PropTypes from 'prop-types'; import isEqual from 'lodash/isEqual'; import { connect } from 'react-redux'; -import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; +import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view'; import { withTheme } from '../theme'; import EventEmitter from '../utils/events'; diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js index b9429c05a..5d2726cdd 100644 --- a/app/views/NewServerView.js +++ b/app/views/NewServerView.js @@ -7,11 +7,12 @@ import { connect } from 'react-redux'; import * as FileSystem from 'expo-file-system'; import DocumentPicker from 'react-native-document-picker'; import RNUserDefaults from 'rn-user-defaults'; -import { encode } from 'base-64'; +import { Base64 } from 'js-base64'; import parse from 'url-parse'; import EventEmitter from '../utils/events'; import { selectServerRequest, serverRequest } from '../actions/server'; +import { inviteLinksClear as inviteLinksClearAction } from '../actions/inviteLinks'; import sharedStyles from './Styles'; import Button from '../containers/Button'; import TextInput from '../containers/TextInput'; @@ -20,7 +21,7 @@ import FormContainer, { FormContainerInner } from '../containers/FormContainer'; import I18n from '../i18n'; import { isIOS } from '../utils/deviceInfo'; import { themes } from '../constants/colors'; -import log from '../utils/log'; +import log, { logEvent, events } from '../utils/log'; import { animateNextTransition } from '../utils/layoutAnimation'; import { withTheme } from '../theme'; import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch'; @@ -72,16 +73,13 @@ class NewServerView extends React.Component { connectServer: PropTypes.func.isRequired, selectServer: PropTypes.func.isRequired, adding: PropTypes.bool, - previousServer: PropTypes.string + previousServer: PropTypes.string, + inviteLinksClear: PropTypes.func } constructor(props) { super(props); - if (props.adding) { - props.navigation.setOptions({ - headerLeft: () => - }); - } + this.setHeader(); this.state = { text: '', @@ -92,11 +90,27 @@ class NewServerView extends React.Component { BackHandler.addEventListener('hardwareBackPress', this.handleBackPress); } + componentDidUpdate(prevProps) { + const { adding } = this.props; + if (prevProps.adding !== adding) { + this.setHeader(); + } + } + componentWillUnmount() { EventEmitter.removeListener('NewServer', this.handleNewServerEvent); BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress); } + setHeader = () => { + const { adding, navigation } = this.props; + if (adding) { + navigation.setOptions({ + headerLeft: () => + }); + } + } + handleBackPress = () => { const { navigation, previousServer } = this.props; if (navigation.isFocused() && previousServer) { @@ -111,7 +125,8 @@ class NewServerView extends React.Component { } close = () => { - const { selectServer, previousServer } = this.props; + const { selectServer, previousServer, inviteLinksClear } = this.props; + inviteLinksClear(); selectServer(previousServer); } @@ -124,6 +139,7 @@ class NewServerView extends React.Component { } submit = async() => { + logEvent(events.CONNECT_TO_WORKSPACE); const { text, certificate } = this.state; const { connectServer } = this.props; let cert = null; @@ -135,6 +151,7 @@ class NewServerView extends React.Component { try { await FileSystem.copyAsync({ from: certificate.path, to: certificatePath }); } catch (e) { + logEvent(events.CONNECT_TO_WORKSPACE_FAIL); log(e); } cert = { @@ -152,6 +169,7 @@ class NewServerView extends React.Component { } connectOpen = () => { + logEvent(events.JOIN_OPEN_WORKSPACE); this.setState({ connectingOpen: true }); const { connectServer } = this.props; connectServer('https://open.rocket.chat'); @@ -161,7 +179,7 @@ class NewServerView extends React.Component { try { const parsedUrl = parse(text, true); if (parsedUrl.auth.length) { - const credentials = encode(parsedUrl.auth); + const credentials = Base64.encode(parsedUrl.auth); await RNUserDefaults.set(`${ BASIC_AUTH_KEY }-${ server }`, credentials); setBasicAuth(credentials); } @@ -321,7 +339,8 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ connectServer: (server, certificate) => dispatch(serverRequest(server, certificate)), - selectServer: server => dispatch(selectServerRequest(server)) + selectServer: server => dispatch(selectServerRequest(server)), + inviteLinksClear: () => dispatch(inviteLinksClearAction()) }); export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewServerView)); diff --git a/app/views/NotificationPreferencesView/index.js b/app/views/NotificationPreferencesView/index.js index 31a0fc679..7c97ec169 100644 --- a/app/views/NotificationPreferencesView/index.js +++ b/app/views/NotificationPreferencesView/index.js @@ -71,58 +71,58 @@ Info.propTypes = { const OPTIONS = { desktopNotifications: [{ - label: I18n.t('Default'), value: 'default' + label: 'Default', value: 'default' }, { - label: I18n.t('All_Messages'), value: 'all' + label: 'All_Messages', value: 'all' }, { - label: I18n.t('Mentions'), value: 'mentions' + label: 'Mentions', value: 'mentions' }, { - label: I18n.t('Nothing'), value: 'nothing' + label: 'Nothing', value: 'nothing' }], audioNotifications: [{ - label: I18n.t('Default'), value: 'default' + label: 'Default', value: 'default' }, { - label: I18n.t('All_Messages'), value: 'all' + label: 'All_Messages', value: 'all' }, { - label: I18n.t('Mentions'), value: 'mentions' + label: 'Mentions', value: 'mentions' }, { - label: I18n.t('Nothing'), value: 'nothing' + label: 'Nothing', value: 'nothing' }], mobilePushNotifications: [{ - label: I18n.t('Default'), value: 'default' + label: 'Default', value: 'default' }, { - label: I18n.t('All_Messages'), value: 'all' + label: 'All_Messages', value: 'all' }, { - label: I18n.t('Mentions'), value: 'mentions' + label: 'Mentions', value: 'mentions' }, { - label: I18n.t('Nothing'), value: 'nothing' + label: 'Nothing', value: 'nothing' }], emailNotifications: [{ - label: I18n.t('Default'), value: 'default' + label: 'Default', value: 'default' }, { - label: I18n.t('All_Messages'), value: 'all' + label: 'All_Messages', value: 'all' }, { - label: I18n.t('Mentions'), value: 'mentions' + label: 'Mentions', value: 'mentions' }, { - label: I18n.t('Nothing'), value: 'nothing' + label: 'Nothing', value: 'nothing' }], desktopNotificationDuration: [{ - label: I18n.t('Default'), value: 0 + label: 'Default', value: 0 }, { - label: I18n.t('Seconds', { second: 1 }), value: 1 + label: 'Seconds', second: 1, value: 1 }, { - label: I18n.t('Seconds', { second: 2 }), value: 2 + label: 'Seconds', second: 2, value: 2 }, { - label: I18n.t('Seconds', { second: 3 }), value: 3 + label: 'Seconds', second: 3, value: 3 }, { - label: I18n.t('Seconds', { second: 4 }), value: 4 + label: 'Seconds', second: 4, value: 4 }, { - label: I18n.t('Seconds', { second: 5 }), value: 5 + label: 'Seconds', second: 5, value: 5 }], audioNotificationValue: [{ label: 'None', value: 'none None' }, { - label: I18n.t('Default'), value: '0 Default' + label: 'Default', value: '0 Default' }, { label: 'Beep', value: 'beep Beep' }, { @@ -229,7 +229,7 @@ class NotificationPreferencesView extends React.Component { const { room } = this.state; const { theme } = this.props; const text = room[key] ? OPTIONS[key].find(option => option.value === room[key]) : OPTIONS[key][0]; - return {text?.label}; + return {I18n.t(text?.label, { defaultValue: text?.label, second: text?.second })}; } renderSwitch = (key) => { diff --git a/app/views/OnboardingView/index.js b/app/views/OnboardingView/index.js index 0a1ac7383..0d509e0d4 100644 --- a/app/views/OnboardingView/index.js +++ b/app/views/OnboardingView/index.js @@ -14,6 +14,7 @@ import { isTablet } from '../../utils/deviceInfo'; import { themes } from '../../constants/colors'; import { withTheme } from '../../theme'; import FormContainer, { FormContainerInner } from '../../containers/FormContainer'; +import { logEvent, events } from '../../utils/log'; class OnboardingView extends React.Component { static navigationOptions = { @@ -69,15 +70,17 @@ class OnboardingView extends React.Component { } connectServer = () => { + logEvent(events.JOIN_A_WORKSPACE); const { navigation } = this.props; navigation.navigate('NewServerView'); } createWorkspace = async() => { + logEvent(events.CREATE_NEW_WORKSPACE); try { await Linking.openURL('https://cloud.rocket.chat/trial'); } catch { - // do nothing + logEvent(events.CREATE_NEW_WORKSPACE_FAIL); } } diff --git a/app/views/PickerView.js b/app/views/PickerView.js index 489d8a42f..062e373ab 100644 --- a/app/views/PickerView.js +++ b/app/views/PickerView.js @@ -42,7 +42,7 @@ const Item = React.memo(({ theme }) => ( )} onPress={onItemPress} theme={theme} diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js index 1e0ce2654..d95fb7938 100644 --- a/app/views/RegisterView.js +++ b/app/views/RegisterView.js @@ -6,7 +6,7 @@ import { import { connect } from 'react-redux'; import RNPickerSelect from 'react-native-picker-select'; -import log from '../utils/log'; +import log, { logEvent, events } from '../utils/log'; import sharedStyles from './Styles'; import Button from '../containers/Button'; import I18n from '../i18n'; @@ -114,6 +114,7 @@ class RegisterView extends React.Component { } submit = async() => { + logEvent(events.DEFAULT_SIGN_UP); if (!this.valid()) { return; } @@ -149,6 +150,7 @@ class RegisterView extends React.Component { return loginRequest({ user: email, password }); } if (e.data?.error) { + logEvent(events.DEFAULT_SIGN_UP_FAIL); showErrorAlert(e.data.error, I18n.t('Oops')); } } diff --git a/app/views/RoomView/List.js b/app/views/RoomView/List.js index d0dc7c88d..209ec18bb 100644 --- a/app/views/RoomView/List.js +++ b/app/views/RoomView/List.js @@ -1,7 +1,6 @@ import React from 'react'; import { FlatList, RefreshControl } from 'react-native'; import PropTypes from 'prop-types'; -import orderBy from 'lodash/orderBy'; import { Q } from '@nozbe/watermelondb'; import moment from 'moment'; import isEqual from 'lodash/isEqual'; @@ -15,9 +14,10 @@ import EmptyRoom from './EmptyRoom'; import { isIOS } from '../../utils/deviceInfo'; import { animateNextTransition } from '../../utils/layoutAnimation'; import ActivityIndicator from '../../containers/ActivityIndicator'; -import debounce from '../../utils/debounce'; import { themes } from '../../constants/colors'; +const QUERY_SIZE = 50; + class List extends React.Component { static propTypes = { onEndReached: PropTypes.func, @@ -47,7 +47,8 @@ class List extends React.Component { super(props); console.time(`${ this.constructor.name } init`); console.time(`${ this.constructor.name } mount`); - + this.count = 0; + this.needsFetch = false; this.mounted = false; this.state = { loading: true, @@ -56,7 +57,7 @@ class List extends React.Component { refreshing: false, animated: false }; - this.init(); + this.query(); this.unsubscribeFocus = props.navigation.addListener('focus', () => { if (this.mounted) { this.setState({ animated: true }); @@ -72,72 +73,6 @@ class List extends React.Component { console.timeEnd(`${ this.constructor.name } mount`); } - // eslint-disable-next-line react/sort-comp - async init() { - const { rid, tmid } = this.props; - const db = database.active; - - // handle servers with version < 3.0.0 - let { hideSystemMessages = [] } = this.props; - if (!Array.isArray(hideSystemMessages)) { - hideSystemMessages = []; - } - - if (tmid) { - try { - this.thread = await db.collections - .get('threads') - .find(tmid); - } catch (e) { - console.log(e); - } - this.messagesObservable = db.collections - .get('thread_messages') - .query(Q.where('rid', tmid), Q.or(Q.where('t', Q.notIn(hideSystemMessages)), Q.where('t', Q.eq(null)))) - .observe(); - } else if (rid) { - this.messagesObservable = db.collections - .get('messages') - .query(Q.where('rid', rid), Q.or(Q.where('t', Q.notIn(hideSystemMessages)), Q.where('t', Q.eq(null)))) - .observe(); - } - - if (rid) { - this.unsubscribeMessages(); - this.messagesSubscription = this.messagesObservable - .subscribe((data) => { - if (tmid && this.thread) { - data = [this.thread, ...data]; - } - const messages = orderBy(data, ['ts'], ['desc']); - if (this.mounted) { - this.setState({ messages }, () => this.update()); - } else { - this.state.messages = messages; - } - this.readThreads(); - }); - } - } - - // eslint-disable-next-line react/sort-comp - reload = () => { - this.unsubscribeMessages(); - this.init(); - } - - readThreads = async() => { - const { tmid } = this.props; - - if (tmid) { - try { - await RocketChat.readThreads(tmid); - } catch { - // Do nothing - } - } - } - shouldComponentUpdate(nextProps, nextState) { const { loading, end, refreshing } = this.state; const { hideSystemMessages, theme } = this.props; @@ -177,7 +112,7 @@ class List extends React.Component { console.countReset(`${ this.constructor.name }.render calls`); } - onEndReached = debounce(async() => { + fetchData = async() => { const { loading, end, messages, latest = messages[messages.length - 1]?.ts } = this.state; @@ -196,12 +131,99 @@ class List extends React.Component { result = await RocketChat.loadMessagesForRoom({ rid, t, latest }); } - this.setState({ end: result.length < 50, loading: false, latest: result[result.length - 1]?.ts }, () => this.loadMoreMessages(result)); + this.setState({ end: result.length < QUERY_SIZE, loading: false, latest: result[result.length - 1]?.ts }, () => this.loadMoreMessages(result)); } catch (e) { this.setState({ loading: false }); log(e); } - }, 300) + } + + query = async() => { + this.count += QUERY_SIZE; + const { rid, tmid } = this.props; + const db = database.active; + + // handle servers with version < 3.0.0 + let { hideSystemMessages = [] } = this.props; + if (!Array.isArray(hideSystemMessages)) { + hideSystemMessages = []; + } + + if (tmid) { + try { + this.thread = await db.collections + .get('threads') + .find(tmid); + } catch (e) { + console.log(e); + } + this.messagesObservable = db.collections + .get('thread_messages') + .query( + Q.where('rid', tmid), + Q.experimentalSortBy('ts', Q.desc), + Q.experimentalSkip(0), + Q.experimentalTake(this.count) + ) + .observe(); + } else if (rid) { + this.messagesObservable = db.collections + .get('messages') + .query( + Q.where('rid', rid), + Q.experimentalSortBy('ts', Q.desc), + Q.experimentalSkip(0), + Q.experimentalTake(this.count) + ) + .observe(); + } + + if (rid) { + this.unsubscribeMessages(); + this.messagesSubscription = this.messagesObservable + .subscribe((messages) => { + if (messages.length <= this.count) { + this.needsFetch = true; + } + if (tmid && this.thread) { + messages = [...messages, this.thread]; + } + messages = messages.filter(m => !m.t || !hideSystemMessages?.includes(m.t)); + + if (this.mounted) { + this.setState({ messages }, () => this.update()); + } else { + this.state.messages = messages; + } + this.readThreads(); + }); + } + } + + reload = () => { + this.count = 0; + this.query(); + } + + readThreads = async() => { + const { tmid } = this.props; + + if (tmid) { + try { + await RocketChat.readThreads(tmid); + } catch { + // Do nothing + } + } + } + + onEndReached = async() => { + if (this.needsFetch) { + this.needsFetch = false; + await this.fetchData(); + } + this.query(); + } loadMoreMessages = (result) => { const { end } = this.state; @@ -305,7 +327,7 @@ class List extends React.Component { removeClippedSubviews={isIOS} initialNumToRender={7} onEndReached={this.onEndReached} - onEndReachedThreshold={5} + onEndReachedThreshold={0.5} maxToRenderPerBatch={5} windowSize={10} ListFooterComponent={this.renderFooter} diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index 0b6afd18f..4a54117dd 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -206,12 +206,10 @@ class RoomView extends React.Component { const { appState, insets } = this.props; if (appState === 'foreground' && appState !== prevProps.appState && this.rid) { - this.onForegroundInteraction = InteractionManager.runAfterInteractions(() => { - // Fire List.init() just to keep observables working - if (this.list && this.list.current) { - this.list.current.init(); - } - }); + // Fire List.query() just to keep observables working + if (this.list && this.list.current) { + this.list.current?.query?.(); + } } // If it's not direct message if (this.t !== 'd') { @@ -267,9 +265,6 @@ class RoomView extends React.Component { if (this.didMountInteraction && this.didMountInteraction.cancel) { this.didMountInteraction.cancel(); } - if (this.onForegroundInteraction && this.onForegroundInteraction.cancel) { - this.onForegroundInteraction.cancel(); - } if (this.willBlurListener && this.willBlurListener.remove) { this.willBlurListener.remove(); } diff --git a/app/views/RoomsListView/Header/Header.js b/app/views/RoomsListView/Header/Header.js index 839048bc0..fb5d7eb9e 100644 --- a/app/views/RoomsListView/Header/Header.js +++ b/app/views/RoomsListView/Header/Header.js @@ -41,7 +41,7 @@ const Header = React.memo(({ const { isLandscape } = useOrientation(); const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1; const titleFontSize = 16 * scale; - const subTitleFontSize = 12 * scale; + const subTitleFontSize = 14 * scale; if (showSearchHeader) { return ( @@ -78,11 +78,11 @@ const Header = React.memo(({ - {subtitle ? {subtitle} : null} + {subtitle ? {subtitle} : null} ); diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 232ea3228..d2bd6cced 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -9,7 +9,7 @@ import { RefreshControl } from 'react-native'; import { connect } from 'react-redux'; -import { isEqual, orderBy } from 'lodash'; +import isEqual from 'react-fast-compare'; import Orientation from 'react-native-orientation-locker'; import { Q } from '@nozbe/watermelondb'; import { withSafeAreaInsets } from 'react-native-safe-area-context'; @@ -71,6 +71,7 @@ const DISCUSSIONS_HEADER = 'Discussions'; const CHANNELS_HEADER = 'Channels'; const DM_HEADER = 'Direct_Messages'; const GROUPS_HEADER = 'Private_Groups'; +const QUERY_SIZE = 20; const filterIsUnread = s => (s.unread > 0 || s.alert) && !s.hideUnreadStatus; const filterIsFavorite = s => s.f; @@ -140,11 +141,12 @@ class RoomsListView extends React.Component { this.gotSubscriptions = false; this.animated = false; + this.count = 0; this.state = { searching: false, search: [], loading: true, - allChats: [], + chatsOrder: [], chats: [], item: {} }; @@ -211,7 +213,7 @@ class RoomsListView extends React.Component { } shouldComponentUpdate(nextProps, nextState) { - const { allChats, searching, item } = this.state; + const { chatsOrder, searching, item } = this.state; // eslint-disable-next-line react/destructuring-assignment const propsUpdated = shouldUpdateProps.some(key => nextProps[key] !== this.props[key]); if (propsUpdated) { @@ -219,7 +221,7 @@ class RoomsListView extends React.Component { } // Compare changes only once - const chatsNotEqual = !isEqual(nextState.allChats, allChats); + const chatsNotEqual = !isEqual(nextState.chatsOrder, chatsOrder); // If they aren't equal, set to update if focused if (chatsNotEqual) { @@ -290,7 +292,7 @@ class RoomsListView extends React.Component { && prevProps.showUnread === showUnread ) ) { - this.getSubscriptions(true); + this.getSubscriptions(); } else if ( appState === 'foreground' && appState !== prevProps.appState @@ -309,9 +311,7 @@ class RoomsListView extends React.Component { } componentWillUnmount() { - if (this.querySubscription && this.querySubscription.unsubscribe) { - this.querySubscription.unsubscribe(); - } + this.unsubscribeQuery(); if (this.unsubscribeFocus) { this.unsubscribeFocus(); } @@ -396,17 +396,8 @@ class RoomsListView extends React.Component { return allData; } - getSubscriptions = async(force = false) => { - if (this.gotSubscriptions && !force) { - return; - } - this.gotSubscriptions = true; - - if (this.querySubscription && this.querySubscription.unsubscribe) { - this.querySubscription.unsubscribe(); - } - - this.setState({ loading: true }); + getSubscriptions = async() => { + this.unsubscribeQuery(); const { sortBy, @@ -416,41 +407,49 @@ class RoomsListView extends React.Component { } = this.props; const db = database.active; - const observable = await db.collections - .get('subscriptions') - .query( - Q.where('archived', false), - Q.where('open', true) - ) - .observeWithColumns(['room_updated_at', 'unread', 'alert', 'user_mentions', 'f', 't']); + let observable; + + const defaultWhereClause = [ + Q.where('archived', false), + Q.where('open', true) + ]; + + if (sortBy === 'alphabetical') { + defaultWhereClause.push(Q.experimentalSortBy(`${ this.useRealName ? 'fname' : 'name' }`, Q.asc)); + } else { + defaultWhereClause.push(Q.experimentalSortBy('room_updated_at', Q.desc)); + } + + // When we're grouping by something + if (this.isGrouping) { + observable = await db.collections + .get('subscriptions') + .query(...defaultWhereClause) + .observe(); + + // When we're NOT grouping + } else { + this.count += QUERY_SIZE; + observable = await db.collections + .get('subscriptions') + .query( + ...defaultWhereClause, + Q.experimentalSkip(0), + Q.experimentalTake(this.count) + ) + .observe(); + } + this.querySubscription = observable.subscribe((data) => { let tempChats = []; - let chats = []; - if (sortBy === 'alphabetical') { - chats = orderBy(data, [`${ this.useRealName ? 'fname' : 'name' }`], ['asc']); - } else { - chats = orderBy(data, ['roomUpdatedAt'], ['desc']); - } + let chats = data; - // it's better to map and test all subs altogether then testing them individually - const allChats = data.map(item => ({ - alert: item.alert, - unread: item.unread, - userMentions: item.userMentions, - isRead: this.getIsRead(item), - favorite: item.f, - lastMessage: item.lastMessage, - name: this.getRoomTitle(item), - _updatedAt: item.roomUpdatedAt, - key: item._id, - rid: item.rid, - type: item.t, - prid: item.prid, - uids: item.uids, - usernames: item.usernames, - visitor: item.visitor - })); + /** + * We trigger re-render only when chats order changes + * RoomItem handles its own re-render + */ + const chatsOrder = data.map(item => item.rid); // unread if (showUnread) { @@ -484,12 +483,18 @@ class RoomsListView extends React.Component { this.internalSetState({ chats: tempChats, - allChats, + chatsOrder, loading: false }); }); } + unsubscribeQuery = () => { + if (this.querySubscription && this.querySubscription.unsubscribe) { + this.querySubscription.unsubscribe(); + } + } + initSearching = () => { const { openSearchHeader } = this.props; this.internalSetState({ searching: true }, () => { @@ -548,10 +553,19 @@ class RoomsListView extends React.Component { getRoomAvatar = item => RocketChat.getRoomAvatar(item) + isGroupChat = item => RocketChat.isGroupChat(item) + + isRead = item => RocketChat.isRead(item) + getUserPresence = uid => RocketChat.getUserPresence(uid) getUidDirectMessage = room => RocketChat.getUidDirectMessage(room); + get isGrouping() { + const { showUnread, showFavorites, groupByType } = this.props; + return showUnread || showFavorites || groupByType; + } + onPressItem = (item = {}) => { const { navigation, isMasterDetail } = this.props; if (!navigation.isFocused()) { @@ -743,6 +757,13 @@ class RoomsListView extends React.Component { roomsRequest({ allData: true }); } + onEndReached = () => { + // Run only when we're not grouping by anything + if (!this.isGrouping) { + this.getSubscriptions(); + } + } + getScrollRef = ref => (this.scroll = ref); renderListHeader = () => { @@ -774,12 +795,6 @@ class RoomsListView extends React.Component { ); } - getIsRead = (item) => { - let isUnread = item.archived !== true && item.open === true; // item is not archived and not opened - isUnread = isUnread && (item.unread > 0 || item.alert === true); // either its unread count > 0 or its alert - return !isUnread; - }; - renderItem = ({ item }) => { if (item.separator) { return this.renderSectionHeader(item.rid); @@ -800,32 +815,19 @@ class RoomsListView extends React.Component { width } = this.props; const id = this.getUidDirectMessage(item); - const isGroupChat = RocketChat.isGroupChat(item); return ( this.onPressItem(item)} + onPress={this.onPressItem} testID={`rooms-list-view-item-${ item.name }`} width={isMasterDetail ? MAX_SIDEBAR_WIDTH : width} toggleFav={this.toggleFav} @@ -833,7 +835,10 @@ class RoomsListView extends React.Component { hideChannel={this.hideChannel} useRealName={useRealName} getUserPresence={this.getUserPresence} - isGroupChat={isGroupChat} + getRoomTitle={this.getRoomTitle} + getRoomAvatar={this.getRoomAvatar} + getIsGroupChat={this.isGroupChat} + getIsRead={this.isRead} visitor={item.visitor} isFocused={currentItem?.rid === item.rid} /> @@ -880,6 +885,8 @@ class RoomsListView extends React.Component { /> )} windowSize={9} + onEndReached={this.onEndReached} + onEndReachedThreshold={0.5} /> ); }; diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js index 335b1c7f5..17bddd4f8 100644 --- a/app/views/SettingsView/index.js +++ b/app/views/SettingsView/index.js @@ -38,7 +38,7 @@ import { appStart as appStartAction, ROOT_LOADING } from '../../actions/app'; import { onReviewPress } from '../../utils/review'; import { getUserSelector } from '../../selectors/login'; import SafeAreaView from '../../containers/SafeAreaView'; -import { isGooglePlayBuild } from '../../constants/environment'; +import { isFDroidBuild } from '../../constants/environment'; const SectionSeparator = React.memo(({ theme }) => ( @@ -121,15 +121,14 @@ class SettingsView extends React.Component { AsyncStorage.setItem(CRASH_REPORT_KEY, JSON.stringify(value)); const { toggleCrashReport } = this.props; toggleCrashReport(value); - loggerConfig.autoNotify = value; - if (isGooglePlayBuild) { + if (!isFDroidBuild) { + loggerConfig.autoNotify = value; analytics().setAnalyticsCollectionEnabled(value); - } - - if (value) { - loggerConfig.clearBeforeSendCallbacks(); - } else { - loggerConfig.registerBeforeSendCallback(() => false); + if (value) { + loggerConfig.clearBeforeSendCallbacks(); + } else { + loggerConfig.registerBeforeSendCallback(() => false); + } } } @@ -162,7 +161,7 @@ class SettingsView extends React.Component { shareApp = () => { // eslint-disable-next-line no-nested-ternary - Share.share({ message: isAndroid ? (isGooglePlayBuild ? PLAY_MARKET_LINK : FDROID_MARKET_LINK) : APP_STORE_LINK }); + Share.share({ message: isAndroid ? (!isFDroidBuild ? PLAY_MARKET_LINK : FDROID_MARKET_LINK) : APP_STORE_LINK }); } copyServerVersion = () => { @@ -258,7 +257,7 @@ class SettingsView extends React.Component { theme={theme} /> - {isGooglePlayBuild ? ( + {!isFDroidBuild ? ( <> ) : null} - {isGooglePlayBuild ? ( + {!isFDroidBuild ? ( <> ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); const keyExtractor = item => item.rid; @@ -47,7 +46,7 @@ class ShareListView extends React.Component { constructor(props) { super(props); - this.data = []; + this.chats = []; this.state = { searching: false, searchText: '', @@ -186,22 +185,36 @@ class ShareListView extends React.Component { this.setState(...args); } - getSubscriptions = async(server) => { + query = (text) => { const db = database.active; + const defaultWhereClause = [ + Q.where('archived', false), + Q.where('open', true), + Q.experimentalSkip(0), + Q.experimentalTake(50), + Q.experimentalSortBy('room_updated_at', Q.desc) + ]; + if (text) { + return db.collections + .get('subscriptions') + .query( + ...defaultWhereClause, + Q.or( + Q.where('name', Q.like(`%${ Q.sanitizeLikeString(text) }%`)), + Q.where('fname', Q.like(`%${ Q.sanitizeLikeString(text) }%`)) + ) + ).fetch(); + } + return db.collections.get('subscriptions').query(...defaultWhereClause).fetch(); + } + + getSubscriptions = async(server) => { const serversDB = database.servers; if (server) { - this.data = await db.collections - .get('subscriptions') - .query( - Q.where('archived', false), - Q.where('open', true) - ).fetch(); - this.data = orderBy(this.data, ['roomUpdatedAt'], ['desc']); - + this.chats = await this.query(); const serversCollection = serversDB.collections.get('servers'); this.servers = await serversCollection.query().fetch(); - this.chats = this.data.slice(0, LIMIT); let serverInfo = {}; try { serverInfo = await serversCollection.find(server); @@ -210,8 +223,8 @@ class ShareListView extends React.Component { } this.internalSetState({ - chats: this.chats ? this.chats.slice() : [], - servers: this.servers ? this.servers.slice() : [], + chats: this.chats ?? [], + servers: this.servers ?? [], loading: false, serverInfo }); @@ -253,10 +266,10 @@ class ShareListView extends React.Component { }); } - search = (text) => { - const result = this.data.filter(item => item.name.includes(text)) || []; + search = async(text) => { + const result = await this.query(text); this.internalSetState({ - searchResults: result.slice(0, LIMIT), + searchResults: result, searchText: text }); } @@ -297,9 +310,26 @@ class ShareListView extends React.Component { } renderItem = ({ item }) => { + const { serverInfo } = this.state; + const { useRealName } = serverInfo; const { userId, token, server, theme } = this.props; + let description; + switch (item.t) { + case 'c': + description = item.topic || item.description; + break; + case 'p': + description = item.topic || item.description; + break; + case 'd': + description = useRealName ? item.name : item.fname; + break; + default: + description = item.fname; + break; + } return ( this.shareMessage(item)} testID={`share-extension-item-${ item.name }`} diff --git a/app/views/StatusView.js b/app/views/StatusView.js index 2c91fa294..5676e0bb0 100644 --- a/app/views/StatusView.js +++ b/app/views/StatusView.js @@ -24,16 +24,16 @@ import SafeAreaView from '../containers/SafeAreaView'; const STATUS = [{ id: 'online', - name: I18n.t('Online') + name: 'Online' }, { id: 'busy', - name: I18n.t('Busy') + name: 'Busy' }, { id: 'away', - name: I18n.t('Away') + name: 'Away' }, { id: 'offline', - name: I18n.t('Invisible') + name: 'Invisible' }]; const styles = StyleSheet.create({ @@ -164,7 +164,7 @@ class StatusView extends React.Component { const { id, name } = item; return ( { if (user.status !== item.id) { try { diff --git a/app/views/ThemeView.js b/app/views/ThemeView.js index d8cd96902..f6c4f96cb 100644 --- a/app/views/ThemeView.js +++ b/app/views/ThemeView.js @@ -21,28 +21,28 @@ const THEME_GROUP = 'THEME_GROUP'; const DARK_GROUP = 'DARK_GROUP'; const SYSTEM_THEME = { - label: I18n.t('Automatic'), + label: 'Automatic', value: 'automatic', group: THEME_GROUP }; const THEMES = [ { - label: I18n.t('Light'), + label: 'Light', value: 'light', group: THEME_GROUP }, { - label: I18n.t('Dark'), + label: 'Dark', value: 'dark', group: THEME_GROUP }, { - label: I18n.t('Dark'), + label: 'Dark', value: 'dark', separator: true, - header: I18n.t('Dark_level'), + header: 'Dark_level', group: DARK_GROUP }, { - label: I18n.t('Black'), + label: 'Black', value: 'black', group: DARK_GROUP } @@ -129,7 +129,7 @@ class ThemeView extends React.Component { <> {item.separator || isFirst ? this.renderSectionHeader(item.header) : null} this.onClick(item)} testID={`theme-view-${ value }`} right={this.isSelected(item) ? this.renderIcon : null} @@ -139,12 +139,12 @@ class ThemeView extends React.Component { ); } - renderSectionHeader = (header = I18n.t('Theme')) => { + renderSectionHeader = (header = 'Theme') => { const { theme } = this.props; return ( <> - {header} + {I18n.t(header)} {this.renderSeparator()} @@ -169,7 +169,7 @@ class ThemeView extends React.Component { item.value} + keyExtractor={item => item.value + item.group} contentContainerStyle={[ styles.list, { borderColor: themes[theme].separatorColor } diff --git a/e2e/data.js b/e2e/data.js index 4c4c8ca16..d3f4a0df9 100644 --- a/e2e/data.js +++ b/e2e/data.js @@ -9,34 +9,39 @@ const data = { regular: { username: `userone${ value }`, password: '123', - email: `diego.mello+regular${ value }@rocket.chat` + email: `mobile+regular${ value }@rocket.chat` }, alternate: { username: `usertwo${ value }`, password: '123', - email: `diego.mello+alternate${ value }@rocket.chat`, + email: `mobile+alternate${ value }@rocket.chat`, totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ' }, profileChanges: { username: `userthree${ value }`, password: '123', - email: `diego.mello+profileChanges${ value }@rocket.chat` + email: `mobile+profileChanges${ value }@rocket.chat` }, existing: { username: `existinguser${ value }`, password: '123', - email: `diego.mello+existing${ value }@rocket.chat` + email: `mobile+existing${ value }@rocket.chat` } }, channels: { - public: { + detoxpublic: { name: 'detox-public' } }, + groups: { + private: { + name: `detox-private-${ value }` + } + }, registeringUser: { username: `newuser${ value }`, password: `password${ value }`, - email: `diego.mello+registering${ value }@rocket.chat` + email: `mobile+registering${ value }@rocket.chat` }, random: value } diff --git a/e2e/data/data.cloud.js b/e2e/data/data.cloud.js index 4c4c8ca16..d3f4a0df9 100644 --- a/e2e/data/data.cloud.js +++ b/e2e/data/data.cloud.js @@ -9,34 +9,39 @@ const data = { regular: { username: `userone${ value }`, password: '123', - email: `diego.mello+regular${ value }@rocket.chat` + email: `mobile+regular${ value }@rocket.chat` }, alternate: { username: `usertwo${ value }`, password: '123', - email: `diego.mello+alternate${ value }@rocket.chat`, + email: `mobile+alternate${ value }@rocket.chat`, totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ' }, profileChanges: { username: `userthree${ value }`, password: '123', - email: `diego.mello+profileChanges${ value }@rocket.chat` + email: `mobile+profileChanges${ value }@rocket.chat` }, existing: { username: `existinguser${ value }`, password: '123', - email: `diego.mello+existing${ value }@rocket.chat` + email: `mobile+existing${ value }@rocket.chat` } }, channels: { - public: { + detoxpublic: { name: 'detox-public' } }, + groups: { + private: { + name: `detox-private-${ value }` + } + }, registeringUser: { username: `newuser${ value }`, password: `password${ value }`, - email: `diego.mello+registering${ value }@rocket.chat` + email: `mobile+registering${ value }@rocket.chat` }, random: value } diff --git a/e2e/data/data.docker.js b/e2e/data/data.docker.js index 5a7ed505c..a22423a63 100644 --- a/e2e/data/data.docker.js +++ b/e2e/data/data.docker.js @@ -9,34 +9,39 @@ const data = { regular: { username: `userone${ value }`, password: '123', - email: `diego.mello+regular${ value }@rocket.chat` + email: `mobile+regular${ value }@rocket.chat` }, alternate: { username: `usertwo${ value }`, password: '123', - email: `diego.mello+alternate${ value }@rocket.chat`, + email: `mobile+alternate${ value }@rocket.chat`, totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ' }, profileChanges: { username: `userthree${ value }`, password: '123', - email: `diego.mello+profileChanges${ value }@rocket.chat` + email: `mobile+profileChanges${ value }@rocket.chat` }, existing: { username: `existinguser${ value }`, password: '123', - email: `diego.mello+existing${ value }@rocket.chat` + email: `mobile+existing${ value }@rocket.chat` } }, channels: { - public: { + detoxpublic: { name: 'detox-public' } }, + groups: { + private: { + name: `detox-private-${ value }` + } + }, registeringUser: { username: `newuser${ value }`, password: `password${ value }`, - email: `diego.mello+registering${ value }@rocket.chat` + email: `mobile+registering${ value }@rocket.chat` }, random: value } diff --git a/e2e/docker/controlRCDemoEnv.sh b/e2e/docker/controlRCDemoEnv.sh index 16dc49a7a..527d837e3 100755 --- a/e2e/docker/controlRCDemoEnv.sh +++ b/e2e/docker/controlRCDemoEnv.sh @@ -42,7 +42,7 @@ if [ "$COMMAND" == "start" ]; then MAX_ATTEMPTS=60 while [ $ATTEMPT_NUMBER -lt $MAX_ATTEMPTS ]; do # https://stackoverflow.com/a/21189312/399007 ATTEMPT_NUMBER=$((ATTEMPT_NUMBER + 1 )) - echo "Waiting for server to be up ($ATTEMPT_NUMBER of $MAX_ATTEMPTS)" + echo "Checking if servers are ready (attempt $ATTEMPT_NUMBER of $MAX_ATTEMPTS)" LOGS=$(docker logs rc_test_env_rocketchat_1 2> /dev/null) if grep -q 'SERVER RUNNING' <<< $LOGS ; then echo "RocketChat is ready!" diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js index 88ef3d5ad..92feb61d3 100644 --- a/e2e/helpers/app.js +++ b/e2e/helpers/app.js @@ -31,7 +31,6 @@ async function login(username, password) { await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000); await element(by.id('login-view-email')).replaceText(username); await element(by.id('login-view-password')).replaceText(password); - await sleep(300); await element(by.id('login-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); } @@ -61,6 +60,33 @@ async function mockMessage(message) { await element(by.label(`${ data.random }${ message }`)).atIndex(0).tap(); }; +async function starMessage(message){ + const messageLabel = `${ data.random }${ message }` + await waitFor(element(by.label(messageLabel))).toBeVisible().withTimeout(5000); + await element(by.label(messageLabel)).atIndex(0).longPress(); + await expect(element(by.id('action-sheet'))).toExist(); + await expect(element(by.id('action-sheet-handle'))).toBeVisible(); + await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); + await element(by.label('Star')).tap(); + await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000); +}; + +async function pinMessage(message){ + const messageLabel = `${ data.random }${ message }` + await waitFor(element(by.label(messageLabel)).atIndex(0)).toExist(); + await element(by.label(messageLabel)).atIndex(0).longPress(); + await expect(element(by.id('action-sheet'))).toExist(); + await expect(element(by.id('action-sheet-handle'))).toBeVisible(); + await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); + await element(by.label('Pin')).tap(); + await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000); +} + +async function dismissReviewNag(){ + await waitFor(element(by.text('Are you enjoying this app?'))).toExist().withTimeout(60000); + await element(by.label('No').and(by.type('_UIAlertControllerActionView'))).tap(); // Tap `no` on ask for review alert +} + async function tapBack() { await element(by.id('header-back')).atIndex(0).tap(); } @@ -74,7 +100,22 @@ async function searchRoom(room) { await expect(element(by.id('rooms-list-view-search-input'))).toExist(); await waitFor(element(by.id('rooms-list-view-search-input'))).toExist().withTimeout(5000); await element(by.id('rooms-list-view-search-input')).typeText(room); - await sleep(2000); +} + +async function tryTapping(theElement, timeout, longtap = false){ + try { + if(longtap){ + await theElement.longPress() + } else { + await theElement.tap() + } + } catch(e) { + if(timeout <= 0){ //TODO: Maths. How closely has the timeout been honoured here? + throw e + } + await sleep(100) + await tryTapping(theElement, timeout - 100) + } } module.exports = { @@ -84,7 +125,11 @@ module.exports = { login, logout, mockMessage, + starMessage, + pinMessage, + dismissReviewNag, tapBack, sleep, - searchRoom + searchRoom, + tryTapping }; \ No newline at end of file diff --git a/e2e/helpers/data_setup.js b/e2e/helpers/data_setup.js index 84e5927ea..d16b41a55 100644 --- a/e2e/helpers/data_setup.js +++ b/e2e/helpers/data_setup.js @@ -38,7 +38,7 @@ const createUser = async (username, password, name, email) => { } const createChannelIfNotExists = async (channelname) => { - console.log(`Creating channel ${channelname}`) + console.log(`Creating public channel ${channelname}`) try { await rocketchat.post('channels.create', { "name": channelname @@ -49,7 +49,24 @@ const createChannelIfNotExists = async (channelname) => { } catch (infoError) { console.log(JSON.stringify(createError)) console.log(JSON.stringify(infoError)) - throw "Failed to find or create channel" + throw "Failed to find or create public channel" + } + } +} + +const createGroupIfNotExists = async (groupname) => { + console.log(`Creating private group ${groupname}`) + try { + await rocketchat.post('groups.create', { + "name": groupname + }) + } catch (createError) { + try { //Maybe it exists already? + await rocketchat.get(`group.info?roomName=${groupname}`) + } catch (infoError) { + console.log(JSON.stringify(createError)) + console.log(JSON.stringify(infoError)) + throw "Failed to find or create private group" } } } @@ -71,6 +88,15 @@ const setup = async () => { } } + await login(data.users.regular.username, data.users.regular.password) + + for (var groupKey in data.groups) { + if (data.groups.hasOwnProperty(groupKey)) { + const group = data.groups[groupKey] + await createGroupIfNotExists(group.name) + } + } + return } diff --git a/e2e/tests/assorted/01-changeserver.spec.js b/e2e/tests/assorted/01-changeserver.spec.js index 0664970a7..7210d7060 100644 --- a/e2e/tests/assorted/01-changeserver.spec.js +++ b/e2e/tests/assorted/01-changeserver.spec.js @@ -9,12 +9,12 @@ const checkServer = async(server) => { await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.label(label))).toBeVisible().withTimeout(60000); - await expect(element(by.label(label))).toBeVisible(); await element(by.id('sidebar-close-drawer')).tap(); } describe('Change server', () => { before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); @@ -28,8 +28,6 @@ describe('Change server', () => { await sleep(5000); await element(by.id('rooms-list-header-server-dropdown-button')).tap(); await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000); - await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist(); - await sleep(1000); await element(by.id('rooms-list-header-server-add')).tap(); // TODO: refactor @@ -37,19 +35,16 @@ describe('Change server', () => { await element(by.id('new-server-view-input')).replaceText(data.alternateServer); await element(by.id('new-server-view-button')).tap(); await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('workspace-view'))).toBeVisible(); await element(by.id('workspace-view-register')).tap(); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('register-view'))).toBeVisible(); + // Register new user await element(by.id('register-view-name')).replaceText(data.registeringUser.username); await element(by.id('register-view-username')).replaceText(data.registeringUser.username); await element(by.id('register-view-email')).replaceText(data.registeringUser.email); await element(by.id('register-view-password')).replaceText(data.registeringUser.password); - await sleep(1000); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); // For a sanity test, to make sure roomslist is showing correct rooms // app CANNOT show public room created on previous tests @@ -59,11 +54,8 @@ describe('Change server', () => { }); it('should change back', async() => { - await sleep(5000); await element(by.id('rooms-list-header-server-dropdown-button')).tap(); await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000); - await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist(); - await sleep(1000); await element(by.id(`rooms-list-header-server-${ data.server }`)).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await checkServer(data.server); diff --git a/e2e/tests/assorted/02-broadcast.spec.js b/e2e/tests/assorted/02-broadcast.spec.js index 1b2fcc1db..9e87c5e14 100644 --- a/e2e/tests/assorted/02-broadcast.spec.js +++ b/e2e/tests/assorted/02-broadcast.spec.js @@ -23,28 +23,16 @@ describe('Broadcast room', () => { await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000); await element(by.id('select-users-view-search')).replaceText(otheruser.username); await waitFor(element(by.id(`select-users-view-item-${ otheruser.username }`))).toBeVisible().withTimeout(60000); - await expect(element(by.id(`select-users-view-item-${ otheruser.username }`))).toBeVisible(); await element(by.id(`select-users-view-item-${ otheruser.username }`)).tap(); await waitFor(element(by.id(`selected-user-${ otheruser.username }`))).toBeVisible().withTimeout(5000); - await sleep(1000); await element(by.id('selected-users-view-submit')).tap(); - await sleep(1000); await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000); await element(by.id('create-channel-name')).replaceText(`broadcast${ data.random }`); - await sleep(2000); - await element(by.id('create-channel-broadcast')).tap(); - if (device.getPlatform() === 'ios') { //Because this tap is FLAKY on iOS - await expect(element(by.id('create-channel-broadcast'))).toHaveValue('1') - } - await sleep(500); + await element(by.id('create-channel-broadcast')).longPress(); //https://github.com/facebook/react-native/issues/28032 await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('room-view'))).toBeVisible(); await waitFor(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible().withTimeout(60000); - await expect(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible(); - await sleep(1000); await element(by.id('room-view-header-actions')).tap(); - await sleep(1000); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000); await element(by.id('room-actions-info')).tap(); await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000); @@ -64,25 +52,19 @@ describe('Broadcast room', () => { it('should login as user without write message authorization and enter room', async() => { await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await navigateToLogin(); - await element(by.id('login-view-email')).replaceText(otheruser.username); - await element(by.id('login-view-password')).replaceText(otheruser.password); - await sleep(1000); - await element(by.id('login-view-submit')).tap(); + await login(otheruser.username, otheruser.password); + //await waitFor(element(by.id('two-factor'))).toBeVisible().withTimeout(5000); //await expect(element(by.id('two-factor'))).toBeVisible(); //const code = GA.gen(data.alternateUserTOTPSecret); //await element(by.id('two-factor-input')).replaceText(code); - //await sleep(1000); //await element(by.id('two-factor-send')).tap(); - await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); + await searchRoom(`broadcast${ data.random }`); await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000); - await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist(); await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000); await waitFor(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible().withTimeout(60000); - await expect(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible(); - await sleep(1000); }); it('should not have messagebox', async() => { @@ -95,7 +77,6 @@ describe('Broadcast room', () => { it('should have the message created earlier', async() => { await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible(); }); it('should have reply button', async() => { @@ -104,9 +85,7 @@ describe('Broadcast room', () => { it('should tap on reply button and navigate to direct room', async() => { await element(by.id('message-broadcast-reply')).tap(); - await sleep(1000); await waitFor(element(by.id(`room-view-title-${ testuser.username }`))).toBeVisible().withTimeout(5000); - await expect(element(by.id(`room-view-title-${ testuser.username }`))).toBeVisible(); }); it('should reply broadcasted message', async() => { diff --git a/e2e/tests/assorted/03-profile.spec.js b/e2e/tests/assorted/03-profile.spec.js index e5c321757..9700da1b2 100644 --- a/e2e/tests/assorted/03-profile.spec.js +++ b/e2e/tests/assorted/03-profile.spec.js @@ -13,7 +13,7 @@ async function waitForToast() { // await expect(element(by.id('toast'))).toBeVisible(); // await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000); // await expect(element(by.id('toast'))).toBeNotVisible(); - await sleep(5000); + await sleep(1); } describe('Profile screen', () => { @@ -24,7 +24,6 @@ describe('Profile screen', () => { await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-profile'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('sidebar-profile'))).toBeVisible(); await element(by.id('sidebar-profile')).tap(); await waitFor(element(by.id('profile-view'))).toBeVisible().withTimeout(2000); }); @@ -60,22 +59,18 @@ describe('Profile screen', () => { it('should have reset avatar button', async() => { await waitFor(element(by.id('profile-view-reset-avatar'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down'); - await expect(element(by.id('profile-view-reset-avatar'))).toExist(); }); it('should have upload avatar button', async() => { await waitFor(element(by.id('profile-view-upload-avatar'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down'); - await expect(element(by.id('profile-view-upload-avatar'))).toExist(); }); it('should have avatar url button', async() => { await waitFor(element(by.id('profile-view-avatar-url-button'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down'); - await expect(element(by.id('profile-view-avatar-url-button'))).toExist(); }); it('should have submit button', async() => { await waitFor(element(by.id('profile-view-submit'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down'); - await expect(element(by.id('profile-view-submit'))).toExist(); }); }); @@ -84,15 +79,13 @@ describe('Profile screen', () => { await element(by.type('UIScrollView')).atIndex(1).swipe('down'); await element(by.id('profile-view-name')).replaceText(`${ profileChangeUser.username }new`); await element(by.id('profile-view-username')).replaceText(`${ profileChangeUser.username }new`); - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - await sleep(1000); await element(by.id('profile-view-submit')).tap(); await waitForToast(); }); it('should change email and password', async() => { - await element(by.id('profile-view-email')).replaceText(`diego.mello+profileChangesNew${ data.random }@rocket.chat`); + await element(by.id('profile-view-email')).replaceText(`mobile+profileChangesNew${ data.random }@rocket.chat`); await element(by.id('profile-view-new-password')).replaceText(`${ profileChangeUser.password }new`); await element(by.id('profile-view-submit')).tap(); await element(by.type('_UIAlertControllerTextField')).replaceText(`${ profileChangeUser.password }`) @@ -103,7 +96,6 @@ describe('Profile screen', () => { it('should reset avatar', async() => { await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - await sleep(1000); await element(by.id('profile-view-reset-avatar')).tap(); await waitForToast(); }); diff --git a/e2e/tests/assorted/04-setting.spec.js b/e2e/tests/assorted/04-setting.spec.js index c2cfae6f1..951ccc70d 100644 --- a/e2e/tests/assorted/04-setting.spec.js +++ b/e2e/tests/assorted/04-setting.spec.js @@ -1,12 +1,18 @@ const { device, expect, element, by, waitFor } = require('detox'); +const { navigateToLogin, login } = require('../../helpers/app'); + +const data = require('../../data'); + +const testuser = data.users.regular describe('Settings screen', () => { before(async() => { - await device.launchApp({ newInstance: true }); + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(testuser.username, testuser.password); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000); diff --git a/e2e/tests/assorted/05-joinpublicroom.spec.js b/e2e/tests/assorted/05-joinpublicroom.spec.js index d68d1a955..1c946f0c2 100644 --- a/e2e/tests/assorted/05-joinpublicroom.spec.js +++ b/e2e/tests/assorted/05-joinpublicroom.spec.js @@ -2,12 +2,12 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app'); -const room = 'detox-public'; +const testuser = data.users.regular +const room = data.channels.detoxpublic.name; async function navigateToRoom() { - await sleep(2000); await searchRoom(room); await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toBeVisible().withTimeout(60000); await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap(); @@ -15,15 +15,15 @@ async function navigateToRoom() { } async function navigateToRoomActions() { - await sleep(2000); await element(by.id('room-view-header-actions')).tap(); - await sleep(2000); await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000); } describe('Join public room', () => { before(async() => { - await device.launchApp({ newInstance: true }); + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(testuser.username, testuser.password); await navigateToRoom(); }); @@ -167,9 +167,7 @@ describe('Join public room', () => { await element(by.text('Yes, leave it!')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); // await element(by.id('rooms-list-view-search')).typeText(''); - await sleep(2000); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible(); }); }); }); diff --git a/e2e/tests/assorted/06-status.spec.js b/e2e/tests/assorted/06-status.spec.js index ee592d41f..887dcb1e0 100644 --- a/e2e/tests/assorted/06-status.spec.js +++ b/e2e/tests/assorted/06-status.spec.js @@ -1,14 +1,21 @@ const { expect, element, by, waitFor } = require('detox'); -const { sleep } = require('../../helpers/app'); +const { navigateToLogin, login, sleep } = require('../../helpers/app'); + +const data = require('../../data'); +const testuser = data.users.regular async function waitForToast() { - await sleep(5000); + await sleep(1); } describe('Status screen', () => { - before(async() => { + before(async () => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(testuser.username, testuser.password); + await element(by.id('rooms-list-view-sidebar')).tap(); await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('sidebar-custom-status'))).toBeVisible().withTimeout(2000); @@ -17,30 +24,27 @@ describe('Status screen', () => { await waitFor(element(by.id('status-view'))).toBeVisible().withTimeout(2000); }); - describe('Render', async() => { - it('should have status input', async() => { + describe('Render', async () => { + it('should have status input', async () => { await expect(element(by.id('status-view-input'))).toBeVisible(); await expect(element(by.id('status-view-online'))).toExist(); await expect(element(by.id('status-view-busy'))).toExist(); await expect(element(by.id('status-view-away'))).toExist(); await expect(element(by.id('status-view-offline'))).toExist(); - }); - }); - - describe('Usage', async() => { - it('should change status', async() => { - await sleep(1000); - await element(by.id('status-view-busy')).tap(); - await sleep(1000); - await expect(element(by.id('status-view-current-busy'))).toExist(); - }); + }); + }); - it('should change status text', async() => { + describe('Usage', async () => { + it('should change status', async () => { + await element(by.id('status-view-busy')).tap(); + await expect(element(by.id('status-view-current-busy'))).toExist(); + }); + + it('should change status text', async () => { await element(by.id('status-view-input')).replaceText('status-text-new'); - await sleep(1000); await element(by.id('status-view-submit')).tap(); await waitForToast(); await waitFor(element(by.label('status-text-new').withAncestor(by.id('sidebar-custom-status')))).toBeVisible().withTimeout(2000); - }); - }); -}); + }); + }); +}); \ No newline at end of file diff --git a/e2e/tests/init.js b/e2e/tests/init.js index bef12f6d3..9eaa721b6 100644 --- a/e2e/tests/init.js +++ b/e2e/tests/init.js @@ -1,11 +1,21 @@ const detox = require('detox'); const config = require('../../package.json').detox; const dataSetup = require('../helpers/data_setup') +const adapter = require('detox/runners/mocha/adapter'); before(async() => { - await dataSetup() - await detox.init(config, { launchApp: false }); - await device.launchApp({ permissions: { notifications: 'YES' } }); + await Promise.all([dataSetup(), detox.init(config, { launchApp: false })]) + //await dataSetup() + //await detox.init(config, { launchApp: false }); + //await device.launchApp({ permissions: { notifications: 'YES' } }); +}); + +beforeEach(async function() { + await adapter.beforeEach(this); +}); + +afterEach(async function() { + await adapter.afterEach(this); }); after(async() => { diff --git a/e2e/tests/onboarding/01-onboarding.spec.js b/e2e/tests/onboarding/01-onboarding.spec.js index c84928d60..9c049099e 100644 --- a/e2e/tests/onboarding/01-onboarding.spec.js +++ b/e2e/tests/onboarding/01-onboarding.spec.js @@ -31,7 +31,6 @@ describe('Onboarding', () => { it('should navigate to join a workspace', async() => { await element(by.id('join-workspace')).tap(); await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('new-server-view'))).toBeVisible(); }); it('should enter an invalid server and get error', async() => { @@ -39,14 +38,12 @@ describe('Onboarding', () => { await element(by.id('new-server-view-button')).tap(); const errorText = 'Oops!'; await waitFor(element(by.text(errorText))).toBeVisible().withTimeout(60000); - await expect(element(by.text(errorText))).toBeVisible(); await element(by.text('OK')).tap(); }); it('should tap on "Join our open workspace" and navigate', async() => { await element(by.id('new-server-view-open')).tap(); await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('workspace-view'))).toBeVisible(); }); it('should enter a valid server without login services and navigate to login', async() => { @@ -57,7 +54,6 @@ describe('Onboarding', () => { await element(by.id('new-server-view-input')).replaceText(data.server); await element(by.id('new-server-view-button')).tap(); await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('workspace-view'))).toBeVisible(); }); }); }); diff --git a/e2e/tests/onboarding/02-legal.spec.js b/e2e/tests/onboarding/02-legal.spec.js index e8109ece5..191e0647c 100644 --- a/e2e/tests/onboarding/02-legal.spec.js +++ b/e2e/tests/onboarding/02-legal.spec.js @@ -4,54 +4,62 @@ const { const { navigateToRegister, navigateToLogin } = require('../../helpers/app'); describe('Legal screen', () => { - it('should have legal button on login', async() => { - await device.launchApp({ newInstance: true }); - await navigateToLogin(); - await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('login-view-more'))).toBeVisible(); - }); - it('should navigate to legal from login', async() => { - await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000); - await element(by.id('login-view-more')).tap(); - }); - - it('should have legal button on register', async() => { - await device.launchApp({ newInstance: true }); - await navigateToRegister(); - await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('register-view-more'))).toBeVisible(); - }); - - it('should navigate to legal from register', async() => { - await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000); - await element(by.id('register-view-more')).tap(); - }); - - it('should have legal screen', async() => { - await expect(element(by.id('legal-view'))).toBeVisible(); - }); - - it('should have terms of service button', async() => { - await expect(element(by.id('legal-terms-button'))).toBeVisible(); - }); - - it('should have privacy policy button', async() => { - await expect(element(by.id('legal-privacy-button'))).toBeVisible(); - }); + describe('From Login', () => { + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + }); - - // We can't simulate how webview behaves, so I had to disable :( - // it('should navigate to terms', async() => { - // await element(by.id('legal-terms-button')).tap(); - // await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000); - // await expect(element(by.id('terms-view'))).toBeVisible(); - // }); + it('should have legal button on login', async() => { + await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000); + }); - // it('should navigate to privacy', async() => { - // await tapBack(); - // await element(by.id('legal-privacy-button')).tap(); - // await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000); - // await expect(element(by.id('privacy-view'))).toBeVisible(); - // }); + it('should navigate to legal from login', async() => { + await expect(element(by.id('login-view-more'))).toBeVisible(); + await element(by.id('login-view-more')).tap(); + await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(4000) + }); + }); + + describe('From Register', () => { + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToRegister(); + }); + + it('should have legal button on register', async() => { + await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000); + }); + + it('should navigate to legal from register', async() => { + await expect(element(by.id('register-view-more'))).toBeVisible(); + await element(by.id('register-view-more')).tap(); + await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(4000); + }); + + it('should have terms of service button', async() => { + await expect(element(by.id('legal-terms-button'))).toBeVisible(); + }); + + it('should have privacy policy button', async() => { + await expect(element(by.id('legal-privacy-button'))).toBeVisible(); + }); + + // We can't simulate how webview behaves, so I had to disable :( + /* + it('should navigate to terms', async() => { + await element(by.id('legal-terms-button')).tap(); + await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.id('terms-view'))).toBeVisible(); + }); + + it('should navigate to privacy', async() => { + await tapBack(); + await element(by.id('legal-privacy-button')).tap(); + await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000); + await expect(element(by.id('privacy-view'))).toBeVisible(); + }); + */ + }); }); diff --git a/e2e/tests/onboarding/03-forgotpassword.spec.js b/e2e/tests/onboarding/03-forgotpassword.spec.js index 2fa710469..88d4c3e90 100644 --- a/e2e/tests/onboarding/03-forgotpassword.spec.js +++ b/e2e/tests/onboarding/03-forgotpassword.spec.js @@ -32,7 +32,6 @@ describe('Forgot password screen', () => { await element(by.id('forgot-password-view-submit')).tap(); await element(by.text('OK')).tap(); await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('login-view'))).toBeVisible(); }); }); }); diff --git a/e2e/tests/onboarding/04-createuser.spec.js b/e2e/tests/onboarding/04-createuser.spec.js index 6abd8fc16..aa75807f2 100644 --- a/e2e/tests/onboarding/04-createuser.spec.js +++ b/e2e/tests/onboarding/04-createuser.spec.js @@ -53,10 +53,8 @@ describe('Create user screen', () => { await element(by.id('register-view-username')).replaceText(data.registeringUser.username); await element(by.id('register-view-email')).replaceText(data.users.existing.email); await element(by.id('register-view-password')).replaceText(data.registeringUser.password); - await sleep(300); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.text('Email already exists. [403]')).atIndex(0)).toExist().withTimeout(10000); - await expect(element(by.text('Email already exists. [403]')).atIndex(0)).toExist(); await element(by.text('OK')).tap(); }); @@ -65,10 +63,8 @@ describe('Create user screen', () => { await element(by.id('register-view-username')).replaceText(data.users.existing.username); await element(by.id('register-view-email')).replaceText(data.registeringUser.email); await element(by.id('register-view-password')).replaceText(data.registeringUser.password); - await sleep(300); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.text('Username is already in use')).atIndex(0)).toExist().withTimeout(10000); - await expect(element(by.text('Username is already in use')).atIndex(0)).toExist(); await element(by.text('OK')).tap(); }); @@ -77,10 +73,8 @@ describe('Create user screen', () => { await element(by.id('register-view-username')).replaceText(data.registeringUser.username); await element(by.id('register-view-email')).replaceText(data.registeringUser.email); await element(by.id('register-view-password')).replaceText(data.registeringUser.password); - await sleep(300); await element(by.id('register-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); }); }); }); diff --git a/e2e/tests/onboarding/05-login.spec.js b/e2e/tests/onboarding/05-login.spec.js index 3734b543d..7c6b4853c 100644 --- a/e2e/tests/onboarding/05-login.spec.js +++ b/e2e/tests/onboarding/05-login.spec.js @@ -44,33 +44,27 @@ describe('Login screen', () => { it('should navigate to register', async() => { await element(by.id('login-view-register')).tap(); await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('register-view'))).toBeVisible(); await tapBack(); }); it('should navigate to forgot password', async() => { await element(by.id('login-view-forgot-password')).tap(); await waitFor(element(by.id('forgot-password-view'))).toExist().withTimeout(2000); - await expect(element(by.id('forgot-password-view'))).toExist(); await tapBack(); }); it('should insert wrong password and get error', async() => { await element(by.id('login-view-email')).replaceText(data.users.regular.username); await element(by.id('login-view-password')).replaceText('NotMyActualPassword'); - await sleep(300); await element(by.id('login-view-submit')).tap(); await waitFor(element(by.text('Your credentials were rejected! Please try again.'))).toBeVisible().withTimeout(10000); - await expect(element(by.text('Your credentials were rejected! Please try again.'))).toBeVisible(); await element(by.text('OK')).tap(); }); it('should login with success', async() => { await element(by.id('login-view-password')).replaceText(data.users.regular.password); - await sleep(300); await element(by.id('login-view-submit')).tap(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); }); }); }); diff --git a/e2e/tests/onboarding/06-roomslist.spec.js b/e2e/tests/onboarding/06-roomslist.spec.js index 5e7e1e8aa..bbe0b886b 100644 --- a/e2e/tests/onboarding/06-roomslist.spec.js +++ b/e2e/tests/onboarding/06-roomslist.spec.js @@ -1,9 +1,17 @@ const { device, expect, element, by, waitFor } = require('detox'); -const { logout, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { login, navigateToLogin, logout, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const data = require('../../data'); describe('Rooms list screen', () => { + + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password) + }); + describe('Render', () => { it('should have rooms list screen', async() => { await expect(element(by.id('rooms-list-view'))).toBeVisible(); @@ -29,18 +37,12 @@ describe('Rooms list screen', () => { it('should search room and navigate', async() => { await searchRoom('rocket.cat'); await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible(); await element(by.id('rooms-list-view-item-rocket.cat')).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(10000); - await expect(element(by.id('room-view'))).toBeVisible(); await waitFor(element(by.id('room-view-title-rocket.cat'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('room-view-title-rocket.cat'))).toBeVisible(); await tapBack(); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('rooms-list-view'))).toBeVisible(); - await sleep(2000); await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toExist().withTimeout(60000); - await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toExist(); }); it('should logout', async() => { diff --git a/e2e/tests/room/01-createroom.spec.js b/e2e/tests/room/01-createroom.spec.js index 1cfbf16a7..35b007dba 100644 --- a/e2e/tests/room/01-createroom.spec.js +++ b/e2e/tests/room/01-createroom.spec.js @@ -2,48 +2,50 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { tapBack, sleep, navigateToLogin, login } = require('../../helpers/app'); +const { tapBack, sleep, navigateToLogin, login, tryTapping } = require('../../helpers/app'); + + describe('Create room screen', () => { before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); await navigateToLogin(); await login(data.users.regular.username, data.users.regular.password); - await element(by.id('rooms-list-view-create-channel')).tap(); - await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); }); describe('New Message', async() => { + before(async() => { + await element(by.id('rooms-list-view-create-channel')).tap(); + }); + describe('Render', async() => { it('should have new message screen', async() => { - await expect(element(by.id('new-message-view'))).toExist(); + await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000); }); it('should have search input', async() => { - await waitFor(element(by.id('new-message-view-search'))).toExist().withTimeout(2000); - await expect(element(by.id('new-message-view-search'))).toExist(); + await waitFor(element(by.id('new-message-view-search'))).toBeVisible().withTimeout(2000); }); }) describe('Usage', async() => { it('should back to rooms list', async() => { - await sleep(1000); + await waitFor(element(by.id('new-message-view-close'))).toBeVisible().withTimeout(2000); await element(by.id('new-message-view-close')).tap(); - await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); - await expect(element(by.id('rooms-list-view'))).toExist(); - await element(by.id('rooms-list-view-create-channel')).tap(); + + await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); + + await tryTapping(element(by.id('rooms-list-view-create-channel')), 3000); + //await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); - await expect(element(by.id('new-message-view'))).toExist(); }); it('should search user and navigate', async() => { await element(by.id('new-message-view-search')).replaceText('rocket.cat'); await waitFor(element(by.id('new-message-view-item-rocket.cat'))).toExist().withTimeout(60000); - await expect(element(by.id('new-message-view-item-rocket.cat'))).toExist(); await element(by.id('new-message-view-item-rocket.cat')).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(10000); - await expect(element(by.id('room-view'))).toExist(); await waitFor(element(by.id('room-view-title-rocket.cat'))).toExist().withTimeout(60000); - await expect(element(by.id('room-view-title-rocket.cat'))).toExist(); await tapBack(); await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); }); @@ -51,11 +53,8 @@ describe('Create room screen', () => { it('should navigate to select users', async() => { await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); - await expect(element(by.id('new-message-view'))).toExist(); - await sleep(1000); await element(by.id('new-message-view-create-channel')).tap(); await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000); - await expect(element(by.id('select-users-view'))).toExist(); }); }) }); @@ -108,7 +107,6 @@ describe('Create room screen', () => { const room = `public${ data.random }`; await element(by.id('create-channel-name')).replaceText(room); await element(by.id('create-channel-type')).tap(); - await sleep(1000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000); await expect(element(by.id('room-view'))).toExist(); @@ -123,20 +121,15 @@ describe('Create room screen', () => { it('should create private room', async() => { const room = `private${ data.random }`; await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); - // await device.launchApp({ newInstance: true }); - await sleep(1000); await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('new-message-view-create-channel')).tap(); await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('select-users-view-item-rocket.cat')).tap(); await waitFor(element(by.id('selected-user-rocket.cat'))).toExist().withTimeout(5000); await element(by.id('selected-users-view-submit')).tap(); await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000); await element(by.id('create-channel-name')).replaceText(room); - await sleep(1000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000); await expect(element(by.id('room-view'))).toExist(); @@ -152,17 +145,13 @@ describe('Create room screen', () => { const room = `empty${ data.random }`; await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); // await device.launchApp({ newInstance: true }); - await sleep(1000); await element(by.id('rooms-list-view-create-channel')).tap(); await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('new-message-view-create-channel')).tap(); await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('selected-users-view-submit')).tap(); await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000); await element(by.id('create-channel-name')).replaceText(room); - await sleep(1000); await element(by.id('create-channel-submit')).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000); await expect(element(by.id('room-view'))).toExist(); diff --git a/e2e/tests/room/02-room.spec.js b/e2e/tests/room/02-room.spec.js index 86fdfc21f..00d5d434d 100644 --- a/e2e/tests/room/02-room.spec.js +++ b/e2e/tests/room/02-room.spec.js @@ -2,27 +2,29 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom, starMessage, pinMessage, dismissReviewNag, tryTapping } = require('../../helpers/app'); -async function navigateToRoom() { - await searchRoom(`private${ data.random }`); - await waitFor(element(by.id(`rooms-list-view-item-private${ data.random }`))).toExist().withTimeout(60000); - await element(by.id(`rooms-list-view-item-private${ data.random }`)).tap(); +async function navigateToRoom(roomName) { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password); + await searchRoom(`${ roomName }`); + await waitFor(element(by.id(`rooms-list-view-item-${ roomName }`))).toExist().withTimeout(60000); + await element(by.id(`rooms-list-view-item-${ roomName }`)).tap(); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000); } describe('Room screen', () => { - const mainRoom = `private${ data.random }`; + const mainRoom = data.groups.private.name; before(async() => { - await navigateToRoom(); + await navigateToRoom(mainRoom); }); describe('Render', async() => { it('should have room screen', async() => { await expect(element(by.id('room-view'))).toExist(); await waitFor(element(by.id(`room-view-title-${ mainRoom }`))).toExist().withTimeout(5000); - await expect(element(by.id(`room-view-title-${ mainRoom }`))).toExist(); }); // Render - Header @@ -69,22 +71,15 @@ describe('Room screen', () => { await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toExist(); }); - it('should ask for review', async() => { - await waitFor(element(by.text('Are you enjoying this app?'))).toExist().withTimeout(60000); - await expect(element(by.text('Are you enjoying this app?')).atIndex(0)).toExist(); - await element(by.label('No').and(by.type('_UIAlertControllerActionView'))).tap(); // Tap `no` on ask for review alert - }) it('should show/hide emoji keyboard', async () => { if (device.getPlatform() === 'android') { await element(by.id('messagebox-open-emoji')).tap(); await waitFor(element(by.id('messagebox-keyboard-emoji'))).toExist().withTimeout(10000); - await expect(element(by.id('messagebox-keyboard-emoji'))).toExist(); await expect(element(by.id('messagebox-close-emoji'))).toExist(); await expect(element(by.id('messagebox-open-emoji'))).toBeNotVisible(); await element(by.id('messagebox-close-emoji')).tap(); await waitFor(element(by.id('messagebox-keyboard-emoji'))).toBeNotVisible().withTimeout(10000); - await expect(element(by.id('messagebox-keyboard-emoji'))).toBeNotVisible(); await expect(element(by.id('messagebox-close-emoji'))).toBeNotVisible(); await expect(element(by.id('messagebox-open-emoji'))).toExist(); } @@ -94,10 +89,8 @@ describe('Room screen', () => { await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText(':joy'); await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(10000); - await expect(element(by.id('messagebox-container'))).toExist(); await element(by.id('messagebox-input')).clearText(); await waitFor(element(by.id('messagebox-container'))).toBeNotVisible().withTimeout(10000); - await expect(element(by.id('messagebox-container'))).toBeNotVisible(); }); it('should show and tap on emoji autocomplete', async() => { @@ -105,8 +98,6 @@ describe('Room screen', () => { await element(by.id('messagebox-input')).replaceText(':'); await element(by.id('messagebox-input')).typeText('joy'); // workaround for number keyboard await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(10000); - await expect(element(by.id('messagebox-container'))).toExist(); - await sleep(1000); await element(by.id('mention-item-joy')).tap(); await expect(element(by.id('messagebox-input'))).toHaveText(':joy: '); await element(by.id('messagebox-input')).clearText(); @@ -116,25 +107,22 @@ describe('Room screen', () => { const username = data.users.regular.username await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText(`@${ username }`); - await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(60000); - await expect(element(by.id('messagebox-container'))).toExist(); - await sleep(1000); - await element(by.id(`mention-item-${ username }`)).tap(); + await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000); + await waitFor(element(by.id(`mention-item-${ username }`))).toBeVisible().withTimeout(4000) + await tryTapping(element(by.id(`mention-item-${ username }`)), 2000, true); await expect(element(by.id('messagebox-input'))).toHaveText(`@${ username } `); - await element(by.id('messagebox-input')).tap(); + await tryTapping(element(by.id('messagebox-input')), 2000) await element(by.id('messagebox-input')).typeText(`${ data.random }mention`); await element(by.id('messagebox-send-message')).tap(); // await waitFor(element(by.label(`@${ data.user } ${ data.random }mention`)).atIndex(0)).toExist().withTimeout(60000); - await sleep(2000); }); it('should show and tap on room autocomplete', async() => { await element(by.id('messagebox-input')).tap(); await element(by.id('messagebox-input')).typeText('#general'); - await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(60000); - await expect(element(by.id('messagebox-container'))).toExist(); - await sleep(1000); - await element(by.id('mention-item-general')).tap(); + //await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000); + await waitFor(element(by.id('mention-item-general'))).toBeVisible().withTimeout(4000); + await tryTapping(element(by.id('mention-item-general')), 2000, true) await expect(element(by.id('messagebox-input'))).toHaveText('#general '); await element(by.id('messagebox-input')).clearText(); }); @@ -147,7 +135,6 @@ describe('Room screen', () => { await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await element(by.label('Permalink')).tap(); - await sleep(1000); // TODO: test clipboard }); @@ -158,28 +145,20 @@ describe('Room screen', () => { await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await element(by.label('Copy')).tap(); - await sleep(1000); // TODO: test clipboard }); it('should star message', async() => { - await element(by.label(`${ data.random }message`)).atIndex(0).longPress(); - await expect(element(by.id('action-sheet'))).toExist(); - await expect(element(by.id('action-sheet-handle'))).toBeVisible(); - await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Star')).tap(); - await sleep(1000); - await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000); + await starMessage('message') + await sleep(1000) //https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/2324 await element(by.label(`${ data.random }message`)).atIndex(0).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await waitFor(element(by.label('Unstar'))).toBeVisible().withTimeout(2000); - await expect(element(by.label('Unstar'))).toBeVisible(); await element(by.id('action-sheet-backdrop')).tap(); - await sleep(1000); }); it('should react to message', async() => { @@ -189,14 +168,10 @@ describe('Room screen', () => { await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await element(by.id('add-reaction')).tap(); await waitFor(element(by.id('reaction-picker'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('reaction-picker'))).toBeVisible(); await element(by.id('reaction-picker-😃')).tap(); await waitFor(element(by.id('reaction-picker-grinning'))).toExist().withTimeout(2000); - await expect(element(by.id('reaction-picker-grinning'))).toExist(); await element(by.id('reaction-picker-grinning')).tap(); await waitFor(element(by.id('message-reaction-:grinning:'))).toExist().withTimeout(60000); - await expect(element(by.id('message-reaction-:grinning:'))).toExist(); - await sleep(1000); }); it('should react to message with frequently used emoji', async() => { @@ -205,30 +180,27 @@ describe('Room screen', () => { await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await waitFor(element(by.id('message-actions-emoji-+1'))).toBeVisible().withTimeout(2000); - await expect(element(by.id('message-actions-emoji-+1'))).toBeVisible(); await element(by.id('message-actions-emoji-+1')).tap(); await waitFor(element(by.id('message-reaction-:+1:'))).toBeVisible().withTimeout(60000); - await expect(element(by.id('message-reaction-:+1:'))).toBeVisible(); - await sleep(1000); }); it('should show reaction picker on add reaction button pressed and have frequently used emoji', async() => { await element(by.id('message-add-reaction')).tap(); await waitFor(element(by.id('reaction-picker'))).toExist().withTimeout(2000); - await expect(element(by.id('reaction-picker'))).toExist(); await waitFor(element(by.id('reaction-picker-grinning'))).toExist().withTimeout(2000); - await expect(element(by.id('reaction-picker-grinning'))).toExist(); await element(by.id('reaction-picker-😃')).tap(); await waitFor(element(by.id('reaction-picker-grimacing'))).toExist().withTimeout(2000); await element(by.id('reaction-picker-grimacing')).tap(); await waitFor(element(by.id('message-reaction-:grimacing:'))).toExist().withTimeout(60000); - await sleep(1000); }); + + it('should ask for review', async() => { + await dismissReviewNag() //TODO: Create a proper test for this elsewhere. + }) it('should remove reaction', async() => { await element(by.id('message-reaction-:grinning:')).tap(); await waitFor(element(by.id('message-reaction-:grinning:'))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id('message-reaction-:grinning:'))).toBeNotVisible(); }); it('should edit message', async() => { @@ -241,7 +213,6 @@ describe('Room screen', () => { await element(by.id('messagebox-input')).typeText('ed'); await element(by.id('messagebox-send-message')).tap(); await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist().withTimeout(60000); - await expect(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist(); }); it('should quote message', async() => { @@ -253,46 +224,39 @@ describe('Room screen', () => { await element(by.label('Quote')).tap(); await element(by.id('messagebox-input')).typeText(`${ data.random }quoted`); await element(by.id('messagebox-send-message')).tap(); - await sleep(1000); // TODO: test if quote was sent }); it('should pin message', async() => { - await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist(); - await element(by.label(`${ data.random }edited (edited)`)).atIndex(0).longPress(); - await expect(element(by.id('action-sheet'))).toExist(); - await expect(element(by.id('action-sheet-handle'))).toBeVisible(); - await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); - await element(by.label('Pin')).tap(); - await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000); - await sleep(1500); - - await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toBeVisible(); - await element(by.label(`${ data.random }edited (edited)`)).atIndex(0).longPress(); - await expect(element(by.id('action-sheet'))).toExist(); + await mockMessage('pin') + await pinMessage('pin') + + await waitFor(element(by.label(`${ data.random }pin`)).atIndex(0)).toBeVisible().withTimeout(2000); + await waitFor(element(by.label('Message pinned')).atIndex(0)).toBeVisible().withTimeout(2000); + await element(by.label(`${ data.random }pin`)).atIndex(0).longPress(); + await waitFor(element(by.id('action-sheet'))).toExist().withTimeout(1000); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await waitFor(element(by.label('Unpin'))).toBeVisible().withTimeout(2000); - await expect(element(by.label('Unpin'))).toBeVisible(); await element(by.id('action-sheet-backdrop')).tap(); }); it('should delete message', async() => { - await waitFor(element(by.label(`${ data.random }quoted`)).atIndex(0)).toBeVisible(); - await element(by.label(`${ data.random }quoted`)).atIndex(0).longPress(); + await mockMessage('delete') + + await waitFor(element(by.label(`${ data.random }delete`)).atIndex(0)).toBeVisible(); + await element(by.label(`${ data.random }delete`)).atIndex(0).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5); await element(by.label('Delete')).tap(); const deleteAlertMessage = 'You will not be able to recover this message!'; - await waitFor(element(by.text(deleteAlertMessage)).atIndex(0)).toExist().withTimeout(10000); - await expect(element(by.text(deleteAlertMessage)).atIndex(0)).toExist(); + await waitFor(element(by.text(deleteAlertMessage)).atIndex(0)).toExist().withTimeout(10000); await element(by.text('Delete')).tap(); - await sleep(1000); - await expect(element(by.label(`${ data.random }quoted`)).atIndex(0)).toNotExist(); + await waitFor(element(by.label(`${ data.random }delete`)).atIndex(0)).toNotExist().withTimeout(2000); }); }); @@ -317,7 +281,6 @@ describe('Room screen', () => { await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000); await expect(element(by.id(`room-view-title-${ thread }`))).toExist(); await tapBack(); - await sleep(1000); }); it('should toggle follow thread', async() => { @@ -332,10 +295,13 @@ describe('Room screen', () => { await waitFor(element(by.id('room-view-header-unfollow'))).toExist().withTimeout(60000); await expect(element(by.id('room-view-header-unfollow'))).toExist(); await tapBack(); - await sleep(1000); }); it('should navigate to thread from thread name', async() => { + await waitFor(element(by.id('room-view-header-actions').and(by.label(` ${ mainRoom }`)))).toBeVisible().withTimeout(2000); + await waitFor(element(by.id('room-view-header-actions').and(by.label(` ${ data.random }thread`)))).toBeNotVisible().withTimeout(2000); + await sleep(500) //TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :( + await mockMessage('dummymessagebetweenthethread'); await element(by.label(thread)).atIndex(0).longPress(); await expect(element(by.id('action-sheet'))).toExist(); @@ -352,10 +318,10 @@ describe('Room screen', () => { await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000); await expect(element(by.id(`room-view-title-${ thread }`))).toExist(); await tapBack(); - await sleep(1000); }); it('should navigate to thread from threads view', async() => { + await waitFor(element(by.id('room-view-header-threads'))).toExist().withTimeout(1000); await element(by.id('room-view-header-threads')).tap(); await waitFor(element(by.id('thread-messages-view'))).toExist().withTimeout(5000); await expect(element(by.id('thread-messages-view'))).toExist(); diff --git a/e2e/tests/room/03-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js index dd5c8defd..bd5b7c6bc 100644 --- a/e2e/tests/room/03-roomactions.spec.js +++ b/e2e/tests/room/03-roomactions.spec.js @@ -2,7 +2,7 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, tapBack, sleep, searchRoom, mockMessage, starMessage, pinMessage } = require('../../helpers/app'); const scrollDown = 200; @@ -11,13 +11,12 @@ async function navigateToRoomActions(type) { if (type === 'd') { room = 'rocket.cat'; } else { - room = `private${ data.random }`; + room = data.groups.private.name; } await searchRoom(room); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000); await element(by.id(`rooms-list-view-item-${ room }`)).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('room-view-header-actions')).tap(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); } @@ -25,7 +24,6 @@ async function navigateToRoomActions(type) { async function backToActions() { await tapBack(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); - await expect(element(by.id('room-actions-view'))).toExist(); } async function backToRoomsList() { @@ -36,10 +34,16 @@ async function backToRoomsList() { } describe('Room actions screen', () => { + + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password); + }); + describe('Render', async() => { describe('Direct', async() => { before(async() => { - await device.launchApp({ newInstance: true }); await navigateToRoomActions('d'); }); @@ -197,65 +201,89 @@ describe('Room actions screen', () => { it('should show mentioned messages', async() => { await element(by.id('room-actions-mentioned')).tap(); await waitFor(element(by.id('mentioned-messages-view'))).toExist().withTimeout(2000); - await expect(element(by.id('mentioned-messages-view'))).toExist(); // await waitFor(element(by.text(` ${ data.random }mention`))).toExist().withTimeout(60000); - // await expect(element(by.text(` ${ data.random }mention`))).toExist(); await backToActions(); }); it('should show starred message and unstar it', async() => { + + //Go back to room and send a message + await tapBack(); + await mockMessage('messageToStar'); + + //Star the message + await starMessage('messageToStar') + + //Back into Room Actions + await element(by.id('room-view-header-actions')).tap(); + await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); + + //Go to starred messages await element(by.id('room-actions-starred')).tap(); await waitFor(element(by.id('starred-messages-view'))).toExist().withTimeout(2000); - await sleep(1000); - await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeVisible(); - await element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view'))).longPress(); - + await waitFor(element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view')))).toBeVisible().withTimeout(60000); + + //Unstar message + await element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view'))).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.label('Unstar')).tap(); - await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible(); + await waitFor(element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible().withTimeout(60000); await backToActions(); }); it('should show pinned message and unpin it', async() => { + + //Go back to room and send a message + await tapBack(); + await mockMessage('messageToPin'); + + //Pin the message + await pinMessage('messageToPin') + + //Back into Room Actions + await element(by.id('room-view-header-actions')).tap(); + await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); + await waitFor(element(by.id('room-actions-pinned'))).toExist(); await element(by.id('room-actions-pinned')).tap(); await waitFor(element(by.id('pinned-messages-view'))).toExist().withTimeout(2000); - await sleep(1000); - await waitFor(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeVisible(); - await element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view'))).longPress(); + await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toBeVisible().withTimeout(60000); + await element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view'))).longPress(); await expect(element(by.id('action-sheet'))).toExist(); await expect(element(by.id('action-sheet-handle'))).toBeVisible(); await element(by.label('Unpin')).tap(); - await waitFor(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible().withTimeout(60000); - await expect(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible(); + await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible().withTimeout(60000); await backToActions(); }); it('should search and find a message', async() => { + + //Go back to room and send a message + await tapBack(); + await mockMessage('messageToFind'); + + //Back into Room Actions + await element(by.id('room-view-header-actions')).tap(); + await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); + await element(by.id('room-actions-search')).tap(); await waitFor(element(by.id('search-messages-view'))).toExist().withTimeout(2000); await expect(element(by.id('search-message-view-input'))).toExist(); - await element(by.id('search-message-view-input')).replaceText(`/${ data.random }message/`); - await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('search-messages-view')))).toExist().withTimeout(60000); - await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('search-messages-view')))).toExist(); + await element(by.id('search-message-view-input')).replaceText(`/${ data.random }messageToFind/`); + await waitFor(element(by.label(`${ data.random }messageToFind`).withAncestor(by.id('search-messages-view')))).toExist().withTimeout(60000); await backToActions(); }); }); describe('Notification', async() => { it('should navigate to notification preference view', async() => { - await waitFor(element(by.id('room-actions-notifications'))).toExist(); - await expect(element(by.id('room-actions-notifications'))).toExist(); + await waitFor(element(by.id('room-actions-notifications'))).toExist().withTimeout(2000); await element(by.id('room-actions-notifications')).tap(); await waitFor(element(by.id('notification-preference-view'))).toExist().withTimeout(2000); - await expect(element(by.id('notification-preference-view'))).toExist(); }); it('should have receive notification option', async() => { @@ -271,30 +299,25 @@ describe('Room actions screen', () => { }); it('should have push notification option', async() => { - await waitFor(element(by.id('notification-preference-view-push-notification'))).toExist(); - await expect(element(by.id('notification-preference-view-push-notification'))).toExist(); + await waitFor(element(by.id('notification-preference-view-push-notification'))).toExist().withTimeout(4000); }); it('should have notification audio option', async() => { - await waitFor(element(by.id('notification-preference-view-audio'))).toExist(); - await expect(element(by.id('notification-preference-view-audio'))).toExist(); + await waitFor(element(by.id('notification-preference-view-audio'))).toExist().withTimeout(4000); }); it('should have notification sound option', async() => { // Ugly hack to scroll on detox await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom'); - await waitFor(element(by.id('notification-preference-view-sound'))).toExist(); - await expect(element(by.id('notification-preference-view-sound'))).toExist(); + await waitFor(element(by.id('notification-preference-view-sound'))).toExist().withTimeout(4000); }); it('should have notification duration option', async() => { - await waitFor(element(by.id('notification-preference-view-notification-duration'))).toExist(); - await expect(element(by.id('notification-preference-view-notification-duration'))).toExist(); + await waitFor(element(by.id('notification-preference-view-notification-duration'))).toExist().withTimeout(4000); }); it('should have email alert option', async() => { - await waitFor(element(by.id('notification-preference-view-email-alert'))).toExist(); - await expect(element(by.id('notification-preference-view-email-alert'))).toExist(); + await waitFor(element(by.id('notification-preference-view-email-alert'))).toExist().withTimeout(4000); }); after(async() => { @@ -309,34 +332,28 @@ describe('Room actions screen', () => { const user = data.users.alternate it('should tap on leave channel and raise alert', async() => { - await waitFor(element(by.id('room-actions-leave-channel'))).toExist(); - await expect(element(by.id('room-actions-leave-channel'))).toExist(); + await waitFor(element(by.id('room-actions-leave-channel'))).toExist().withTimeout(2000); await element(by.id('room-actions-leave-channel')).tap(); await waitFor(element(by.text('Yes, leave it!'))).toExist().withTimeout(2000); - await expect(element(by.text('Yes, leave it!'))).toExist(); await element(by.text('Yes, leave it!')).tap(); - await waitFor(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toExist().withTimeout(60000); - await expect(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toExist(); + await waitFor(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toExist().withTimeout(8000); await element(by.text('OK')).tap(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); }); it('should add user to the room', async() => { - await waitFor(element(by.id('room-actions-add-user'))).toExist(); + await waitFor(element(by.id('room-actions-add-user'))).toExist().withTimeout(4000); await element(by.id('room-actions-add-user')).tap(); await element(by.id('select-users-view-search')).tap(); await element(by.id('select-users-view-search')).replaceText(user.username); - await waitFor(element(by.id(`select-users-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`select-users-view-item-${ user.username }`))).toExist(); + await waitFor(element(by.id(`select-users-view-item-${ user.username }`))).toExist().withTimeout(10000); await element(by.id(`select-users-view-item-${ user.username }`)).tap(); await waitFor(element(by.id(`selected-user-${ user.username }`))).toExist().withTimeout(5000); - await expect(element(by.id(`selected-user-${ user.username }`))).toExist(); await element(by.id('selected-users-view-submit')).tap(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000); await element(by.id('room-actions-members')).tap(); await element(by.id('room-members-view-toggle-status')).tap(); await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist(); await backToActions(1); }); @@ -344,26 +361,20 @@ describe('Room actions screen', () => { before(async() => { await element(by.id('room-actions-members')).tap(); await waitFor(element(by.id('room-members-view'))).toExist().withTimeout(2000); - await expect(element(by.id('room-members-view'))).toExist(); }); it('should show all users', async() => { - await sleep(1000); await element(by.id('room-members-view-toggle-status')).tap(); await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist(); }); it('should filter user', async() => { await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist(); await element(by.id('room-members-view-search')).replaceText('rocket'); await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toBeNotVisible(); await element(by.id('room-members-view-search')).tap(); await element(by.id('room-members-view-search')).clearText(''); await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist(); }); // FIXME: mute/unmute isn't working @@ -391,9 +402,7 @@ describe('Room actions screen', () => { await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(5000); await element(by.id(`room-members-view-item-${ user.username }`)).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000); - await expect(element(by.id('room-view'))).toExist(); await waitFor(element(by.id(`room-view-title-${ user.username }`))).toExist().withTimeout(60000); - await expect(element(by.id(`room-view-title-${ user.username }`))).toExist(); await tapBack(); await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000); }); @@ -407,13 +416,10 @@ describe('Room actions screen', () => { it('should block/unblock user', async() => { await waitFor(element(by.id('room-actions-block-user'))).toExist(); - await sleep(1000); await element(by.id('room-actions-block-user')).tap(); await waitFor(element(by.label('Unblock user'))).toExist().withTimeout(60000); - await expect(element(by.label('Unblock user'))).toExist(); await element(by.id('room-actions-block-user')).tap(); await waitFor(element(by.label('Block user'))).toExist().withTimeout(60000); - await expect(element(by.label('Block user'))).toExist(); }); }); }); diff --git a/e2e/tests/room/04-roominfo.spec.js b/e2e/tests/room/04-roominfo.spec.js index 35a45441b..9382f16cf 100644 --- a/e2e/tests/room/04-roominfo.spec.js +++ b/e2e/tests/room/04-roominfo.spec.js @@ -2,23 +2,23 @@ const { device, expect, element, by, waitFor } = require('detox'); const data = require('../../data'); -const { tapBack, sleep, searchRoom } = require('../../helpers/app'); +const { navigateToLogin, login, tapBack, sleep, searchRoom } = require('../../helpers/app'); + +const privateRoomName = data.groups.private.name async function navigateToRoomInfo(type) { let room; if (type === 'd') { room = 'rocket.cat'; } else { - room = `private${ data.random }`; + room = privateRoomName; } await searchRoom(room); await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000); await element(by.id(`rooms-list-view-item-${ room }`)).tap(); await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('room-view-header-actions')).tap(); await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000); - await sleep(1000); await element(by.id('room-actions-info')).tap(); await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000); } @@ -28,13 +28,19 @@ async function waitForToast() { // await expect(element(by.id('toast'))).toExist(); // await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000); // await expect(element(by.id('toast'))).toBeNotVisible(); - await sleep(5000); + await sleep(1); } describe('Room info screen', () => { + + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password); + }); + describe('Direct', async() => { before(async() => { - await device.launchApp({ newInstance: true }); await navigateToRoomInfo('d'); }); @@ -42,11 +48,16 @@ describe('Room info screen', () => { await expect(element(by.id('room-info-view'))).toExist(); await expect(element(by.id('room-info-view-name'))).toExist(); }); + + after(async() => { + await tapBack() + await tapBack() + await tapBack() + }) }); describe('Channel/Group', async() => { before(async() => { - await device.launchApp({ newInstance: true }); await navigateToRoomInfo('c'); }); @@ -78,7 +89,6 @@ describe('Room info screen', () => { describe('Render Edit', async() => { before(async() => { - await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); @@ -141,7 +151,6 @@ describe('Room info screen', () => { }); describe('Usage', async() => { - const room = `private${ data.random }`; // it('should enter "invalid name" and get error', async() => { // await element(by.type('UIScrollView')).atIndex(1).swipe('down'); // await element(by.id('room-info-edit-view-name')).replaceText('invalid name'); @@ -155,22 +164,17 @@ describe('Room info screen', () => { // }); it('should change room name', async() => { - await element(by.id('room-info-edit-view-name')).replaceText(`${ room }new`); + await element(by.id('room-info-edit-view-name')).replaceText(`${ privateRoomName }new`); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); - await sleep(5000); await tapBack(); await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000); - await sleep(1000); - await expect(element(by.id('room-info-view-name'))).toHaveLabel(`${ room }new`); + await expect(element(by.id('room-info-view-name'))).toHaveLabel(`${ privateRoomName }new`); // change name to original await element(by.id('room-info-view-edit-button')).tap(); - await sleep(1000); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); - await sleep(1000); - await element(by.id('room-info-edit-view-name')).replaceText(`${ room }`); + await element(by.id('room-info-edit-view-name')).replaceText(`${ privateRoomName }`); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); - await sleep(1000); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await element(by.type('UIScrollView')).atIndex(1).swipe('down'); @@ -184,14 +188,11 @@ describe('Room info screen', () => { await element(by.id('room-info-edit-view-password')).replaceText('abc'); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-t')).tap(); - await sleep(1000); - await element(by.id('room-info-edit-view-ro')).tap(); - await sleep(1000); + await element(by.id('room-info-edit-view-ro')).longPress(); //https://github.com/facebook/react-native/issues/28032 await element(by.id('room-info-edit-view-react-when-ro')).tap(); - await sleep(1000); await element(by.id('room-info-edit-view-reset')).tap(); // after reset - await expect(element(by.id('room-info-edit-view-name'))).toHaveText(room); + await expect(element(by.id('room-info-edit-view-name'))).toHaveText(privateRoomName); await expect(element(by.id('room-info-edit-view-description'))).toHaveText(''); await expect(element(by.id('room-info-edit-view-topic'))).toHaveText(''); await expect(element(by.id('room-info-edit-view-announcement'))).toHaveText(''); @@ -203,55 +204,45 @@ describe('Room info screen', () => { }); it('should change room description', async() => { - await sleep(1000); await element(by.id('room-info-edit-view-description')).replaceText('new description'); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await tapBack(); await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000); - await sleep(1000); await expect(element(by.label('new description').withAncestor(by.id('room-info-view-description')))).toExist(); }); it('should change room topic', async() => { - await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('room-info-edit-view-topic')).replaceText('new topic'); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await tapBack(); await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000); - await sleep(1000); await expect(element(by.label('new topic').withAncestor(by.id('room-info-view-topic')))).toExist(); }); it('should change room announcement', async() => { - await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.id('room-info-edit-view-announcement')).replaceText('new announcement'); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-submit')).tap(); await waitForToast(); await tapBack(); await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000); - await sleep(1000); await expect(element(by.label('new announcement').withAncestor(by.id('room-info-view-announcement')))).toExist(); }); it('should change room password', async() => { - await sleep(1000); await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000); await element(by.id('room-info-view-edit-button')).tap(); await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000); - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-password')).replaceText('password'); await element(by.id('room-info-edit-view-submit')).tap(); @@ -259,7 +250,6 @@ describe('Room info screen', () => { }); it('should change room type', async() => { - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-t')).tap(); await element(by.id('room-info-edit-view-submit')).tap(); @@ -282,14 +272,11 @@ describe('Room info screen', () => { // }); it('should archive room', async() => { - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-archive')).tap(); await waitFor(element(by.text('Yes, archive it!'))).toExist().withTimeout(5000); - await expect(element(by.text('Yes, archive it!'))).toExist(); await element(by.text('Yes, archive it!')).tap(); await waitFor(element(by.id('room-info-edit-view-unarchive'))).toExist().withTimeout(60000); - await expect(element(by.id('room-info-edit-view-unarchive'))).toExist(); await expect(element(by.id('room-info-edit-view-archive'))).toBeNotVisible(); // TODO: needs permission to unarchive // await element(by.id('room-info-edit-view-archive')).tap(); @@ -301,16 +288,12 @@ describe('Room info screen', () => { }); it('should delete room', async() => { - await sleep(1000); await element(by.type('UIScrollView')).atIndex(1).swipe('up'); await element(by.id('room-info-edit-view-delete')).tap(); await waitFor(element(by.text('Yes, delete it!'))).toExist().withTimeout(5000); - await expect(element(by.text('Yes, delete it!'))).toExist(); await element(by.text('Yes, delete it!')).tap(); await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000); - await sleep(2000); - await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000); - await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible(); + await waitFor(element(by.id(`rooms-list-view-item-${ privateRoomName }`))).toBeNotVisible().withTimeout(60000); }); }); }); diff --git a/ios/Podfile b/ios/Podfile index 35f65a777..c1eca515e 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,143 +1,23 @@ platform :ios, '11.0' +require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' require_relative '../node_modules/react-native-unimodules/cocoapods.rb' -def add_flipper_pods!(versions = {}) - versions['Flipper'] ||= '~> 0.33.1' - versions['DoubleConversion'] ||= '1.1.7' - versions['Flipper-Folly'] ||= '~> 2.1' - versions['Flipper-Glog'] ||= '0.3.6' - versions['Flipper-PeerTalk'] ||= '~> 0.0.4' - versions['Flipper-RSocket'] ||= '~> 1.0' - - pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug' - - # List all transitive dependencies for FlipperKit pods - # to avoid them being linked in Release builds - pod 'Flipper', versions['Flipper'], :configuration => 'Debug' - pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug' - pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug' - pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug' - pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug' - pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug' - pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug' - pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug' -end - -# Post Install processing for Flipper -def flipper_post_install(installer) - installer.pods_project.targets.each do |target| - if target.name == 'YogaKit' - target.build_configurations.each do |config| - config.build_settings['SWIFT_VERSION'] = '4.1' - end - end - end - file_name = Dir.glob("*.xcodeproj")[0] - app_project = Xcodeproj::Project.open(file_name) - app_project.native_targets.each do |target| - target.build_configurations.each do |config| - cflags = config.build_settings['OTHER_CFLAGS'] || '$(inherited) ' - unless cflags.include? '-DFB_SONARKIT_ENABLED=1' - puts 'Adding -DFB_SONARKIT_ENABLED=1 in OTHER_CFLAGS...' - cflags << '-DFB_SONARKIT_ENABLED=1' - end - config.build_settings['OTHER_CFLAGS'] = cflags - end - app_project.save - end - installer.pods_project.save +def all_pods + config = use_native_modules! + use_unimodules! + use_react_native!(:path => config["reactNativePath"]) + use_flipper! end target 'RocketChatRN' do - pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" - pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" - pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired" - pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety" - pod 'React', :path => '../node_modules/react-native/' - pod 'React-Core', :path => '../node_modules/react-native/' - pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules' - pod 'React-Core/DevSupport', :path => '../node_modules/react-native/' - pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS' - pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation' - pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob' - pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image' - pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS' - pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network' - pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings' - pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text' - pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration' - pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/' - - pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' - pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' - pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' - pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' - pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon" - pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" - pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true - - pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' - pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' - pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' - - use_native_modules! - use_unimodules! + all_pods end target 'ShareRocketChatRN' do - pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" - pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" - pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired" - pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety" - pod 'React', :path => '../node_modules/react-native/' - pod 'React-Core', :path => '../node_modules/react-native/' - pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules' - pod 'React-Core/DevSupport', :path => '../node_modules/react-native/' - pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS' - pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation' - pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob' - pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image' - pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS' - pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network' - pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings' - pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text' - pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration' - pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/' - - pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' - pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' - pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' - pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' - pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon" - pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" - pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true - - pod 'JitsiMeetSDK', :git => 'https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git' - - pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' - pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' - pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' - - use_native_modules! - use_unimodules! + all_pods end -# Enables Flipper. -# -# Note that if you have use_frameworks! enabled, Flipper will not work and -# you should disable these next few lines. -add_flipper_pods! post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| @@ -145,4 +25,4 @@ post_install do |installer| end end flipper_post_install(installer) -end +end \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 0355ffe49..c72afcec8 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,89 +1,95 @@ PODS: - boost-for-react-native (1.63.0) - - BugsnagReactNative (2.23.7): - - BugsnagReactNative/Core (= 2.23.7) + - BugsnagReactNative (2.23.10): + - BugsnagReactNative/Core (= 2.23.10) - React - - BugsnagReactNative/Core (2.23.7): + - BugsnagReactNative/Core (2.23.10): - React - CocoaAsyncSocket (7.6.4) - CocoaLibEvent (1.0.0) - - Crashlytics (3.14.0): - - Fabric (~> 1.10.2) - DoubleConversion (1.1.6) - EXAppleAuthentication (2.2.1): - UMCore - - EXAV (8.1.0): + - EXAV (8.2.1): - UMCore - UMFileSystemInterface - UMPermissionsInterface - - EXConstants (9.0.0): + - EXConstants (9.1.1): - UMConstantsInterface - UMCore - - EXFileSystem (8.1.0): + - EXFileSystem (9.0.1): - UMCore - UMFileSystemInterface - - EXHaptics (8.1.0): + - EXHaptics (8.2.1): - UMCore - - EXImageLoader (1.0.1): + - EXImageLoader (1.1.1): - React-Core - UMCore - UMImageLoaderInterface - - EXKeepAwake (8.1.0): + - EXKeepAwake (8.2.1): - UMCore - - EXLocalAuthentication (9.0.0): + - EXLocalAuthentication (9.2.0): - UMConstantsInterface - UMCore - - EXPermissions (8.1.0): + - EXPermissions (9.0.1): - UMCore - UMPermissionsInterface - - EXVideoThumbnails (4.1.1): + - EXVideoThumbnails (4.2.1): - UMCore - UMFileSystemInterface - - EXWebBrowser (8.2.1): + - EXWebBrowser (8.3.1): - UMCore - - Fabric (1.10.2) - - FBLazyVector (0.62.2) - - FBReactNativeSpec (0.62.2): - - Folly (= 2018.10.22.00) - - RCTRequired (= 0.62.2) - - RCTTypeSafety (= 0.62.2) - - React-Core (= 0.62.2) - - React-jsi (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - Firebase/Core (6.24.0): + - FBLazyVector (0.63.1) + - FBReactNativeSpec (0.63.1): + - Folly (= 2020.01.13.00) + - RCTRequired (= 0.63.1) + - RCTTypeSafety (= 0.63.1) + - React-Core (= 0.63.1) + - React-jsi (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - Firebase/Analytics (6.27.1): + - Firebase/Core + - Firebase/Core (6.27.1): - Firebase/CoreOnly - - FirebaseAnalytics (= 6.5.0) - - Firebase/CoreOnly (6.24.0): - - FirebaseCore (= 6.7.0) - - FirebaseAnalytics (6.5.0): - - FirebaseCore (~> 6.7) - - FirebaseInstallations (~> 1.2) - - GoogleAppMeasurement (= 6.5.0) + - FirebaseAnalytics (= 6.6.2) + - Firebase/CoreOnly (6.27.1): + - FirebaseCore (= 6.8.1) + - Firebase/Crashlytics (6.27.1): + - Firebase/CoreOnly + - FirebaseCrashlytics (~> 4.2.0) + - FirebaseAnalytics (6.6.2): + - FirebaseCore (~> 6.8) + - FirebaseInstallations (~> 1.4) + - GoogleAppMeasurement (= 6.6.2) - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - nanopb (~> 1.30905.0) - - FirebaseCore (6.7.0): + - FirebaseCore (6.8.1): - FirebaseCoreDiagnostics (~> 1.3) - - FirebaseCoreDiagnosticsInterop (~> 1.2) - GoogleUtilities/Environment (~> 6.5) - GoogleUtilities/Logger (~> 6.5) - - FirebaseCoreDiagnostics (1.3.0): - - FirebaseCoreDiagnosticsInterop (~> 1.2) + - FirebaseCoreDiagnostics (1.4.0): - GoogleDataTransportCCTSupport (~> 3.1) - GoogleUtilities/Environment (~> 6.5) - GoogleUtilities/Logger (~> 6.5) - nanopb (~> 1.30905.0) - - FirebaseCoreDiagnosticsInterop (1.2.0) - - FirebaseInstallations (1.2.0): - - FirebaseCore (~> 6.6) - - GoogleUtilities/Environment (~> 6.6) - - GoogleUtilities/UserDefaults (~> 6.6) + - FirebaseCrashlytics (4.2.0): + - FirebaseCore (~> 6.8) + - FirebaseInstallations (~> 1.1) + - GoogleDataTransport (~> 6.1) + - GoogleDataTransportCCTSupport (~> 3.1) + - nanopb (~> 1.30905.0) - PromisesObjC (~> 1.2) - - Flipper (0.33.1): - - Flipper-Folly (~> 2.1) - - Flipper-RSocket (~> 1.0) + - FirebaseInstallations (1.5.0): + - FirebaseCore (~> 6.8) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/UserDefaults (~> 6.7) + - PromisesObjC (~> 1.2) + - Flipper (0.41.5): + - Flipper-Folly (~> 2.2) + - Flipper-RSocket (~> 1.1) - Flipper-DoubleConversion (1.1.7) - Flipper-Folly (2.2.0): - boost-for-react-native @@ -95,76 +101,76 @@ PODS: - Flipper-PeerTalk (0.0.4) - Flipper-RSocket (1.1.0): - Flipper-Folly (~> 2.2) - - FlipperKit (0.33.1): - - FlipperKit/Core (= 0.33.1) - - FlipperKit/Core (0.33.1): - - Flipper (~> 0.33.1) + - FlipperKit (0.41.5): + - FlipperKit/Core (= 0.41.5) + - FlipperKit/Core (0.41.5): + - Flipper (~> 0.41.5) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - - FlipperKit/CppBridge (0.33.1): - - Flipper (~> 0.33.1) - - FlipperKit/FBCxxFollyDynamicConvert (0.33.1): - - Flipper-Folly (~> 2.1) - - FlipperKit/FBDefines (0.33.1) - - FlipperKit/FKPortForwarding (0.33.1): + - FlipperKit/CppBridge (0.41.5): + - Flipper (~> 0.41.5) + - FlipperKit/FBCxxFollyDynamicConvert (0.41.5): + - Flipper-Folly (~> 2.2) + - FlipperKit/FBDefines (0.41.5) + - FlipperKit/FKPortForwarding (0.41.5): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.33.1) - - FlipperKit/FlipperKitLayoutPlugin (0.33.1): + - FlipperKit/FlipperKitHighlightOverlay (0.41.5) + - FlipperKit/FlipperKitLayoutPlugin (0.41.5): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.33.1) - - FlipperKit/FlipperKitNetworkPlugin (0.33.1): + - FlipperKit/FlipperKitLayoutTextSearchable (0.41.5) + - FlipperKit/FlipperKitNetworkPlugin (0.41.5): - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.33.1): + - FlipperKit/FlipperKitReactPlugin (0.41.5): - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.33.1): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.41.5): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.33.1): + - FlipperKit/SKIOSNetworkPlugin (0.41.5): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - - Folly (2018.10.22.00): + - Folly (2020.01.13.00): - boost-for-react-native - DoubleConversion - - Folly/Default (= 2018.10.22.00) + - Folly/Default (= 2020.01.13.00) - glog - - Folly/Default (2018.10.22.00): + - Folly/Default (2020.01.13.00): - boost-for-react-native - DoubleConversion - glog - glog (0.3.5) - - GoogleAppMeasurement (6.5.0): + - GoogleAppMeasurement (6.6.2): - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/Network (~> 6.0) - "GoogleUtilities/NSData+zlib (~> 6.0)" - nanopb (~> 1.30905.0) - - GoogleDataTransport (6.1.0) - - GoogleDataTransportCCTSupport (3.1.0): + - GoogleDataTransport (6.2.1) + - GoogleDataTransportCCTSupport (3.2.0): - GoogleDataTransport (~> 6.1) - nanopb (~> 1.30905.0) - - GoogleUtilities/AppDelegateSwizzler (6.6.0): + - GoogleUtilities/AppDelegateSwizzler (6.7.1): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - - GoogleUtilities/Environment (6.6.0): + - GoogleUtilities/Environment (6.7.1): - PromisesObjC (~> 1.2) - - GoogleUtilities/Logger (6.6.0): + - GoogleUtilities/Logger (6.7.1): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.6.0): + - GoogleUtilities/MethodSwizzler (6.7.1): - GoogleUtilities/Logger - - GoogleUtilities/Network (6.6.0): + - GoogleUtilities/Network (6.7.1): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.6.0)" - - GoogleUtilities/Reachability (6.6.0): + - "GoogleUtilities/NSData+zlib (6.7.1)" + - GoogleUtilities/Reachability (6.7.1): - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.6.0): + - GoogleUtilities/UserDefaults (6.7.1): - GoogleUtilities/Logger - JitsiMeetSDK (2.8.1) - KeyCommands (2.0.3): @@ -186,177 +192,180 @@ PODS: - OpenSSL-Universal (1.0.2.19): - OpenSSL-Universal/Static (= 1.0.2.19) - OpenSSL-Universal/Static (1.0.2.19) - - PromisesObjC (1.2.8) - - RCTRequired (0.62.2) - - RCTTypeSafety (0.62.2): - - FBLazyVector (= 0.62.2) - - Folly (= 2018.10.22.00) - - RCTRequired (= 0.62.2) - - React-Core (= 0.62.2) - - React (0.62.2): - - React-Core (= 0.62.2) - - React-Core/DevSupport (= 0.62.2) - - React-Core/RCTWebSocket (= 0.62.2) - - React-RCTActionSheet (= 0.62.2) - - React-RCTAnimation (= 0.62.2) - - React-RCTBlob (= 0.62.2) - - React-RCTImage (= 0.62.2) - - React-RCTLinking (= 0.62.2) - - React-RCTNetwork (= 0.62.2) - - React-RCTSettings (= 0.62.2) - - React-RCTText (= 0.62.2) - - React-RCTVibration (= 0.62.2) - - React-Core (0.62.2): - - Folly (= 2018.10.22.00) + - PromisesObjC (1.2.9) + - RCTRequired (0.63.1) + - RCTTypeSafety (0.63.1): + - FBLazyVector (= 0.63.1) + - Folly (= 2020.01.13.00) + - RCTRequired (= 0.63.1) + - React-Core (= 0.63.1) + - React (0.63.1): + - React-Core (= 0.63.1) + - React-Core/DevSupport (= 0.63.1) + - React-Core/RCTWebSocket (= 0.63.1) + - React-RCTActionSheet (= 0.63.1) + - React-RCTAnimation (= 0.63.1) + - React-RCTBlob (= 0.63.1) + - React-RCTImage (= 0.63.1) + - React-RCTLinking (= 0.63.1) + - React-RCTNetwork (= 0.63.1) + - React-RCTSettings (= 0.63.1) + - React-RCTText (= 0.63.1) + - React-RCTVibration (= 0.63.1) + - React-callinvoker (0.63.1) + - React-Core (0.63.1): + - Folly (= 2020.01.13.00) - glog - - React-Core/Default (= 0.62.2) - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-Core/Default (= 0.63.1) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/CoreModulesHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/CoreModulesHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/Default (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/Default (0.63.1): + - Folly (= 2020.01.13.00) - glog - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/DevSupport (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/DevSupport (0.63.1): + - Folly (= 2020.01.13.00) - glog - - React-Core/Default (= 0.62.2) - - React-Core/RCTWebSocket (= 0.62.2) - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) - - React-jsinspector (= 0.62.2) + - React-Core/Default (= 0.63.1) + - React-Core/RCTWebSocket (= 0.63.1) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) + - React-jsinspector (= 0.63.1) - Yoga - - React-Core/RCTActionSheetHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTActionSheetHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTAnimationHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTAnimationHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTBlobHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTBlobHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTImageHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTImageHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTLinkingHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTLinkingHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTNetworkHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTNetworkHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTSettingsHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTSettingsHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTTextHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTTextHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTVibrationHeaders (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTVibrationHeaders (0.63.1): + - Folly (= 2020.01.13.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-Core/RCTWebSocket (0.62.2): - - Folly (= 2018.10.22.00) + - React-Core/RCTWebSocket (0.63.1): + - Folly (= 2020.01.13.00) - glog - - React-Core/Default (= 0.62.2) - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsiexecutor (= 0.62.2) + - React-Core/Default (= 0.63.1) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsiexecutor (= 0.63.1) - Yoga - - React-CoreModules (0.62.2): - - FBReactNativeSpec (= 0.62.2) - - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.2) - - React-Core/CoreModulesHeaders (= 0.62.2) - - React-RCTImage (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - React-cxxreact (0.62.2): + - React-CoreModules (0.63.1): + - FBReactNativeSpec (= 0.63.1) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.1) + - React-Core/CoreModulesHeaders (= 0.63.1) + - React-jsi (= 0.63.1) + - React-RCTImage (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - React-cxxreact (0.63.1): - boost-for-react-native (= 1.63.0) - DoubleConversion - - Folly (= 2018.10.22.00) + - Folly (= 2020.01.13.00) - glog - - React-jsinspector (= 0.62.2) - - React-jsi (0.62.2): + - React-callinvoker (= 0.63.1) + - React-jsinspector (= 0.63.1) + - React-jsi (0.63.1): - boost-for-react-native (= 1.63.0) - DoubleConversion - - Folly (= 2018.10.22.00) + - Folly (= 2020.01.13.00) - glog - - React-jsi/Default (= 0.62.2) - - React-jsi/Default (0.62.2): + - React-jsi/Default (= 0.63.1) + - React-jsi/Default (0.63.1): - boost-for-react-native (= 1.63.0) - DoubleConversion - - Folly (= 2018.10.22.00) + - Folly (= 2020.01.13.00) - glog - - React-jsiexecutor (0.62.2): + - React-jsiexecutor (0.63.1): - DoubleConversion - - Folly (= 2018.10.22.00) + - Folly (= 2020.01.13.00) - glog - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - React-jsinspector (0.62.2) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) + - React-jsinspector (0.63.1) - react-native-appearance (0.3.4): - React - react-native-background-timer (2.2.0): - React - - react-native-cameraroll (1.6.0): + - react-native-cameraroll (4.0.0): - React - - react-native-document-picker (3.3.3): + - react-native-document-picker (3.5.3): - React - react-native-jitsi-meet (2.1.1): - JitsiMeetSDK (= 2.8.1) @@ -365,71 +374,72 @@ PODS: - React - react-native-orientation-locker (1.1.8): - React - - react-native-safe-area-context (3.0.2): + - react-native-safe-area-context (3.1.1): - React - - react-native-slider (2.0.9): + - react-native-slider (3.0.2): - React - - react-native-webview (9.4.0): + - react-native-webview (10.3.2): - React - - React-RCTActionSheet (0.62.2): - - React-Core/RCTActionSheetHeaders (= 0.62.2) - - React-RCTAnimation (0.62.2): - - FBReactNativeSpec (= 0.62.2) - - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.2) - - React-Core/RCTAnimationHeaders (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - React-RCTBlob (0.62.2): - - FBReactNativeSpec (= 0.62.2) - - Folly (= 2018.10.22.00) - - React-Core/RCTBlobHeaders (= 0.62.2) - - React-Core/RCTWebSocket (= 0.62.2) - - React-jsi (= 0.62.2) - - React-RCTNetwork (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - React-RCTImage (0.62.2): - - FBReactNativeSpec (= 0.62.2) - - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.2) - - React-Core/RCTImageHeaders (= 0.62.2) - - React-RCTNetwork (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - React-RCTLinking (0.62.2): - - FBReactNativeSpec (= 0.62.2) - - React-Core/RCTLinkingHeaders (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - React-RCTNetwork (0.62.2): - - FBReactNativeSpec (= 0.62.2) - - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.2) - - React-Core/RCTNetworkHeaders (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - React-RCTSettings (0.62.2): - - FBReactNativeSpec (= 0.62.2) - - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.2) - - React-Core/RCTSettingsHeaders (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - React-RCTText (0.62.2): - - React-Core/RCTTextHeaders (= 0.62.2) - - React-RCTVibration (0.62.2): - - FBReactNativeSpec (= 0.62.2) - - Folly (= 2018.10.22.00) - - React-Core/RCTVibrationHeaders (= 0.62.2) - - ReactCommon/turbomodule/core (= 0.62.2) - - ReactCommon/callinvoker (0.62.2): + - React-RCTActionSheet (0.63.1): + - React-Core/RCTActionSheetHeaders (= 0.63.1) + - React-RCTAnimation (0.63.1): + - FBReactNativeSpec (= 0.63.1) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.1) + - React-Core/RCTAnimationHeaders (= 0.63.1) + - React-jsi (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - React-RCTBlob (0.63.1): + - FBReactNativeSpec (= 0.63.1) + - Folly (= 2020.01.13.00) + - React-Core/RCTBlobHeaders (= 0.63.1) + - React-Core/RCTWebSocket (= 0.63.1) + - React-jsi (= 0.63.1) + - React-RCTNetwork (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - React-RCTImage (0.63.1): + - FBReactNativeSpec (= 0.63.1) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.1) + - React-Core/RCTImageHeaders (= 0.63.1) + - React-jsi (= 0.63.1) + - React-RCTNetwork (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - React-RCTLinking (0.63.1): + - FBReactNativeSpec (= 0.63.1) + - React-Core/RCTLinkingHeaders (= 0.63.1) + - React-jsi (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - React-RCTNetwork (0.63.1): + - FBReactNativeSpec (= 0.63.1) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.1) + - React-Core/RCTNetworkHeaders (= 0.63.1) + - React-jsi (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - React-RCTSettings (0.63.1): + - FBReactNativeSpec (= 0.63.1) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.1) + - React-Core/RCTSettingsHeaders (= 0.63.1) + - React-jsi (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - React-RCTText (0.63.1): + - React-Core/RCTTextHeaders (= 0.63.1) + - React-RCTVibration (0.63.1): + - FBReactNativeSpec (= 0.63.1) + - Folly (= 2020.01.13.00) + - React-Core/RCTVibrationHeaders (= 0.63.1) + - React-jsi (= 0.63.1) + - ReactCommon/turbomodule/core (= 0.63.1) + - ReactCommon/turbomodule/core (0.63.1): - DoubleConversion - - Folly (= 2018.10.22.00) + - Folly (= 2020.01.13.00) - glog - - React-cxxreact (= 0.62.2) - - ReactCommon/turbomodule/core (0.62.2): - - DoubleConversion - - Folly (= 2018.10.22.00) - - glog - - React-Core (= 0.62.2) - - React-cxxreact (= 0.62.2) - - React-jsi (= 0.62.2) - - ReactCommon/callinvoker (= 0.62.2) + - React-callinvoker (= 0.63.1) + - React-Core (= 0.63.1) + - React-cxxreact (= 0.63.1) + - React-jsi (= 0.63.1) - ReactNativeART (1.2.0): - React - ReactNativeKeyboardInput (6.0.0): @@ -442,27 +452,30 @@ PODS: - React-Core - RNBootSplash (2.2.5): - React - - RNCAsyncStorage (1.10.3): + - RNCAsyncStorage (1.11.0): - React - RNCMaskedView (0.1.10): - React - - RNDateTimePicker (2.3.2): + - RNDateTimePicker (2.6.0): - React - - RNDeviceInfo (5.5.7): + - RNDeviceInfo (5.6.2): - React - RNFastImage (8.1.5): - React - SDWebImage (~> 5.0) - SDWebImageWebPCoder (~> 0.4.1) - - RNFirebase (5.6.0): - - Firebase/Core + - RNFBAnalytics (7.3.1): + - Firebase/Analytics (~> 6.27.0) - React - - RNFirebase/Crashlytics (= 5.6.0) - - RNFirebase/Crashlytics (5.6.0): - - Crashlytics - - Fabric - - Firebase/Core + - RNFBApp + - RNFBApp (8.2.0): + - Firebase/CoreOnly (~> 6.27.0) - React + - RNFBCrashlytics (8.1.2): + - Firebase/Core (~> 6.27.0) + - Firebase/Crashlytics (~> 6.27.0) + - React + - RNFBApp - RNGestureHandler (1.6.1): - React - RNImageCropPicker (0.31.1): @@ -476,40 +489,40 @@ PODS: - TOCropViewController - RNLocalize (1.4.0): - React - - RNReanimated (1.8.0): + - RNReanimated (1.9.0): - React - RNRootView (1.0.3): - React - - RNScreens (2.7.0): + - RNScreens (2.9.0): - React - RNUserDefaults (1.8.1): - React - - RNVectorIcons (6.6.0): + - RNVectorIcons (7.0.0): - React - - SDWebImage (5.8.0): - - SDWebImage/Core (= 5.8.0) - - SDWebImage/Core (5.8.0) + - SDWebImage (5.8.4): + - SDWebImage/Core (= 5.8.4) + - SDWebImage/Core (5.8.4) - SDWebImageWebPCoder (0.4.1): - libwebp (~> 1.0) - SDWebImage/Core (~> 5.5) - - TOCropViewController (2.5.2) - - UMAppLoader (1.0.2) - - UMBarCodeScannerInterface (5.1.0) - - UMCameraInterface (5.1.0) - - UMConstantsInterface (5.1.0) - - UMCore (5.1.2) - - UMFaceDetectorInterface (5.1.0) - - UMFileSystemInterface (5.1.0) - - UMFontInterface (5.1.0) - - UMImageLoaderInterface (5.1.0) - - UMPermissionsInterface (5.1.0): + - TOCropViewController (2.5.3) + - UMAppLoader (1.2.0) + - UMBarCodeScannerInterface (5.2.1) + - UMCameraInterface (5.2.1) + - UMConstantsInterface (5.2.1) + - UMCore (5.3.0) + - UMFaceDetectorInterface (5.2.1) + - UMFileSystemInterface (5.2.1) + - UMFontInterface (5.2.1) + - UMImageLoaderInterface (5.2.1) + - UMPermissionsInterface (5.2.1): - UMCore - - UMReactNativeAdapter (5.2.0): + - UMReactNativeAdapter (5.4.0): - React-Core - UMCore - UMFontInterface - - UMSensorsInterface (5.1.0) - - UMTaskManagerInterface (5.1.0) + - UMSensorsInterface (5.2.1) + - UMTaskManagerInterface (5.2.1) - Yoga (1.14.0) - YogaKit (1.18.1): - Yoga (~> 1.14) @@ -530,32 +543,32 @@ DEPENDENCIES: - EXWebBrowser (from `../node_modules/expo-web-browser/ios`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) - - Flipper (~> 0.33.1) + - Flipper (~> 0.41.1) - Flipper-DoubleConversion (= 1.1.7) - - Flipper-Folly (~> 2.1) + - Flipper-Folly (~> 2.2) - Flipper-Glog (= 0.3.6) - Flipper-PeerTalk (~> 0.0.4) - - Flipper-RSocket (~> 1.0) - - FlipperKit (~> 0.33.1) - - FlipperKit/Core (~> 0.33.1) - - FlipperKit/CppBridge (~> 0.33.1) - - FlipperKit/FBCxxFollyDynamicConvert (~> 0.33.1) - - FlipperKit/FBDefines (~> 0.33.1) - - FlipperKit/FKPortForwarding (~> 0.33.1) - - FlipperKit/FlipperKitHighlightOverlay (~> 0.33.1) - - FlipperKit/FlipperKitLayoutPlugin (~> 0.33.1) - - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.33.1) - - FlipperKit/FlipperKitNetworkPlugin (~> 0.33.1) - - FlipperKit/FlipperKitReactPlugin (~> 0.33.1) - - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.33.1) - - FlipperKit/SKIOSNetworkPlugin (~> 0.33.1) + - Flipper-RSocket (~> 1.1) + - FlipperKit (~> 0.41.1) + - FlipperKit/Core (~> 0.41.1) + - FlipperKit/CppBridge (~> 0.41.1) + - FlipperKit/FBCxxFollyDynamicConvert (~> 0.41.1) + - FlipperKit/FBDefines (~> 0.41.1) + - FlipperKit/FKPortForwarding (~> 0.41.1) + - FlipperKit/FlipperKitHighlightOverlay (~> 0.41.1) + - FlipperKit/FlipperKitLayoutPlugin (~> 0.41.1) + - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.41.1) + - FlipperKit/FlipperKitNetworkPlugin (~> 0.41.1) + - FlipperKit/FlipperKitReactPlugin (~> 0.41.1) + - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.41.1) + - FlipperKit/SKIOSNetworkPlugin (~> 0.41.1) - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - - JitsiMeetSDK (from `https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git`) - KeyCommands (from `../node_modules/react-native-keycommands`) - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - React (from `../node_modules/react-native/`) + - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - React-Core (from `../node_modules/react-native/`) - React-Core/DevSupport (from `../node_modules/react-native/`) - React-Core/RCTWebSocket (from `../node_modules/react-native/`) @@ -583,7 +596,6 @@ DEPENDENCIES: - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) - React-RCTText (from `../node_modules/react-native/Libraries/Text`) - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - - ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - "ReactNativeART (from `../node_modules/@react-native-community/art`)" - ReactNativeKeyboardInput (from `../node_modules/react-native-keyboard-input`) @@ -596,7 +608,9 @@ DEPENDENCIES: - "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)" - RNDeviceInfo (from `../node_modules/react-native-device-info`) - "RNFastImage (from `../node_modules/@rocket.chat/react-native-fast-image`)" - - RNFirebase (from `../node_modules/react-native-firebase/ios`) + - "RNFBAnalytics (from `../node_modules/@react-native-firebase/analytics`)" + - "RNFBApp (from `../node_modules/@react-native-firebase/app`)" + - "RNFBCrashlytics (from `../node_modules/@react-native-firebase/crashlytics`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`) - RNLocalize (from `../node_modules/react-native-localize`) @@ -625,13 +639,11 @@ SPEC REPOS: - boost-for-react-native - CocoaAsyncSocket - CocoaLibEvent - - Crashlytics - - Fabric - Firebase - FirebaseAnalytics - FirebaseCore - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop + - FirebaseCrashlytics - FirebaseInstallations - Flipper - Flipper-DoubleConversion @@ -644,6 +656,7 @@ SPEC REPOS: - GoogleDataTransport - GoogleDataTransportCCTSupport - GoogleUtilities + - JitsiMeetSDK - libwebp - nanopb - OpenSSL-Universal @@ -688,8 +701,6 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec" glog: :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" - JitsiMeetSDK: - :git: https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git KeyCommands: :path: "../node_modules/react-native-keycommands" RCTRequired: @@ -698,6 +709,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/TypeSafety" React: :path: "../node_modules/react-native/" + React-callinvoker: + :path: "../node_modules/react-native/ReactCommon/callinvoker" React-Core: :path: "../node_modules/react-native/" React-CoreModules: @@ -772,8 +785,12 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-device-info" RNFastImage: :path: "../node_modules/@rocket.chat/react-native-fast-image" - RNFirebase: - :path: "../node_modules/react-native-firebase/ios" + RNFBAnalytics: + :path: "../node_modules/@react-native-firebase/analytics" + RNFBApp: + :path: "../node_modules/@react-native-firebase/app" + RNFBCrashlytics: + :path: "../node_modules/@react-native-firebase/crashlytics" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNImageCropPicker: @@ -819,125 +836,121 @@ EXTERNAL SOURCES: Yoga: :path: "../node_modules/react-native/ReactCommon/yoga" -CHECKOUT OPTIONS: - JitsiMeetSDK: - :commit: 9177aaa3afb379e17cc687887485e91e5cd24a49 - :git: https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git - SPEC CHECKSUMS: boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c - BugsnagReactNative: 14c1b59cfbf34fd5591b734bfec65a277b677ef8 + BugsnagReactNative: 98fb350df4bb0c94cce903023531a1a5cc11fa51 CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f - Crashlytics: 540b7e5f5da5a042647227a5e3ac51d85eed06df - DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 + DoubleConversion: cde416483dac037923206447da6e1454df403714 EXAppleAuthentication: 5b3da71bada29e2423d8ea27e5538ef0d75aba62 - EXAV: 2edd9cd30fe98d04b55325303c7ff01db02d1d0f - EXConstants: 5304709b1bea70a4828f48ba4c7fc3ec3b2d9b17 - EXFileSystem: cf4232ba7c62dc49b78c2d36005f97b6fddf0b01 - EXHaptics: 013b5065946d4dd7b46ea547b58072d45a206dbd - EXImageLoader: 5ad6896fa1ef2ee814b551873cbf7a7baccc694a - EXKeepAwake: d045bc2cf1ad5a04f0323cc7c894b95b414042e0 - EXLocalAuthentication: bbf1026cc289d729da4f29240dd7a8f6a14e4b20 - EXPermissions: 24b97f734ce9172d245a5be38ad9ccfcb6135964 - EXVideoThumbnails: be6984a3cda1e44c45b5c6278244e99855f99a0a - EXWebBrowser: 5902f99ac5ac551e5c82ff46f13a337b323aa9ea - Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74 - FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245 - FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e - Firebase: b28e55c60efd98963cd9011fe2fac5a10c2ba124 - FirebaseAnalytics: 7386fc2176e3f93ad8ef34b5b1f2b33a891e4962 - FirebaseCore: e610482f64097b0e9f056cd97bc6b33dfabcbb6a - FirebaseCoreDiagnostics: 4a773a47bd83bbd5a9b1ccf1ce7caa8b2d535e67 - FirebaseCoreDiagnosticsInterop: 296e2c5f5314500a850ad0b83e9e7c10b011a850 - FirebaseInstallations: 2119fb3e46b0a88bfdbf12562f855ee3252462fa - Flipper: 6c1f484f9a88d30ab3e272800d53688439e50f69 + EXAV: 86344030966e0da7e00556fbb97269d9ad16071d + EXConstants: f907b3b6ce16e20d1750f22af1e095e924574bcb + EXFileSystem: 76875135b61708b9afa7e6a89b72a60ba0fdfa20 + EXHaptics: 5428b344a216ca5d9df6ca8f65720b2a1ad9f109 + EXImageLoader: 02ca02c9cd5cc8a97b423207a73a791e0a86bea5 + EXKeepAwake: 8b0f68242f036b971f9f8976341823cbe6f50812 + EXLocalAuthentication: 985c65e08a6eb84f8f98b51f7435df138b18b9e8 + EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8 + EXVideoThumbnails: f70bdc5511749f3181028f5000bcb7be203c631d + EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4 + FBLazyVector: a50434c875bd42f2b1c99c712bda892a1dc659c7 + FBReactNativeSpec: 393853a536428e05a9da00b6290042f09809b15b + Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892 + FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4 + FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085 + FirebaseCoreDiagnostics: 4505e4d4009b1d93f605088ee7d7764d5f0d1c84 + FirebaseCrashlytics: 7d0fa02ea8842cc4b2ab07b0735edafde1ad77d6 + FirebaseInstallations: 3c520c951305cbf9ca54eb891ff9e6d1fd384881 + Flipper: 33585e2d9810fe5528346be33bcf71b37bb7ae13 Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3 Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 - FlipperKit: 6dc9b8f4ef60d9e5ded7f0264db299c91f18832e - Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 - glog: 1f3da668190260b06b429bb211bfbee5cd790c28 - GoogleAppMeasurement: 4c644d86835d827bab30ab6aabb9ecaf1f500735 - GoogleDataTransport: f6f8eba931df03ebd2232ff4645aa85f8f47b5ab - GoogleDataTransportCCTSupport: d70a561f7d236af529fee598835caad5e25f6d3d - GoogleUtilities: 39530bc0ad980530298e9c4af8549e991fd033b1 + FlipperKit: bc68102cd4952a258a23c9c1b316c7bec1fecf83 + Folly: b73c3869541e86821df3c387eb0af5f65addfab4 + glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 + GoogleAppMeasurement: 8cd1f289d60e629cf16ab03363b9e89c776b9651 + GoogleDataTransport: 9a8a16f79feffc7f42096743de2a7c4815e84020 + GoogleDataTransportCCTSupport: 489c1265d2c85b68187a83a911913d190012158d + GoogleUtilities: e121a3867449ce16b0e35ddf1797ea7a389ffdf2 JitsiMeetSDK: 2984eac1343690bf1c0c72bde75b48b0148d0f79 KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 nanopb: c43f40fadfe79e8b8db116583945847910cbabc9 OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355 - PromisesObjC: c119f3cd559f50b7ae681fa59dc1acd19173b7e6 - RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035 - RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce - React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3 - React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05 - React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0 - React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103 - React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161 - React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da - React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493 + PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75 + RCTRequired: d9b1a9e6fa097744ca3ede59f86a35096df7202b + RCTTypeSafety: c227cd061983e9e964115afbc4e8730d6a6f1395 + React: 86e972a20967ee4137aa19dc48319405927c2e94 + React-callinvoker: 87ee376c25277d74e164ff036b27084e343f3e69 + React-Core: f5ec03baf7ed58d9f3ee04a8f84e4c97ee8bf4c9 + React-CoreModules: 958898aa8c069280e866e35a2f29480a81fcf335 + React-cxxreact: 90de76b9b51575668ad7fd4e33a5a8c143beecc2 + React-jsi: b32a31da32e030f30bbf9a8d3a9c8325df9e793f + React-jsiexecutor: 7ab9cdcdd18d57652fb041f8a147fe9658d4e00a + React-jsinspector: 2e28bb487e42dda6c94dbfa0c648d1343767a0fb react-native-appearance: 0f0e5fc2fcef70e03d48c8fe6b00b9158c2ba8aa react-native-background-timer: 1f7d560647b40e6a60b01c452ba29c54bf581fc4 - react-native-cameraroll: 02e60e9af9273a3cc3b641632bf651189830aaf8 - react-native-document-picker: dd96ce05bf1453b110d7a3912097bf6d298d2cb6 + react-native-cameraroll: ae0a7c0cc8462508855707ff623b1e789b692865 + react-native-document-picker: 825552b827012282baf4b7cbf119d3b37a280c90 react-native-jitsi-meet: f89bcb2cfbd5b15403b9c40738036c4f1af45d05 react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227 react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d - react-native-safe-area-context: b11a34881faac509cad5578726c98161ad4d275c - react-native-slider: e51492f1264d882a8815b71c5870f8978e52887d - react-native-webview: cf5527893252b3b036eea024a1da6996f7344c74 - React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c - React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0 - React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71 - React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3 - React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2 - React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44 - React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a - React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d - React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256 - ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3 + react-native-safe-area-context: 344b969c45af3d8464d36e8dea264942992ef033 + react-native-slider: 0221b417686c5957f6e77cd9ac22c1478a165355 + react-native-webview: 679b6f400176e2ea8a785acf7ae16cf282e7d1eb + React-RCTActionSheet: 1702a1a85e550b5c36e2e03cb2bd3adea053de95 + React-RCTAnimation: ddda576010a878865a4eab83a78acd92176ef6a1 + React-RCTBlob: 34334384284c81577409d5205bd2b9ff594d8ab6 + React-RCTImage: e2a661266dca295cffb33909cc64675a2efedb26 + React-RCTLinking: cd39b9b5e9cbb9e827854e30dfa92d7db074cea8 + React-RCTNetwork: 16939b7e4058d6f662b304a1f61689e249a2bfcc + React-RCTSettings: 24726a62de0c326f9ebfc3838898a501b87ce711 + React-RCTText: 4f95d322b7e6da72817284abf8a2cdcec18b9cd8 + React-RCTVibration: f3a9123c244f35c40d3c9f3ec3f0b9e5717bb292 + ReactCommon: 2905859f84a94a381bb0d8dd3921ccb1a0047cb8 ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab ReactNativeKeyboardInput: c37e26821519869993b3b61844350feb9177ff37 ReactNativeKeyboardTrackingView: 02137fac3b2ebd330d74fa54ead48b14750a2306 rn-extensions-share: 8db79372089567cbc5aefe8444869bbc808578d3 rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba RNBootSplash: b3836aa90c5bec690c6cd3c9ab355fcf98d0fe1d - RNCAsyncStorage: cd7234ff15c010723ed7c499ae4eedc605eac1fd + RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36 RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f - RNDateTimePicker: 4bd49e09f91ca73d69119a9e1173b0d43b82f5e5 - RNDeviceInfo: e2102056bde3ad5d137fd029d8d431510a00486a + RNDateTimePicker: e386ff4ef3300964ed0cad97ce6f206e0effbfdb + RNDeviceInfo: ed8557a8bd6443cbc0ab5d375e6808a38a279744 RNFastImage: 35ae972d6727c84ee3f5c6897e07f84d0a3445e9 - RNFirebase: 37daa9a346d070f9f6ee1f3b4aaf4c8e3b1d5d1c + RNFBAnalytics: dae6d7b280ba61c96e1bbdd34aca3154388f025e + RNFBApp: 6fd8a7e757135d4168bf033a8812c241af7363a0 + RNFBCrashlytics: 88de72c2476b5868a892d9523b89b86c527c540e RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38 RNImageCropPicker: 38865ab4af1b0b2146ad66061196bc0184946855 RNLocalize: b6df30cc25ae736d37874f9bce13351db2f56796 - RNReanimated: 955cf4068714003d2f1a6e2bae3fb1118f359aff + RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43 RNRootView: 895a4813dedeaca82db2fa868ca1c333d790e494 - RNScreens: cf198f915f8a2bf163de94ca9f5bfc8d326c3706 + RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da RNUserDefaults: c421fd97ad06b35c16608c5d0fe675db353f632d - RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4 - SDWebImage: 84000f962cbfa70c07f19d2234cbfcf5d779b5dc + RNVectorIcons: da6fe858f5a65d7bbc3379540a889b0b12aa5976 + SDWebImage: cf6922231e95550934da2ada0f20f2becf2ceba9 SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8 - TOCropViewController: e9da34f484aedd4e5d5a8ab230ba217cfe16c729 - UMAppLoader: ee77a072f9e15128f777ccd6d2d00f52ab4387e6 - UMBarCodeScannerInterface: 9dc692b87e5f20fe277fa57aa47f45d418c3cc6c - UMCameraInterface: 625878bbf2ba188a8548675e1d1d2e438a653e6d - UMConstantsInterface: 64060cf86587bcd90b1dbd804cceb6d377a308c1 - UMCore: eb200e882eadafcd31ead290770835fd648c0945 - UMFaceDetectorInterface: d6677d6ddc9ab95a0ca857aa7f8ba76656cc770f - UMFileSystemInterface: c70ea7147198b9807080f3597f26236be49b0165 - UMFontInterface: d9d3b27af698c5389ae9e20b99ef56a083f491fb - UMImageLoaderInterface: 14dd2c46c67167491effc9e91250e9510f12709e - UMPermissionsInterface: 5e83a9167c177e4a0f0a3539345983cc749efb3e - UMReactNativeAdapter: 126da3486c1a1f11945b649d557d6c2ebb9407b2 - UMSensorsInterface: 48941f70175e2975af1a9386c6d6cb16d8126805 - UMTaskManagerInterface: cb890c79c63885504ddc0efd7a7d01481760aca2 - Yoga: 3ebccbdd559724312790e7742142d062476b698e + TOCropViewController: 20a14b6a7a098308bf369e7c8d700dc983a974e6 + UMAppLoader: 61049c8d55590b74e9ae1d5429bf68d96b4a2528 + UMBarCodeScannerInterface: e5e4c87797d3d01214e25cd1618866caf5d4f17f + UMCameraInterface: 415ac060034edecacdbbaa739c223e3f276e0056 + UMConstantsInterface: 1a52f2d884c95e8829439da13e36b7669a1a8fb4 + UMCore: d98083b522b08c0a8ba3992bc263c624ae5d887c + UMFaceDetectorInterface: 67c6c82451338da01a4bc00ec46365a2a8ea9057 + UMFileSystemInterface: 303d696ede28102a7e11d111808bd2ed2c5eb62f + UMFontInterface: 6edf1ee8bc55d2030766f8cf0a7b20a5d5a913b0 + UMImageLoaderInterface: 9cdbf3bab6a513bddd88505cb2340fe02d6a11c0 + UMPermissionsInterface: 019170ad655f464e3f8d23d2a8bcbda2e645cde4 + UMReactNativeAdapter: 538efe92e781b5d7678cf95b34c46f2d0989a557 + UMSensorsInterface: cb5bf31d52c4349f0ff9e3c049bbe4df0d80d383 + UMTaskManagerInterface: 80653f25c55d9e6d79d6a0a65589fa213feaee11 + Yoga: d5bd05a2b6b94c52323745c2c2b64557c8c66f64 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a -PODFILE CHECKSUM: 35d9478dd32cf502959b8efc14411ecf09c66c95 +PODFILE CHECKSUM: 55c04243097892160d63f79f3a23157165b7ac68 -COCOAPODS: 1.8.4 +COCOAPODS: 1.9.3 diff --git a/ios/Pods/Crashlytics/Crashlytics.framework/README b/ios/Pods/Crashlytics/Crashlytics.framework/README deleted file mode 100644 index 3ebf76719..000000000 --- a/ios/Pods/Crashlytics/Crashlytics.framework/README +++ /dev/null @@ -1 +0,0 @@ -We've now combined all our supported platforms into a single podspec. As a result, we moved our submit script to a new location for Cocoapods projects: ${PODS_ROOT}/Crashlytics/submit. To avoid breaking functionality that references the old location of the submit, we've placed this dummy script that calls to the correct location, while providing a helpful warning if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning! diff --git a/ios/Pods/Crashlytics/Crashlytics.framework/submit b/ios/Pods/Crashlytics/Crashlytics.framework/submit deleted file mode 100755 index b7de4e377..000000000 --- a/ios/Pods/Crashlytics/Crashlytics.framework/submit +++ /dev/null @@ -1,6 +0,0 @@ -if [[ -z $PODS_ROOT ]]; then -echo "error: The submit binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Crashlytics/submit'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the submit binary to fix this issue." -else -echo "warning: The submit script is now located at '$"{"PODS_ROOT"}"/Crashlytics/submit'. To remove this warning, update your path to point to this new location." -sh "${PODS_ROOT}/Crashlytics/submit" "$@" -fi diff --git a/ios/Pods/Crashlytics/README.md b/ios/Pods/Crashlytics/README.md deleted file mode 100644 index 996af5078..000000000 --- a/ios/Pods/Crashlytics/README.md +++ /dev/null @@ -1,23 +0,0 @@ - -# Crashlytics - -## Overview - -[Crashlytics](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) offers the most powerful, yet lightest weight crash reporting solution for iOS. - - -## Setup - -To start using Crashlytics, there are two options: - -1) The recommended way is to go to the [Firebase Crashlytics Docs](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) and follow the directions there. - -2) If you aren't using Firebase yet, go to [Fabric Kits](https://fabric.io/kits), and follow the directions for Crashlytics. - - -## Resources - -* [API Reference](https://firebase.google.com/docs/reference/ios/crashlytics/api/reference/Classes) -* [Forums](https://stackoverflow.com/questions/tagged/google-fabric) -* [Website](https://firebase.google.com/docs/crashlytics) -* Follow us on Twitter: [@crashlytics](https://twitter.com/crashlytics) diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics deleted file mode 100755 index 214fb9051..000000000 Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics and /dev/null differ diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h deleted file mode 100644 index 6ec011d93..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// ANSCompatibility.h -// AnswersKit -// -// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. -// - -#pragma once - -#if !__has_feature(nullability) -#define nonnull -#define nullable -#define _Nullable -#define _Nonnull -#endif - -#ifndef NS_ASSUME_NONNULL_BEGIN -#define NS_ASSUME_NONNULL_BEGIN -#endif - -#ifndef NS_ASSUME_NONNULL_END -#define NS_ASSUME_NONNULL_END -#endif - -#if __has_feature(objc_generics) -#define ANS_GENERIC_NSARRAY(type) NSArray -#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary -#else -#define ANS_GENERIC_NSARRAY(type) NSArray -#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary -#endif diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h deleted file mode 100644 index 8deacbee5..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h +++ /dev/null @@ -1,210 +0,0 @@ -// -// Answers.h -// Crashlytics -// -// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. -// - -#import -#import "ANSCompatibility.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * This class exposes the Answers Events API, allowing you to track key - * user user actions and metrics in your app. - */ -@interface Answers : NSObject - -/** - * Log a Sign Up event to see users signing up for your app in real-time, understand how - * many users are signing up with different methods and their success rate signing up. - * - * @param signUpMethodOrNil The method by which a user logged in, e.g. Twitter or Digits. - * @param signUpSucceededOrNil The ultimate success or failure of the login - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logSignUpWithMethod:(nullable NSString *)signUpMethodOrNil - success:(nullable NSNumber *)signUpSucceededOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log an Log In event to see users logging into your app in real-time, understand how many - * users are logging in with different methods and their success rate logging into your app. - * - * @param loginMethodOrNil The method by which a user logged in, e.g. email, Twitter or Digits. - * @param loginSucceededOrNil The ultimate success or failure of the login - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logLoginWithMethod:(nullable NSString *)loginMethodOrNil - success:(nullable NSNumber *)loginSucceededOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Share event to see users sharing from your app in real-time, letting you - * understand what content they're sharing from the type or genre down to the specific id. - * - * @param shareMethodOrNil The method by which a user shared, e.g. email, Twitter, SMS. - * @param contentNameOrNil The human readable name for this piece of content. - * @param contentTypeOrNil The type of content shared. - * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item. - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logShareWithMethod:(nullable NSString *)shareMethodOrNil - contentName:(nullable NSString *)contentNameOrNil - contentType:(nullable NSString *)contentTypeOrNil - contentId:(nullable NSString *)contentIdOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log an Invite Event to track how users are inviting other users into - * your application. - * - * @param inviteMethodOrNil The method of invitation, e.g. GameCenter, Twitter, email. - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logInviteWithMethod:(nullable NSString *)inviteMethodOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Purchase event to see your revenue in real-time, understand how many users are making purchases, see which - * items are most popular, and track plenty of other important purchase-related metrics. - * - * @param itemPriceOrNil The purchased item's price. - * @param currencyOrNil The ISO4217 currency code. Example: USD - * @param purchaseSucceededOrNil Was the purchase successful or unsuccessful - * @param itemNameOrNil The human-readable form of the item's name. Example: - * @param itemTypeOrNil The type, or genre of the item. Example: Song - * @param itemIdOrNil The machine-readable, unique item identifier Example: SKU - * @param customAttributesOrNil A dictionary of custom attributes to associate with this purchase. - */ -+ (void)logPurchaseWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil - currency:(nullable NSString *)currencyOrNil - success:(nullable NSNumber *)purchaseSucceededOrNil - itemName:(nullable NSString *)itemNameOrNil - itemType:(nullable NSString *)itemTypeOrNil - itemId:(nullable NSString *)itemIdOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Level Start Event to track where users are in your game. - * - * @param levelNameOrNil The level name - * @param customAttributesOrNil A dictionary of custom attributes to associate with this level start event. - */ -+ (void)logLevelStart:(nullable NSString *)levelNameOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Level End event to track how users are completing levels in your game. - * - * @param levelNameOrNil The name of the level completed, E.G. "1" or "Training" - * @param scoreOrNil The score the user completed the level with. - * @param levelCompletedSuccesfullyOrNil A boolean representing whether or not the level was completed successfully. - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logLevelEnd:(nullable NSString *)levelNameOrNil - score:(nullable NSNumber *)scoreOrNil - success:(nullable NSNumber *)levelCompletedSuccesfullyOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log an Add to Cart event to see users adding items to a shopping cart in real-time, understand how - * many users start the purchase flow, see which items are most popular, and track plenty of other important - * purchase-related metrics. - * - * @param itemPriceOrNil The purchased item's price. - * @param currencyOrNil The ISO4217 currency code. Example: USD - * @param itemNameOrNil The human-readable form of the item's name. Example: - * @param itemTypeOrNil The type, or genre of the item. Example: Song - * @param itemIdOrNil The machine-readable, unique item identifier Example: SKU - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logAddToCartWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil - currency:(nullable NSString *)currencyOrNil - itemName:(nullable NSString *)itemNameOrNil - itemType:(nullable NSString *)itemTypeOrNil - itemId:(nullable NSString *)itemIdOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Start Checkout event to see users moving through the purchase funnel in real-time, understand how many - * users are doing this and how much they're spending per checkout, and see how it related to other important - * purchase-related metrics. - * - * @param totalPriceOrNil The total price of the cart. - * @param currencyOrNil The ISO4217 currency code. Example: USD - * @param itemCountOrNil The number of items in the cart. - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logStartCheckoutWithPrice:(nullable NSDecimalNumber *)totalPriceOrNil - currency:(nullable NSString *)currencyOrNil - itemCount:(nullable NSNumber *)itemCountOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Rating event to see users rating content within your app in real-time and understand what - * content is most engaging, from the type or genre down to the specific id. - * - * @param ratingOrNil The integer rating given by the user. - * @param contentNameOrNil The human readable name for this piece of content. - * @param contentTypeOrNil The type of content shared. - * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item. - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logRating:(nullable NSNumber *)ratingOrNil - contentName:(nullable NSString *)contentNameOrNil - contentType:(nullable NSString *)contentTypeOrNil - contentId:(nullable NSString *)contentIdOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Content View event to see users viewing content within your app in real-time and - * understand what content is most engaging, from the type or genre down to the specific id. - * - * @param contentNameOrNil The human readable name for this piece of content. - * @param contentTypeOrNil The type of content shared. - * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item. - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logContentViewWithName:(nullable NSString *)contentNameOrNil - contentType:(nullable NSString *)contentTypeOrNil - contentId:(nullable NSString *)contentIdOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Search event allows you to see users searching within your app in real-time and understand - * exactly what they're searching for. - * - * @param queryOrNil The user's query. - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. - */ -+ (void)logSearchWithQuery:(nullable NSString *)queryOrNil - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -/** - * Log a Custom Event to see user actions that are uniquely important for your app in real-time, to see how often - * they're performing these actions with breakdowns by different categories you add. Use a human-readable name for - * the name of the event, since this is how the event will appear in Answers. - * - * @param eventName The human-readable name for the event. - * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. Attribute keys - * must be NSString and values must be NSNumber or NSString. - * @discussion How we treat NSNumbers: - * We will provide information about the distribution of values over time. - * - * How we treat NSStrings: - * NSStrings are used as categorical data, allowing comparison across different category values. - * Strings are limited to a maximum length of 100 characters, attributes over this length will be - * truncated. - * - * When tracking the Tweet views to better understand user engagement, sending the tweet's length - * and the type of media present in the tweet allows you to track how tweet length and the type of media influence - * engagement. - */ -+ (void)logCustomEventWithName:(NSString *)eventName - customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h deleted file mode 100644 index 1526b0dca..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// CLSAttributes.h -// Crashlytics -// -// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. -// - -#pragma once - -#define CLS_DEPRECATED(x) __attribute__ ((deprecated(x))) - -#if !__has_feature(nullability) - #define nonnull - #define nullable - #define _Nullable - #define _Nonnull -#endif - -#ifndef NS_ASSUME_NONNULL_BEGIN - #define NS_ASSUME_NONNULL_BEGIN -#endif - -#ifndef NS_ASSUME_NONNULL_END - #define NS_ASSUME_NONNULL_END -#endif - -#if __has_feature(objc_generics) - #define CLS_GENERIC_NSARRAY(type) NSArray - #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary -#else - #define CLS_GENERIC_NSARRAY(type) NSArray - #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary -#endif diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h deleted file mode 100644 index 59590d546..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// CLSLogging.h -// Crashlytics -// -// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. -// -#ifdef __OBJC__ -#import "CLSAttributes.h" -#import - -NS_ASSUME_NONNULL_BEGIN -#endif - - - -/** - * - * The CLS_LOG macro provides as easy way to gather more information in your log messages that are - * sent with your crash data. CLS_LOG prepends your custom log message with the function name and - * line number where the macro was used. If your app was built with the DEBUG preprocessor macro - * defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog. - * If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only. - * - * Example output: - * -[AppDelegate login:] line 134 $ login start - * - * If you would like to change this macro, create a new header file, unset our define and then define - * your own version. Make sure this new header file is imported after the Crashlytics header file. - * - * #undef CLS_LOG - * #define CLS_LOG(__FORMAT__, ...) CLSNSLog... - * - **/ -#ifdef __OBJC__ -#ifdef DEBUG -#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) -#else -#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) -#endif -#endif - -/** - * - * Add logging that will be sent with your crash data. This logging will not show up in the system.log - * and will only be visible in your Crashlytics dashboard. - * - **/ - -#ifdef __OBJC__ -OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); -OBJC_EXTERN void CLSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0); - -/** - * - * Add logging that will be sent with your crash data. This logging will show up in the system.log - * and your Crashlytics dashboard. It is not recommended for Release builds. - * - **/ -OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2); -OBJC_EXTERN void CLSNSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0); - - -NS_ASSUME_NONNULL_END -#endif diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h deleted file mode 100644 index a8ff3b0b9..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// CLSReport.h -// Crashlytics -// -// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. -// - -#import -#import "CLSAttributes.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details. - **/ -@protocol CLSCrashReport - -@property (nonatomic, copy, readonly) NSString *identifier; -@property (nonatomic, copy, readonly) NSDictionary *customKeys; -@property (nonatomic, copy, readonly) NSString *bundleVersion; -@property (nonatomic, copy, readonly) NSString *bundleShortVersionString; -@property (nonatomic, readonly, nullable) NSDate *crashedOnDate; -@property (nonatomic, copy, readonly) NSString *OSVersion; -@property (nonatomic, copy, readonly) NSString *OSBuildVersion; - -@end - -/** - * The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can - * use this class to get information about the event, and can also set some values after the - * event has occurred. - **/ -@interface CLSReport : NSObject - -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)new NS_UNAVAILABLE; - -/** - * Returns the session identifier for the report. - **/ -@property (nonatomic, copy, readonly) NSString *identifier; - -/** - * Returns the custom key value data for the report. - **/ -@property (nonatomic, copy, readonly) NSDictionary *customKeys; - -/** - * Returns the CFBundleVersion of the application that generated the report. - **/ -@property (nonatomic, copy, readonly) NSString *bundleVersion; - -/** - * Returns the CFBundleShortVersionString of the application that generated the report. - **/ -@property (nonatomic, copy, readonly) NSString *bundleShortVersionString; - -/** - * Returns the date that the report was created. - **/ -@property (nonatomic, copy, readonly) NSDate *dateCreated; - -/** - * Returns the os version that the application crashed on. - **/ -@property (nonatomic, copy, readonly) NSString *OSVersion; - -/** - * Returns the os build version that the application crashed on. - **/ -@property (nonatomic, copy, readonly) NSString *OSBuildVersion; - -/** - * Returns YES if the report contains any crash information, otherwise returns NO. - **/ -@property (nonatomic, assign, readonly) BOOL isCrash; - -/** - * You can use this method to set, after the event, additional custom keys. The rules - * and semantics for this method are the same as those documented in Crashlytics.h. Be aware - * that the maximum size and count of custom keys is still enforced, and you can overwrite keys - * and/or cause excess keys to be deleted by using this method. - **/ -- (void)setObjectValue:(nullable id)value forKey:(NSString *)key; - -/** - * Record an application-specific user identifier. See Crashlytics.h for details. - **/ -@property (nonatomic, copy, nullable) NSString * userIdentifier; - -/** - * Record a user name. See Crashlytics.h for details. - **/ -@property (nonatomic, copy, nullable) NSString * userName; - -/** - * Record a user email. See Crashlytics.h for details. - **/ -@property (nonatomic, copy, nullable) NSString * userEmail; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h deleted file mode 100644 index cdb5596cc..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// CLSStackFrame.h -// Crashlytics -// -// Copyright 2015 Crashlytics, Inc. All rights reserved. -// - -#import -#import "CLSAttributes.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * - * This class is used in conjunction with -[Crashlytics recordCustomExceptionName:reason:frameArray:] to - * record information about non-ObjC/C++ exceptions. All information included here will be displayed - * in the Crashlytics UI, and can influence crash grouping. Be particularly careful with the use of the - * address property. If set, Crashlytics will attempt symbolication and could overwrite other properities - * in the process. - * - **/ -@interface CLSStackFrame : NSObject - -+ (instancetype)stackFrame; -+ (instancetype)stackFrameWithAddress:(NSUInteger)address; -+ (instancetype)stackFrameWithSymbol:(NSString *)symbol; - -@property (nonatomic, copy, nullable) NSString *symbol; -@property (nonatomic, copy, nullable) NSString *rawSymbol; -@property (nonatomic, copy, nullable) NSString *library; -@property (nonatomic, copy, nullable) NSString *fileName; -@property (nonatomic, assign) uint32_t lineNumber; -@property (nonatomic, assign) uint64_t offset; -@property (nonatomic, assign) uint64_t address; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h deleted file mode 100644 index 7104ca812..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h +++ /dev/null @@ -1,288 +0,0 @@ -// -// Crashlytics.h -// Crashlytics -// -// Copyright (c) 2015 Crashlytics, Inc. All rights reserved. -// - -#import - -#import "CLSAttributes.h" -#import "CLSLogging.h" -#import "CLSReport.h" -#import "CLSStackFrame.h" -#import "Answers.h" - -NS_ASSUME_NONNULL_BEGIN - -@protocol CrashlyticsDelegate; - -/** - * Crashlytics. Handles configuration and initialization of Crashlytics. - * - * Note: The Crashlytics class cannot be subclassed. If this is causing you pain for - * testing, we suggest using either a wrapper class or a protocol extension. - */ -@interface Crashlytics : NSObject - -@property (nonatomic, readonly, copy) NSString *APIKey; -@property (nonatomic, readonly, copy) NSString *version; -@property (nonatomic, assign) BOOL debugMode; - -/** - * - * The delegate can be used to influence decisions on reporting and behavior, as well as reacting - * to previous crashes. - * - * Make certain that the delegate is setup before starting Crashlytics with startWithAPIKey:... or - * via +[Fabric with:...]. Failure to do will result in missing any delegate callbacks that occur - * synchronously during start. - * - **/ -@property (nonatomic, assign, nullable) id delegate; - -/** - * The recommended way to install Crashlytics into your application is to place a call to +startWithAPIKey: - * in your -application:didFinishLaunchingWithOptions: or -applicationDidFinishLaunching: - * method. - * - * Note: Starting with 3.0, the submission process has been significantly improved. The delay parameter - * is no longer required to throttle submissions on launch, performance will be great without it. - * - * @param apiKey The Crashlytics API Key for this app - * - * @return The singleton Crashlytics instance - */ -+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey; -+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey: instead."); - -/** - * If you need the functionality provided by the CrashlyticsDelegate protocol, you can use - * these convenience methods to activate the framework and set the delegate in one call. - * - * @param apiKey The Crashlytics API Key for this app - * @param delegate A delegate object which conforms to CrashlyticsDelegate. - * - * @return The singleton Crashlytics instance - */ -+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id)delegate; -+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id)delegate afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey:delegate: instead."); - -/** - * Access the singleton Crashlytics instance. - * - * @return The singleton Crashlytics instance - */ -+ (Crashlytics *)sharedInstance; - -/** - * The easiest way to cause a crash - great for testing! - */ -- (void)crash; - -/** - * The easiest way to cause a crash with an exception - great for testing. - */ -- (void)throwException; - -/** - * Specify a user identifier which will be visible in the Crashlytics UI. - * - * Many of our customers have requested the ability to tie crashes to specific end-users of their - * application in order to facilitate responses to support requests or permit the ability to reach - * out for more information. We allow you to specify up to three separate values for display within - * the Crashlytics UI - but please be mindful of your end-user's privacy. - * - * We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record - * in your system. This could be a database id, hash, or other value that is meaningless to a - * third-party observer but can be indexed and queried by you. - * - * Optionally, you may also specify the end-user's name or username, as well as email address if you - * do not have a system that works well with obscured identifiers. - * - * Pursuant to our EULA, this data is transferred securely throughout our system and we will not - * disseminate end-user data unless required to by law. That said, if you choose to provide end-user - * contact information, we strongly recommend that you disclose this in your application's privacy - * policy. Data privacy is of our utmost concern. - * - * @param identifier An arbitrary user identifier string which ties an end-user to a record in your system. - */ -- (void)setUserIdentifier:(nullable NSString *)identifier; - -/** - * Specify a user name which will be visible in the Crashlytics UI. - * Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs. - * @see setUserIdentifier: - * - * @param name An end user's name. - */ -- (void)setUserName:(nullable NSString *)name; - -/** - * Specify a user email which will be visible in the Crashlytics UI. - * Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs. - * - * @see setUserIdentifier: - * - * @param email An end user's email address. - */ -- (void)setUserEmail:(nullable NSString *)email; - -+ (void)setUserIdentifier:(nullable NSString *)identifier CLS_DEPRECATED("Please access this method via +sharedInstance"); -+ (void)setUserName:(nullable NSString *)name CLS_DEPRECATED("Please access this method via +sharedInstance"); -+ (void)setUserEmail:(nullable NSString *)email CLS_DEPRECATED("Please access this method via +sharedInstance"); - -/** - * Set a value for a for a key to be associated with your crash data which will be visible in the Crashlytics UI. - * When setting an object value, the object is converted to a string. This is typically done by calling - * -[NSObject description]. - * - * @param value The object to be associated with the key - * @param key The key with which to associate the value - */ -- (void)setObjectValue:(nullable id)value forKey:(NSString *)key; - -/** - * Set an int value for a key to be associated with your crash data which will be visible in the Crashlytics UI. - * - * @param value The integer value to be set - * @param key The key with which to associate the value - */ -- (void)setIntValue:(int)value forKey:(NSString *)key; - -/** - * Set an BOOL value for a key to be associated with your crash data which will be visible in the Crashlytics UI. - * - * @param value The BOOL value to be set - * @param key The key with which to associate the value - */ -- (void)setBoolValue:(BOOL)value forKey:(NSString *)key; - -/** - * Set an float value for a key to be associated with your crash data which will be visible in the Crashlytics UI. - * - * @param value The float value to be set - * @param key The key with which to associate the value - */ -- (void)setFloatValue:(float)value forKey:(NSString *)key; - -+ (void)setObjectValue:(nullable id)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance"); -+ (void)setIntValue:(int)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance"); -+ (void)setBoolValue:(BOOL)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance"); -+ (void)setFloatValue:(float)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance"); - -/** - * This method can be used to record a single exception structure in a report. This is particularly useful - * when your code interacts with non-native languages like Lua, C#, or Javascript. This call can be - * expensive and should only be used shortly before process termination. This API is not intended be to used - * to log NSException objects. All safely-reportable NSExceptions are automatically captured by - * Crashlytics. - * - * @param name The name of the custom exception - * @param reason The reason this exception occurred - * @param frameArray An array of CLSStackFrame objects - */ -- (void)recordCustomExceptionName:(NSString *)name reason:(nullable NSString *)reason frameArray:(CLS_GENERIC_NSARRAY(CLSStackFrame *) *)frameArray; - -/** - * - * This allows you to record a non-fatal event, described by an NSError object. These events will be grouped and - * displayed similarly to crashes. Keep in mind that this method can be expensive. Also, the total number of - * NSErrors that can be recorded during your app's life-cycle is limited by a fixed-size circular buffer. If the - * buffer is overrun, the oldest data is dropped. Errors are relayed to Crashlytics on a subsequent launch - * of your application. - * - * You can also use the -recordError:withAdditionalUserInfo: to include additional context not represented - * by the NSError instance itself. - * - **/ -- (void)recordError:(NSError *)error; -- (void)recordError:(NSError *)error withAdditionalUserInfo:(nullable CLS_GENERIC_NSDICTIONARY(NSString *, id) *)userInfo; - -- (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:"); -- (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:"); -+ (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:"); -+ (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:"); - -@end - -/** - * - * The CrashlyticsDelegate protocol provides a mechanism for your application to take - * action on events that occur in the Crashlytics crash reporting system. You can make - * use of these calls by assigning an object to the Crashlytics' delegate property directly, - * or through the convenience +startWithAPIKey:delegate: method. - * - */ -@protocol CrashlyticsDelegate -@optional - - -- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:"); -- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id )crash CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:"); - -/** - * - * Called when a Crashlytics instance has determined that the last execution of the - * application resulted in a saved report. This is called synchronously on Crashlytics - * initialization. Your delegate must invoke the completionHandler, but does not need to do so - * synchronously, or even on the main thread. Invoking completionHandler with NO will cause the - * detected report to be deleted and not submitted to Crashlytics. This is useful for - * implementing permission prompts, or other more-complex forms of logic around submitting crashes. - * - * Instead of using this method, you should try to make use of -crashlyticsDidDetectReportForLastExecution: - * if you can. - * - * @warning Failure to invoke the completionHandler will prevent submissions from being reported. Watch out. - * - * @warning Just implementing this delegate method will disable all forms of synchronous report submission. This can - * impact the reliability of reporting crashes very early in application launch. - * - * @param report The CLSReport object representing the last detected report - * @param completionHandler The completion handler to call when your logic has completed. - * - */ -- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report completionHandler:(void (^)(BOOL submit))completionHandler; - -/** - * - * Called when a Crashlytics instance has determined that the last execution of the - * application resulted in a saved report. This method differs from - * -crashlyticsDidDetectReportForLastExecution:completionHandler: in three important ways: - * - * - it is not called synchronously during initialization - * - it does not give you the ability to prevent the report from being submitted - * - the report object itself is immutable - * - * Thanks to these limitations, making use of this method does not impact reporting - * reliabilty in any way. - * - * @param report The read-only CLSReport object representing the last detected report - * - */ - -- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report; - -/** - * If your app is running on an OS that supports it (OS X 10.9+, iOS 7.0+), Crashlytics will submit - * most reports using out-of-process background networking operations. This results in a significant - * improvement in reliability of reporting, as well as power and performance wins for your users. - * If you don't want this functionality, you can disable by returning NO from this method. - * - * @warning Background submission is not supported for extensions on iOS or OS X. - * - * @param crashlytics The Crashlytics singleton instance - * - * @return Return NO if you don't want out-of-process background network operations. - * - */ -- (BOOL)crashlyticsCanUseBackgroundSessions:(Crashlytics *)crashlytics; - -@end - -/** - * `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, use Crashlytics.sharedInstance() - */ -#define CrashlyticsKit [Crashlytics sharedInstance] - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist deleted file mode 100644 index d86059be5..000000000 Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist and /dev/null differ diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap deleted file mode 100644 index da0845e39..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap +++ /dev/null @@ -1,14 +0,0 @@ -framework module Crashlytics { - header "Crashlytics.h" - header "Answers.h" - header "ANSCompatibility.h" - header "CLSLogging.h" - header "CLSReport.h" - header "CLSStackFrame.h" - header "CLSAttributes.h" - - export * - - link "z" - link "c++" -} diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/run b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/run deleted file mode 100755 index 736cd2ff5..000000000 --- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/run +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh - -# run -# -# Copyright (c) 2015 Crashlytics. All rights reserved. -# -# -# This script is meant to be run as a Run Script in the "Build Phases" section -# of your Xcode project. It sends debug symbols to symbolicate stacktraces, -# sends build events to track versions, and onboard apps for Crashlytics. -# -# This script calls upload-symbols twice: -# -# 1) First it calls upload-symbols synchronously in "validation" mode. If the -# script finds issues with the build environment, it will report errors to Xcode. -# In validation mode it exits before doing any time consuming work. -# -# 2) Then it calls upload-symbols in the background to actually send the build -# event and upload symbols. It does this in the background so that it doesn't -# slow down your builds. If an error happens here, you won't see it in Xcode. -# -# You can find the output for the background execution in Console.app, by -# searching for "upload-symbols". -# -# If you want verbose output, you can pass the --debug flag to this script -# - -# Figure out where we're being called from -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) - -# If the first argument is specified without a dash, treat it as the Fabric API -# Key and add it as an argument -if [ -z "$1" ] || [[ $1 == -* ]]; then - API_KEY_ARG="" -else - API_KEY_ARG="-a $1"; shift -fi - -# If a second argument is specified without a dash, treat it as the Build Secret -# and add it as an argument -if [ -z "$1" ] || [[ $1 == -* ]]; then - BUILD_SECRET_ARG="" -else - BUILD_SECRET_ARG="-bs $1"; shift -fi - -# Build up the arguments list, passing through any flags added after the -# API Key and Build Secret -ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@" -VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate" -UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase" - -# Quote the path to handle folders with special characters -COMMAND_PATH="\"$DIR/upload-symbols\" " - -# Ensure params are as expected, run in sync mode to validate, -# and cause a build error if validation fails -eval $COMMAND_PATH$VALIDATE_ARGUMENTS -return_code=$? - -if [[ $return_code != 0 ]]; then - exit $return_code -fi - -# Verification passed, convert and upload cSYMs in the background to prevent -# build delays -# -# Note: Validation is performed again at this step before upload -# -# Note: Output can still be found in Console.app, by searching for -# "upload-symbols" -# -eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 & diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/submit b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/submit deleted file mode 100755 index 3fda5cff1..000000000 Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/submit and /dev/null differ diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols deleted file mode 100755 index 5af65decc..000000000 Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols and /dev/null differ diff --git a/ios/Pods/Crashlytics/submit b/ios/Pods/Crashlytics/submit deleted file mode 100755 index 3fda5cff1..000000000 Binary files a/ios/Pods/Crashlytics/submit and /dev/null differ diff --git a/ios/Pods/Fabric/Fabric.framework/README b/ios/Pods/Fabric/Fabric.framework/README deleted file mode 100644 index 3b1fbe24e..000000000 --- a/ios/Pods/Fabric/Fabric.framework/README +++ /dev/null @@ -1 +0,0 @@ -We've now combined all our supported platforms into a single podspec. As a result, we moved our run script to a new location for Cocoapods projects: ${PODS_ROOT}/Fabric/run. To avoid breaking builds that reference the old location of the run script, we've placed this dummy script that calls to the correct location, while providing a helpful warning in Xcode if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning! diff --git a/ios/Pods/Fabric/Fabric.framework/run b/ios/Pods/Fabric/Fabric.framework/run deleted file mode 100755 index b9edd17f9..000000000 --- a/ios/Pods/Fabric/Fabric.framework/run +++ /dev/null @@ -1,6 +0,0 @@ -if [[ -z $PODS_ROOT ]]; then - echo "error: The run binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Fabric/run'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the run binary to fix this issue." -else - echo "warning: The run script is now located at '$"{"PODS_ROOT"}"/Fabric/run'. To remove this warning, update your Run Script Build Phase to point to this new location." - sh "${PODS_ROOT}/Fabric/run" "$@" -fi diff --git a/ios/Pods/Fabric/README.md b/ios/Pods/Fabric/README.md deleted file mode 100644 index e99ce8344..000000000 --- a/ios/Pods/Fabric/README.md +++ /dev/null @@ -1,27 +0,0 @@ - -# Fabric - -## Overview - -[Fabric](https://get.fabric.io) provides developers with the tools they need to build the best apps. Developed and maintained by Google and the team that built Crashlytics. - -For a full list of SDKs provided through Fabric visit [https://fabric.io/kits](https://fabric.io/kits). - -To follow the migration to Firebase, check out the [Fabric Roadmap](https://get.fabric.io/roadmap). - - -## Setup - -Fabric is a dependency for the Crashlytics SDK. To start using Crashlytics, there are two options: - -1) The recommended way is to go to the [Firebase Crashlytics Docs](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) and follow the directions there. - -2) If you aren't using Firebase yet, go to [Fabric Kits](https://fabric.io/kits), and follow the directions for Crashlytics. - - -## Resources - -* [Documentation](https://docs.fabric.io/) -* [Forums](https://stackoverflow.com/questions/tagged/google-fabric) -* [Website](https://get.fabric.io) -* Follow us on Twitter: [@fabric](https://twitter.com/fabric) diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Fabric b/ios/Pods/Fabric/iOS/Fabric.framework/Fabric deleted file mode 100755 index aa394a3de..000000000 Binary files a/ios/Pods/Fabric/iOS/Fabric.framework/Fabric and /dev/null differ diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h b/ios/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h deleted file mode 100644 index 3a9355a7c..000000000 --- a/ios/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// FABAttributes.h -// Fabric -// -// Copyright (C) 2015 Twitter, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#pragma once - -#define FAB_UNAVAILABLE(x) __attribute__((unavailable(x))) - -#if !__has_feature(nullability) - #define nonnull - #define nullable - #define _Nullable - #define _Nonnull -#endif - -#ifndef NS_ASSUME_NONNULL_BEGIN - #define NS_ASSUME_NONNULL_BEGIN -#endif - -#ifndef NS_ASSUME_NONNULL_END - #define NS_ASSUME_NONNULL_END -#endif - - -/** - * The following macros are defined here to provide - * backwards compatability. If you are still using - * them you should migrate to the native nullability - * macros. - */ -#define fab_nullable nullable -#define fab_nonnull nonnull -#define FAB_NONNULL __fab_nonnull -#define FAB_NULLABLE __fab_nullable -#define FAB_START_NONNULL NS_ASSUME_NONNULL_BEGIN -#define FAB_END_NONNULL NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h b/ios/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h deleted file mode 100644 index ecbdb53b8..000000000 --- a/ios/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h +++ /dev/null @@ -1,82 +0,0 @@ -// -// Fabric.h -// Fabric -// -// Copyright (C) 2015 Twitter, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import -#import "FABAttributes.h" - -NS_ASSUME_NONNULL_BEGIN - -#if TARGET_OS_IPHONE -#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 - #error "Fabric's minimum iOS version is 6.0" -#endif -#else -#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070 - #error "Fabric's minimum OS X version is 10.7" -#endif -#endif - -/** - * Fabric Base. Coordinates configuration and starts all provided kits. - */ -@interface Fabric : NSObject - -/** - * Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use. - * - * For example, in Objective-C: - * - * `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];` - * - * Swift: - * - * `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])` - * - * Only the first call to this method is honored. Subsequent calls are no-ops. - * - * @param kitClasses An array of kit Class objects - * - * @return Returns the shared Fabric instance. In most cases this can be ignored. - */ -+ (instancetype)with:(NSArray *)kitClasses; - -/** - * Returns the Fabric singleton object. - */ -+ (instancetype)sharedSDK; - -/** - * This BOOL enables or disables debug logging, such as kit version information. The default value is NO. - */ -@property (nonatomic, assign) BOOL debug; - -/** - * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. - */ -- (id)init FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance."); - -/** - * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance. - */ -+ (instancetype)new FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance."); - -@end - -NS_ASSUME_NONNULL_END - diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Info.plist b/ios/Pods/Fabric/iOS/Fabric.framework/Info.plist deleted file mode 100644 index a617b035f..000000000 Binary files a/ios/Pods/Fabric/iOS/Fabric.framework/Info.plist and /dev/null differ diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap b/ios/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap deleted file mode 100644 index 2a312239d..000000000 --- a/ios/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap +++ /dev/null @@ -1,6 +0,0 @@ -framework module Fabric { - umbrella header "Fabric.h" - - export * - module * { export * } -} \ No newline at end of file diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/run b/ios/Pods/Fabric/iOS/Fabric.framework/run deleted file mode 100755 index 736cd2ff5..000000000 --- a/ios/Pods/Fabric/iOS/Fabric.framework/run +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh - -# run -# -# Copyright (c) 2015 Crashlytics. All rights reserved. -# -# -# This script is meant to be run as a Run Script in the "Build Phases" section -# of your Xcode project. It sends debug symbols to symbolicate stacktraces, -# sends build events to track versions, and onboard apps for Crashlytics. -# -# This script calls upload-symbols twice: -# -# 1) First it calls upload-symbols synchronously in "validation" mode. If the -# script finds issues with the build environment, it will report errors to Xcode. -# In validation mode it exits before doing any time consuming work. -# -# 2) Then it calls upload-symbols in the background to actually send the build -# event and upload symbols. It does this in the background so that it doesn't -# slow down your builds. If an error happens here, you won't see it in Xcode. -# -# You can find the output for the background execution in Console.app, by -# searching for "upload-symbols". -# -# If you want verbose output, you can pass the --debug flag to this script -# - -# Figure out where we're being called from -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) - -# If the first argument is specified without a dash, treat it as the Fabric API -# Key and add it as an argument -if [ -z "$1" ] || [[ $1 == -* ]]; then - API_KEY_ARG="" -else - API_KEY_ARG="-a $1"; shift -fi - -# If a second argument is specified without a dash, treat it as the Build Secret -# and add it as an argument -if [ -z "$1" ] || [[ $1 == -* ]]; then - BUILD_SECRET_ARG="" -else - BUILD_SECRET_ARG="-bs $1"; shift -fi - -# Build up the arguments list, passing through any flags added after the -# API Key and Build Secret -ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@" -VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate" -UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase" - -# Quote the path to handle folders with special characters -COMMAND_PATH="\"$DIR/upload-symbols\" " - -# Ensure params are as expected, run in sync mode to validate, -# and cause a build error if validation fails -eval $COMMAND_PATH$VALIDATE_ARGUMENTS -return_code=$? - -if [[ $return_code != 0 ]]; then - exit $return_code -fi - -# Verification passed, convert and upload cSYMs in the background to prevent -# build delays -# -# Note: Validation is performed again at this step before upload -# -# Note: Output can still be found in Console.app, by searching for -# "upload-symbols" -# -eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 & diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/upload-symbols b/ios/Pods/Fabric/iOS/Fabric.framework/upload-symbols deleted file mode 100755 index a18d0b68f..000000000 Binary files a/ios/Pods/Fabric/iOS/Fabric.framework/upload-symbols and /dev/null differ diff --git a/ios/Pods/Fabric/run b/ios/Pods/Fabric/run deleted file mode 100755 index 736cd2ff5..000000000 --- a/ios/Pods/Fabric/run +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh - -# run -# -# Copyright (c) 2015 Crashlytics. All rights reserved. -# -# -# This script is meant to be run as a Run Script in the "Build Phases" section -# of your Xcode project. It sends debug symbols to symbolicate stacktraces, -# sends build events to track versions, and onboard apps for Crashlytics. -# -# This script calls upload-symbols twice: -# -# 1) First it calls upload-symbols synchronously in "validation" mode. If the -# script finds issues with the build environment, it will report errors to Xcode. -# In validation mode it exits before doing any time consuming work. -# -# 2) Then it calls upload-symbols in the background to actually send the build -# event and upload symbols. It does this in the background so that it doesn't -# slow down your builds. If an error happens here, you won't see it in Xcode. -# -# You can find the output for the background execution in Console.app, by -# searching for "upload-symbols". -# -# If you want verbose output, you can pass the --debug flag to this script -# - -# Figure out where we're being called from -DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) - -# If the first argument is specified without a dash, treat it as the Fabric API -# Key and add it as an argument -if [ -z "$1" ] || [[ $1 == -* ]]; then - API_KEY_ARG="" -else - API_KEY_ARG="-a $1"; shift -fi - -# If a second argument is specified without a dash, treat it as the Build Secret -# and add it as an argument -if [ -z "$1" ] || [[ $1 == -* ]]; then - BUILD_SECRET_ARG="" -else - BUILD_SECRET_ARG="-bs $1"; shift -fi - -# Build up the arguments list, passing through any flags added after the -# API Key and Build Secret -ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@" -VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate" -UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase" - -# Quote the path to handle folders with special characters -COMMAND_PATH="\"$DIR/upload-symbols\" " - -# Ensure params are as expected, run in sync mode to validate, -# and cause a build error if validation fails -eval $COMMAND_PATH$VALIDATE_ARGUMENTS -return_code=$? - -if [[ $return_code != 0 ]]; then - exit $return_code -fi - -# Verification passed, convert and upload cSYMs in the background to prevent -# build delays -# -# Note: Validation is performed again at this step before upload -# -# Note: Output can still be found in Console.app, by searching for -# "upload-symbols" -# -eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 & diff --git a/ios/Pods/Fabric/upload-symbols b/ios/Pods/Fabric/upload-symbols deleted file mode 100755 index a18d0b68f..000000000 Binary files a/ios/Pods/Fabric/upload-symbols and /dev/null differ diff --git a/ios/Pods/Firebase/README.md b/ios/Pods/Firebase/README.md index 1747cca7a..d778205ff 100644 --- a/ios/Pods/Firebase/README.md +++ b/ios/Pods/Firebase/README.md @@ -2,14 +2,20 @@ [![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) [![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Actions Status][gh-abtesting-badge]][gh-actions] [![Actions Status][gh-auth-badge]][gh-actions] [![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-crashlytics-badge]][gh-actions] +[![Actions Status][gh-database-badge]][gh-actions] [![Actions Status][gh-datatransport-badge]][gh-actions] [![Actions Status][gh-dynamiclinks-badge]][gh-actions] [![Actions Status][gh-firebasepod-badge]][gh-actions] [![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-functions-badge]][gh-actions] +[![Actions Status][gh-inappmessaging-badge]][gh-actions] [![Actions Status][gh-interop-badge]][gh-actions] [![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-remoteconfig-badge]][gh-actions] [![Actions Status][gh-storage-badge]][gh-actions] [![Actions Status][gh-symbolcollision-badge]][gh-actions] [![Actions Status][gh-zip-badge]][gh-actions] @@ -92,7 +98,7 @@ Instructions for installing binary frameworks via To develop Firebase software in this repository, ensure that you have at least the following software: - * Xcode 10.1 (or later) + * Xcode 10.3 (or later) * CocoaPods 1.7.2 (or later) * [CocoaPods generate](https://github.com/square/cocoapods-generate) @@ -123,6 +129,10 @@ Firestore has a self contained Xcode project. See See [AddNewPod.md](AddNewPod.md). +### Managing Headers and Imports + +See [HeadersImports.md](HeadersImports.md). + ### Code Formatting To ensure that the code is formatted consistently, run the script @@ -185,8 +195,16 @@ building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database -To run the Database Integration tests, make your database authentication rules -[public](https://firebase.google.com/docs/database/security/quickstart). +The Firebase Database Integration tests can be run against a locally running Database Emulator +or against a production instance. + +To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before +running the integration test. + +To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to +`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to +[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are +running. ### Firebase Storage @@ -274,14 +292,20 @@ Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). [gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg [gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg [gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg +[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg [gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg [gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg [gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg [gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg +[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg [gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg [gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg [gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg [gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg [gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector index bb3a7cae8..8746d38f6 100755 Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector and b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector differ diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics index 46030f0c4..e00c279a8 100755 Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics and b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics differ diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h index be0b1faee..15b693c90 100755 --- a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h +++ b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h @@ -90,12 +90,10 @@ NS_SWIFT_NAME(Analytics) /// Must be called on the main thread. /// /// Note that screen reporting is enabled automatically and records the class name of the current -/// UIViewController for you without requiring you to call this method. If you implement -/// viewDidAppear in your UIViewController but do not call [super viewDidAppear:], that screen class -/// will not be automatically tracked. The class name can optionally be overridden by calling this -/// method in the viewDidAppear callback of your UIViewController and specifying the -/// screenClassOverride parameter. setScreenName:screenClass: must be called after -/// [super viewDidAppear:]. +/// UIViewController for you without requiring you to call this method. The class name can +/// optionally be overridden by calling this method in the viewDidAppear callback of your +/// UIViewController and specifying the screenClassOverride parameter. +/// `setScreenName:screenClass:` must be called after `[super viewDidAppear:]`. /// /// If your app does not use a distinct UIViewController for each screen, you should call this /// method and specify a distinct screenName each time a new screen is presented to the user. @@ -103,6 +101,9 @@ NS_SWIFT_NAME(Analytics) /// The screen name and screen class remain in effect until the current UIViewController changes or /// a new call to setScreenName:screenClass: is made. /// +/// @warning If you override `viewDidAppear:` in your UIViewController but do not call +/// `[super viewDidAppear:]`, that screen class will not be tracked. +/// /// @param screenName The name of the current screen. Should contain 1 to 100 characters. Set to nil /// to clear the current screen name. /// @param screenClassOverride The name of the screen class. Should contain 1 to 100 characters. By diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h index 674f8b121..807b6159e 100755 --- a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h +++ b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h @@ -366,7 +366,6 @@ static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewIte ///
    ///
  • @c kFIRParameterSearchTerm (NSString)
  • ///
-/// This constant has been deprecated. static NSString *const kFIREventViewSearchResults NS_SWIFT_NAME(AnalyticsEventViewSearchResults) = @"view_search_results"; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.h similarity index 100% rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.h diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.m index 3a7d6de0e..07c786cb7 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.m @@ -14,7 +14,7 @@ #import -#import "FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h" +#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-implementations" diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m index 002f93c51..4dfbfa838 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m @@ -22,14 +22,14 @@ #import #endif -#import +#import "FirebaseCore/Sources/Public/FIRApp.h" +#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h" #import "FirebaseCore/Sources/FIRBundleUtil.h" +#import "FirebaseCore/Sources/FIRComponentContainerInternal.h" +#import "FirebaseCore/Sources/FIRConfigurationInternal.h" #import "FirebaseCore/Sources/FIRVersion.h" -#import "FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h" #import "FirebaseCore/Sources/Private/FIRAppInternal.h" -#import "FirebaseCore/Sources/Private/FIRComponentContainerInternal.h" -#import "FirebaseCore/Sources/Private/FIRConfigurationInternal.h" #import "FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h" #import "FirebaseCore/Sources/Private/FIRLibrary.h" #import "FirebaseCore/Sources/Private/FIRLogger.h" @@ -166,7 +166,7 @@ static dispatch_once_t sFirebaseUserAgentOnceToken; if ([name isEqualToString:kFIRDefaultAppName]) { if (sDefaultApp) { - // The default app already exixts. Handle duplicate `configure` calls and return. + // The default app already exists. Handle duplicate `configure` calls and return. [self appWasConfiguredTwice:sDefaultApp usingOptions:options]; return; } diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppAssociationRegistration.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.h similarity index 100% rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppAssociationRegistration.h rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.h diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.m index e4125cd25..f3f812c74 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.m @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import "FirebaseCore/Sources/Private/FIRAppAssociationRegistration.h" +#import "FirebaseCore/Sources/FIRAppAssociationRegistration.h" #import diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRBundleUtil.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRBundleUtil.m index b858f14c4..de2c29542 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRBundleUtil.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRBundleUtil.m @@ -49,15 +49,19 @@ + (BOOL)hasBundleIdentifierPrefix:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles { for (NSBundle *bundle in bundles) { - // This allows app extensions that have the app's bundle as their prefix to pass this test. - NSString *applicationBundleIdentifier = - [GULAppEnvironmentUtil isAppExtension] - ? [self bundleIdentifierByRemovingLastPartFrom:bundle.bundleIdentifier] - : bundle.bundleIdentifier; - - if ([applicationBundleIdentifier isEqualToString:bundleIdentifier]) { + if ([bundle.bundleIdentifier isEqualToString:bundleIdentifier]) { return YES; } + + if ([GULAppEnvironmentUtil isAppExtension]) { + // A developer could be using the same `FIROptions` for both their app and extension. Since + // extensions have a suffix added to the bundleID, we consider a matching prefix as valid. + NSString *appBundleIDFromExtension = + [self bundleIdentifierByRemovingLastPartFrom:bundle.bundleIdentifier]; + if ([appBundleIDFromExtension isEqualToString:bundleIdentifier]) { + return YES; + } + } } return NO; } diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainerInternal.h similarity index 93% rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainerInternal.h index bf39bc6ce..82356060d 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainerInternal.h @@ -15,8 +15,8 @@ */ #import -#import -#import +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#import "FirebaseCore/Sources/Private/FIRComponentContainer.h" @class FIRApp; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentType.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentType.m index 6410f2ea1..9051336a6 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentType.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentType.m @@ -16,7 +16,7 @@ #import "FirebaseCore/Sources/Private/FIRComponentType.h" -#import "FirebaseCore/Sources/Private/FIRComponentContainerInternal.h" +#import "FirebaseCore/Sources/FIRComponentContainerInternal.h" @implementation FIRComponentType diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfiguration.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfiguration.m index a1c9f4a2e..83b3248c3 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfiguration.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfiguration.m @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import "FirebaseCore/Sources/Private/FIRConfigurationInternal.h" +#import "FirebaseCore/Sources/FIRConfigurationInternal.h" -#import "FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h" +#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h" extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel); diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfigurationInternal.h similarity index 95% rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfigurationInternal.h index 0d1a36f66..ee1688670 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfigurationInternal.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#import +#import "FIRConfiguration.h" @class FIRAnalyticsConfiguration; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRCoreDiagnosticsConnector.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRCoreDiagnosticsConnector.m index 4981ca1b0..730dd6ef9 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRCoreDiagnosticsConnector.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRCoreDiagnosticsConnector.m @@ -16,12 +16,12 @@ #import "FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h" -#import +#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h" -#import +#import "FirebaseCore/Sources/Public/FIROptions.h" +#import "FirebaseCore/Sources/FIRDiagnosticsData.h" #import "FirebaseCore/Sources/Private/FIRAppInternal.h" -#import "FirebaseCore/Sources/Private/FIRDiagnosticsData.h" #import "FirebaseCore/Sources/Private/FIROptionsInternal.h" // Define the interop class symbol declared as an extern in FIRCoreDiagnosticsInterop. diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.h similarity index 94% rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.h index ac5ef2c4f..5b5ff8ad9 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.h @@ -16,7 +16,7 @@ #import -#import +#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h" NS_ASSUME_NONNULL_BEGIN diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.m index bbe0561d9..da9489d99 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.m @@ -14,9 +14,9 @@ * limitations under the License. */ -#import "FirebaseCore/Sources/Private/FIRDiagnosticsData.h" +#import "FirebaseCore/Sources/FIRDiagnosticsData.h" -#import +#import "FirebaseCore/Sources/Public/FIRApp.h" #import "FirebaseCore/Sources/Private/FIRAppInternal.h" #import "FirebaseCore/Sources/Private/FIROptionsInternal.h" diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRLogger.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRLogger.m index ba2ee1f58..a749cbd94 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRLogger.m +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRLogger.m @@ -14,9 +14,9 @@ #import "FirebaseCore/Sources/Private/FIRLogger.h" -#import #import #import +#import "FirebaseCore/Sources/Public/FIRLoggerLevel.h" #import "FirebaseCore/Sources/FIRVersion.h" diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppInternal.h index ad1a186b9..9a0c943db 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppInternal.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppInternal.h @@ -14,8 +14,19 @@ * limitations under the License. */ +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIRApp.h" +#else #import +#endif + +// The has_include is a workaround so the old IID needed for the FIS tests can find FIRErrors.h +#if __has_include("FirebaseCore/Sources/Private/FIRErrors.h") +#import "FirebaseCore/Sources/Private/FIRErrors.h" +#else #import +#endif @class FIRComponentContainer; @protocol FIRLibrary; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainer.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainer.h index 8dfab9c10..db2bafef8 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainer.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainer.h @@ -15,8 +15,14 @@ */ #import +// The has_include is a workaround so the old IID needed for the FIS tests can find the headers. +#if __has_include("FirebaseCore/Sources/Private/FIRComponentType.h") +#import "FirebaseCore/Sources/Private/FIRComponentType.h" +#import "FirebaseCore/Sources/Private/FIRLibrary.h" +#else #import #import +#endif NS_ASSUME_NONNULL_BEGIN diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRErrorCode.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRErrorCode.h index f77b3d002..c90d9eecf 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRErrorCode.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRErrorCode.h @@ -32,7 +32,8 @@ typedef NS_ENUM(NSInteger, FIRErrorCode) { FIRErrorCodeInvalidAppID = -101, /** - * Error code for failing to configure a specific service. + * Error code for failing to configure a specific service. It's deprecated, but + * still used after copybara. */ FIRErrorCodeConfigFailed = -114, }; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLibrary.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLibrary.h index af9d9685d..e7a9e077c 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLibrary.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLibrary.h @@ -19,7 +19,12 @@ #import +// The has_include is a workaround so the old IID needed for the FIS tests can find the headers. +#if __has_include("FirebaseCore/Sources/Private/FIRComponent.h") +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#else #import +#endif @class FIRApp; diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLogger.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLogger.h index 548e389a4..6fd778441 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLogger.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLogger.h @@ -16,7 +16,12 @@ #import +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIRLoggerLevel.h" +#else #import +#endif NS_ASSUME_NONNULL_BEGIN diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIROptionsInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIROptionsInternal.h index 0660a3cd8..acaf45868 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIROptionsInternal.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIROptionsInternal.h @@ -14,7 +14,12 @@ * limitations under the License. */ +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIROptions.h" +#else #import +#endif /** * Keys for the strings in the plist file. diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FirebaseCoreInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FirebaseCoreInternal.h new file mode 100644 index 000000000..93af6cb8d --- /dev/null +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FirebaseCoreInternal.h @@ -0,0 +1,31 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// An umbrella header, for any other libraries in this repo to access Firebase Public and Private +// headers. Any package manager complexity should be handled here. + +#if SWIFT_PACKAGE +@import FirebaseCore; +#else +#import +#endif + +#import "FirebaseCore/Sources/Private/FIRAppInternal.h" +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#import "FirebaseCore/Sources/Private/FIRComponentContainer.h" +#import "FirebaseCore/Sources/Private/FIRDependency.h" +#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h" +#import "FirebaseCore/Sources/Private/FIRLibrary.h" +#import "FirebaseCore/Sources/Private/FIRLogger.h" +#import "FirebaseCore/Sources/Private/FIROptionsInternal.h" diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRConfiguration.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRConfiguration.h index 8de3b076a..2b8e678ce 100644 --- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRConfiguration.h +++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRConfiguration.h @@ -16,7 +16,7 @@ #import -#import +#import "FIRLoggerLevel.h" NS_ASSUME_NONNULL_BEGIN diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h b/ios/Pods/FirebaseCore/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h similarity index 100% rename from ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h rename to ios/Pods/FirebaseCore/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h b/ios/Pods/FirebaseCore/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h similarity index 100% rename from ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h rename to ios/Pods/FirebaseCore/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h diff --git a/ios/Pods/FirebaseCore/README.md b/ios/Pods/FirebaseCore/README.md index 1747cca7a..d778205ff 100644 --- a/ios/Pods/FirebaseCore/README.md +++ b/ios/Pods/FirebaseCore/README.md @@ -2,14 +2,20 @@ [![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) [![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Actions Status][gh-abtesting-badge]][gh-actions] [![Actions Status][gh-auth-badge]][gh-actions] [![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-crashlytics-badge]][gh-actions] +[![Actions Status][gh-database-badge]][gh-actions] [![Actions Status][gh-datatransport-badge]][gh-actions] [![Actions Status][gh-dynamiclinks-badge]][gh-actions] [![Actions Status][gh-firebasepod-badge]][gh-actions] [![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-functions-badge]][gh-actions] +[![Actions Status][gh-inappmessaging-badge]][gh-actions] [![Actions Status][gh-interop-badge]][gh-actions] [![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-remoteconfig-badge]][gh-actions] [![Actions Status][gh-storage-badge]][gh-actions] [![Actions Status][gh-symbolcollision-badge]][gh-actions] [![Actions Status][gh-zip-badge]][gh-actions] @@ -92,7 +98,7 @@ Instructions for installing binary frameworks via To develop Firebase software in this repository, ensure that you have at least the following software: - * Xcode 10.1 (or later) + * Xcode 10.3 (or later) * CocoaPods 1.7.2 (or later) * [CocoaPods generate](https://github.com/square/cocoapods-generate) @@ -123,6 +129,10 @@ Firestore has a self contained Xcode project. See See [AddNewPod.md](AddNewPod.md). +### Managing Headers and Imports + +See [HeadersImports.md](HeadersImports.md). + ### Code Formatting To ensure that the code is formatted consistently, run the script @@ -185,8 +195,16 @@ building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database -To run the Database Integration tests, make your database authentication rules -[public](https://firebase.google.com/docs/database/security/quickstart). +The Firebase Database Integration tests can be run against a locally running Database Emulator +or against a production instance. + +To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before +running the integration test. + +To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to +`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to +[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are +running. ### Firebase Storage @@ -274,14 +292,20 @@ Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). [gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg [gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg [gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg +[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg [gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg [gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg [gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg [gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg +[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg [gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg [gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg [gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg [gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg [gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m index 87d57c4e8..2db7db839 100644 --- a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m +++ b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m @@ -26,8 +26,8 @@ #import #import -#import -#import +#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h" +#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h" #import #import diff --git a/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h b/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h new file mode 100644 index 000000000..69c40721a --- /dev/null +++ b/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** If present, is a BOOL wrapped in an NSNumber. */ +#define kFIRCDIsDataCollectionDefaultEnabledKey @"FIRCDIsDataCollectionDefaultEnabledKey" + +/** If present, is an int32_t wrapped in an NSNumber. */ +#define kFIRCDConfigurationTypeKey @"FIRCDConfigurationTypeKey" + +/** If present, is an NSString. */ +#define kFIRCDSdkNameKey @"FIRCDSdkNameKey" + +/** If present, is an NSString. */ +#define kFIRCDSdkVersionKey @"FIRCDSdkVersionKey" + +/** If present, is an int32_t wrapped in an NSNumber. */ +#define kFIRCDllAppsCountKey @"FIRCDllAppsCountKey" + +/** If present, is an NSString. */ +#define kFIRCDGoogleAppIDKey @"FIRCDGoogleAppIDKey" + +/** If present, is an NSString. */ +#define kFIRCDBundleIDKey @"FIRCDBundleID" + +/** If present, is a BOOL wrapped in an NSNumber. */ +#define kFIRCDUsingOptionsFromDefaultPlistKey @"FIRCDUsingOptionsFromDefaultPlistKey" + +/** If present, is an NSString. */ +#define kFIRCDLibraryVersionIDKey @"FIRCDLibraryVersionIDKey" + +/** If present, is an NSString. */ +#define kFIRCDFirebaseUserAgentKey @"FIRCDFirebaseUserAgentKey" + +/** Defines the interface of a data object needed to log diagnostics data. */ +@protocol FIRCoreDiagnosticsData + +@required + +/** A dictionary containing data (non-exhaustive) to be logged in diagnostics. */ +@property(nonatomic) NSDictionary *diagnosticObjects; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h b/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h new file mode 100644 index 000000000..2b0eb710c --- /dev/null +++ b/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h @@ -0,0 +1,34 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#import "FIRCoreDiagnosticsData.h" + +NS_ASSUME_NONNULL_BEGIN + +/** Allows the interoperation of FirebaseCore and FirebaseCoreDiagnostics. */ +@protocol FIRCoreDiagnosticsInterop + +/** Sends the given diagnostics data. + * + * @param diagnosticsData The diagnostics data object to send. + */ ++ (void)sendDiagnosticsData:(id)diagnosticsData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCoreDiagnostics/README.md b/ios/Pods/FirebaseCoreDiagnostics/README.md index 1747cca7a..d778205ff 100644 --- a/ios/Pods/FirebaseCoreDiagnostics/README.md +++ b/ios/Pods/FirebaseCoreDiagnostics/README.md @@ -2,14 +2,20 @@ [![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) [![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Actions Status][gh-abtesting-badge]][gh-actions] [![Actions Status][gh-auth-badge]][gh-actions] [![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-crashlytics-badge]][gh-actions] +[![Actions Status][gh-database-badge]][gh-actions] [![Actions Status][gh-datatransport-badge]][gh-actions] [![Actions Status][gh-dynamiclinks-badge]][gh-actions] [![Actions Status][gh-firebasepod-badge]][gh-actions] [![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-functions-badge]][gh-actions] +[![Actions Status][gh-inappmessaging-badge]][gh-actions] [![Actions Status][gh-interop-badge]][gh-actions] [![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-remoteconfig-badge]][gh-actions] [![Actions Status][gh-storage-badge]][gh-actions] [![Actions Status][gh-symbolcollision-badge]][gh-actions] [![Actions Status][gh-zip-badge]][gh-actions] @@ -92,7 +98,7 @@ Instructions for installing binary frameworks via To develop Firebase software in this repository, ensure that you have at least the following software: - * Xcode 10.1 (or later) + * Xcode 10.3 (or later) * CocoaPods 1.7.2 (or later) * [CocoaPods generate](https://github.com/square/cocoapods-generate) @@ -123,6 +129,10 @@ Firestore has a self contained Xcode project. See See [AddNewPod.md](AddNewPod.md). +### Managing Headers and Imports + +See [HeadersImports.md](HeadersImports.md). + ### Code Formatting To ensure that the code is formatted consistently, run the script @@ -185,8 +195,16 @@ building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database -To run the Database Integration tests, make your database authentication rules -[public](https://firebase.google.com/docs/database/security/quickstart). +The Firebase Database Integration tests can be run against a locally running Database Emulator +or against a production instance. + +To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before +running the integration test. + +To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to +`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to +[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are +running. ### Firebase Storage @@ -274,14 +292,20 @@ Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). [gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg [gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg [gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg +[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg [gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg [gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg [gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg [gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg +[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg [gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg [gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg [gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg [gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg [gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.h new file mode 100644 index 000000000..75536f7cd --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.h @@ -0,0 +1,88 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#if CLS_TARGET_OS_HAS_UIKIT +#import +#endif + +__BEGIN_DECLS + +#define FIRCLSApplicationActivityDefault \ + (NSActivitySuddenTerminationDisabled | NSActivityAutomaticTerminationDisabled) + +/** + * Type to indicate application installation source + */ +typedef NS_ENUM(NSInteger, FIRCLSApplicationInstallationSourceType) { + FIRCLSApplicationInstallationSourceTypeDeveloperInstall = 1, + // 2 and 3 are reserved for legacy values. + FIRCLSApplicationInstallationSourceTypeAppStore = 4 +}; + +/** + * Returns the application bundle identifier with occurences of "/" replaced by "_" + */ +NSString* FIRCLSApplicationGetBundleIdentifier(void); + +/** + * Returns the SDK's bundle ID + */ +NSString* FIRCLSApplicationGetSDKBundleID(void); + +/** + * Returns the platform identifier, either: ios, mac, or tvos. + * Catalyst apps are treated as mac. + */ +NSString* FIRCLSApplicationGetPlatform(void); + +/** + * Returns the user-facing app name + */ +NSString* FIRCLSApplicationGetName(void); + +/** + * Returns the build number + */ +NSString* FIRCLSApplicationGetBundleVersion(void); + +/** + * Returns the human-readable build version + */ +NSString* FIRCLSApplicationGetShortBundleVersion(void); + +/** + * Returns a number to indicate how the app has been installed: Developer / App Store + */ +FIRCLSApplicationInstallationSourceType FIRCLSApplicationInstallationSource(void); + +BOOL FIRCLSApplicationIsExtension(void); +NSString* FIRCLSApplicationExtensionPointIdentifier(void); + +#if CLS_TARGET_OS_HAS_UIKIT +UIApplication* FIRCLSApplicationSharedInstance(void); +#else +id FIRCLSApplicationSharedInstance(void); +#endif + +void FIRCLSApplicationOpenURL(NSURL* url, + NSExtensionContext* extensionContext, + void (^completionBlock)(BOOL success)); + +id FIRCLSApplicationBeginActivity(NSActivityOptions options, NSString* reason); +void FIRCLSApplicationEndActivity(id activity); + +void FIRCLSApplicationActivity(NSActivityOptions options, NSString* reason, void (^block)(void)); + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.m new file mode 100644 index 000000000..219b4bb7a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.m @@ -0,0 +1,211 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSApplication.h" + +#import "FIRCLSHost.h" +#import "FIRCLSUtility.h" + +#if CLS_TARGET_OS_OSX +#import +#endif + +#if CLS_TARGET_OS_HAS_UIKIT +#import +#endif + +NSString* FIRCLSApplicationGetBundleIdentifier(void) { + return [[[NSBundle mainBundle] bundleIdentifier] stringByReplacingOccurrencesOfString:@"/" + withString:@"_"]; +} + +NSString* FIRCLSApplicationGetSDKBundleID(void) { + return + [@"com.google.firebase.crashlytics." stringByAppendingString:FIRCLSApplicationGetPlatform()]; +} + +NSString* FIRCLSApplicationGetPlatform(void) { +#if defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST + return @"mac"; +#elif TARGET_OS_IOS + return @"ios"; +#elif TARGET_OS_OSX + return @"mac"; +#elif TARGET_OS_TV + return @"tvos"; +#endif +} + +// these defaults match the FIRCLSInfoPlist helper in FIRCLSIDEFoundation +NSString* FIRCLSApplicationGetBundleVersion(void) { + return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; +} + +NSString* FIRCLSApplicationGetShortBundleVersion(void) { + return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; +} + +NSString* FIRCLSApplicationGetName(void) { + NSString* name; + NSBundle* mainBundle; + + mainBundle = [NSBundle mainBundle]; + + name = [mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"]; + if (name) { + return name; + } + + name = [mainBundle objectForInfoDictionaryKey:@"CFBundleName"]; + if (name) { + return name; + } + + return FIRCLSApplicationGetBundleVersion(); +} + +BOOL FIRCLSApplicationHasAppStoreReceipt(void) { + NSURL* url = NSBundle.mainBundle.appStoreReceiptURL; + return [NSFileManager.defaultManager fileExistsAtPath:[url path]]; +} + +FIRCLSApplicationInstallationSourceType FIRCLSApplicationInstallationSource(void) { + if (FIRCLSApplicationHasAppStoreReceipt()) { + return FIRCLSApplicationInstallationSourceTypeAppStore; + } + + return FIRCLSApplicationInstallationSourceTypeDeveloperInstall; +} + +BOOL FIRCLSApplicationIsExtension(void) { + return FIRCLSApplicationExtensionPointIdentifier() != nil; +} + +NSString* FIRCLSApplicationExtensionPointIdentifier(void) { + id extensionDict = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"NSExtension"]; + + if (!extensionDict) { + return nil; + } + + if (![extensionDict isKindOfClass:[NSDictionary class]]) { + FIRCLSSDKLog("Error: NSExtension Info.plist entry is mal-formed\n"); + return nil; + } + + id typeValue = [(NSDictionary*)extensionDict objectForKey:@"NSExtensionPointIdentifier"]; + + if (![typeValue isKindOfClass:[NSString class]]) { + FIRCLSSDKLog("Error: NSExtensionPointIdentifier Info.plist entry is mal-formed\n"); + return nil; + } + + return typeValue; +} + +#if CLS_TARGET_OS_HAS_UIKIT +UIApplication* FIRCLSApplicationSharedInstance(void) { + if (FIRCLSApplicationIsExtension()) { + return nil; + } + + return [[UIApplication class] performSelector:@selector(sharedApplication)]; +} +#elif CLS_TARGET_OS_OSX +id FIRCLSApplicationSharedInstance(void) { + return [NSClassFromString(@"NSApplication") sharedApplication]; +} +#else +id FIRCLSApplicationSharedInstance(void) { + return nil; // FIXME: what do we actually return for watch? +} +#endif + +void FIRCLSApplicationOpenURL(NSURL* url, + NSExtensionContext* extensionContext, + void (^completionBlock)(BOOL success)) { + if (extensionContext) { + [extensionContext openURL:url completionHandler:completionBlock]; + return; + } + + BOOL result = NO; + +#if TARGET_OS_IOS + // What's going on here is the value returned is a scalar, but we really need an object to + // call this dynamically. Hoops must be jumped. + NSInvocationOperation* op = + [[NSInvocationOperation alloc] initWithTarget:FIRCLSApplicationSharedInstance() + selector:@selector(openURL:) + object:url]; + [op start]; + [op.result getValue:&result]; +#elif CLS_TARGET_OS_OSX + result = [[NSClassFromString(@"NSWorkspace") sharedWorkspace] openURL:url]; +#endif + + completionBlock(result); +} + +id FIRCLSApplicationBeginActivity(NSActivityOptions options, NSString* reason) { + if ([[NSProcessInfo processInfo] respondsToSelector:@selector(beginActivityWithOptions: + reason:)]) { + return [[NSProcessInfo processInfo] beginActivityWithOptions:options reason:reason]; + } + +#if CLS_TARGET_OS_OSX + if (options & NSActivitySuddenTerminationDisabled) { + [[NSProcessInfo processInfo] disableSuddenTermination]; + } + + if (options & NSActivityAutomaticTerminationDisabled) { + [[NSProcessInfo processInfo] disableAutomaticTermination:reason]; + } +#endif + + // encode the options, so we can undo our work later + return @{@"options" : @(options), @"reason" : reason}; +} + +void FIRCLSApplicationEndActivity(id activity) { + if (!activity) { + return; + } + + if ([[NSProcessInfo processInfo] respondsToSelector:@selector(endActivity:)]) { + [[NSProcessInfo processInfo] endActivity:activity]; + return; + } + +#if CLS_TARGET_OS_OSX + NSInteger options = [[(NSDictionary*)activity objectForKey:@"options"] integerValue]; + + if (options & NSActivitySuddenTerminationDisabled) { + [[NSProcessInfo processInfo] enableSuddenTermination]; + } + + if (options & NSActivityAutomaticTerminationDisabled) { + [[NSProcessInfo processInfo] + enableAutomaticTermination:[(NSDictionary*)activity objectForKey:@"reason"]]; + } +#endif +} + +void FIRCLSApplicationActivity(NSActivityOptions options, NSString* reason, void (^block)(void)) { + id activity = FIRCLSApplicationBeginActivity(options, reason); + + block(); + + FIRCLSApplicationEndActivity(activity); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h new file mode 100644 index 000000000..f3c0bd718 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h @@ -0,0 +1,81 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include "FIRCLSFeatures.h" +#include "FIRCLSFile.h" +#include "FIRCLSMachO.h" + +__BEGIN_DECLS + +// Typically, apps seem to have ~300 binary images loaded +#define CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT (512) +#define CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE (32) +#define CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME 0 + +#define FIRCLSUUIDStringLength (33) + +typedef struct { + _Atomic(void*) volatile baseAddress; + uint64_t size; +#if CLS_DWARF_UNWINDING_SUPPORTED + const void* ehFrame; +#endif +#if CLS_COMPACT_UNWINDING_SUPPORTED + const void* unwindInfo; +#endif + const void* crashInfo; +#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME + char name[CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE]; +#endif +} FIRCLSBinaryImageRuntimeNode; + +typedef struct { + char uuidString[FIRCLSUUIDStringLength]; + bool encrypted; + FIRCLSMachOVersion builtSDK; + FIRCLSMachOVersion minSDK; + FIRCLSBinaryImageRuntimeNode node; + struct FIRCLSMachOSlice slice; + intptr_t vmaddr_slide; +} FIRCLSBinaryImageDetails; + +typedef struct { + const char* path; +} FIRCLSBinaryImageReadOnlyContext; + +typedef struct { + FIRCLSFile file; + FIRCLSBinaryImageRuntimeNode nodes[CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT]; +} FIRCLSBinaryImageReadWriteContext; + +void FIRCLSBinaryImageInit(FIRCLSBinaryImageReadOnlyContext* roContext, + FIRCLSBinaryImageReadWriteContext* rwContext); + +#if CLS_COMPACT_UNWINDING_SUPPORTED +bool FIRCLSBinaryImageSafeFindImageForAddress(uintptr_t address, + FIRCLSBinaryImageRuntimeNode* image); +bool FIRCLSBinaryImageSafeHasUnwindInfo(FIRCLSBinaryImageRuntimeNode* image); +#endif + +bool FIRCLSBinaryImageFindImageForUUID(const char* uuidString, + FIRCLSBinaryImageDetails* imageDetails); + +bool FIRCLSBinaryImageRecordMainExecutable(FIRCLSFile* file); + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.m new file mode 100644 index 000000000..8b707bb44 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.m @@ -0,0 +1,571 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSBinaryImage.h" + +#include +#include + +#include + +#include + +#include "FIRCLSByteUtility.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSFile.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSHost.h" +#include "FIRCLSMachO.h" +#include "FIRCLSUtility.h" + +#include + +// this is defined only if __OPEN_SOURCE__ is *not* defined in the TVOS SDK's mach-o/loader.h +// also, it has not yet made it back to the OSX SDKs, for example +#ifndef LC_VERSION_MIN_TVOS +#define LC_VERSION_MIN_TVOS 0x2F +#endif + +#pragma mark Prototypes +static bool FIRCLSBinaryImageOpenIfNeeded(bool* needsClosing); + +static void FIRCLSBinaryImageAddedCallback(const struct mach_header* mh, intptr_t vmaddr_slide); +static void FIRCLSBinaryImageRemovedCallback(const struct mach_header* mh, intptr_t vmaddr_slide); +static void FIRCLSBinaryImageChanged(bool added, + const struct mach_header* mh, + intptr_t vmaddr_slide); +static bool FIRCLSBinaryImageFillInImageDetails(FIRCLSBinaryImageDetails* details); + +static void FIRCLSBinaryImageStoreNode(bool added, FIRCLSBinaryImageDetails imageDetails); +static void FIRCLSBinaryImageRecordSlice(bool added, const FIRCLSBinaryImageDetails imageDetails); + +#pragma mark - Core API +void FIRCLSBinaryImageInit(FIRCLSBinaryImageReadOnlyContext* roContext, + FIRCLSBinaryImageReadWriteContext* rwContext) { + // initialize our node array to all zeros + memset(&_firclsContext.writable->binaryImage, 0, sizeof(_firclsContext.writable->binaryImage)); + _firclsContext.writable->binaryImage.file.fd = -1; + + dispatch_async(FIRCLSGetBinaryImageQueue(), ^{ + if (!FIRCLSUnlinkIfExists(_firclsContext.readonly->binaryimage.path)) { + FIRCLSSDKLog("Unable to reset the binary image log file %s\n", strerror(errno)); + } + + bool needsClosing; // unneeded + if (!FIRCLSBinaryImageOpenIfNeeded(&needsClosing)) { + FIRCLSSDKLog("Error: Unable to open the binary image log file during init\n"); + } + }); + + _dyld_register_func_for_add_image(FIRCLSBinaryImageAddedCallback); + _dyld_register_func_for_remove_image(FIRCLSBinaryImageRemovedCallback); + + dispatch_async(FIRCLSGetBinaryImageQueue(), ^{ + FIRCLSFileClose(&_firclsContext.writable->binaryImage.file); + }); +} + +static bool FIRCLSBinaryImageOpenIfNeeded(bool* needsClosing) { + if (!FIRCLSIsValidPointer(_firclsContext.writable)) { + return false; + } + + if (!FIRCLSIsValidPointer(_firclsContext.readonly)) { + return false; + } + + if (!FIRCLSIsValidPointer(needsClosing)) { + return false; + } + + *needsClosing = false; + + if (FIRCLSFileIsOpen(&_firclsContext.writable->binaryImage.file)) { + return true; + } + + if (!FIRCLSFileInitWithPath(&_firclsContext.writable->binaryImage.file, + _firclsContext.readonly->binaryimage.path, false)) { + FIRCLSSDKLog("Error: unable to open binary image log file\n"); + return false; + } + + *needsClosing = true; + + return true; +} + +#if CLS_COMPACT_UNWINDING_SUPPORTED +bool FIRCLSBinaryImageSafeFindImageForAddress(uintptr_t address, + FIRCLSBinaryImageRuntimeNode* image) { + if (!FIRCLSContextIsInitialized()) { + return false; + } + + if (address == 0) { + return false; + } + + if (!FIRCLSIsValidPointer(image)) { + return false; + } + + FIRCLSBinaryImageRuntimeNode* nodes = _firclsContext.writable->binaryImage.nodes; + if (!nodes) { + FIRCLSSDKLogError("The node structure is NULL\n"); + return false; + } + + for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) { + FIRCLSBinaryImageRuntimeNode* node = &nodes[i]; + if (!FIRCLSIsValidPointer(node)) { + FIRCLSSDKLog( + "Invalid node pointer encountered in context's writable binary image at index %i", i); + continue; + } + + if ((address >= (uintptr_t)node->baseAddress) && + (address < (uintptr_t)node->baseAddress + node->size)) { + *image = *node; // copy the image + return true; + } + } + + return false; +} + +bool FIRCLSBinaryImageSafeHasUnwindInfo(FIRCLSBinaryImageRuntimeNode* image) { + return FIRCLSIsValidPointer(image->unwindInfo); +} +#endif + +bool FIRCLSBinaryImageFindImageForUUID(const char* uuidString, + FIRCLSBinaryImageDetails* imageDetails) { + if (!imageDetails || !uuidString) { + FIRCLSSDKLog("null input\n"); + return false; + } + + uint32_t imageCount = _dyld_image_count(); + + for (uint32_t i = 0; i < imageCount; ++i) { + const struct mach_header* mh = _dyld_get_image_header(i); + + FIRCLSBinaryImageDetails image; + + image.slice = FIRCLSMachOSliceWithHeader((void*)mh); + FIRCLSBinaryImageFillInImageDetails(&image); + + if (strncmp(uuidString, image.uuidString, FIRCLSUUIDStringLength) == 0) { + *imageDetails = image; + return true; + } + } + + return false; +} + +#pragma mark - DYLD callback handlers +static void FIRCLSBinaryImageAddedCallback(const struct mach_header* mh, intptr_t vmaddr_slide) { + FIRCLSBinaryImageChanged(true, mh, vmaddr_slide); +} + +static void FIRCLSBinaryImageRemovedCallback(const struct mach_header* mh, intptr_t vmaddr_slide) { + FIRCLSBinaryImageChanged(false, mh, vmaddr_slide); +} + +#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME +static bool FIRCLSBinaryImagePopulateRuntimeNodeName(FIRCLSBinaryImageDetails* details) { + if (!FIRCLSIsValidPointer(details)) { + return false; + } + + memset(details->node.name, 0, CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE); + + // We have limited storage space for the name. And, we really want to store + // "CoreFoundation", not "/System/Library/Fram", so we have to play tricks + // to make sure we get the right side of the string. + const char* imageName = FIRCLSMachOSliceGetExecutablePath(&details->slice); + if (!imageName) { + return false; + } + + const size_t imageNameLength = strlen(imageName); + + // Remember to leave one character for null-termination. + if (imageNameLength > CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1) { + imageName = imageName + (imageNameLength - (CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1)); + } + + // subtract one to make sure the string is always null-terminated + strncpy(details->node.name, imageName, CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1); + + return true; +} +#endif + +// There were plans later to replace this with FIRCLSMachO +static FIRCLSMachOSegmentCommand FIRCLSBinaryImageMachOGetSegmentCommand( + const struct load_command* cmd) { + FIRCLSMachOSegmentCommand segmentCommand; + + memset(&segmentCommand, 0, sizeof(FIRCLSMachOSegmentCommand)); + + if (!cmd) { + return segmentCommand; + } + + if (cmd->cmd == LC_SEGMENT) { + struct segment_command* segCmd = (struct segment_command*)cmd; + + memcpy(segmentCommand.segname, segCmd->segname, 16); + segmentCommand.vmaddr = segCmd->vmaddr; + segmentCommand.vmsize = segCmd->vmsize; + } else if (cmd->cmd == LC_SEGMENT_64) { + struct segment_command_64* segCmd = (struct segment_command_64*)cmd; + + memcpy(segmentCommand.segname, segCmd->segname, 16); + segmentCommand.vmaddr = segCmd->vmaddr; + segmentCommand.vmsize = segCmd->vmsize; + } + + return segmentCommand; +} + +static bool FIRCLSBinaryImageMachOSliceInitSectionByName(FIRCLSMachOSliceRef slice, + const char* segName, + const char* sectionName, + FIRCLSMachOSection* section) { + if (!FIRCLSIsValidPointer(slice)) { + return false; + } + + if (!section) { + return false; + } + + memset(section, 0, sizeof(FIRCLSMachOSection)); + + if (FIRCLSMachOSliceIs64Bit(slice)) { + const struct section_64* sect = + getsectbynamefromheader_64(slice->startAddress, segName, sectionName); + if (!sect) { + return false; + } + + section->addr = sect->addr; + section->size = sect->size; + section->offset = sect->offset; + } else { + const struct section* sect = getsectbynamefromheader(slice->startAddress, segName, sectionName); + if (!sect) { + return false; + } + + section->addr = sect->addr; + section->size = sect->size; + section->offset = sect->offset; + } + + return true; +} + +static bool FIRCLSBinaryImageFillInImageDetails(FIRCLSBinaryImageDetails* details) { + if (!FIRCLSIsValidPointer(details)) { + return false; + } + + if (!FIRCLSIsValidPointer(details->slice.startAddress)) { + return false; + } + +#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME + // this is done for debugging purposes, so if it fails, its ok to continue + FIRCLSBinaryImagePopulateRuntimeNodeName(details); +#endif + + // This cast might look a little dubious, but its just because we're using the same + // struct types in a few different places. + details->node.baseAddress = (void* volatile)details->slice.startAddress; + + FIRCLSMachOSliceEnumerateLoadCommands( + &details->slice, ^(uint32_t type, uint32_t size, const struct load_command* cmd) { + switch (type) { + case LC_UUID: { + const uint8_t* uuid = FIRCLSMachOGetUUID(cmd); + FIRCLSSafeHexToString(uuid, 16, details->uuidString); + } break; + case LC_ENCRYPTION_INFO: + details->encrypted = FIRCLSMachOGetEncrypted(cmd); + break; + case LC_SEGMENT: + case LC_SEGMENT_64: { + FIRCLSMachOSegmentCommand segmentCommand = FIRCLSBinaryImageMachOGetSegmentCommand(cmd); + + if (strncmp(segmentCommand.segname, SEG_TEXT, sizeof(SEG_TEXT)) == 0) { + details->node.size = segmentCommand.vmsize; + } + } break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + details->minSDK = FIRCLSMachOGetMinimumOSVersion(cmd); + details->builtSDK = FIRCLSMachOGetLinkedSDKVersion(cmd); + break; + } + }); + + // We look up the section we want, and we *should* be able to use: + // + // address of data we want = start address + section.offset + // + // However, the offset value is coming back funky in iOS 9. So, instead we look up the address + // the section should be loaded at, and compute the offset by looking up the address of the + // segment itself. + + FIRCLSMachOSection section; + +#if CLS_COMPACT_UNWINDING_SUPPORTED + if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_TEXT, "__unwind_info", + §ion)) { + details->node.unwindInfo = (void*)(section.addr + details->vmaddr_slide); + } +#endif + +#if CLS_DWARF_UNWINDING_SUPPORTED + if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_TEXT, "__eh_frame", + §ion)) { + details->node.ehFrame = (void*)(section.addr + details->vmaddr_slide); + } +#endif + + if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_DATA, "__crash_info", + §ion)) { + details->node.crashInfo = (void*)(section.addr + details->vmaddr_slide); + } + + return true; +} + +static void FIRCLSBinaryImageChanged(bool added, + const struct mach_header* mh, + intptr_t vmaddr_slide) { + // FIRCLSSDKLog("Binary image %s %p\n", added ? "loaded" : "unloaded", mh); + + FIRCLSBinaryImageDetails imageDetails; + + memset(&imageDetails, 0, sizeof(FIRCLSBinaryImageDetails)); + + imageDetails.slice = FIRCLSMachOSliceWithHeader((void*)mh); + imageDetails.vmaddr_slide = vmaddr_slide; + FIRCLSBinaryImageFillInImageDetails(&imageDetails); + + // this is an atomic operation + FIRCLSBinaryImageStoreNode(added, imageDetails); + + // this isn't, so do it on a serial queue + dispatch_async(FIRCLSGetBinaryImageQueue(), ^{ + FIRCLSBinaryImageRecordSlice(added, imageDetails); + }); +} + +#pragma mark - In-Memory Storage +static void FIRCLSBinaryImageStoreNode(bool added, FIRCLSBinaryImageDetails imageDetails) { + // This function is mutating a structure that needs to be accessed at crash time. We + // need to make sure the structure is always in as valid a state as possible. + // FIRCLSSDKLog("Storing %s node %p\n", added ? "loaded" : "unloaded", + // (void*)imageDetails.node.baseAddress); + + if (!_firclsContext.writable) { + FIRCLSSDKLog("Error: Writable context is NULL\n"); + return; + } + + void* searchAddress = NULL; + bool success = false; + FIRCLSBinaryImageRuntimeNode* nodes = _firclsContext.writable->binaryImage.nodes; + + if (!added) { + // capture the search address first + searchAddress = imageDetails.node.baseAddress; + + // If we are removing a node, we need to set its entries to zero. By clearing all of + // these values, we can just copy in imageDetails.node. Using memset here is slightly + // weird, since we have to restore one field. But, this way, if/when the structure changes, + // we still do the right thing. + memset(&imageDetails.node, 0, sizeof(FIRCLSBinaryImageRuntimeNode)); + + // restore the baseAddress, which just got zeroed, and is used for indexing + imageDetails.node.baseAddress = searchAddress; + } + + for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) { + FIRCLSBinaryImageRuntimeNode* node = &nodes[i]; + + if (!node) { + FIRCLSSDKLog("Error: Binary image storage is NULL\n"); + break; + } + + // navigate through the array, looking for our matching address + if (node->baseAddress != searchAddress) { + continue; + } + + // Attempt to swap the base address with whatever we are searching for. Success means that + // entry has been claims/cleared. Failure means some other thread beat us to it. + if (atomic_compare_exchange_strong(&node->baseAddress, &searchAddress, + imageDetails.node.baseAddress)) { + *node = imageDetails.node; + success = true; + + break; + } + + // If this is an unload, getting here means two threads unloaded at the same time. I think + // that's highly unlikely, and possibly even impossible. So, I'm choosing to abort the process + // at this point. + if (!added) { + FIRCLSSDKLog("Error: Failed to swap during image unload\n"); + break; + } + } + + if (!success) { + FIRCLSSDKLog("Error: Unable to track a %s node %p\n", added ? "loaded" : "unloaded", + (void*)imageDetails.node.baseAddress); + } +} + +#pragma mark - On-Disk Storage +static void FIRCLSBinaryImageRecordDetails(FIRCLSFile* file, + const FIRCLSBinaryImageDetails imageDetails) { + if (!file) { + FIRCLSSDKLog("Error: file is invalid\n"); + return; + } + + FIRCLSFileWriteHashEntryString(file, "uuid", imageDetails.uuidString); + FIRCLSFileWriteHashEntryUint64(file, "base", (uintptr_t)imageDetails.slice.startAddress); + FIRCLSFileWriteHashEntryUint64(file, "size", imageDetails.node.size); +} + +static void FIRCLSBinaryImageRecordLibraryFrameworkInfo(FIRCLSFile* file, const char* path) { + if (!file) { + FIRCLSSDKLog("Error: file is invalid\n"); + return; + } + + if (!path) { + return; + } + + // Because this function is so expensive, we've decided to omit this info for all Apple-supplied + // frameworks. This really isn't that bad, because we can know their info ahead of time (within a + // small margin of error). With this implemenation, we will still record this info for any + // user-built framework, which in the end is the most important thing. + if (strncmp(path, "/System", 7) == 0) { + return; + } + + // check to see if this is a potential framework bundle + if (!strstr(path, ".framework")) { + return; + } + + // My.framework/Versions/A/My for OS X + // My.framework/My for iOS + + NSString* frameworkPath = [NSString stringWithUTF8String:path]; +#if TARGET_OS_IPHONE + frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; +#else + frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework/Versions/A + frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework/Versions + frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework +#endif + + NSBundle* const bundle = [NSBundle bundleWithPath:frameworkPath]; + + if (!bundle) { + return; + } + + FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(file, "bundle_id", [bundle bundleIdentifier]); + FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty( + file, "build_version", [bundle objectForInfoDictionaryKey:@"CFBundleVersion"]); + FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty( + file, "display_version", [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]); +} + +static void FIRCLSBinaryImageRecordSlice(bool added, const FIRCLSBinaryImageDetails imageDetails) { + bool needsClosing = false; + if (!FIRCLSBinaryImageOpenIfNeeded(&needsClosing)) { + FIRCLSSDKLog("Error: unable to open binary image log file\n"); + return; + } + + FIRCLSFile* file = &_firclsContext.writable->binaryImage.file; + + FIRCLSFileWriteSectionStart(file, added ? "load" : "unload"); + + FIRCLSFileWriteHashStart(file); + + const char* path = FIRCLSMachOSliceGetExecutablePath((FIRCLSMachOSliceRef)&imageDetails.slice); + + FIRCLSFileWriteHashEntryString(file, "path", path); + + if (added) { + // this won't work if the binary has been unloaded + FIRCLSBinaryImageRecordLibraryFrameworkInfo(file, path); + } + + FIRCLSBinaryImageRecordDetails(file, imageDetails); + + FIRCLSFileWriteHashEnd(file); + + FIRCLSFileWriteSectionEnd(file); + + if (needsClosing) { + FIRCLSFileClose(file); + } +} + +bool FIRCLSBinaryImageRecordMainExecutable(FIRCLSFile* file) { + FIRCLSBinaryImageDetails imageDetails; + + memset(&imageDetails, 0, sizeof(FIRCLSBinaryImageDetails)); + + imageDetails.slice = FIRCLSMachOSliceGetCurrent(); + FIRCLSBinaryImageFillInImageDetails(&imageDetails); + + FIRCLSFileWriteSectionStart(file, "executable"); + FIRCLSFileWriteHashStart(file); + + FIRCLSFileWriteHashEntryString(file, "architecture", + FIRCLSMachOSliceGetArchitectureName(&imageDetails.slice)); + + FIRCLSBinaryImageRecordDetails(file, imageDetails); + FIRCLSFileWriteHashEntryBoolean(file, "encrypted", imageDetails.encrypted); + FIRCLSFileWriteHashEntryString(file, "minimum_sdk_version", + [FIRCLSMachOFormatVersion(&imageDetails.minSDK) UTF8String]); + FIRCLSFileWriteHashEntryString(file, "built_sdk_version", + [FIRCLSMachOFormatVersion(&imageDetails.builtSDK) UTF8String]); + + FIRCLSFileWriteHashEnd(file); + FIRCLSFileWriteSectionEnd(file); + + return true; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.h new file mode 100644 index 000000000..bdb43418c --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.h @@ -0,0 +1,121 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "FIRCLSAllocate.h" +#include "FIRCLSBinaryImage.h" +#include "FIRCLSException.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSHost.h" +#include "FIRCLSInternalLogging.h" +#include "FIRCLSMachException.h" +#include "FIRCLSSignal.h" +#include "FIRCLSUserLogging.h" + +#include +#include + +// The purpose of the crash context is to hold values that absolutely must be read and/or written at +// crash time. For robustness against memory corruption, they are protected with guard pages. +// Further, the context is seperated into read-only and read-write sections. + +__BEGIN_DECLS + +#ifdef __OBJC__ +@class FIRCLSInternalReport; +@class FIRCLSSettings; +@class FIRCLSInstallIdentifierModel; +@class FIRCLSFileManager; +#endif + +typedef struct { + volatile bool initialized; + volatile bool debuggerAttached; + const char* previouslyCrashedFileFullPath; + const char* logPath; +#if CLS_USE_SIGALTSTACK + void* signalStack; +#endif +#if CLS_MACH_EXCEPTION_SUPPORTED + void* machStack; +#endif + void* delegate; + void* callbackDelegate; + + FIRCLSBinaryImageReadOnlyContext binaryimage; + FIRCLSExceptionReadOnlyContext exception; + FIRCLSHostReadOnlyContext host; + FIRCLSSignalReadContext signal; +#if CLS_MACH_EXCEPTION_SUPPORTED + FIRCLSMachExceptionReadContext machException; +#endif + FIRCLSUserLoggingReadOnlyContext logging; +} FIRCLSReadOnlyContext; + +typedef struct { + FIRCLSInternalLoggingWritableContext internalLogging; + volatile bool crashOccurred; + FIRCLSBinaryImageReadWriteContext binaryImage; + FIRCLSUserLoggingWritableContext logging; + FIRCLSExceptionWritableContext exception; +} FIRCLSReadWriteContext; + +typedef struct { + FIRCLSReadOnlyContext* readonly; + FIRCLSReadWriteContext* writable; + FIRCLSAllocatorRef allocator; +} FIRCLSContext; + +typedef struct { + void* delegate; + const char* customBundleId; + const char* rootPath; + const char* previouslyCrashedFileRootPath; + const char* sessionId; + const char* installId; + const char* betaToken; +#if CLS_MACH_EXCEPTION_SUPPORTED + exception_mask_t machExceptionMask; +#endif + bool errorsEnabled; + bool customExceptionsEnabled; + uint32_t maxCustomExceptions; + uint32_t maxErrorLogSize; + uint32_t maxLogSize; + uint32_t maxKeyValues; +} FIRCLSContextInitData; + +#ifdef __OBJC__ +bool FIRCLSContextInitialize(FIRCLSInternalReport* report, + FIRCLSSettings* settings, + FIRCLSInstallIdentifierModel* installIDModel, + FIRCLSFileManager* fileManager); + +// Re-writes the metadata file on the current thread +void FIRCLSContextUpdateMetadata(FIRCLSInternalReport* report, + FIRCLSSettings* settings, + FIRCLSInstallIdentifierModel* installIDModel, + FIRCLSFileManager* fileManager); +#endif + +void FIRCLSContextBaseInit(void); +void FIRCLSContextBaseDeinit(void); + +bool FIRCLSContextIsInitialized(void); +bool FIRCLSContextHasCrashed(void); +void FIRCLSContextMarkHasCrashed(void); +bool FIRCLSContextMarkAndCheckIfCrashed(void); + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.m new file mode 100644 index 000000000..26d46c72e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.m @@ -0,0 +1,468 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSContext.h" + +#include +#include + +#import "FIRCLSFileManager.h" +#import "FIRCLSInstallIdentifierModel.h" +#import "FIRCLSInternalReport.h" +#import "FIRCLSSettings.h" + +#include "FIRCLSApplication.h" +#include "FIRCLSCrashedMarkerFile.h" +#include "FIRCLSDefines.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSProcess.h" +#include "FIRCLSUtility.h" + +// The writable size is our handler stack plus whatever scratch we need. We have to use this space +// extremely carefully, however, because thread stacks always needs to be page-aligned. Only the +// first allocation is gauranteed to be page-aligned. +// +// CLS_SIGNAL_HANDLER_STACK_SIZE and CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE are platform dependant, +// defined as 0 for tv/watch. +#define CLS_MINIMUM_READWRITE_SIZE \ + (CLS_SIGNAL_HANDLER_STACK_SIZE + CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE + \ + sizeof(FIRCLSReadWriteContext)) + +// We need enough space here for the context, plus storage for strings. +#define CLS_MINIMUM_READABLE_SIZE (sizeof(FIRCLSReadOnlyContext) + 4096 * 4) + +static const int64_t FIRCLSContextInitWaitTime = 5LL * NSEC_PER_SEC; + +static bool FIRCLSContextRecordMetadata(const char* path, const FIRCLSContextInitData* initData); +static const char* FIRCLSContextAppendToRoot(NSString* root, NSString* component); +static void FIRCLSContextAllocate(FIRCLSContext* context); + +FIRCLSContextInitData FIRCLSContextBuildInitData(FIRCLSInternalReport* report, + FIRCLSSettings* settings, + FIRCLSInstallIdentifierModel* installIDModel, + FIRCLSFileManager* fileManager) { + // Because we need to start the crash reporter right away, + // it starts up either with default settings, or cached settings + // from the last time they were fetched + + FIRCLSContextInitData initData; + + memset(&initData, 0, sizeof(FIRCLSContextInitData)); + + initData.customBundleId = nil; + initData.installId = [installIDModel.installID UTF8String]; + initData.sessionId = [[report identifier] UTF8String]; + initData.rootPath = [[report path] UTF8String]; + initData.previouslyCrashedFileRootPath = [[fileManager rootPath] UTF8String]; + initData.errorsEnabled = [settings errorReportingEnabled]; + initData.customExceptionsEnabled = [settings customExceptionsEnabled]; + initData.maxCustomExceptions = [settings maxCustomExceptions]; + initData.maxErrorLogSize = [settings errorLogBufferSize]; + initData.maxLogSize = [settings logBufferSize]; + initData.maxKeyValues = [settings maxCustomKeys]; + + // If this is set, then we could attempt to do a synchronous submission for certain kinds of + // events (exceptions). This is a very cool feature, but adds complexity to the backend. For now, + // we're going to leave this disabled. It does work in the exception case, but will ultimtely + // result in the following crash to be discared. Usually that crash isn't interesting. But, if it + // was, we'd never have a chance to see it. + initData.delegate = nil; + +#if CLS_MACH_EXCEPTION_SUPPORTED + __block exception_mask_t mask = 0; + + // TODO(b/141241224) This if statement was hardcoded to no, so this block was never run + // FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) { + // if ([self.delegate ensureDeliveryOfUnixSignal:signal]) { + // mask |= FIRCLSMachExceptionMaskForSignal(signal); + // } + // }); + + initData.machExceptionMask = mask; +#endif + + return initData; +} + +bool FIRCLSContextInitialize(FIRCLSInternalReport* report, + FIRCLSSettings* settings, + FIRCLSInstallIdentifierModel* installIDModel, + FIRCLSFileManager* fileManager) { + FIRCLSContextInitData initDataObj = + FIRCLSContextBuildInitData(report, settings, installIDModel, fileManager); + FIRCLSContextInitData* initData = &initDataObj; + + if (!initData) { + return false; + } + + FIRCLSContextBaseInit(); + + dispatch_group_t group = dispatch_group_create(); + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + if (!FIRCLSIsValidPointer(initData->rootPath)) { + return false; + } + + NSString* rootPath = [NSString stringWithUTF8String:initData->rootPath]; + + // setup our SDK log file synchronously, because other calls may depend on it + _firclsContext.readonly->logPath = FIRCLSContextAppendToRoot(rootPath, @"sdk.log"); + if (!FIRCLSUnlinkIfExists(_firclsContext.readonly->logPath)) { + FIRCLSErrorLog(@"Unable to write initialize SDK write paths %s", strerror(errno)); + } + + // some values that aren't tied to particular subsystem + _firclsContext.readonly->debuggerAttached = FIRCLSProcessDebuggerAttached(); + _firclsContext.readonly->delegate = initData->delegate; + + dispatch_group_async(group, queue, ^{ + FIRCLSHostInitialize(&_firclsContext.readonly->host); + }); + + dispatch_group_async(group, queue, ^{ + _firclsContext.readonly->logging.errorStorage.maxSize = 0; + _firclsContext.readonly->logging.errorStorage.maxEntries = + initData->errorsEnabled ? initData->maxCustomExceptions : 0; + _firclsContext.readonly->logging.errorStorage.restrictBySize = false; + _firclsContext.readonly->logging.errorStorage.entryCount = + &_firclsContext.writable->logging.errorsCount; + _firclsContext.readonly->logging.errorStorage.aPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportErrorAFile); + _firclsContext.readonly->logging.errorStorage.bPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportErrorBFile); + + _firclsContext.readonly->logging.logStorage.maxSize = initData->maxLogSize; + _firclsContext.readonly->logging.logStorage.maxEntries = 0; + _firclsContext.readonly->logging.logStorage.restrictBySize = true; + _firclsContext.readonly->logging.logStorage.entryCount = NULL; + _firclsContext.readonly->logging.logStorage.aPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportLogAFile); + _firclsContext.readonly->logging.logStorage.bPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportLogBFile); + _firclsContext.readonly->logging.customExceptionStorage.aPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportCustomExceptionAFile); + _firclsContext.readonly->logging.customExceptionStorage.bPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportCustomExceptionBFile); + _firclsContext.readonly->logging.customExceptionStorage.maxSize = 0; + _firclsContext.readonly->logging.customExceptionStorage.restrictBySize = false; + _firclsContext.readonly->logging.customExceptionStorage.maxEntries = + initData->maxCustomExceptions; + _firclsContext.readonly->logging.customExceptionStorage.entryCount = + &_firclsContext.writable->exception.customExceptionCount; + + _firclsContext.readonly->logging.userKVStorage.maxCount = initData->maxKeyValues; + _firclsContext.readonly->logging.userKVStorage.incrementalPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportUserIncrementalKVFile); + _firclsContext.readonly->logging.userKVStorage.compactedPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportUserCompactedKVFile); + + _firclsContext.readonly->logging.internalKVStorage.maxCount = 32; // Hardcode = bad + _firclsContext.readonly->logging.internalKVStorage.incrementalPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportInternalIncrementalKVFile); + _firclsContext.readonly->logging.internalKVStorage.compactedPath = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportInternalCompactedKVFile); + + FIRCLSUserLoggingInit(&_firclsContext.readonly->logging, &_firclsContext.writable->logging); + }); + + dispatch_group_async(group, queue, ^{ + _firclsContext.readonly->binaryimage.path = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportBinaryImageFile); + + FIRCLSBinaryImageInit(&_firclsContext.readonly->binaryimage, + &_firclsContext.writable->binaryImage); + }); + + dispatch_group_async(group, queue, ^{ + NSString* rootPath = [NSString stringWithUTF8String:initData->previouslyCrashedFileRootPath]; + NSString* fileName = [NSString stringWithUTF8String:FIRCLSCrashedMarkerFileName]; + _firclsContext.readonly->previouslyCrashedFileFullPath = + FIRCLSContextAppendToRoot(rootPath, fileName); + }); + + if (!_firclsContext.readonly->debuggerAttached) { + dispatch_group_async(group, queue, ^{ + _firclsContext.readonly->signal.path = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportSignalFile); + + FIRCLSSignalInitialize(&_firclsContext.readonly->signal); + }); + +#if CLS_MACH_EXCEPTION_SUPPORTED + dispatch_group_async(group, queue, ^{ + _firclsContext.readonly->machException.path = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportMachExceptionFile); + + FIRCLSMachExceptionInit(&_firclsContext.readonly->machException, initData->machExceptionMask); + }); +#endif + + dispatch_group_async(group, queue, ^{ + _firclsContext.readonly->exception.path = + FIRCLSContextAppendToRoot(rootPath, FIRCLSReportExceptionFile); + _firclsContext.readonly->exception.maxCustomExceptions = + initData->customExceptionsEnabled ? initData->maxCustomExceptions : 0; + + FIRCLSExceptionInitialize(&_firclsContext.readonly->exception, + &_firclsContext.writable->exception, initData->delegate); + }); + } else { + FIRCLSSDKLog("Debugger present - not installing handlers\n"); + } + + dispatch_group_async(group, queue, ^{ + const char* metaDataPath = [[rootPath stringByAppendingPathComponent:FIRCLSReportMetadataFile] + fileSystemRepresentation]; + if (!FIRCLSContextRecordMetadata(metaDataPath, initData)) { + FIRCLSSDKLog("Unable to record context metadata\n"); + } + }); + + // At this point we need to do two things. First, we need to do our memory protection *only* after + // all of these initialization steps are really done. But, we also want to wait as long as + // possible for these to be complete. If we do not, there's a chance that we will not be able to + // correctly report a crash shortly after start. + + // Note at this will retain the group, so its totally fine to release the group here. + dispatch_group_notify(group, queue, ^{ + _firclsContext.readonly->initialized = true; + __sync_synchronize(); + + if (!FIRCLSAllocatorProtect(_firclsContext.allocator)) { + FIRCLSSDKLog("Error: Memory protection failed\n"); + } + }); + + if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, FIRCLSContextInitWaitTime)) != + 0) { + FIRCLSSDKLog("Error: Delayed initialization\n"); + } + + return true; +} + +void FIRCLSContextUpdateMetadata(FIRCLSInternalReport* report, + FIRCLSSettings* settings, + FIRCLSInstallIdentifierModel* installIDModel, + FIRCLSFileManager* fileManager) { + FIRCLSContextInitData initDataObj = + FIRCLSContextBuildInitData(report, settings, installIDModel, fileManager); + FIRCLSContextInitData* initData = &initDataObj; + + NSString* rootPath = [NSString stringWithUTF8String:initData->rootPath]; + + const char* metaDataPath = + [[rootPath stringByAppendingPathComponent:FIRCLSReportMetadataFile] fileSystemRepresentation]; + + if (!FIRCLSContextRecordMetadata(metaDataPath, initData)) { + FIRCLSErrorLog(@"Unable to update context metadata"); + } +} + +void FIRCLSContextBaseInit(void) { + NSString* sdkBundleID = FIRCLSApplicationGetSDKBundleID(); + + NSString* loggingQueueName = [sdkBundleID stringByAppendingString:@".logging"]; + NSString* binaryImagesQueueName = [sdkBundleID stringByAppendingString:@".binary-images"]; + NSString* exceptionQueueName = [sdkBundleID stringByAppendingString:@".exception"]; + + _firclsLoggingQueue = dispatch_queue_create([loggingQueueName UTF8String], DISPATCH_QUEUE_SERIAL); + _firclsBinaryImageQueue = + dispatch_queue_create([binaryImagesQueueName UTF8String], DISPATCH_QUEUE_SERIAL); + _firclsExceptionQueue = + dispatch_queue_create([exceptionQueueName UTF8String], DISPATCH_QUEUE_SERIAL); + + FIRCLSContextAllocate(&_firclsContext); + + _firclsContext.writable->internalLogging.logFd = -1; + _firclsContext.writable->internalLogging.logLevel = FIRCLSInternalLogLevelDebug; + _firclsContext.writable->crashOccurred = false; + + _firclsContext.readonly->initialized = false; + + __sync_synchronize(); +} + +static void FIRCLSContextAllocate(FIRCLSContext* context) { + // create the allocator, and the contexts + // The ordering here is really important, because the "stack" variable must be + // page-aligned. There's no mechanism to ask the allocator to do alignment, but we + // do know the very first allocation in a region is aligned to a page boundary. + + context->allocator = FIRCLSAllocatorCreate(CLS_MINIMUM_READWRITE_SIZE, CLS_MINIMUM_READABLE_SIZE); + + context->readonly = + FIRCLSAllocatorSafeAllocate(context->allocator, sizeof(FIRCLSReadOnlyContext), CLS_READONLY); + memset(context->readonly, 0, sizeof(FIRCLSReadOnlyContext)); + +#if CLS_MEMORY_PROTECTION_ENABLED +#if CLS_MACH_EXCEPTION_SUPPORTED + context->readonly->machStack = FIRCLSAllocatorSafeAllocate( + context->allocator, CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE, CLS_READWRITE); +#endif +#if CLS_USE_SIGALTSTACK + context->readonly->signalStack = + FIRCLSAllocatorSafeAllocate(context->allocator, CLS_SIGNAL_HANDLER_STACK_SIZE, CLS_READWRITE); +#endif +#else +#if CLS_MACH_EXCEPTION_SUPPORTED + context->readonly->machStack = valloc(CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE); +#endif +#if CLS_USE_SIGALTSTACK + context->readonly->signalStack = valloc(CLS_SIGNAL_HANDLER_STACK_SIZE); +#endif +#endif + +#if CLS_MACH_EXCEPTION_SUPPORTED + memset(_firclsContext.readonly->machStack, 0, CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE); +#endif +#if CLS_USE_SIGALTSTACK + memset(_firclsContext.readonly->signalStack, 0, CLS_SIGNAL_HANDLER_STACK_SIZE); +#endif + + context->writable = FIRCLSAllocatorSafeAllocate(context->allocator, + sizeof(FIRCLSReadWriteContext), CLS_READWRITE); + memset(context->writable, 0, sizeof(FIRCLSReadWriteContext)); +} + +void FIRCLSContextBaseDeinit(void) { + _firclsContext.readonly->initialized = false; + + FIRCLSAllocatorDestroy(_firclsContext.allocator); +} + +bool FIRCLSContextIsInitialized(void) { + __sync_synchronize(); + if (!FIRCLSIsValidPointer(_firclsContext.readonly)) { + return false; + } + + return _firclsContext.readonly->initialized; +} + +bool FIRCLSContextHasCrashed(void) { + if (!FIRCLSContextIsInitialized()) { + return false; + } + + // we've already run a full barrier above, so this read is ok + return _firclsContext.writable->crashOccurred; +} + +void FIRCLSContextMarkHasCrashed(void) { + if (!FIRCLSContextIsInitialized()) { + return; + } + + _firclsContext.writable->crashOccurred = true; + __sync_synchronize(); +} + +bool FIRCLSContextMarkAndCheckIfCrashed(void) { + if (!FIRCLSContextIsInitialized()) { + return false; + } + + if (_firclsContext.writable->crashOccurred) { + return true; + } + + _firclsContext.writable->crashOccurred = true; + __sync_synchronize(); + + return false; +} + +static const char* FIRCLSContextAppendToRoot(NSString* root, NSString* component) { + return FIRCLSDupString( + [[root stringByAppendingPathComponent:component] fileSystemRepresentation]); +} + +static bool FIRCLSContextRecordIdentity(FIRCLSFile* file, const FIRCLSContextInitData* initData) { + FIRCLSFileWriteSectionStart(file, "identity"); + + FIRCLSFileWriteHashStart(file); + + FIRCLSFileWriteHashEntryString(file, "generator", CLS_SDK_GENERATOR_NAME); + FIRCLSFileWriteHashEntryString(file, "display_version", CLS_SDK_DISPLAY_VERSION); + FIRCLSFileWriteHashEntryString(file, "build_version", CLS_SDK_DISPLAY_VERSION); + FIRCLSFileWriteHashEntryUint64(file, "started_at", time(NULL)); + + FIRCLSFileWriteHashEntryString(file, "session_id", initData->sessionId); + FIRCLSFileWriteHashEntryString(file, "install_id", initData->installId); + FIRCLSFileWriteHashEntryString(file, "beta_token", initData->betaToken); + FIRCLSFileWriteHashEntryBoolean(file, "absolute_log_timestamps", true); + + FIRCLSFileWriteHashEnd(file); + FIRCLSFileWriteSectionEnd(file); + + return true; +} + +static bool FIRCLSContextRecordApplication(FIRCLSFile* file, const char* customBundleId) { + FIRCLSFileWriteSectionStart(file, "application"); + + FIRCLSFileWriteHashStart(file); + + FIRCLSFileWriteHashEntryString(file, "bundle_id", + [FIRCLSApplicationGetBundleIdentifier() UTF8String]); + FIRCLSFileWriteHashEntryString(file, "custom_bundle_id", customBundleId); + FIRCLSFileWriteHashEntryString(file, "build_version", + [FIRCLSApplicationGetBundleVersion() UTF8String]); + FIRCLSFileWriteHashEntryString(file, "display_version", + [FIRCLSApplicationGetShortBundleVersion() UTF8String]); + FIRCLSFileWriteHashEntryString(file, "extension_id", + [FIRCLSApplicationExtensionPointIdentifier() UTF8String]); + + FIRCLSFileWriteHashEnd(file); + FIRCLSFileWriteSectionEnd(file); + + return true; +} + +static bool FIRCLSContextRecordMetadata(const char* path, const FIRCLSContextInitData* initData) { + if (!FIRCLSUnlinkIfExists(path)) { + FIRCLSSDKLog("Unable to unlink existing metadata file %s\n", strerror(errno)); + } + + FIRCLSFile file; + + if (!FIRCLSFileInitWithPath(&file, path, false)) { + FIRCLSSDKLog("Unable to open metadata file %s\n", strerror(errno)); + return false; + } + + if (!FIRCLSContextRecordIdentity(&file, initData)) { + FIRCLSSDKLog("Unable to write out identity metadata\n"); + } + + if (!FIRCLSHostRecord(&file)) { + FIRCLSSDKLog("Unable to write out host metadata\n"); + } + + if (!FIRCLSContextRecordApplication(&file, initData->customBundleId)) { + FIRCLSSDKLog("Unable to write out application metadata\n"); + } + + if (!FIRCLSBinaryImageRecordMainExecutable(&file)) { + FIRCLSSDKLog("Unable to write out executable metadata\n"); + } + + FIRCLSFileClose(&file); + + return true; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.c new file mode 100644 index 000000000..5dd67fdec --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.c @@ -0,0 +1,31 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSCrashedMarkerFile.h" +#include "FIRCLSFile.h" +#include "FIRCLSUtility.h" + +const char *FIRCLSCrashedMarkerFileName = "previously-crashed"; + +void FIRCLSCreateCrashedMarkerFile() { + FIRCLSFile file; + + if (!FIRCLSFileInitWithPath(&file, _firclsContext.readonly->previouslyCrashedFileFullPath, false)) { + FIRCLSSDKLog("Unable to create the crashed marker file\n"); + return; + } + + FIRCLSFileClose(&file); + FIRCLSSDKLog("Created the crashed marker file\n"); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h new file mode 100644 index 000000000..ccf427673 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h @@ -0,0 +1,19 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +extern const char *FIRCLSCrashedMarkerFileName; + +void FIRCLSCreateCrashedMarkerFile(void); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h new file mode 100644 index 000000000..4f125cf08 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h @@ -0,0 +1,28 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSContext.h" + +__BEGIN_DECLS + +extern FIRCLSContext _firclsContext; +extern dispatch_queue_t _firclsLoggingQueue; +extern dispatch_queue_t _firclsBinaryImageQueue; +extern dispatch_queue_t _firclsExceptionQueue; + +#define FIRCLSGetLoggingQueue() (_firclsLoggingQueue) +#define FIRCLSGetBinaryImageQueue() (_firclsBinaryImageQueue) +#define FIRCLSGetExceptionQueue() (_firclsExceptionQueue) + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.h new file mode 100644 index 000000000..24b1acd13 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.h @@ -0,0 +1,37 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include "FIRCLSFile.h" + +typedef struct { + const char* documentDirectoryPath; + vm_size_t pageSize; +} FIRCLSHostReadOnlyContext; + +__BEGIN_DECLS + +void FIRCLSHostInitialize(FIRCLSHostReadOnlyContext* roContext); + +vm_size_t FIRCLSHostGetPageSize(void); + +bool FIRCLSHostRecord(FIRCLSFile* file); + +void FIRCLSHostWriteDiskUsage(FIRCLSFile* file); + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.m new file mode 100644 index 000000000..87ff0f7ae --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.m @@ -0,0 +1,161 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSHost.h" + +#include +#include +#include + +#import "FIRCLSApplication.h" +#include "FIRCLSDefines.h" +#import "FIRCLSFABHost.h" +#include "FIRCLSFile.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSUtility.h" + +#if TARGET_OS_IPHONE +#import +#else +#import +#endif + +#define CLS_HOST_SYSCTL_BUFFER_SIZE (128) + +#if CLS_CPU_ARM64 +#define CLS_MAX_NATIVE_PAGE_SIZE (1024 * 16) +#else +// return 4K, which is correct for all platforms except arm64, currently +#define CLS_MAX_NATIVE_PAGE_SIZE (1024 * 4) +#endif +#define CLS_MIN_NATIVE_PAGE_SIZE (1024 * 4) + +#pragma mark Prototypes +static void FIRCLSHostWriteSysctlEntry( + FIRCLSFile* file, const char* key, const char* sysctlKey, void* buffer, size_t bufferSize); +static void FIRCLSHostWriteModelInfo(FIRCLSFile* file); +static void FIRCLSHostWriteOSVersionInfo(FIRCLSFile* file); + +#pragma mark - API +void FIRCLSHostInitialize(FIRCLSHostReadOnlyContext* roContext) { + _firclsContext.readonly->host.pageSize = FIRCLSHostGetPageSize(); + _firclsContext.readonly->host.documentDirectoryPath = NULL; + + // determine where the document directory is mounted, so we can get file system statistics later + NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + if ([paths count]) { + _firclsContext.readonly->host.documentDirectoryPath = + FIRCLSDupString([[paths objectAtIndex:0] fileSystemRepresentation]); + } +} + +vm_size_t FIRCLSHostGetPageSize(void) { + size_t size; + int pageSize; + + // hw.pagesize is defined as HW_PAGESIZE, which is an int. It's important to match + // these types. Turns out that sysctl will not init the data to zero, but it appears + // that sysctlbyname does. This API is nicer, but that's important to keep in mind. + + pageSize = 0; + size = sizeof(pageSize); + if (sysctlbyname("hw.pagesize", &pageSize, &size, NULL, 0) != 0) { + FIRCLSSDKLog("sysctlbyname failed while trying to get hw.pagesize\n"); + + return CLS_MAX_NATIVE_PAGE_SIZE; + } + + // if the returned size is not the expected value, abort + if (size != sizeof(pageSize)) { + return CLS_MAX_NATIVE_PAGE_SIZE; + } + + // put in some guards to make sure our size is reasonable + if (pageSize > CLS_MAX_NATIVE_PAGE_SIZE) { + return CLS_MAX_NATIVE_PAGE_SIZE; + } + + if (pageSize < CLS_MIN_NATIVE_PAGE_SIZE) { + return CLS_MIN_NATIVE_PAGE_SIZE; + } + + return pageSize; +} + +static void FIRCLSHostWriteSysctlEntry( + FIRCLSFile* file, const char* key, const char* sysctlKey, void* buffer, size_t bufferSize) { + if (sysctlbyname(sysctlKey, buffer, &bufferSize, NULL, 0) != 0) { + FIRCLSFileWriteHashEntryString(file, key, "(failed)"); + return; + } + + FIRCLSFileWriteHashEntryString(file, key, buffer); +} + +static void FIRCLSHostWriteModelInfo(FIRCLSFile* file) { + FIRCLSFileWriteHashEntryString(file, "model", [FIRCLSHostModelInfo() UTF8String]); + + // allocate a static buffer for the sysctl values, which are typically + // quite short + char buffer[CLS_HOST_SYSCTL_BUFFER_SIZE]; + +#if TARGET_OS_EMBEDDED + FIRCLSHostWriteSysctlEntry(file, "machine", "hw.model", buffer, CLS_HOST_SYSCTL_BUFFER_SIZE); +#else + FIRCLSHostWriteSysctlEntry(file, "machine", "hw.machine", buffer, CLS_HOST_SYSCTL_BUFFER_SIZE); + FIRCLSHostWriteSysctlEntry(file, "cpu", "machdep.cpu.brand_string", buffer, + CLS_HOST_SYSCTL_BUFFER_SIZE); +#endif +} + +static void FIRCLSHostWriteOSVersionInfo(FIRCLSFile* file) { + FIRCLSFileWriteHashEntryString(file, "os_build_version", [FIRCLSHostOSBuildVersion() UTF8String]); + FIRCLSFileWriteHashEntryString(file, "os_display_version", + [FIRCLSHostOSDisplayVersion() UTF8String]); + FIRCLSFileWriteHashEntryString(file, "platform", [FIRCLSApplicationGetPlatform() UTF8String]); +} + +bool FIRCLSHostRecord(FIRCLSFile* file) { + FIRCLSFileWriteSectionStart(file, "host"); + + FIRCLSFileWriteHashStart(file); + + FIRCLSHostWriteModelInfo(file); + FIRCLSHostWriteOSVersionInfo(file); + FIRCLSFileWriteHashEntryString(file, "locale", + [[[NSLocale currentLocale] localeIdentifier] UTF8String]); + + FIRCLSFileWriteHashEnd(file); + + FIRCLSFileWriteSectionEnd(file); + + return true; +} + +void FIRCLSHostWriteDiskUsage(FIRCLSFile* file) { + struct statfs tStats; + + FIRCLSFileWriteSectionStart(file, "storage"); + + FIRCLSFileWriteHashStart(file); + + if (statfs(_firclsContext.readonly->host.documentDirectoryPath, &tStats) == 0) { + FIRCLSFileWriteHashEntryUint64(file, "free", tStats.f_bavail * tStats.f_bsize); + FIRCLSFileWriteHashEntryUint64(file, "total", tStats.f_blocks * tStats.f_bsize); + } + + FIRCLSFileWriteHashEnd(file); + + FIRCLSFileWriteSectionEnd(file); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.c new file mode 100644 index 000000000..415466ec4 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.c @@ -0,0 +1,824 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSProcess.h" +#include "FIRCLSDefines.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSProfiling.h" +#include "FIRCLSThreadState.h" +#include "FIRCLSUnwind.h" +#include "FIRCLSUtility.h" + +#include +#include +#include +#include + +#define THREAD_NAME_BUFFER_SIZE (64) + +#pragma mark Prototypes +static bool FIRCLSProcessGetThreadName(FIRCLSProcess *process, + thread_t thread, + char *buffer, + size_t length); +static const char *FIRCLSProcessGetThreadDispatchQueueName(FIRCLSProcess *process, thread_t thread); + +#pragma mark - API +bool FIRCLSProcessInit(FIRCLSProcess *process, thread_t crashedThread, void *uapVoid) { + if (!process) { + return false; + } + + process->task = mach_task_self(); + process->thisThread = mach_thread_self(); + process->crashedThread = crashedThread; + process->uapVoid = uapVoid; + + if (task_threads(process->task, &process->threads, &process->threadCount) != KERN_SUCCESS) { + // failed to get all threads + process->threadCount = 0; + FIRCLSSDKLog("Error: unable to get task threads\n"); + + return false; + } + + return true; +} + +bool FIRCLSProcessDestroy(FIRCLSProcess *process) { + return false; +} + +// https://developer.apple.com/library/mac/#qa/qa2004/qa1361.html +bool FIRCLSProcessDebuggerAttached(void) { + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + if (junk != 0) { + FIRCLSSDKLog("sysctl failed while trying to get kinfo_proc\n"); + return false; + } + + // We're being debugged if the P_TRACED flag is set. + return (info.kp_proc.p_flag & P_TRACED) != 0; +} + +#pragma mark - Thread Support +static bool FIRCLSProcessIsCurrentThread(FIRCLSProcess *process, thread_t thread) { + return MACH_PORT_INDEX(process->thisThread) == MACH_PORT_INDEX(thread); +} + +static bool FIRCLSProcessIsCrashedThread(FIRCLSProcess *process, thread_t thread) { + return MACH_PORT_INDEX(process->crashedThread) == MACH_PORT_INDEX(thread); +} + +static uint32_t FIRCLSProcessGetThreadCount(FIRCLSProcess *process) { + return process->threadCount; +} + +static thread_t FIRCLSProcessGetThread(FIRCLSProcess *process, uint32_t index) { + if (index >= process->threadCount) { + return MACH_PORT_NULL; + } + + return process->threads[index]; +} + +bool FIRCLSProcessSuspendAllOtherThreads(FIRCLSProcess *process) { + mach_msg_type_number_t i; + bool success; + + success = true; + for (i = 0; i < process->threadCount; ++i) { + thread_t thread; + + thread = FIRCLSProcessGetThread(process, i); + + if (FIRCLSProcessIsCurrentThread(process, thread)) { + continue; + } + + // FIXME: workaround to get this building on watch, but we need to suspend/resume threads! +#if CLS_CAN_SUSPEND_THREADS + success = success && (thread_suspend(thread) == KERN_SUCCESS); +#endif + } + + return success; +} + +bool FIRCLSProcessResumeAllOtherThreads(FIRCLSProcess *process) { + mach_msg_type_number_t i; + bool success; + + success = true; + for (i = 0; i < process->threadCount; ++i) { + thread_t thread; + + thread = FIRCLSProcessGetThread(process, i); + + if (FIRCLSProcessIsCurrentThread(process, thread)) { + continue; + } + + // FIXME: workaround to get this building on watch, but we need to suspend/resume threads! +#if CLS_CAN_SUSPEND_THREADS + success = success && (thread_resume(thread) == KERN_SUCCESS); +#endif + } + + return success; +} + +#pragma mark - Thread Properties +void *FIRCLSThreadGetCurrentPC(void) { + return __builtin_return_address(0); +} + +static bool FIRCLSProcessGetThreadState(FIRCLSProcess *process, + thread_t thread, + FIRCLSThreadContext *context) { + if (!FIRCLSIsValidPointer(context)) { + FIRCLSSDKLogError("invalid context supplied"); + return false; + } + + // If the thread context we should use is non-NULL, then just assign it here. Otherwise, + // query the thread state + if (FIRCLSProcessIsCrashedThread(process, thread) && FIRCLSIsValidPointer(process->uapVoid)) { + *context = *((_STRUCT_UCONTEXT *)process->uapVoid)->uc_mcontext; + return true; + } + + // Here's a wild trick: emulate what thread_get_state would do. It apppears that + // we cannot reliably unwind out of thread_get_state. So, instead of trying, setup + // a thread context that resembles what the real thing would look like + if (FIRCLSProcessIsCurrentThread(process, thread)) { + FIRCLSSDKLog("Faking current thread\n"); + memset(context, 0, sizeof(FIRCLSThreadContext)); + + // Compute the frame address, and then base the stack value off of that. A frame pushes + // two pointers onto the stack, so we have to offset. + const uintptr_t frameAddress = (uintptr_t)__builtin_frame_address(0); + const uintptr_t stackAddress = FIRCLSUnwindStackPointerFromFramePointer(frameAddress); + +#if CLS_CPU_X86_64 + context->__ss.__rip = (uintptr_t)FIRCLSThreadGetCurrentPC(); + context->__ss.__rbp = frameAddress; + context->__ss.__rsp = stackAddress; +#elif CLS_CPU_I386 + context->__ss.__eip = (uintptr_t)FIRCLSThreadGetCurrentPC(); + context->__ss.__ebp = frameAddress; + context->__ss.__esp = stackAddress; +#elif CLS_CPU_ARM64 + FIRCLSThreadContextSetPC(context, (uintptr_t)FIRCLSThreadGetCurrentPC()); + FIRCLSThreadContextSetFramePointer(context, frameAddress); + FIRCLSThreadContextSetLinkRegister(context, (uintptr_t)__builtin_return_address(0)); + FIRCLSThreadContextSetStackPointer(context, stackAddress); +#elif CLS_CPU_ARM + context->__ss.__pc = (uintptr_t)FIRCLSThreadGetCurrentPC(); + context->__ss.__r[7] = frameAddress; + context->__ss.__lr = (uintptr_t)__builtin_return_address(0); + context->__ss.__sp = stackAddress; +#endif + + return true; + } + +#if !TARGET_OS_WATCH + // try to get the value by querying the thread state + mach_msg_type_number_t stateCount = FIRCLSThreadStateCount; + if (thread_get_state(thread, FIRCLSThreadState, (thread_state_t)(&(context->__ss)), + &stateCount) != KERN_SUCCESS) { + FIRCLSSDKLogError("failed to get thread state\n"); + return false; + } + + return true; +#else + return false; +#endif +} + +static bool FIRCLSProcessGetThreadName(FIRCLSProcess *process, + thread_t thread, + char *buffer, + size_t length) { + pthread_t pthread; + + if (!buffer || length <= 0) { + return false; + } + + pthread = pthread_from_mach_thread_np(thread); + + return pthread_getname_np(pthread, buffer, length) == 0; +} + +static const char *FIRCLSProcessGetThreadDispatchQueueName(FIRCLSProcess *process, + thread_t thread) { + thread_identifier_info_data_t info; + mach_msg_type_number_t infoCount; + dispatch_queue_t *queueAddress; + dispatch_queue_t queue; + const char *string; + + infoCount = THREAD_IDENTIFIER_INFO_COUNT; + if (thread_info(thread, THREAD_IDENTIFIER_INFO, (thread_info_t)&info, &infoCount) != + KERN_SUCCESS) { + FIRCLSSDKLog("unable to get thread info\n"); + return NULL; + } + + queueAddress = (dispatch_queue_t *)info.dispatch_qaddr; + if (queueAddress == NULL) { + return ""; + } + + // Sometimes a queue address is invalid. I cannot explain why this is, but + // it can cause a crash. + if (!FIRCLSReadMemory((vm_address_t)queueAddress, &queue, sizeof(void *))) { + return ""; + } + + // here, we know it is safe to de-reference this address, so attempt to get the queue name + if (!queue) { + return ""; + } + + string = dispatch_queue_get_label(queue); + + // but, we still don't if the entire string is valid, so check that too + if (!FIRCLSReadString((vm_address_t)string, (char **)&string, 128)) { + return ""; + } + + return string; +} + +#pragma mark - Data Recording +static bool FIRCLSProcessRecordThreadRegisters(FIRCLSThreadContext context, FIRCLSFile *file) { +#if CLS_CPU_ARM + FIRCLSFileWriteHashEntryUint64(file, "r0", context.__ss.__r[0]); + FIRCLSFileWriteHashEntryUint64(file, "r1", context.__ss.__r[1]); + FIRCLSFileWriteHashEntryUint64(file, "r2", context.__ss.__r[2]); + FIRCLSFileWriteHashEntryUint64(file, "r3", context.__ss.__r[3]); + FIRCLSFileWriteHashEntryUint64(file, "r4", context.__ss.__r[4]); + FIRCLSFileWriteHashEntryUint64(file, "r5", context.__ss.__r[5]); + FIRCLSFileWriteHashEntryUint64(file, "r6", context.__ss.__r[6]); + FIRCLSFileWriteHashEntryUint64(file, "r7", context.__ss.__r[7]); + FIRCLSFileWriteHashEntryUint64(file, "r8", context.__ss.__r[8]); + FIRCLSFileWriteHashEntryUint64(file, "r9", context.__ss.__r[9]); + FIRCLSFileWriteHashEntryUint64(file, "r10", context.__ss.__r[10]); + FIRCLSFileWriteHashEntryUint64(file, "r11", context.__ss.__r[11]); + FIRCLSFileWriteHashEntryUint64(file, "ip", context.__ss.__r[12]); + FIRCLSFileWriteHashEntryUint64(file, "sp", context.__ss.__sp); + FIRCLSFileWriteHashEntryUint64(file, "lr", context.__ss.__lr); + FIRCLSFileWriteHashEntryUint64(file, "pc", context.__ss.__pc); + FIRCLSFileWriteHashEntryUint64(file, "cpsr", context.__ss.__cpsr); +#elif CLS_CPU_ARM64 + FIRCLSFileWriteHashEntryUint64(file, "x0", context.__ss.__x[0]); + FIRCLSFileWriteHashEntryUint64(file, "x1", context.__ss.__x[1]); + FIRCLSFileWriteHashEntryUint64(file, "x2", context.__ss.__x[2]); + FIRCLSFileWriteHashEntryUint64(file, "x3", context.__ss.__x[3]); + FIRCLSFileWriteHashEntryUint64(file, "x4", context.__ss.__x[4]); + FIRCLSFileWriteHashEntryUint64(file, "x5", context.__ss.__x[5]); + FIRCLSFileWriteHashEntryUint64(file, "x6", context.__ss.__x[6]); + FIRCLSFileWriteHashEntryUint64(file, "x7", context.__ss.__x[7]); + FIRCLSFileWriteHashEntryUint64(file, "x8", context.__ss.__x[8]); + FIRCLSFileWriteHashEntryUint64(file, "x9", context.__ss.__x[9]); + FIRCLSFileWriteHashEntryUint64(file, "x10", context.__ss.__x[10]); + FIRCLSFileWriteHashEntryUint64(file, "x11", context.__ss.__x[11]); + FIRCLSFileWriteHashEntryUint64(file, "x12", context.__ss.__x[12]); + FIRCLSFileWriteHashEntryUint64(file, "x13", context.__ss.__x[13]); + FIRCLSFileWriteHashEntryUint64(file, "x14", context.__ss.__x[14]); + FIRCLSFileWriteHashEntryUint64(file, "x15", context.__ss.__x[15]); + FIRCLSFileWriteHashEntryUint64(file, "x16", context.__ss.__x[16]); + FIRCLSFileWriteHashEntryUint64(file, "x17", context.__ss.__x[17]); + FIRCLSFileWriteHashEntryUint64(file, "x18", context.__ss.__x[18]); + FIRCLSFileWriteHashEntryUint64(file, "x19", context.__ss.__x[19]); + FIRCLSFileWriteHashEntryUint64(file, "x20", context.__ss.__x[20]); + FIRCLSFileWriteHashEntryUint64(file, "x21", context.__ss.__x[21]); + FIRCLSFileWriteHashEntryUint64(file, "x22", context.__ss.__x[22]); + FIRCLSFileWriteHashEntryUint64(file, "x23", context.__ss.__x[23]); + FIRCLSFileWriteHashEntryUint64(file, "x24", context.__ss.__x[24]); + FIRCLSFileWriteHashEntryUint64(file, "x25", context.__ss.__x[25]); + FIRCLSFileWriteHashEntryUint64(file, "x26", context.__ss.__x[26]); + FIRCLSFileWriteHashEntryUint64(file, "x27", context.__ss.__x[27]); + FIRCLSFileWriteHashEntryUint64(file, "x28", context.__ss.__x[28]); + FIRCLSFileWriteHashEntryUint64(file, "fp", FIRCLSThreadContextGetFramePointer(&context)); + FIRCLSFileWriteHashEntryUint64(file, "sp", FIRCLSThreadContextGetStackPointer(&context)); + FIRCLSFileWriteHashEntryUint64(file, "lr", FIRCLSThreadContextGetLinkRegister(&context)); + FIRCLSFileWriteHashEntryUint64(file, "pc", FIRCLSThreadContextGetPC(&context)); + FIRCLSFileWriteHashEntryUint64(file, "cpsr", context.__ss.__cpsr); +#elif CLS_CPU_I386 + FIRCLSFileWriteHashEntryUint64(file, "eax", context.__ss.__eax); + FIRCLSFileWriteHashEntryUint64(file, "ebx", context.__ss.__ebx); + FIRCLSFileWriteHashEntryUint64(file, "ecx", context.__ss.__ecx); + FIRCLSFileWriteHashEntryUint64(file, "edx", context.__ss.__edx); + FIRCLSFileWriteHashEntryUint64(file, "edi", context.__ss.__edi); + FIRCLSFileWriteHashEntryUint64(file, "esi", context.__ss.__esi); + FIRCLSFileWriteHashEntryUint64(file, "ebp", context.__ss.__ebp); + FIRCLSFileWriteHashEntryUint64(file, "esp", context.__ss.__esp); + FIRCLSFileWriteHashEntryUint64(file, "ss", context.__ss.__ss); + FIRCLSFileWriteHashEntryUint64(file, "eflags", context.__ss.__eflags); + FIRCLSFileWriteHashEntryUint64(file, "eip", context.__ss.__eip); + FIRCLSFileWriteHashEntryUint64(file, "cs", context.__ss.__cs); + FIRCLSFileWriteHashEntryUint64(file, "ds", context.__ss.__ds); + FIRCLSFileWriteHashEntryUint64(file, "es", context.__ss.__es); + FIRCLSFileWriteHashEntryUint64(file, "fs", context.__ss.__fs); + FIRCLSFileWriteHashEntryUint64(file, "gs", context.__ss.__gs); + + // how do we get the cr2 register? +#elif CLS_CPU_X86_64 + FIRCLSFileWriteHashEntryUint64(file, "rax", context.__ss.__rax); + FIRCLSFileWriteHashEntryUint64(file, "rbx", context.__ss.__rbx); + FIRCLSFileWriteHashEntryUint64(file, "rcx", context.__ss.__rcx); + FIRCLSFileWriteHashEntryUint64(file, "rdx", context.__ss.__rdx); + FIRCLSFileWriteHashEntryUint64(file, "rdi", context.__ss.__rdi); + FIRCLSFileWriteHashEntryUint64(file, "rsi", context.__ss.__rsi); + FIRCLSFileWriteHashEntryUint64(file, "rbp", context.__ss.__rbp); + FIRCLSFileWriteHashEntryUint64(file, "rsp", context.__ss.__rsp); + FIRCLSFileWriteHashEntryUint64(file, "r8", context.__ss.__r8); + FIRCLSFileWriteHashEntryUint64(file, "r9", context.__ss.__r9); + FIRCLSFileWriteHashEntryUint64(file, "r10", context.__ss.__r10); + FIRCLSFileWriteHashEntryUint64(file, "r11", context.__ss.__r11); + FIRCLSFileWriteHashEntryUint64(file, "r12", context.__ss.__r12); + FIRCLSFileWriteHashEntryUint64(file, "r13", context.__ss.__r13); + FIRCLSFileWriteHashEntryUint64(file, "r14", context.__ss.__r14); + FIRCLSFileWriteHashEntryUint64(file, "r15", context.__ss.__r15); + FIRCLSFileWriteHashEntryUint64(file, "rip", context.__ss.__rip); + FIRCLSFileWriteHashEntryUint64(file, "rflags", context.__ss.__rflags); + FIRCLSFileWriteHashEntryUint64(file, "cs", context.__ss.__cs); + FIRCLSFileWriteHashEntryUint64(file, "fs", context.__ss.__fs); + FIRCLSFileWriteHashEntryUint64(file, "gs", context.__ss.__gs); +#endif + + return true; +} + +static bool FIRCLSProcessRecordThread(FIRCLSProcess *process, thread_t thread, FIRCLSFile *file) { + FIRCLSUnwindContext unwindContext; + FIRCLSThreadContext context; + + if (!FIRCLSProcessGetThreadState(process, thread, &context)) { + FIRCLSSDKLogError("unable to get thread state"); + return false; + } + + if (!FIRCLSUnwindInit(&unwindContext, context)) { + FIRCLSSDKLog("unable to init unwind context\n"); + + return false; + } + + FIRCLSFileWriteHashStart(file); + + // registers + FIRCLSFileWriteHashKey(file, "registers"); + FIRCLSFileWriteHashStart(file); + + FIRCLSProcessRecordThreadRegisters(context, file); + + FIRCLSFileWriteHashEnd(file); + + // stacktrace + FIRCLSFileWriteHashKey(file, "stacktrace"); + + // stacktrace is an array of integers + FIRCLSFileWriteArrayStart(file); + + uint32_t repeatedPCCount = 0; + uint64_t repeatedPC = 0; + const FIRCLSInternalLogLevel level = _firclsContext.writable->internalLogging.logLevel; + + while (FIRCLSUnwindNextFrame(&unwindContext)) { + const uintptr_t pc = FIRCLSUnwindGetPC(&unwindContext); + const uint32_t frameCount = FIRCLSUnwindGetFrameRepeatCount(&unwindContext); + + if (repeatedPC == pc && repeatedPC != 0) { + // actively counting a recursion + repeatedPCCount = frameCount; + continue; + } + + if (frameCount >= FIRCLSUnwindInfiniteRecursionCountThreshold && repeatedPC == 0) { + repeatedPC = pc; + FIRCLSSDKLogWarn("Possible infinite recursion - suppressing logging\n"); + _firclsContext.writable->internalLogging.logLevel = FIRCLSInternalLogLevelWarn; + continue; + } + + if (repeatedPC != 0) { + // at this point, we've recorded a repeated PC, but it is now no longer + // repeating, so we can restore the logging + _firclsContext.writable->internalLogging.logLevel = level; + } + + FIRCLSFileWriteArrayEntryUint64(file, pc); + } + + FIRCLSFileWriteArrayEnd(file); + + // crashed? + if (FIRCLSProcessIsCrashedThread(process, thread)) { + FIRCLSFileWriteHashEntryBoolean(file, "crashed", true); + } + + if (repeatedPC != 0) { + FIRCLSFileWriteHashEntryUint64(file, "repeated_pc", repeatedPC); + FIRCLSFileWriteHashEntryUint64(file, "repeat_count", repeatedPCCount); + } + + // Just for extra safety, restore the logging level again. The logic + // above is fairly tricky, this is cheap, and no logging is a real pain. + _firclsContext.writable->internalLogging.logLevel = level; + + // end thread info + FIRCLSFileWriteHashEnd(file); + + return true; +} + +bool FIRCLSProcessRecordAllThreads(FIRCLSProcess *process, FIRCLSFile *file) { + uint32_t threadCount; + uint32_t i; + + threadCount = FIRCLSProcessGetThreadCount(process); + + FIRCLSFileWriteSectionStart(file, "threads"); + + FIRCLSFileWriteArrayStart(file); + + for (i = 0; i < threadCount; ++i) { + thread_t thread; + + thread = FIRCLSProcessGetThread(process, i); + + FIRCLSSDKLogInfo("recording thread %d data\n", i); + if (!FIRCLSProcessRecordThread(process, thread, file)) { + return false; + } + } + + FIRCLSFileWriteArrayEnd(file); + + FIRCLSFileWriteSectionEnd(file); + + FIRCLSSDKLogInfo("completed recording all thread data\n"); + + return true; +} + +void FIRCLSProcessRecordThreadNames(FIRCLSProcess *process, FIRCLSFile *file) { + uint32_t threadCount; + uint32_t i; + + FIRCLSFileWriteSectionStart(file, "thread_names"); + + FIRCLSFileWriteArrayStart(file); + + threadCount = FIRCLSProcessGetThreadCount(process); + for (i = 0; i < threadCount; ++i) { + thread_t thread; + char name[THREAD_NAME_BUFFER_SIZE]; + + thread = FIRCLSProcessGetThread(process, i); + + name[0] = 0; // null-terminate, just in case nothing is written + + FIRCLSProcessGetThreadName(process, thread, name, THREAD_NAME_BUFFER_SIZE); + + FIRCLSFileWriteArrayEntryString(file, name); + } + + FIRCLSFileWriteArrayEnd(file); + FIRCLSFileWriteSectionEnd(file); +} + +void FIRCLSProcessRecordDispatchQueueNames(FIRCLSProcess *process, FIRCLSFile *file) { + uint32_t threadCount; + uint32_t i; + + FIRCLSFileWriteSectionStart(file, "dispatch_queue_names"); + + FIRCLSFileWriteArrayStart(file); + + threadCount = FIRCLSProcessGetThreadCount(process); + for (i = 0; i < threadCount; ++i) { + thread_t thread; + const char *name; + + thread = FIRCLSProcessGetThread(process, i); + + name = FIRCLSProcessGetThreadDispatchQueueName(process, thread); + + FIRCLSFileWriteArrayEntryString(file, name); + } + + FIRCLSFileWriteArrayEnd(file); + FIRCLSFileWriteSectionEnd(file); +} + +#pragma mark - Othe Process Info +bool FIRCLSProcessGetMemoryUsage(uint64_t *active, + uint64_t *inactive, + uint64_t *wired, + uint64_t *freeMem) { + mach_port_t hostPort; + mach_msg_type_number_t hostSize; + vm_size_t pageSize; + vm_statistics_data_t vmStat; + + hostPort = mach_host_self(); + + hostSize = sizeof(vm_statistics_data_t) / sizeof(integer_t); + + pageSize = _firclsContext.readonly->host.pageSize; + + if (host_statistics(hostPort, HOST_VM_INFO, (host_info_t)&vmStat, &hostSize) != KERN_SUCCESS) { + FIRCLSSDKLog("Failed to get vm statistics\n"); + return false; + } + + if (!(active && inactive && wired && freeMem)) { + FIRCLSSDKLog("Invalid pointers\n"); + return false; + } + + // compute the sizes in bytes and return the values + *active = vmStat.active_count * pageSize; + *inactive = vmStat.inactive_count * pageSize; + *wired = vmStat.wire_count * pageSize; + *freeMem = vmStat.free_count * pageSize; + + return true; +} + +bool FIRCLSProcessGetInfo(FIRCLSProcess *process, + uint64_t *virtualSize, + uint64_t *residentSize, + time_value_t *userTime, + time_value_t *systemTime) { + struct task_basic_info_64 taskInfo; + mach_msg_type_number_t count; + + count = TASK_BASIC_INFO_64_COUNT; + if (task_info(process->task, TASK_BASIC_INFO_64, (task_info_t)&taskInfo, &count) != + KERN_SUCCESS) { + FIRCLSSDKLog("Failed to get task info\n"); + return false; + } + + if (!(virtualSize && residentSize && userTime && systemTime)) { + FIRCLSSDKLog("Invalid pointers\n"); + return false; + } + + *virtualSize = taskInfo.virtual_size; + *residentSize = taskInfo.resident_size; + *userTime = taskInfo.user_time; + *systemTime = taskInfo.system_time; + + return true; +} + +void FIRCLSProcessRecordStats(FIRCLSProcess *process, FIRCLSFile *file) { + uint64_t active; + uint64_t inactive; + uint64_t virtualSize; + uint64_t residentSize; + uint64_t wired; + uint64_t freeMem; + time_value_t userTime; + time_value_t systemTime; + + if (!FIRCLSProcessGetMemoryUsage(&active, &inactive, &wired, &freeMem)) { + FIRCLSSDKLog("Unable to get process memory usage\n"); + return; + } + + if (!FIRCLSProcessGetInfo(process, &virtualSize, &residentSize, &userTime, &systemTime)) { + FIRCLSSDKLog("Unable to get process stats\n"); + return; + } + + FIRCLSFileWriteSectionStart(file, "process_stats"); + + FIRCLSFileWriteHashStart(file); + + FIRCLSFileWriteHashEntryUint64(file, "active", active); + FIRCLSFileWriteHashEntryUint64(file, "inactive", inactive); + FIRCLSFileWriteHashEntryUint64(file, "wired", wired); + FIRCLSFileWriteHashEntryUint64(file, "freeMem", freeMem); // Intentionally left in, for now. Arg. + FIRCLSFileWriteHashEntryUint64(file, "free_mem", freeMem); + FIRCLSFileWriteHashEntryUint64(file, "virtual", virtualSize); + FIRCLSFileWriteHashEntryUint64(file, "resident", active); + FIRCLSFileWriteHashEntryUint64(file, "user_time", + (userTime.seconds * 1000 * 1000) + userTime.microseconds); + FIRCLSFileWriteHashEntryUint64(file, "sys_time", + (systemTime.seconds * 1000 * 1000) + systemTime.microseconds); + + FIRCLSFileWriteHashEnd(file); + + FIRCLSFileWriteSectionEnd(file); +} + +#pragma mark - Runtime Info +#define OBJC_MSG_SEND_START ((vm_address_t)objc_msgSend) +#define OBJC_MSG_SEND_SUPER_START ((vm_address_t)objc_msgSendSuper) +#define OBJC_MSG_SEND_END (OBJC_MSG_SEND_START + 66) +#define OBJC_MSG_SEND_SUPER_END (OBJC_MSG_SEND_SUPER_START + 66) + +#if !CLS_CPU_ARM64 +#define OBJC_MSG_SEND_STRET_START ((vm_address_t)objc_msgSend_stret) +#define OBJC_MSG_SEND_SUPER_STRET_START ((vm_address_t)objc_msgSendSuper_stret) +#define OBJC_MSG_SEND_STRET_END (OBJC_MSG_SEND_STRET_START + 66) +#define OBJC_MSG_SEND_SUPER_STRET_END (OBJC_MSG_SEND_SUPER_STRET_START + 66) +#endif + +#if CLS_CPU_X86 +#define OBJC_MSG_SEND_FPRET_START ((vm_address_t)objc_msgSend_fpret) +#define OBJC_MSG_SEND_FPRET_END (OBJC_MSG_SEND_FPRET_START + 66) +#endif + +static const char *FIRCLSProcessGetObjCSelectorName(FIRCLSThreadContext registers) { + void *selectorAddress; + void *selRegister; +#if !CLS_CPU_ARM64 + void *stretSelRegister; +#endif + vm_address_t pc; + + // First, did we crash in objc_msgSend? The two ways I can think + // of doing this are to use dladdr, and then comparing the strings to + // objc_msg*, or looking up the symbols, and guessing if we are "close enough". + + selectorAddress = NULL; + +#if CLS_CPU_ARM + pc = registers.__ss.__pc; + selRegister = (void *)registers.__ss.__r[1]; + stretSelRegister = (void *)registers.__ss.__r[2]; +#elif CLS_CPU_ARM64 + pc = FIRCLSThreadContextGetPC(®isters); + selRegister = (void *)registers.__ss.__x[1]; +#elif CLS_CPU_I386 + pc = registers.__ss.__eip; + selRegister = (void *)registers.__ss.__ecx; + stretSelRegister = (void *)registers.__ss.__ecx; +#elif CLS_CPU_X86_64 + pc = registers.__ss.__rip; + selRegister = (void *)registers.__ss.__rsi; + stretSelRegister = (void *)registers.__ss.__rdx; +#endif + + if ((pc >= OBJC_MSG_SEND_START) && (pc <= OBJC_MSG_SEND_END)) { + selectorAddress = selRegister; + } + +#if !CLS_CPU_ARM64 + if ((pc >= OBJC_MSG_SEND_SUPER_START) && (pc <= OBJC_MSG_SEND_SUPER_END)) { + selectorAddress = selRegister; + } + + if ((pc >= OBJC_MSG_SEND_STRET_START) && (pc <= OBJC_MSG_SEND_STRET_END)) { + selectorAddress = stretSelRegister; + } + + if ((pc >= OBJC_MSG_SEND_SUPER_STRET_START) && (pc <= OBJC_MSG_SEND_SUPER_STRET_END)) { + selectorAddress = stretSelRegister; + } + +#if CLS_CPU_X86 + if ((pc >= OBJC_MSG_SEND_FPRET_START) && (pc <= OBJC_MSG_SEND_FPRET_END)) { + selectorAddress = selRegister; + } +#endif +#endif + + if (!selectorAddress) { + return ""; + } + + if (!FIRCLSReadString((vm_address_t)selectorAddress, (char **)&selectorAddress, 128)) { + FIRCLSSDKLog("Unable to read the selector string\n"); + return ""; + } + + return selectorAddress; +} + +#define CRASH_ALIGN __attribute__((aligned(8))) +typedef struct { + unsigned version CRASH_ALIGN; + const char *message CRASH_ALIGN; + const char *signature CRASH_ALIGN; + const char *backtrace CRASH_ALIGN; + const char *message2 CRASH_ALIGN; + void *reserved CRASH_ALIGN; + void *reserved2 CRASH_ALIGN; +} crash_info_t; + +static void FIRCLSProcessRecordCrashInfo(FIRCLSFile *file) { + // TODO: this should be abstracted into binary images, if possible + FIRCLSBinaryImageRuntimeNode *nodes = _firclsContext.writable->binaryImage.nodes; + if (!nodes) { + FIRCLSSDKLogError("The node structure is NULL\n"); + return; + } + + for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) { + FIRCLSBinaryImageRuntimeNode *node = &nodes[i]; + + if (!node->crashInfo) { + continue; + } + + crash_info_t info; + + if (!FIRCLSReadMemory((vm_address_t)node->crashInfo, &info, sizeof(crash_info_t))) { + continue; + } + + FIRCLSSDKLogDebug("Found crash info with version %d\n", info.version); + + // Currently support versions 0 through 5. + // 4 was in use for a long time, but it appears that with iOS 9 / swift 2.0, the verison has + // been bumped. + if (info.version > 5) { + continue; + } + + if (!info.message) { + continue; + } + +#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME + FIRCLSSDKLogInfo("Found crash info for %s\n", node->name); +#endif + + FIRCLSSDKLogDebug("attempting to read crash info string\n"); + + char *string = NULL; + + if (!FIRCLSReadString((vm_address_t)info.message, &string, 256)) { + FIRCLSSDKLogError("Failed to copy crash info string\n"); + continue; + } + + FIRCLSFileWriteArrayEntryHexEncodedString(file, string); + } +} + +void FIRCLSProcessRecordRuntimeInfo(FIRCLSProcess *process, FIRCLSFile *file) { + FIRCLSThreadContext mcontext; + + if (!FIRCLSProcessGetThreadState(process, process->crashedThread, &mcontext)) { + FIRCLSSDKLogError("unable to get crashed thread state"); + } + + FIRCLSFileWriteSectionStart(file, "runtime"); + + FIRCLSFileWriteHashStart(file); + + FIRCLSFileWriteHashEntryString(file, "objc_selector", FIRCLSProcessGetObjCSelectorName(mcontext)); + + FIRCLSFileWriteHashKey(file, "crash_info_entries"); + + FIRCLSFileWriteArrayStart(file); + FIRCLSProcessRecordCrashInfo(file); + FIRCLSFileWriteArrayEnd(file); + + FIRCLSFileWriteHashEnd(file); + + FIRCLSFileWriteSectionEnd(file); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.h new file mode 100644 index 000000000..dbe758ffd --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.h @@ -0,0 +1,45 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include "FIRCLSFile.h" + +typedef struct { + // task info + mach_port_t task; + + // thread stuff + thread_t thisThread; + thread_t crashedThread; + thread_act_array_t threads; + mach_msg_type_number_t threadCount; + void *uapVoid; // current thread state +} FIRCLSProcess; + +bool FIRCLSProcessInit(FIRCLSProcess *process, thread_t crashedThread, void *uapVoid); +bool FIRCLSProcessDestroy(FIRCLSProcess *process); +bool FIRCLSProcessDebuggerAttached(void); + +bool FIRCLSProcessSuspendAllOtherThreads(FIRCLSProcess *process); +bool FIRCLSProcessResumeAllOtherThreads(FIRCLSProcess *process); + +void FIRCLSProcessRecordThreadNames(FIRCLSProcess *process, FIRCLSFile *file); +void FIRCLSProcessRecordDispatchQueueNames(FIRCLSProcess *process, FIRCLSFile *file); +bool FIRCLSProcessRecordAllThreads(FIRCLSProcess *process, FIRCLSFile *file); +void FIRCLSProcessRecordStats(FIRCLSProcess *process, FIRCLSFile *file); +void FIRCLSProcessRecordRuntimeInfo(FIRCLSProcess *process, FIRCLSFile *file); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h new file mode 100644 index 000000000..e1ee897d6 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h @@ -0,0 +1,101 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "FIRCLSFile.h" + +__BEGIN_DECLS + +#ifdef __OBJC__ +extern NSString* const FIRCLSStartTimeKey; +extern NSString* const FIRCLSFirstRunloopTurnTimeKey; +extern NSString* const FIRCLSInBackgroundKey; +#if TARGET_OS_IPHONE +extern NSString* const FIRCLSDeviceOrientationKey; +extern NSString* const FIRCLSUIOrientationKey; +#endif +extern NSString* const FIRCLSUserIdentifierKey; +extern NSString* const FIRCLSUserNameKey; +extern NSString* const FIRCLSUserEmailKey; +extern NSString* const FIRCLSDevelopmentPlatformNameKey; +extern NSString* const FIRCLSDevelopmentPlatformVersionKey; +#endif + +extern const uint32_t FIRCLSUserLoggingMaxKVEntries; + +typedef struct { + const char* incrementalPath; + const char* compactedPath; + + uint32_t maxIncrementalCount; + uint32_t maxCount; +} FIRCLSUserLoggingKVStorage; + +typedef struct { + const char* aPath; + const char* bPath; + uint32_t maxSize; + uint32_t maxEntries; + bool restrictBySize; + uint32_t* entryCount; +} FIRCLSUserLoggingABStorage; + +typedef struct { + FIRCLSUserLoggingKVStorage userKVStorage; + FIRCLSUserLoggingKVStorage internalKVStorage; + + FIRCLSUserLoggingABStorage logStorage; + FIRCLSUserLoggingABStorage errorStorage; + FIRCLSUserLoggingABStorage customExceptionStorage; +} FIRCLSUserLoggingReadOnlyContext; + +typedef struct { + const char* activeUserLogPath; + const char* activeErrorLogPath; + const char* activeCustomExceptionPath; + uint32_t userKVCount; + uint32_t internalKVCount; + uint32_t errorsCount; +} FIRCLSUserLoggingWritableContext; + +void FIRCLSUserLoggingInit(FIRCLSUserLoggingReadOnlyContext* roContext, + FIRCLSUserLoggingWritableContext* rwContext); + +#ifdef __OBJC__ +void FIRCLSUserLoggingRecordUserKeyValue(NSString* key, id value); +void FIRCLSUserLoggingRecordInternalKeyValue(NSString* key, id value); +void FIRCLSUserLoggingWriteInternalKeyValue(NSString* key, NSString* value); + +void FIRCLSUserLoggingRecordError(NSError* error, NSDictionary* additionalUserInfo); + +NSDictionary* FIRCLSUserLoggingGetCompactedKVEntries(FIRCLSUserLoggingKVStorage* storage, + bool decodeHex); +void FIRCLSUserLoggingCompactKVEntries(FIRCLSUserLoggingKVStorage* storage); + +void FIRCLSUserLoggingRecordKeyValue(NSString* key, + id value, + FIRCLSUserLoggingKVStorage* storage, + uint32_t* counter); + +void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage* storage, + const char** activePath, + void (^openedFileBlock)(FIRCLSFile* file)); + +NSArray* FIRCLSUserLoggingStoredKeyValues(const char* path); + +OBJC_EXTERN void FIRCLSLog(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2); +#endif + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.m new file mode 100644 index 000000000..0bb768cfe --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.m @@ -0,0 +1,523 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSUserLogging.h" + +#include + +#include "FIRCLSGlobals.h" +#include "FIRCLSUtility.h" + +#import "FIRCLSReportManager_Private.h" + +NSString *const FIRCLSStartTimeKey = @"com.crashlytics.kit-start-time"; +NSString *const FIRCLSFirstRunloopTurnTimeKey = @"com.crashlytics.first-run-loop-time"; +NSString *const FIRCLSInBackgroundKey = @"com.crashlytics.in-background"; +#if TARGET_OS_IPHONE +NSString *const FIRCLSDeviceOrientationKey = @"com.crashlytics.device-orientation"; +NSString *const FIRCLSUIOrientationKey = @"com.crashlytics.ui-orientation"; +#endif +NSString *const FIRCLSUserIdentifierKey = @"com.crashlytics.user-id"; +NSString *const FIRCLSDevelopmentPlatformNameKey = @"com.crashlytics.development-platform-name"; +NSString *const FIRCLSDevelopmentPlatformVersionKey = + @"com.crashlytics.development-platform-version"; + +const uint32_t FIRCLSUserLoggingMaxKVEntries = 64; + +#pragma mark - Prototypes +static void FIRCLSUserLoggingWriteKeyValue(NSString *key, + NSString *value, + FIRCLSUserLoggingKVStorage *storage, + uint32_t *counter); +static void FIRCLSUserLoggingCheckAndSwapABFiles(FIRCLSUserLoggingABStorage *storage, + const char **activePath, + off_t fileSize); +void FIRCLSLogInternal(NSString *message); + +#pragma mark - Setup +void FIRCLSUserLoggingInit(FIRCLSUserLoggingReadOnlyContext *roContext, + FIRCLSUserLoggingWritableContext *rwContext) { + rwContext->activeUserLogPath = roContext->logStorage.aPath; + rwContext->activeErrorLogPath = roContext->errorStorage.aPath; + rwContext->activeCustomExceptionPath = roContext->customExceptionStorage.aPath; + + rwContext->userKVCount = 0; + rwContext->internalKVCount = 0; + rwContext->errorsCount = 0; + + roContext->userKVStorage.maxIncrementalCount = FIRCLSUserLoggingMaxKVEntries; + roContext->internalKVStorage.maxIncrementalCount = roContext->userKVStorage.maxIncrementalCount; +} + +#pragma mark - KV Logging +void FIRCLSUserLoggingRecordInternalKeyValue(NSString *key, id value) { + FIRCLSUserLoggingRecordKeyValue(key, value, &_firclsContext.readonly->logging.internalKVStorage, + &_firclsContext.writable->logging.internalKVCount); +} + +void FIRCLSUserLoggingWriteInternalKeyValue(NSString *key, NSString *value) { + // Unsynchronized - must be run on the correct queue + FIRCLSUserLoggingWriteKeyValue(key, value, &_firclsContext.readonly->logging.internalKVStorage, + &_firclsContext.writable->logging.internalKVCount); +} + +void FIRCLSUserLoggingRecordUserKeyValue(NSString *key, id value) { + FIRCLSUserLoggingRecordKeyValue(key, value, &_firclsContext.readonly->logging.userKVStorage, + &_firclsContext.writable->logging.userKVCount); +} + +static id FIRCLSUserLoggingGetComponent(NSDictionary *entry, + NSString *componentName, + bool decodeHex) { + id value = [entry objectForKey:componentName]; + + return (decodeHex && value != [NSNull null]) ? FIRCLSFileHexDecodeString([value UTF8String]) + : value; +} + +static NSString *FIRCLSUserLoggingGetKey(NSDictionary *entry, bool decodeHex) { + return FIRCLSUserLoggingGetComponent(entry, @"key", decodeHex); +} + +static id FIRCLSUserLoggingGetValue(NSDictionary *entry, bool decodeHex) { + return FIRCLSUserLoggingGetComponent(entry, @"value", decodeHex); +} + +NSDictionary *FIRCLSUserLoggingGetCompactedKVEntries(FIRCLSUserLoggingKVStorage *storage, + bool decodeHex) { + if (!FIRCLSIsValidPointer(storage)) { + FIRCLSSDKLogError("storage invalid\n"); + return nil; + } + + NSArray *incrementalKVs = FIRCLSUserLoggingStoredKeyValues(storage->incrementalPath); + NSArray *compactedKVs = FIRCLSUserLoggingStoredKeyValues(storage->compactedPath); + + NSMutableDictionary *finalKVSet = [NSMutableDictionary new]; + + // These should all be unique, so there might be a more efficient way to + // do this + for (NSDictionary *entry in compactedKVs) { + NSString *key = FIRCLSUserLoggingGetKey(entry, decodeHex); + NSString *value = FIRCLSUserLoggingGetValue(entry, decodeHex); + + if (!key || !value) { + FIRCLSSDKLogError("compacted key/value contains a nil and must be dropped\n"); + continue; + } + + [finalKVSet setObject:value forKey:key]; + } + + // Now, assign the incremental values, in file order, so we overwrite any older values. + for (NSDictionary *entry in incrementalKVs) { + NSString *key = FIRCLSUserLoggingGetKey(entry, decodeHex); + NSString *value = FIRCLSUserLoggingGetValue(entry, decodeHex); + + if (!key || !value) { + FIRCLSSDKLogError("incremental key/value contains a nil and must be dropped\n"); + continue; + } + + if ([value isEqual:[NSNull null]]) { + [finalKVSet removeObjectForKey:key]; + } else { + [finalKVSet setObject:value forKey:key]; + } + } + + return finalKVSet; +} + +void FIRCLSUserLoggingCompactKVEntries(FIRCLSUserLoggingKVStorage *storage) { + if (!FIRCLSIsValidPointer(storage)) { + FIRCLSSDKLogError("Error: storage invalid\n"); + return; + } + + NSDictionary *finalKVs = FIRCLSUserLoggingGetCompactedKVEntries(storage, false); + + if (unlink(storage->compactedPath) != 0) { + FIRCLSSDKLog("Error: Unable to remove compacted KV store before compaction %s\n", + strerror(errno)); + } + + FIRCLSFile file; + + if (!FIRCLSFileInitWithPath(&file, storage->compactedPath, true)) { + FIRCLSSDKLog("Error: Unable to open compacted k-v file\n"); + return; + } + + uint32_t maxCount = storage->maxCount; + if ([finalKVs count] > maxCount) { + // We need to remove keys, to avoid going over the max. + // This is just about the worst way to go about doing this. There are lots of smarter ways, + // but it's very uncommon to go down this path. + NSArray *keys = [finalKVs allKeys]; + + FIRCLSSDKLogInfo("Truncating KV set, which is above max %d\n", maxCount); + + finalKVs = + [finalKVs dictionaryWithValuesForKeys:[keys subarrayWithRange:NSMakeRange(0, maxCount)]]; + } + + for (NSString *key in finalKVs) { + NSString *value = [finalKVs objectForKey:key]; + + FIRCLSFileWriteSectionStart(&file, "kv"); + FIRCLSFileWriteHashStart(&file); + // tricky - the values stored incrementally have already been hex-encoded + FIRCLSFileWriteHashEntryString(&file, "key", [key UTF8String]); + FIRCLSFileWriteHashEntryString(&file, "value", [value UTF8String]); + FIRCLSFileWriteHashEnd(&file); + FIRCLSFileWriteSectionEnd(&file); + } + + FIRCLSFileClose(&file); + + if (unlink(storage->incrementalPath) != 0) { + FIRCLSSDKLog("Error: Unable to remove incremental KV store after compaction %s\n", + strerror(errno)); + } +} + +void FIRCLSUserLoggingRecordKeyValue(NSString *key, + id value, + FIRCLSUserLoggingKVStorage *storage, + uint32_t *counter) { + if (!FIRCLSIsValidPointer(key)) { + FIRCLSSDKLogWarn("User provided bad key\n"); + return; + } + + // ensure that any invalid pointer is actually set to nil + if (!FIRCLSIsValidPointer(value) && value != nil) { + FIRCLSSDKLogWarn("Bad value pointer being clamped to nil\n"); + value = nil; + } + + if (!FIRCLSContextIsInitialized()) { + return; + } + + if ([value respondsToSelector:@selector(description)]) { + value = [value description]; + } else { + // passing nil will result in a JSON null being written, which is deserialized as [NSNull null], + // signaling to remove the key during compaction + value = nil; + } + + dispatch_sync(FIRCLSGetLoggingQueue(), ^{ + FIRCLSUserLoggingWriteKeyValue(key, value, storage, counter); + }); +} + +static void FIRCLSUserLoggingWriteKeyValue(NSString *key, + NSString *value, + FIRCLSUserLoggingKVStorage *storage, + uint32_t *counter) { + FIRCLSFile file; + + if (!FIRCLSIsValidPointer(storage) || !FIRCLSIsValidPointer(counter)) { + FIRCLSSDKLogError("Bad parameters\n"); + return; + } + + if (!FIRCLSFileInitWithPath(&file, storage->incrementalPath, true)) { + FIRCLSSDKLogError("Unable to open k-v file\n"); + return; + } + + FIRCLSFileWriteSectionStart(&file, "kv"); + FIRCLSFileWriteHashStart(&file); + FIRCLSFileWriteHashEntryHexEncodedString(&file, "key", [key UTF8String]); + FIRCLSFileWriteHashEntryHexEncodedString(&file, "value", [value UTF8String]); + FIRCLSFileWriteHashEnd(&file); + FIRCLSFileWriteSectionEnd(&file); + + FIRCLSFileClose(&file); + + *counter += 1; + if (*counter >= storage->maxIncrementalCount) { + dispatch_async(FIRCLSGetLoggingQueue(), ^{ + FIRCLSUserLoggingCompactKVEntries(storage); + *counter = 0; + }); + } +} + +NSArray *FIRCLSUserLoggingStoredKeyValues(const char *path) { + if (!FIRCLSContextIsInitialized()) { + return nil; + } + + return FIRCLSFileReadSections(path, true, ^NSObject *(id obj) { + return [obj objectForKey:@"kv"]; + }); +} + +#pragma mark - NSError Logging +static void FIRCLSUserLoggingRecordErrorUserInfo(FIRCLSFile *file, + const char *fileKey, + NSDictionary *userInfo) { + if ([userInfo count] == 0) { + return; + } + + FIRCLSFileWriteHashKey(file, fileKey); + FIRCLSFileWriteArrayStart(file); + + for (id key in userInfo) { + id value = [userInfo objectForKey:key]; + if (![value respondsToSelector:@selector(description)]) { + continue; + } + + FIRCLSFileWriteArrayStart(file); + FIRCLSFileWriteArrayEntryHexEncodedString(file, [key UTF8String]); + FIRCLSFileWriteArrayEntryHexEncodedString(file, [[value description] UTF8String]); + FIRCLSFileWriteArrayEnd(file); + } + + FIRCLSFileWriteArrayEnd(file); +} + +static void FIRCLSUserLoggingWriteError(FIRCLSFile *file, + NSError *error, + NSDictionary *additionalUserInfo, + NSArray *addresses, + uint64_t timestamp) { + FIRCLSFileWriteSectionStart(file, "error"); + FIRCLSFileWriteHashStart(file); + FIRCLSFileWriteHashEntryHexEncodedString(file, "domain", [[error domain] UTF8String]); + FIRCLSFileWriteHashEntryInt64(file, "code", [error code]); + FIRCLSFileWriteHashEntryUint64(file, "time", timestamp); + + // addresses + FIRCLSFileWriteHashKey(file, "stacktrace"); + FIRCLSFileWriteArrayStart(file); + for (NSNumber *address in addresses) { + FIRCLSFileWriteArrayEntryUint64(file, [address unsignedLongLongValue]); + } + FIRCLSFileWriteArrayEnd(file); + + // user-info + FIRCLSUserLoggingRecordErrorUserInfo(file, "info", [error userInfo]); + FIRCLSUserLoggingRecordErrorUserInfo(file, "extra_info", additionalUserInfo); + + FIRCLSFileWriteHashEnd(file); + FIRCLSFileWriteSectionEnd(file); +} + +void FIRCLSUserLoggingRecordError(NSError *error, + NSDictionary *additionalUserInfo) { + if (!error) { + return; + } + + if (!FIRCLSContextIsInitialized()) { + return; + } + + // record the stacktrace and timestamp here, so we + // are as close as possible to the user's log statement + NSArray *addresses = [NSThread callStackReturnAddresses]; + uint64_t timestamp = time(NULL); + + FIRCLSUserLoggingWriteAndCheckABFiles( + &_firclsContext.readonly->logging.errorStorage, + &_firclsContext.writable->logging.activeErrorLogPath, ^(FIRCLSFile *file) { + FIRCLSUserLoggingWriteError(file, error, additionalUserInfo, addresses, timestamp); + }); +} + +#pragma mark - CLSLog Support +void FIRCLSLog(NSString *format, ...) { + // If the format is nil do nothing just like NSLog. + if (!format) { + return; + } + + va_list args; + va_start(args, format); + NSString *msg = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + + FIRCLSLogInternal(msg); +} + +#pragma mark - Properties +uint32_t FIRCLSUserLoggingMaxLogSize(void) { + // don't forget that the message encoding overhead is 2x, and we + // wrap everything in a json structure with time. So, there is + // quite a penalty + + uint32_t size = 1024 * 64; + + return size * 2; +} + +uint32_t FIRCLSUserLoggingMaxErrorSize(void) { + return FIRCLSUserLoggingMaxLogSize(); +} + +#pragma mark - AB Logging +void FIRCLSUserLoggingCheckAndSwapABFiles(FIRCLSUserLoggingABStorage *storage, + const char **activePath, + off_t fileSize) { + if (!activePath || !storage) { + return; + } + + if (!*activePath) { + return; + } + + if (storage->restrictBySize) { + if (fileSize <= storage->maxSize) { + return; + } + } else { + if (!FIRCLSIsValidPointer(storage->entryCount)) { + FIRCLSSDKLogError("Error: storage has invalid pointer, but is restricted by entry count\n"); + return; + } + + if (*storage->entryCount < storage->maxEntries) { + return; + } + + // Here we have rolled over, so we have to reset our counter. + *storage->entryCount = 0; + } + + // if it is too big: + // - reset the other log + // - make it active + const char *otherPath = NULL; + + if (*activePath == storage->aPath) { + otherPath = storage->bPath; + } else { + // take this path if the pointer is invalid as well, to reset + otherPath = storage->aPath; + } + + // guard here against path being nil or empty + NSString *pathString = [NSString stringWithUTF8String:otherPath]; + + if ([pathString length] > 0) { + // ignore the error, because there is nothing we can do to recover here, and its likely + // any failures would be intermittent + + [[NSFileManager defaultManager] removeItemAtPath:pathString error:nil]; + } + + *activePath = otherPath; +} + +void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage *storage, + const char **activePath, + void (^openedFileBlock)(FIRCLSFile *file)) { + if (!storage || !activePath || !openedFileBlock) { + return; + } + + if (!*activePath) { + return; + } + + if (storage->restrictBySize) { + if (storage->maxSize == 0) { + return; + } + } else { + if (storage->maxEntries == 0) { + return; + } + } + + dispatch_sync(FIRCLSGetLoggingQueue(), ^{ + FIRCLSFile file; + + if (!FIRCLSFileInitWithPath(&file, *activePath, true)) { + FIRCLSSDKLog("Unable to open log file\n"); + return; + } + + openedFileBlock(&file); + + off_t fileSize = 0; + FIRCLSFileCloseWithOffset(&file, &fileSize); + + // increment the count before calling FIRCLSUserLoggingCheckAndSwapABFiles, so the value + // reflects the actual amount of stuff written + if (!storage->restrictBySize && FIRCLSIsValidPointer(storage->entryCount)) { + *storage->entryCount += 1; + } + + dispatch_async(FIRCLSGetLoggingQueue(), ^{ + FIRCLSUserLoggingCheckAndSwapABFiles(storage, activePath, fileSize); + }); + }); +} + +void FIRCLSLogInternalWrite(FIRCLSFile *file, NSString *message, uint64_t time) { + FIRCLSFileWriteSectionStart(file, "log"); + FIRCLSFileWriteHashStart(file); + FIRCLSFileWriteHashEntryHexEncodedString(file, "msg", [message UTF8String]); + FIRCLSFileWriteHashEntryUint64(file, "time", time); + FIRCLSFileWriteHashEnd(file); + FIRCLSFileWriteSectionEnd(file); +} + +void FIRCLSLogInternal(NSString *message) { + if (!message) { + return; + } + + if (!FIRCLSContextIsInitialized()) { + FIRCLSWarningLog(@"WARNING: FIRCLSLog has been used before (or concurrently with) " + @"Crashlytics initialization and cannot be recorded. The message was: \n%@", + message); + return; + } + struct timeval te; + + NSUInteger messageLength = [message length]; + int maxLogSize = _firclsContext.readonly->logging.logStorage.maxSize; + + if (messageLength > maxLogSize) { + FIRCLSWarningLog( + @"WARNING: Attempted to write %zd bytes, but %d is the maximum size of the log. " + @"Truncating to %d bytes.\n", + messageLength, maxLogSize, maxLogSize); + message = [message substringToIndex:maxLogSize]; + } + + // unable to get time - abort + if (gettimeofday(&te, NULL) != 0) { + return; + } + + const uint64_t time = te.tv_sec * 1000LL + te.tv_usec / 1000; + + FIRCLSUserLoggingWriteAndCheckABFiles(&_firclsContext.readonly->logging.logStorage, + &_firclsContext.writable->logging.activeUserLogPath, + ^(FIRCLSFile *file) { + FIRCLSLogInternalWrite(file, message, time); + }); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.h new file mode 100644 index 000000000..2c259c296 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.h @@ -0,0 +1,61 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +typedef NS_ENUM(NSInteger, FIRCLSNetworkClientErrorType) { + FIRCLSNetworkClientErrorTypeUnknown = -1, + FIRCLSNetworkClientErrorTypeFileUnreadable = -2 +}; + +extern NSString *const FIRCLSNetworkClientErrorDomain; + +@protocol FIRCLSNetworkClientDelegate; +@class FIRCLSDataCollectionToken; +@class FIRCLSFileManager; + +@interface FIRCLSNetworkClient : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)initWithQueue:(NSOperationQueue *)queue + fileManager:(FIRCLSFileManager *)fileManager + delegate:(id)delegate; + +@property(nonatomic, weak) id delegate; + +@property(nonatomic, readonly) NSOperationQueue *operationQueue; +@property(nonatomic, readonly) BOOL supportsBackgroundRequests; + +- (void)startUploadRequest:(NSURLRequest *)request + filePath:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + immediately:(BOOL)immediate; + +- (void)attemptToReconnectBackgroundSessionWithCompletionBlock:(void (^)(void))completionBlock; + +@end + +@class FIRCLSNetworkClient; + +@protocol FIRCLSNetworkClientDelegate +@required +- (BOOL)networkClientCanUseBackgroundSessions:(FIRCLSNetworkClient *)client; + +@optional +- (void)networkClient:(FIRCLSNetworkClient *)client + didFinishUploadWithPath:(NSString *)path + error:(NSError *)error; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.m new file mode 100644 index 000000000..5fee0ca9f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.m @@ -0,0 +1,366 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSNetworkClient.h" + +#import "FIRCLSApplication.h" +#import "FIRCLSByteUtility.h" +#import "FIRCLSDataCollectionToken.h" +#import "FIRCLSDefines.h" +#import "FIRCLSFileManager.h" +#import "FIRCLSNetworkResponseHandler.h" +#import "FIRCLSURLSession.h" +#import "FIRCLSURLSessionConfiguration.h" + +#import "FIRCLSUtility.h" + +NSString *const FIRCLSNetworkClientErrorDomain = @"FIRCLSNetworkError"; + +NSString *const FIRCLSNetworkClientBackgroundIdentifierSuffix = @".crash.background-session"; + +@interface FIRCLSNetworkClient () { + NSURLSession *_session; +} + +@property(nonatomic, strong) void (^backgroundCompletionHandler)(void); +@property(nonatomic, strong, readonly) NSURLSession *session; +@property(nonatomic, assign) BOOL canUseBackgroundSession; +@property(nonatomic, strong) FIRCLSFileManager *fileManager; + +@end + +@implementation FIRCLSNetworkClient + +- (instancetype)initWithQueue:(NSOperationQueue *)queue + fileManager:(FIRCLSFileManager *)fileManager + delegate:(id)delegate { + self = [super init]; + if (!self) { + return nil; + } + + _operationQueue = queue; + _delegate = delegate; + _fileManager = fileManager; + + self.canUseBackgroundSession = [_delegate networkClientCanUseBackgroundSessions:self]; + + if (!self.supportsBackgroundRequests) { + FIRCLSDeveloperLog( + "Crashlytics:Crash:Client", + @"Background session uploading not supported, asynchronous uploading will be used"); + } + + return self; +} + +#pragma mark - Background Support +- (NSURLSession *)session { + // Creating a NSURLSession takes some time. Doing it lazily saves us time in the normal case. + if (_session) { + return _session; + } + + NSURLSessionConfiguration *config = nil; + + Class urlSessionClass; + Class urlSessionConfigurationClass; +#if FIRCLSURLSESSION_REQUIRED + urlSessionClass = [FIRCLSURLSession class]; + urlSessionConfigurationClass = [FIRCLSURLSessionConfiguration class]; +#else + urlSessionClass = [NSURLSession class]; + urlSessionConfigurationClass = [NSURLSessionConfiguration class]; +#endif + + if (self.supportsBackgroundRequests) { + NSString *sdkBundleID = FIRCLSApplicationGetSDKBundleID(); + NSString *backgroundConfigName = + [sdkBundleID stringByAppendingString:FIRCLSNetworkClientBackgroundIdentifierSuffix]; + + config = [urlSessionConfigurationClass backgroundSessionConfiguration:backgroundConfigName]; +#if TARGET_OS_IPHONE + [config setSessionSendsLaunchEvents:NO]; +#endif + } + + if (!config) { + // take this code path if we don't support background requests OR if we failed to create a + // background configuration + config = [urlSessionConfigurationClass defaultSessionConfiguration]; + } + + _session = [urlSessionClass sessionWithConfiguration:config + delegate:self + delegateQueue:self.operationQueue]; + + if (!_session || !config) { + FIRCLSErrorLog(@"Failed to initialize"); + } + + return _session; +} + +#if FIRCLSURLSESSION_REQUIRED +- (BOOL)NSURLSessionAvailable { + if ([[FIRCLSURLSession class] respondsToSelector:@selector(NSURLSessionShouldBeUsed)]) { + return [FIRCLSURLSession NSURLSessionShouldBeUsed]; + } + + return NSClassFromString(@"NSURLSession") != nil; +} +#endif + +- (BOOL)supportsBackgroundRequests { + return !FIRCLSApplicationIsExtension() +#if FIRCLSURLSESSION_REQUIRED + && [self NSURLSessionAvailable] +#endif + && self.canUseBackgroundSession; +} + +- (void)attemptToReconnectBackgroundSessionWithCompletionBlock:(void (^)(void))completionBlock { + if (!self.supportsBackgroundRequests) { + if (completionBlock) { + completionBlock(); + } + return; + } + + // This is the absolute minimum necessary. Perhaps we can do better? + if (completionBlock) { + [[NSOperationQueue mainQueue] addOperationWithBlock:completionBlock]; + } +} + +#pragma mark - API +- (void)startUploadRequest:(NSURLRequest *)request + filePath:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + immediately:(BOOL)immediate { + if (![dataCollectionToken isValid]) { + FIRCLSErrorLog(@"An upload was requested with an invalid data collection token."); + return; + } + + if (immediate) { + [self startImmediateUploadRequest:request filePath:path]; + return; + } + + NSString *description = [self relativeTaskPathForAbsolutePath:path]; + [self checkForExistingTaskMatchingDescription:description + completionBlock:^(BOOL found) { + if (found) { + FIRCLSDeveloperLog( + "Crashlytics:Crash:Client", + @"A task currently exists for this upload, skipping"); + return; + } + + [self startNewUploadRequest:request filePath:path]; + }]; +} + +#pragma mark - Support API +- (void)startImmediateUploadRequest:(NSURLRequest *)request filePath:(NSString *)path { + // check the ivar directly, to avoid going back to the delegate + if (self.supportsBackgroundRequests) { + // this can be done here, because the request will be started synchronously. + [self startNewUploadRequest:request filePath:path]; + return; + } + + if (![[NSFileManager defaultManager] isReadableFileAtPath:path]) { + FIRCLSSDKLog("Error: file unreadable\n"); + // Following the same logic as below, do not try to inform the delegate + return; + } + + NSMutableURLRequest *mutableRequest = [request mutableCopy]; + + [mutableRequest setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:path]]; + + NSURLResponse *requestResponse = nil; + + [[NSURLSession sharedSession] + dataTaskWithRequest:mutableRequest + completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, + NSError *_Nullable error) { + [FIRCLSNetworkResponseHandler + clientResponseType:requestResponse + handler:^(FIRCLSNetworkClientResponseType type, NSInteger statusCode) { + if (type != FIRCLSNetworkClientResponseSuccess) { + // don't even inform the delegate of a failure here, because we don't + // want any action to be taken synchronously + return; + } + + [[self delegate] networkClient:self + didFinishUploadWithPath:path + error:error]; + }]; + }]; +} + +- (void)startNewUploadRequest:(NSURLRequest *)request filePath:(NSString *)path { + if (![[NSFileManager defaultManager] isReadableFileAtPath:path]) { + [self.operationQueue addOperationWithBlock:^{ + [self + handleTaskDescription:path + completedWithError:[NSError errorWithDomain:FIRCLSNetworkClientErrorDomain + code:FIRCLSNetworkClientErrorTypeFileUnreadable + userInfo:@{@"path" : path}]]; + }]; + + return; + } + + NSURLSessionUploadTask *task = [self.session uploadTaskWithRequest:request + fromFile:[NSURL fileURLWithPath:path]]; + + // set the description, so we can determine what file was successfully uploaded later on + [task setTaskDescription:[self relativeTaskPathForAbsolutePath:path]]; + + [task resume]; +} + +- (NSString *)rootPath { + return self.fileManager.rootPath; +} + +- (NSString *)absolutePathForRelativeTaskPath:(NSString *)path { + return [self.rootPath stringByAppendingPathComponent:path]; +} + +- (NSString *)relativeTaskPathForAbsolutePath:(NSString *)path { + // make sure this has a tailing slash, so the path looks relative + NSString *root = [self.rootPath stringByAppendingString:@"/"]; + + if (![path hasPrefix:root]) { + FIRCLSSDKLog("Error: path '%s' is not at the root '%s'", [path UTF8String], [root UTF8String]); + return nil; + } + + return [path stringByReplacingOccurrencesOfString:root withString:@""]; +} + +#pragma mark - Task Management +- (BOOL)taskArray:(NSArray *)array hasTaskMatchingDescription:(NSString *)description { + NSUInteger idx = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger arrayIdx, BOOL *stop) { + return [[obj taskDescription] isEqualToString:description]; + }]; + + return idx != NSNotFound; +} + +- (void)checkSession:(NSURLSession *)session + forTasksMatchingDescription:(NSString *)description + completionBlock:(void (^)(BOOL found))block { + if (!session) { + block(NO); + return; + } + + [session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, + NSArray *downloadTasks) { + if ([self taskArray:uploadTasks hasTaskMatchingDescription:description]) { + block(YES); + return; + } + + if ([self taskArray:dataTasks hasTaskMatchingDescription:description]) { + block(YES); + return; + } + + if ([self taskArray:downloadTasks hasTaskMatchingDescription:description]) { + block(YES); + return; + } + + block(NO); + }]; +} + +- (void)checkForExistingTaskMatchingDescription:(NSString *)description + completionBlock:(void (^)(BOOL found))block { + // Do not instantiate the normal session, because if it doesn't exist yet, it cannot possibly have + // existing tasks + [_operationQueue addOperationWithBlock:^{ + [self checkSession:self.session + forTasksMatchingDescription:description + completionBlock:^(BOOL found) { + block(found); + }]; + }]; +} + +#pragma mark - Result Handling +// This method is duplicated from FIRCLSFABNetworkClient. Sharing it is a little weird - I didn't +// feel like it fit into FIRCLSNetworkResponseHandler. +- (void)runAfterRetryValueFromResponse:(NSURLResponse *)response block:(void (^)(void))block { + NSTimeInterval delay = [FIRCLSNetworkResponseHandler retryValueForResponse:response]; + + // FIRCLSDeveloperLog("Network", @"Restarting request after %f", delay); + + FIRCLSAddOperationAfter(delay, _operationQueue, block); +} + +- (void)restartTask:(NSURLSessionTask *)task { + NSURLRequest *request = [task originalRequest]; + + [self runAfterRetryValueFromResponse:[task response] + block:^{ + NSString *path = [self + absolutePathForRelativeTaskPath:[task taskDescription]]; + + [self startNewUploadRequest:request filePath:path]; + }]; +} + +- (void)handleTask:(NSURLSessionTask *)task completedWithError:(NSError *)error { + [self handleTaskDescription:[task taskDescription] completedWithError:error]; +} + +- (void)handleTaskDescription:(NSString *)taskDescription completedWithError:(NSError *)error { + NSString *path = [self absolutePathForRelativeTaskPath:taskDescription]; + + [[self delegate] networkClient:self didFinishUploadWithPath:path error:error]; +} + +#pragma mark - NSURLSessionDelegate +- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { + FIRCLSDeveloperLog("Crashlytics:Crash:Client", @"session became invalid: %@", error); +} + +// Careful! Not implementing this method appears to cause a crash when using a background task +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task + didCompleteWithError:(NSError *)error { + [FIRCLSNetworkResponseHandler handleCompletedResponse:task.response + forOriginalRequest:task.originalRequest + error:error + block:^(BOOL restart, NSError *taskError) { + if (restart) { + [self restartTask:task]; + return; + } + + [self handleTask:task + completedWithError:taskError]; + }]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h new file mode 100644 index 000000000..02b7031a5 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h @@ -0,0 +1,57 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#include "FIRCLSApplicationIdentifierModel.h" +#include "FIRCLSProfiling.h" +#include "FIRCrashlytics.h" + +@class FBLPromise; + +NS_ASSUME_NONNULL_BEGIN + +@class FIRCLSDataCollectionArbiter; +@class FIRCLSFileManager; +@class FIRCLSInternalReport; +@class FIRCLSSettings; +@class GDTCORTransport; +@class FIRInstallations; +@protocol FIRAnalyticsInterop; + +@interface FIRCLSReportManager : NSObject + +- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager + installations:(FIRInstallations *)installations + analytics:(nullable id)analytics + googleAppID:(NSString *)googleAppID + dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter + googleTransport:(GDTCORTransport *)googleTransport + appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel + settings:(FIRCLSSettings *)settings NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +- (FBLPromise *)startWithProfilingMark:(FIRCLSProfileMark)mark; + +- (FBLPromise *)checkForUnsentReports; +- (FBLPromise *)sendUnsentReports; +- (FBLPromise *)deleteUnsentReports; + +@end + +extern NSString *const FIRCLSConfigSubmitReportsKey; +extern NSString *const FIRCLSConfigPackageReportsKey; + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m new file mode 100644 index 000000000..cf04b3f1f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m @@ -0,0 +1,909 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// The report manager has the ability to send to two different endpoints. +// +// The old legacy flow for a report goes through the following states/folders: +// 1. active - .clsrecords optimized for crash time persistence +// 2. processing - .clsrecords with attempted symbolication +// 3. prepared-legacy - .multipartmime of compressed .clsrecords +// +// The new flow for a report goes through the following states/folders: +// 1. active - .clsrecords optimized for crash time persistence +// 2. processing - .clsrecords with attempted symbolication +// 3. prepared - .clsrecords moved from processing with no changes +// +// The code was designed so the report processing workflows are not dramatically different from one +// another. The design will help avoid having a lot of conditional code blocks throughout the +// codebase. +// + +#include + +#if __has_include() +#import +#else +#import "FBLPromises.h" +#endif + +#import "FIRCLSApplication.h" +#import "FIRCLSDataCollectionArbiter.h" +#import "FIRCLSDataCollectionToken.h" +#import "FIRCLSDefines.h" +#import "FIRCLSFeatures.h" +#import "FIRCLSFileManager.h" +#import "FIRCLSInternalReport.h" +#import "FIRCLSLogger.h" +#import "FIRCLSNetworkClient.h" +#import "FIRCLSPackageReportOperation.h" +#import "FIRCLSProcessReportOperation.h" +#import "FIRCLSReportUploader.h" +#import "FIRCLSSettings.h" +#import "FIRCLSSymbolResolver.h" +#import "FIRCLSUserLogging.h" + +#include "FIRCLSGlobals.h" +#include "FIRCLSUtility.h" + +#import "FIRCLSConstants.h" +#import "FIRCLSExecutionIdentifierModel.h" +#import "FIRCLSInstallIdentifierModel.h" +#import "FIRCLSSettingsOnboardingManager.h" + +#import "FIRCLSReportManager_Private.h" + +#import "Interop/Analytics/Public/FIRAnalyticsInterop.h" +#import "Interop/Analytics/Public/FIRAnalyticsInteropListener.h" + +#include "FIRAEvent+Internal.h" +#include "FIRCLSFCRAnalytics.h" + +#if TARGET_OS_IPHONE +#import +#else +#import +#endif + +static NSTimeInterval const CLSReportRetryInterval = 10 * 60; + +static NSString *FIRCLSFirebaseAnalyticsEventLogFormat = @"$A$:%@"; + +@interface FIRCLSAnalyticsInteropListener : NSObject { +} +@end + +@implementation FIRCLSAnalyticsInteropListener + +- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters { + NSDictionary *event = @{ + @"name" : name, + @"parameters" : parameters, + }; + NSString *json = FIRCLSFIRAEventDictionaryToJSON(event); + if (json != nil) { + FIRCLSLog(FIRCLSFirebaseAnalyticsEventLogFormat, json); + } +} + +@end + +/** + * A FIRReportAction is used to indicate how to handle unsent reports. + */ +typedef NS_ENUM(NSInteger, FIRCLSReportAction) { + /** Upload the reports to Crashlytics. */ + FIRCLSReportActionSend, + /** Delete the reports without uploading them. */ + FIRCLSReportActionDelete, +}; + +/** + * This is just a helper to make code using FIRReportAction more readable. + */ +typedef NSNumber FIRCLSWrappedReportAction; +@implementation NSNumber (FIRCLSWrappedReportAction) +- (FIRCLSReportAction)reportActionValue { + return [self intValue]; +} +@end + +/** + * This is a helper to make code using NSNumber for bools more readable. + */ +typedef NSNumber FIRCLSWrappedBool; + +@interface FIRCLSReportManager () { + FIRCLSFileManager *_fileManager; + FIRCLSNetworkClient *_networkClient; + FIRCLSReportUploader *_uploader; + dispatch_queue_t _dispatchQueue; + NSOperationQueue *_operationQueue; + id _analytics; + + // A promise that will be resolved when unsent reports are found on the device, and + // processReports: can be called to decide how to deal with them. + FBLPromise *_unsentReportsAvailable; + + // A promise that will be resolved when the user has provided an action that they want to perform + // for all the unsent reports. + FBLPromise *_reportActionProvided; + + // A promise that will be resolved when all unsent reports have been "handled". They won't + // necessarily have been uploaded, but we will know whether they should be sent or deleted, and + // the initial work to make that happen will have been processed on the work queue. + // + // Currently only used for testing + FBLPromise *_unsentReportsHandled; + + // A token to make sure that checkForUnsentReports only gets called once. + atomic_bool _checkForUnsentReportsCalled; + + BOOL _registeredAnalyticsEventListener; +} + +@property(nonatomic, readonly) NSString *googleAppID; + +@property(nonatomic, strong) FIRCLSDataCollectionArbiter *dataArbiter; + +// Uniquely identifies a build / binary of the app +@property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel; + +// Uniquely identifies an install of the app +@property(nonatomic, strong) FIRCLSInstallIdentifierModel *installIDModel; + +// Uniquely identifies a run of the app +@property(nonatomic, strong) FIRCLSExecutionIdentifierModel *executionIDModel; + +// Settings fetched from the server +@property(nonatomic, strong) FIRCLSSettings *settings; + +// Runs the operations that fetch settings and call onboarding endpoints +@property(nonatomic, strong) FIRCLSSettingsOnboardingManager *settingsAndOnboardingManager; + +@property(nonatomic, strong) GDTCORTransport *googleTransport; + +@end + +@implementation FIRCLSReportManager + +// Used only for internal data collection E2E testing +static void (^reportSentCallback)(void); + +- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager + installations:(FIRInstallations *)installations + analytics:(id)analytics + googleAppID:(NSString *)googleAppID + dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter + googleTransport:(GDTCORTransport *)googleTransport + appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel + settings:(FIRCLSSettings *)settings { + self = [super init]; + if (!self) { + return nil; + } + + _fileManager = fileManager; + _analytics = analytics; + _googleAppID = [googleAppID copy]; + _dataArbiter = dataArbiter; + + _googleTransport = googleTransport; + + NSString *sdkBundleID = FIRCLSApplicationGetSDKBundleID(); + + _operationQueue = [NSOperationQueue new]; + [_operationQueue setMaxConcurrentOperationCount:1]; + [_operationQueue setName:[sdkBundleID stringByAppendingString:@".work-queue"]]; + + _dispatchQueue = dispatch_queue_create("com.google.firebase.crashlytics.startup", 0); + _operationQueue.underlyingQueue = _dispatchQueue; + + _networkClient = [self clientWithOperationQueue:_operationQueue]; + + _unsentReportsAvailable = [FBLPromise pendingPromise]; + _reportActionProvided = [FBLPromise pendingPromise]; + _unsentReportsHandled = [FBLPromise pendingPromise]; + + _checkForUnsentReportsCalled = NO; + + _installIDModel = [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:installations]; + _executionIDModel = [[FIRCLSExecutionIdentifierModel alloc] init]; + + _settings = settings; + _appIDModel = appIDModel; + + _settingsAndOnboardingManager = + [[FIRCLSSettingsOnboardingManager alloc] initWithAppIDModel:appIDModel + installIDModel:self.installIDModel + settings:self.settings + fileManager:self.fileManager + googleAppID:self.googleAppID]; + + return self; +} + +- (FIRCLSNetworkClient *)clientWithOperationQueue:(NSOperationQueue *)queue { + return [[FIRCLSNetworkClient alloc] initWithQueue:queue fileManager:_fileManager delegate:self]; +} + +/** + * Returns the number of unsent reports on the device, including the ones passed in. + */ +- (int)unsentReportsCountWithPreexisting:(NSArray *)paths { + int count = [self countSubmittableAndDeleteUnsubmittableReportPaths:paths]; + + count += _fileManager.processingPathContents.count; + + if (self.settings.shouldUseNewReportEndpoint) { + count += _fileManager.preparedPathContents.count; + } else { + count += _fileManager.legacyPreparedPathContents.count; + } + return count; +} + +// This method returns a promise that is resolved with a wrapped FIRReportAction once the user has +// indicated whether they want to upload currently cached reports. +// This method should only be called when we have determined there is at least 1 unsent report. +// This method waits until either: +// 1. Data collection becomes enabled, in which case, the promise will be resolved with Send. +// 2. The developer uses the processCrashReports API to indicate whether the report +// should be sent or deleted, at which point the promise will be resolved with the action. +- (FBLPromise *)waitForReportAction { + FIRCLSDebugLog(@"[Crashlytics:Crash] Notifying that unsent reports are available."); + [_unsentReportsAvailable fulfill:@YES]; + + // If data collection gets enabled while we are waiting for an action, go ahead and send the + // reports, and any subsequent explicit response will be ignored. + FBLPromise *collectionEnabled = + [[self.dataArbiter waitForCrashlyticsCollectionEnabled] + then:^id _Nullable(NSNumber *_Nullable value) { + return @(FIRCLSReportActionSend); + }]; + + FIRCLSDebugLog(@"[Crashlytics:Crash] Waiting for send/deleteUnsentReports to be called."); + // Wait for either the processReports callback to be called, or data collection to be enabled. + return [FBLPromise race:@[ collectionEnabled, _reportActionProvided ]]; +} + +- (FBLPromise *)checkForUnsentReports { + bool expectedCalled = NO; + if (!atomic_compare_exchange_strong(&_checkForUnsentReportsCalled, &expectedCalled, YES)) { + FIRCLSErrorLog(@"checkForUnsentReports should only be called once per execution."); + return [FBLPromise resolvedWith:@NO]; + } + return _unsentReportsAvailable; +} + +- (FBLPromise *)sendUnsentReports { + [_reportActionProvided fulfill:@(FIRCLSReportActionSend)]; + return _unsentReportsHandled; +} + +- (FBLPromise *)deleteUnsentReports { + [_reportActionProvided fulfill:@(FIRCLSReportActionDelete)]; + return _unsentReportsHandled; +} + +- (FBLPromise *)startWithProfilingMark:(FIRCLSProfileMark)mark { + NSString *executionIdentifier = self.executionIDModel.executionID; + + // This needs to be called before any values are read from settings + NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate]; + [self.settings reloadFromCacheWithGoogleAppID:self.googleAppID currentTimestamp:currentTimestamp]; + + if (![self validateAppIdentifiers]) { + return [FBLPromise resolvedWith:@NO]; + } + +#if DEBUG + FIRCLSDebugLog(@"Root: %@", [_fileManager rootPath]); +#endif + + if ([self.dataArbiter isLegacyDataCollectionKeyInPlist]) { + FIRCLSErrorLog(@"Found legacy data collection key in app's Info.plist: " + @"firebase_crashlytics_collection_enabled"); + FIRCLSErrorLog(@"Please update your Info.plist to use the new data collection key: " + @"FirebaseCrashlyticsCollectionEnabled"); + FIRCLSErrorLog(@"The legacy data collection Info.plist value could be overridden by " + @"calling: [Fabric with:...]"); + FIRCLSErrorLog(@"The new value can be overridden by calling: [[FIRCrashlytics " + @"crashlytics] setCrashlyticsCollectionEnabled:]"); + + return [FBLPromise resolvedWith:@NO]; + } + + if (![_fileManager createReportDirectories]) { + return [FBLPromise resolvedWith:@NO]; + } + + // Grab existing reports + BOOL launchFailure = [self checkForAndCreateLaunchMarker]; + NSArray *preexistingReportPaths = _fileManager.activePathContents; + + FIRCLSInternalReport *report = [self setupCurrentReport:executionIdentifier]; + if (!report) { + FIRCLSErrorLog(@"Unable to setup a new report"); + } + + if (![self startCrashReporterWithProfilingMark:mark report:report]) { + FIRCLSErrorLog(@"Unable to start crash reporter"); + report = nil; + } + + // Regenerate the Install ID on a background thread if it needs to rotate because + // fetching the Firebase Install ID can be slow on some devices. This should happen after we + // create the session on disk so that we can update the Install ID in the written crash report + // metadata. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + [self checkAndRotateInstallUUIDIfNeededWithReport:report]; + }); + + FBLPromise *promise = [FBLPromise resolvedWith:@(report != nil)]; + + if ([self.dataArbiter isCrashlyticsCollectionEnabled]) { + FIRCLSDebugLog(@"Automatic data collection is enabled."); + FIRCLSDebugLog(@"Unsent reports will be uploaded at startup"); + FIRCLSDataCollectionToken *dataCollectionToken = [FIRCLSDataCollectionToken validToken]; + + [self beginSettingsAndOnboardingWithToken:dataCollectionToken waitForSettingsRequest:NO]; + + [self beginReportUploadsWithToken:dataCollectionToken + preexistingReportPaths:preexistingReportPaths + blockingSend:launchFailure + report:report]; + + // If data collection is enabled, the SDK will not notify the user + // when unsent reports are available, or respect Send / DeleteUnsentReports + [_unsentReportsAvailable fulfill:@NO]; + + } else { + FIRCLSDebugLog(@"Automatic data collection is disabled."); + + // TODO: This counting of the file system happens on the main thread. Now that some of the other + // work below has been made async and moved to the dispatch queue, maybe we can move this code + // to the dispatch queue as well. + int unsentReportsCount = [self unsentReportsCountWithPreexisting:preexistingReportPaths]; + if (unsentReportsCount > 0) { + FIRCLSDebugLog( + @"[Crashlytics:Crash] %d unsent reports are available. Checking for upload permission.", + unsentReportsCount); + // Wait for an action to get sent, either from processReports: or automatic data collection. + promise = [[self waitForReportAction] + onQueue:_dispatchQueue + then:^id _Nullable(FIRCLSWrappedReportAction *_Nullable wrappedAction) { + // Process the actions for the reports on disk. + FIRCLSReportAction action = [wrappedAction reportActionValue]; + if (action == FIRCLSReportActionSend) { + FIRCLSDebugLog(@"Sending unsent reports."); + FIRCLSDataCollectionToken *dataCollectionToken = + [FIRCLSDataCollectionToken validToken]; + + // For the new report endpoint, the orgID is not needed. + // For the legacy report endpoint, wait on settings if orgID is not available. + BOOL waitForSetting = + !self.settings.shouldUseNewReportEndpoint && !self.settings.orgID; + + [self beginSettingsAndOnboardingWithToken:dataCollectionToken + waitForSettingsRequest:waitForSetting]; + + [self beginReportUploadsWithToken:dataCollectionToken + preexistingReportPaths:preexistingReportPaths + blockingSend:NO + report:report]; + + } else if (action == FIRCLSReportActionDelete) { + FIRCLSDebugLog(@"Deleting unsent reports."); + [self deleteUnsentReportsWithPreexisting:preexistingReportPaths]; + } else { + FIRCLSErrorLog(@"Unknown report action: %d", action); + } + return @(report != nil); + }]; + } else { + FIRCLSDebugLog(@"[Crashlytics:Crash] There are no unsent reports."); + [_unsentReportsAvailable fulfill:@NO]; + } + } + + if (report != nil) { + // capture the start-up time here, but record it asynchronously + double endMark = FIRCLSProfileEnd(mark); + + dispatch_async(FIRCLSGetLoggingQueue(), ^{ + FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSStartTimeKey, [@(endMark) description]); + }); + } + + // To make the code more predictable and therefore testable, don't resolve the startup promise + // until the operations that got queued up for processing reports have been processed through the + // work queue. + NSOperationQueue *__weak queue = _operationQueue; + FBLPromise *__weak unsentReportsHandled = _unsentReportsHandled; + promise = [promise then:^id _Nullable(NSNumber *_Nullable value) { + [queue waitUntilAllOperationsAreFinished]; + // Signal that to callers of processReports that everything is finished. + [unsentReportsHandled fulfill:nil]; + return value; + }]; + + return promise; +} + +- (void)checkAndRotateInstallUUIDIfNeededWithReport:(FIRCLSInternalReport *)report { + [self.installIDModel regenerateInstallIDIfNeededWithBlock:^(BOOL didRotate) { + if (!didRotate) { + return; + } + + FIRCLSContextUpdateMetadata(report, self.settings, self.installIDModel, self->_fileManager); + }]; +} + +- (void)beginSettingsAndOnboardingWithToken:(FIRCLSDataCollectionToken *)token + waitForSettingsRequest:(BOOL)waitForSettings { + if (self.settings.isCacheExpired) { + // This method can be called more than once if the user calls + // SendUnsentReports again, so don't repeat the settings fetch + static dispatch_once_t settingsFetchOnceToken; + dispatch_once(&settingsFetchOnceToken, ^{ + [self.settingsAndOnboardingManager beginSettingsAndOnboardingWithGoogleAppId:self.googleAppID + token:token + waitForCompletion:waitForSettings]; + }); + } +} + +- (void)beginReportUploadsWithToken:(FIRCLSDataCollectionToken *)token + preexistingReportPaths:(NSArray *)preexistingReportPaths + blockingSend:(BOOL)blockingSend + report:(FIRCLSInternalReport *)report { + if (self.settings.collectReportsEnabled) { + [self processExistingReportPaths:preexistingReportPaths + dataCollectionToken:token + asUrgent:blockingSend]; + [self handleContentsInOtherReportingDirectoriesWithToken:token]; + + } else { + FIRCLSInfoLog(@"Collect crash reports is disabled"); + [self deleteUnsentReportsWithPreexisting:preexistingReportPaths]; + } +} + +- (BOOL)startCrashReporterWithProfilingMark:(FIRCLSProfileMark)mark + report:(FIRCLSInternalReport *)report { + if (!report) { + return NO; + } + + if (!FIRCLSContextInitialize(report, self.settings, self.installIDModel, _fileManager)) { + return NO; + } + + [self setupStateNotifications]; + + [self registerAnalyticsEventListener]; + + [self crashReportingSetupCompleted:mark]; + + return YES; +} + +- (void)crashReportingSetupCompleted:(FIRCLSProfileMark)mark { + // check our handlers + FIRCLSDispatchAfter(2.0, dispatch_get_main_queue(), ^{ + FIRCLSExceptionCheckHandlers((__bridge void *)(self)); + FIRCLSSignalCheckHandlers(); +#if CLS_MACH_EXCEPTION_SUPPORTED + FIRCLSMachExceptionCheckHandlers(); +#endif + }); + + // remove the launch failure marker and record the startup time + dispatch_async(dispatch_get_main_queue(), ^{ + [self removeLaunchFailureMarker]; + dispatch_async(FIRCLSGetLoggingQueue(), ^{ + FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSFirstRunloopTurnTimeKey, + [@(FIRCLSProfileEnd(mark)) description]); + }); + }); +} + +- (BOOL)validateAppIdentifiers { + // When the ApplicationIdentifierModel fails to initialize, it is usually due to + // failing computeExecutableInfo. This can happen if the user sets the + // Exported Symbols File in Build Settings, and leaves off the one symbol + // that Crashlytics needs, "__mh_execute_header" (wich is defined in mach-o/ldsyms.h as + // _MH_EXECUTE_SYM). From https://github.com/firebase/firebase-ios-sdk/issues/5020 + if (!self.appIDModel) { + FIRCLSErrorLog( + @"Crashlytics could not find the symbol for the app's main function and cannot " + @"start up. This can happen when Exported Symbols File is set in Build Settings. To " + @"resolve this, add \"__mh_execute_header\" as a newline to your Exported Symbols File."); + return NO; + } + + if (self.appIDModel.bundleID.length == 0) { + FIRCLSErrorLog(@"An application must have a valid bundle identifier in its Info.plist"); + return NO; + } + + return YES; +} + +- (FIRCLSReportUploader *)uploader { + if (!_uploader) { + _uploader = [[FIRCLSReportUploader alloc] initWithQueue:self.operationQueue + delegate:self + dataSource:self + client:self.networkClient + fileManager:_fileManager + analytics:_analytics]; + } + + return _uploader; +} + +#pragma mark - Reporting Lifecycle + +- (FIRCLSInternalReport *)setupCurrentReport:(NSString *)executionIdentifier { + [self createLaunchFailureMarker]; + + NSString *reportPath = [_fileManager setupNewPathForExecutionIdentifier:executionIdentifier]; + + return [[FIRCLSInternalReport alloc] initWithPath:reportPath + executionIdentifier:executionIdentifier]; +} + +- (int)countSubmittableAndDeleteUnsubmittableReportPaths:(NSArray *)reportPaths { + int count = 0; + for (NSString *path in reportPaths) { + FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path]; + if ([report needsToBeSubmitted]) { + count++; + } else { + [self.operationQueue addOperationWithBlock:^{ + [self->_fileManager removeItemAtPath:path]; + }]; + } + } + return count; +} + +- (void)processExistingReportPaths:(NSArray *)reportPaths + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + asUrgent:(BOOL)urgent { + for (NSString *path in reportPaths) { + [self processExistingActiveReportPath:path + dataCollectionToken:dataCollectionToken + asUrgent:urgent]; + } +} + +- (void)processExistingActiveReportPath:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + asUrgent:(BOOL)urgent { + FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path]; + + // TODO: needsToBeSubmitted should really be called on the background queue. + if (![report needsToBeSubmitted]) { + [self.operationQueue addOperationWithBlock:^{ + [self->_fileManager removeItemAtPath:path]; + }]; + + return; + } + + if (urgent && [dataCollectionToken isValid]) { + // We can proceed without the delegate. + [[self uploader] prepareAndSubmitReport:report + dataCollectionToken:dataCollectionToken + asUrgent:urgent + withProcessing:YES]; + return; + } + + [self submitReport:report dataCollectionToken:dataCollectionToken]; +} + +- (void)submitReport:(FIRCLSInternalReport *)report + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken { + [self.operationQueue addOperationWithBlock:^{ + [[self uploader] prepareAndSubmitReport:report + dataCollectionToken:dataCollectionToken + asUrgent:NO + withProcessing:YES]; + }]; + + [self didSubmitReport]; +} + +// This is the side-effect of calling deleteUnsentReports, or collect_reports setting +// being false +- (void)deleteUnsentReportsWithPreexisting:(NSArray *)preexistingReportPaths { + [self removeExistingReportPaths:preexistingReportPaths]; + + [self removeExistingReportPaths:self.fileManager.processingPathContents]; + if (self.settings.shouldUseNewReportEndpoint) { + [self removeExistingReportPaths:self.fileManager.preparedPathContents]; + } else { + [self removeExistingReportPaths:self.fileManager.legacyPreparedPathContents]; + } +} + +- (void)removeExistingReportPaths:(NSArray *)reportPaths { + [self.operationQueue addOperationWithBlock:^{ + for (NSString *path in reportPaths) { + [self.fileManager removeItemAtPath:path]; + } + }]; +} + +- (void)handleContentsInOtherReportingDirectoriesWithToken:(FIRCLSDataCollectionToken *)token { + [self handleExistingFilesInProcessingWithToken:token]; + [self handleExistingFilesInPreparedWithToken:token]; +} + +- (void)handleExistingFilesInProcessingWithToken:(FIRCLSDataCollectionToken *)token { + NSArray *processingPaths = _fileManager.processingPathContents; + + // deal with stuff in processing more carefully - do not process again + [self.operationQueue addOperationWithBlock:^{ + for (NSString *path in processingPaths) { + FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path]; + [[self uploader] prepareAndSubmitReport:report + dataCollectionToken:token + asUrgent:NO + withProcessing:NO]; + } + }]; +} + +- (void)handleExistingFilesInPreparedWithToken:(FIRCLSDataCollectionToken *)token { + NSArray *preparedPaths = self.settings.shouldUseNewReportEndpoint + ? _fileManager.preparedPathContents + : _fileManager.legacyPreparedPathContents; + + // Give our network client a chance to reconnect here, if needed. This attempts to avoid + // trying to re-submit a prepared file that is already in flight. + [self.networkClient attemptToReconnectBackgroundSessionWithCompletionBlock:^{ + [self.operationQueue addOperationWithBlock:^{ + [self uploadPreexistingFiles:preparedPaths withToken:token]; + }]; + }]; +} + +- (void)uploadPreexistingFiles:(NSArray *)files withToken:(FIRCLSDataCollectionToken *)token { + // Because this could happen quite a bit after the inital set of files was + // captured, some could be completed (deleted). So, just double-check to make sure + // the file still exists. + + for (NSString *path in files) { + if (![[_fileManager underlyingFileManager] fileExistsAtPath:path]) { + continue; + } + + [[self uploader] uploadPackagedReportAtPath:path dataCollectionToken:token asUrgent:NO]; + } +} + +- (void)retryUploadForReportAtPath:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)token { + FIRCLSAddOperationAfter(CLSReportRetryInterval, self.operationQueue, ^{ + FIRCLSDeveloperLog("Crashlytics:Crash", @"re-attempting report submission"); + [[self uploader] uploadPackagedReportAtPath:path dataCollectionToken:token asUrgent:NO]; + }); +} + +#pragma mark - Launch Failure Detection +- (NSString *)launchFailureMarkerPath { + return [[_fileManager structurePath] stringByAppendingPathComponent:@"launchmarker"]; +} + +- (BOOL)createLaunchFailureMarker { + // It's tempting to use - [NSFileManger createFileAtPath:contents:attributes:] here. But that + // operation, even with empty/nil contents does a ton of work to write out nothing via a + // temporarly file. This is a much faster implemenation. + const char *path = [[self launchFailureMarkerPath] fileSystemRepresentation]; + +#if TARGET_OS_IPHONE + /* + * data-protected non-portable open(2) : + * int open_dprotected_np(user_addr_t path, int flags, int class, int dpflags, int mode) + */ + int fd = open_dprotected_np(path, O_WRONLY | O_CREAT | O_TRUNC, 4, 0, 0644); +#else + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); +#endif + if (fd == -1) { + return NO; + } + + return close(fd) == 0; +} + +- (BOOL)launchFailureMarkerPresent { + return [[_fileManager underlyingFileManager] fileExistsAtPath:[self launchFailureMarkerPath]]; +} + +- (BOOL)removeLaunchFailureMarker { + return [_fileManager removeItemAtPath:[self launchFailureMarkerPath]]; +} + +- (BOOL)checkForAndCreateLaunchMarker { + BOOL launchFailure = [self launchFailureMarkerPresent]; + if (launchFailure) { + FIRCLSDeveloperLog("Crashlytics:Crash", + @"Last launch failed: this may indicate a crash shortly after app launch."); + } else { + [self createLaunchFailureMarker]; + } + + return launchFailure; +} + +#pragma mark - + +- (void)registerAnalyticsEventListener { + if (_registeredAnalyticsEventListener) { + return; + } + FIRCLSAnalyticsInteropListener *listener = [[FIRCLSAnalyticsInteropListener alloc] init]; + [FIRCLSFCRAnalytics registerEventListener:listener toAnalytics:_analytics]; + _registeredAnalyticsEventListener = YES; +} + +#pragma mark - Notifications +- (void)setupStateNotifications { + [self captureInitialNotificationStates]; + +#if TARGET_OS_IOS + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(willBecomeActive:) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didBecomeInactive:) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didChangeOrientation:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(didChangeUIOrientation:) + name:UIApplicationDidChangeStatusBarOrientationNotification + object:nil]; +#pragma clang diagnostic pop + +#elif CLS_TARGET_OS_OSX + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(willBecomeActive:) + name:@"NSApplicationWillBecomeActiveNotification" + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didBecomeInactive:) + name:@"NSApplicationDidResignActiveNotification" + object:nil]; +#endif +} + +- (void)captureInitialNotificationStates { +#if TARGET_OS_IOS + UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; + UIInterfaceOrientation statusBarOrientation = + [FIRCLSApplicationSharedInstance() statusBarOrientation]; +#endif + + // It's nice to do this async, so we don't hold up the main thread while doing three + // consecutive IOs here. + dispatch_async(FIRCLSGetLoggingQueue(), ^{ + FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSInBackgroundKey, @"0"); +#if TARGET_OS_IOS + FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSDeviceOrientationKey, + [@(orientation) description]); + FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSUIOrientationKey, + [@(statusBarOrientation) description]); +#endif + }); +} + +- (void)willBecomeActive:(NSNotification *)notification { + FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSInBackgroundKey, @NO); +} + +- (void)didBecomeInactive:(NSNotification *)notification { + FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSInBackgroundKey, @YES); +} + +#if TARGET_OS_IOS +- (void)didChangeOrientation:(NSNotification *)notification { + UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; + + FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDeviceOrientationKey, @(orientation)); +} + +- (void)didChangeUIOrientation:(NSNotification *)notification { + UIInterfaceOrientation statusBarOrientation = + [FIRCLSApplicationSharedInstance() statusBarOrientation]; + + FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSUIOrientationKey, @(statusBarOrientation)); +} +#endif + +#pragma mark - FIRCLSNetworkClientDelegate +- (BOOL)networkClientCanUseBackgroundSessions:(FIRCLSNetworkClient *)client { + return !FIRCLSApplicationIsExtension(); +} + +- (void)networkClient:(FIRCLSNetworkClient *)client + didFinishUploadWithPath:(NSString *)path + error:(NSError *)error { + // Route this through to the reports uploader. + // Since this callback happens after an upload finished, then we can assume that the original data + // collection was authorized. This isn't ideal, but it's better than trying to plumb the data + // collection token through all the system networking callbacks. + FIRCLSDataCollectionToken *token = [FIRCLSDataCollectionToken validToken]; + [[self uploader] reportUploadAtPath:path dataCollectionToken:token completedWithError:error]; +} + +#pragma mark - FIRCLSReportUploaderDelegate + +- (void)didCompletePackageSubmission:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)token + error:(NSError *)error { + if (!error) { + FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission successful"); + return; + } + + FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission failed with error %@", error); + FIRCLSSDKLog("Error: failed to submit report '%s'\n", error.description.UTF8String); + + [self retryUploadForReportAtPath:path dataCollectionToken:token]; +} + +- (void)didCompleteAllSubmissions { + [self.operationQueue addOperationWithBlock:^{ + // Dealloc the reports uploader. If we need it again (if we re-enqueued submissions from + // didCompletePackageSubmission:, we can just create it again + self->_uploader = nil; + + FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission complete"); + }]; +} + +#pragma mark - UITest Helpers + +// Used only for internal data collection E2E testing +- (void)didSubmitReport { + if (reportSentCallback) { + dispatch_async(dispatch_get_main_queue(), ^{ + reportSentCallback(); + }); + } +} + ++ (void)setReportSentCallback:(void (^)(void))callback { + reportSentCallback = callback; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h new file mode 100644 index 000000000..f7f139740 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h @@ -0,0 +1,36 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSReportManager.h" +#import "FIRCLSReportUploader.h" + +@class FIRCLSInstallIdentifierModel; + +@interface FIRCLSReportManager () + +@property(nonatomic, strong) NSOperationQueue *operationQueue; +@property(nonatomic, strong) FIRCLSNetworkClient *networkClient; +@property(nonatomic, readonly) FIRCLSReportUploader *uploader; +@property(nonatomic, strong) FIRCLSFileManager *fileManager; + +@end + +@interface FIRCLSReportManager (PrivateMethods) + +- (BOOL)createLaunchFailureMarker; +- (BOOL)launchFailureMarkerPresent; + +- (BOOL)potentiallySubmittableCrashOccurred; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h new file mode 100644 index 000000000..c1cf8b4a1 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h @@ -0,0 +1,80 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import + +@class FIRCLSDataCollectionToken; +@class FIRCLSInternalReport; +@class FIRCLSSettings; +@class FIRCLSFileManager; +@class FIRCLSNetworkClient; +@class FIRCLSReportUploader; + +@protocol FIRCLSReportUploaderDelegate; +@protocol FIRCLSReportUploaderDataSource; +@protocol FIRAnalyticsInterop; + +@interface FIRCLSReportUploader : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)initWithQueue:(NSOperationQueue *)queue + delegate:(id)delegate + dataSource:(id)dataSource + client:(FIRCLSNetworkClient *)client + fileManager:(FIRCLSFileManager *)fileManager + analytics:(id)analytics NS_DESIGNATED_INITIALIZER; + +@property(nonatomic, weak) id delegate; +@property(nonatomic, weak) id dataSource; + +@property(nonatomic, readonly) NSOperationQueue *operationQueue; +@property(nonatomic, readonly) FIRCLSNetworkClient *networkClient; +@property(nonatomic, readonly) FIRCLSFileManager *fileManager; + +- (BOOL)prepareAndSubmitReport:(FIRCLSInternalReport *)report + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + asUrgent:(BOOL)urgent + withProcessing:(BOOL)shouldProcess; + +- (BOOL)uploadPackagedReportAtPath:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + asUrgent:(BOOL)urgent; + +- (void)reportUploadAtPath:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + completedWithError:(NSError *)error; + +@end + +@protocol FIRCLSReportUploaderDelegate +@required + +- (void)didCompletePackageSubmission:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)token + error:(NSError *)error; +- (void)didCompleteAllSubmissions; + +@end + +@protocol FIRCLSReportUploaderDataSource +@required + +- (NSString *)googleAppID; +- (FIRCLSSettings *)settings; +- (GDTCORTransport *)googleTransport; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m new file mode 100644 index 000000000..6b8263534 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m @@ -0,0 +1,356 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "Interop/Analytics/Public/FIRAnalyticsInterop.h" + +#import "FIRCLSApplication.h" +#import "FIRCLSDataCollectionArbiter.h" +#import "FIRCLSDataCollectionToken.h" +#import "FIRCLSDefines.h" +#import "FIRCLSFCRAnalytics.h" +#import "FIRCLSFileManager.h" +#import "FIRCLSInstallIdentifierModel.h" +#import "FIRCLSInternalReport.h" +#import "FIRCLSNetworkClient.h" +#import "FIRCLSPackageReportOperation.h" +#import "FIRCLSProcessReportOperation.h" +#import "FIRCLSReportAdapter.h" +#import "FIRCLSReportUploader_Private.h" +#import "FIRCLSSettings.h" +#import "FIRCLSSymbolResolver.h" + +#include "FIRCLSUtility.h" + +#import "FIRCLSConstants.h" +#import "FIRCLSMultipartMimeStreamEncoder.h" +#import "FIRCLSURLBuilder.h" + +#import +#import + +@interface FIRCLSReportUploader () { + id _analytics; +} +@end + +@implementation FIRCLSReportUploader + +- (instancetype)initWithQueue:(NSOperationQueue *)queue + delegate:(id)delegate + dataSource:(id)dataSource + client:(FIRCLSNetworkClient *)client + fileManager:(FIRCLSFileManager *)fileManager + analytics:(id)analytics { + self = [super init]; + if (!self) { + return nil; + } + + _operationQueue = queue; + _delegate = delegate; + _dataSource = dataSource; + _networkClient = client; + _fileManager = fileManager; + _analytics = analytics; + + return self; +} + +#pragma mark - Packaging and Submission +- (BOOL)prepareAndSubmitReport:(FIRCLSInternalReport *)report + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + asUrgent:(BOOL)urgent + withProcessing:(BOOL)shouldProcess { + __block BOOL success = NO; + + if (![dataCollectionToken isValid]) { + FIRCLSErrorLog(@"Data collection disabled and report will not be submitted"); + return NO; + } + + if (!self.dataSource.settings.orgID && !self.dataSource.settings.shouldUseNewReportEndpoint) { + FIRCLSDebugLog( + @"Skipping report with id '%@' this run of the app because Organization ID was " + @"nil. Report via the legacy endpoint will upload once settings are download successfully", + report.identifier); + return YES; + } + + FIRCLSApplicationActivity( + FIRCLSApplicationActivityDefault, @"Crashlytics Crash Report Processing", ^{ + if (shouldProcess) { + if (![self.fileManager moveItemAtPath:report.path + toDirectory:self.fileManager.processingPath]) { + FIRCLSErrorLog(@"Unable to move report for processing"); + return; + } + + // adjust the report's path, and process it + [report setPath:[self.fileManager.processingPath + stringByAppendingPathComponent:report.directoryName]]; + + FIRCLSSymbolResolver *resolver = [[FIRCLSSymbolResolver alloc] init]; + + FIRCLSProcessReportOperation *processOperation = + [[FIRCLSProcessReportOperation alloc] initWithReport:report resolver:resolver]; + + [processOperation start]; + } + + NSString *packagedPath; + + FIRCLSDebugLog(@"Preparing the report for the new endpoint: %d", + self.dataSource.settings.shouldUseNewReportEndpoint); + + // With the new report endpoint, the report is deleted once it is written to GDT + // Check if the report has a crash file before the report is moved or deleted + BOOL isCrash = report.isCrash; + + if (self.dataSource.settings.shouldUseNewReportEndpoint) { + // For the new endpoint, just move the .clsrecords from "processing" -> "prepared" + if (![self.fileManager moveItemAtPath:report.path + toDirectory:self.fileManager.preparedPath]) { + FIRCLSErrorLog(@"Unable to move report to prepared"); + return; + } + + packagedPath = [self.fileManager.preparedPath + stringByAppendingPathComponent:report.path.lastPathComponent]; + } else { + // For the legacy endpoint, continue generate the multipartmime file in "prepared-legacy" + FIRCLSPackageReportOperation *packageOperation = + [[FIRCLSPackageReportOperation alloc] initWithReport:report + fileManager:self.fileManager + settings:self.dataSource.settings]; + + [packageOperation start]; + packagedPath = packageOperation.finalPath; + if (!packagedPath) { + FIRCLSErrorLog(@"Unable to package report"); + return; + } + + if (![self.fileManager removeItemAtPath:report.path]) { + FIRCLSErrorLog(@"Unable to remove a processing item"); + } + } + + NSLog(@"[Firebase/Crashlytics] Packaged report with id '%@' for submission", + report.identifier); + + success = [self uploadPackagedReportAtPath:packagedPath + dataCollectionToken:dataCollectionToken + asUrgent:urgent]; + + // If the upload was successful and the report contained a crash forward it to Google + // Analytics. + if (success && isCrash) { + [FIRCLSFCRAnalytics logCrashWithTimeStamp:report.crashedOnDate.timeIntervalSince1970 + toAnalytics:self->_analytics]; + } + }); + + return success; +} + +- (BOOL)submitPackageMultipartMimeAtPath:(NSString *)multipartmimePath + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + synchronously:(BOOL)synchronous { + FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports", "Submitting %@ %@", + synchronous ? @"sync" : @"async", multipartmimePath); + + if ([[[self fileManager] fileSizeAtPath:multipartmimePath] unsignedIntegerValue] == 0) { + FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"Already-submitted report being ignored"); + return NO; + } + + NSTimeInterval timeout = 10.0; + + // If we are submitting synchronously, be more aggressive with the timeout. However, + // we only need this if the client does not support background requests. + if (synchronous && ![[self networkClient] supportsBackgroundRequests]) { + timeout = 2.0; + } + + NSMutableURLRequest *request = [self mutableRequestWithURL:[self reportURL] timeout:timeout]; + + [request setHTTPMethod:@"POST"]; + + if (![self fillInRequest:request forMultipartMimeDataAtPath:multipartmimePath]) { + return NO; + } + + [[self networkClient] startUploadRequest:request + filePath:multipartmimePath + dataCollectionToken:dataCollectionToken + immediately:synchronous]; + + return YES; +} + +- (BOOL)uploadPackagedReportAtPath:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + asUrgent:(BOOL)urgent { + FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"Submitting report%@", + urgent ? @" as urgent" : @""); + + // Check with the legacy path as the new path will always be contained in the legacy path + BOOL isNewPreparedPath = ![path containsString:self.fileManager.legacyPreparedPath]; + + if (isNewPreparedPath && self.dataSource.settings.shouldUseNewReportEndpoint) { + if (![dataCollectionToken isValid]) { + FIRCLSErrorLog(@"A report upload was requested with an invalid data collection token."); + return NO; + } + + FIRCLSReportAdapter *adapter = + [[FIRCLSReportAdapter alloc] initWithPath:path googleAppId:self.dataSource.googleAppID]; + + GDTCOREvent *event = [self.dataSource.googleTransport eventForTransport]; + event.dataObject = adapter; + event.qosTier = GDTCOREventQoSFast; // Bypass batching, send immediately + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + __block BOOL success = YES; + + [self.dataSource.googleTransport + sendDataEvent:event + onComplete:^(BOOL wasWritten, NSError *error) { + if (!wasWritten) { + FIRCLSDeveloperLog("Crashlytics:Crash:Reports", + @"Failed to send crash report due to gdt write failure."); + success = NO; + return; + } + + if (error) { + FIRCLSDeveloperLog("Crashlytics:Crash:Reports", + @"Failed to send crash report due to gdt error: %@", + error.localizedDescription); + success = NO; + return; + } + + FIRCLSDeveloperLog("Crashlytics:Crash:Reports", + @"Completed report submission with id: %@", path.lastPathComponent); + + if (urgent) { + dispatch_semaphore_signal(semaphore); + } + + [self cleanUpSubmittedReportAtPath:path]; + }]; + + if (urgent) { + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + } + + return success; + + } else if (!isNewPreparedPath && !self.dataSource.settings.shouldUseNewReportEndpoint) { + return [self submitPackageMultipartMimeAtPath:path + dataCollectionToken:dataCollectionToken + synchronously:urgent]; + } + + // Unsupported state + return NO; +} + +- (BOOL)cleanUpSubmittedReportAtPath:(NSString *)path { + if (![[self fileManager] removeItemAtPath:path]) { + FIRCLSErrorLog(@"Unable to remove packaged submission"); + return NO; + } + + return YES; +} + +- (void)reportUploadAtPath:(NSString *)path + dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken + completedWithError:(NSError *)error { + FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"completed submission of %@", path); + + if (!error) { + [self cleanUpSubmittedReportAtPath:path]; + } + + [[self delegate] didCompletePackageSubmission:path + dataCollectionToken:dataCollectionToken + error:error]; +} + +#pragma mark - Properties (TODO: Can delete once the experiment is over) + +- (NSURL *)reportURL { + FIRCLSURLBuilder *url = [FIRCLSURLBuilder URLWithBase:FIRCLSReportsEndpoint]; + + [url appendComponent:@"/sdk-api/v1/platforms/"]; + [url appendComponent:FIRCLSApplicationGetPlatform()]; + [url appendComponent:@"/apps/"]; + [url appendComponent:self.dataSource.settings.fetchedBundleID]; + [url appendComponent:@"/reports"]; + + return [url URL]; +} + +- (NSString *)localeIdentifier { + return [[NSLocale currentLocale] localeIdentifier]; +} + +#pragma mark - URL Requests +- (NSMutableURLRequest *)mutableRequestWithURL:(NSURL *)url timeout:(NSTimeInterval)timeout { + NSMutableURLRequest *request = + [NSMutableURLRequest requestWithURL:url + cachePolicy:NSURLRequestReloadIgnoringLocalCacheData + timeoutInterval:timeout]; + + NSString *localeId = [self localeIdentifier]; + + [request setValue:@CLS_SDK_GENERATOR_NAME forHTTPHeaderField:FIRCLSNetworkUserAgent]; + [request setValue:FIRCLSNetworkApplicationJson forHTTPHeaderField:FIRCLSNetworkAccept]; + [request setValue:FIRCLSNetworkUTF8 forHTTPHeaderField:FIRCLSNetworkAcceptCharset]; + [request setValue:localeId forHTTPHeaderField:FIRCLSNetworkAcceptLanguage]; + [request setValue:localeId forHTTPHeaderField:FIRCLSNetworkContentLanguage]; + [request setValue:FIRCLSDeveloperToken forHTTPHeaderField:FIRCLSNetworkCrashlyticsDeveloperToken]; + [request setValue:FIRCLSApplicationGetSDKBundleID() + forHTTPHeaderField:FIRCLSNetworkCrashlyticsAPIClientId]; + [request setValue:@CLS_SDK_DISPLAY_VERSION + forHTTPHeaderField:FIRCLSNetworkCrashlyticsAPIClientDisplayVersion]; + [request setValue:[[self dataSource] googleAppID] + forHTTPHeaderField:FIRCLSNetworkCrashlyticsGoogleAppId]; + + return request; +} + +- (BOOL)fillInRequest:(NSMutableURLRequest *)request forMultipartMimeDataAtPath:(NSString *)path { + NSString *boundary = [[path lastPathComponent] stringByDeletingPathExtension]; + + [request setValue:[FIRCLSMultipartMimeStreamEncoder + contentTypeHTTPHeaderValueWithBoundary:boundary] + forHTTPHeaderField:@"Content-Type"]; + + NSNumber *fileSize = [[self fileManager] fileSizeAtPath:path]; + if (fileSize == nil) { + FIRCLSErrorLog(@"Could not determine size of multipart mime file"); + return NO; + } + + [request setValue:[fileSize stringValue] forHTTPHeaderField:@"Content-Length"]; + + return YES; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h new file mode 100644 index 000000000..f54dc1673 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h @@ -0,0 +1,23 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSReportUploader.h" + +@interface FIRCLSReportUploader (PrivateMethods) + +@property(nonatomic, readonly) NSURL *reportURL; + +- (NSMutableURLRequest *)mutableRequestWithURL:(NSURL *)url timeout:(NSTimeInterval)timeout; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h new file mode 100644 index 000000000..147bc7bbc --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h @@ -0,0 +1,39 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRApp; +@class FBLPromise; + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRCLSDataCollectionArbiter : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict; + +- (BOOL)isLegacyDataCollectionKeyInPlist; + +- (BOOL)isCrashlyticsCollectionEnabled; + +- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled; + +// Returns a promise that is fulfilled once data collection is enabled. +- (FBLPromise *)waitForCrashlyticsCollectionEnabled; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.m new file mode 100644 index 000000000..e7d5996cf --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.m @@ -0,0 +1,148 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSDataCollectionArbiter.h" + +#if __has_include() +#import +#else +#import "FBLPromises.h" +#endif + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" + +#import "FIRCLSUserDefaults.h" + +// The legacy data collection setting allows Fabric customers to turn off auto- +// initialization, but can be overridden by calling [Fabric with:]. +// +// While we support Fabric, we must have two different versions, because +// they require these slightly different semantics. +NSString *const FIRCLSLegacyCrashlyticsCollectionKey = @"firebase_crashlytics_collection_enabled"; + +// The new data collection setting can be set by an API that is stored in FIRCLSUserDefaults +NSString *const FIRCLSDataCollectionEnabledKey = @"com.crashlytics.data_collection"; + +// The new data collection setting also allows Firebase customers to turn off data +// collection in their Info.plist, and can be overridden by setting it to true using +// the setCrashlyticsCollectionEnabled API. +NSString *const FIRCLSCrashlyticsCollectionKey = @"FirebaseCrashlyticsCollectionEnabled"; + +typedef NS_ENUM(NSInteger, FIRCLSDataCollectionSetting) { + FIRCLSDataCollectionSettingNotSet = 0, + FIRCLSDataCollectionSettingEnabled = 1, + FIRCLSDataCollectionSettingDisabled = 2, +}; + +@interface FIRCLSDataCollectionArbiter () { + NSLock *_mutex; + FBLPromise *_dataCollectionEnabled; + BOOL _promiseResolved; + FIRApp *_app; + NSDictionary *_appInfo; +} +@end + +@implementation FIRCLSDataCollectionArbiter + +- (instancetype)initWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict { + self = [super init]; + if (self) { + _mutex = [[NSLock alloc] init]; + _appInfo = dict; + _app = app; + if ([FIRCLSDataCollectionArbiter isCrashlyticsCollectionEnabledWithApp:app withAppInfo:dict]) { + _dataCollectionEnabled = [FBLPromise resolvedWith:nil]; + _promiseResolved = YES; + } else { + _dataCollectionEnabled = [FBLPromise pendingPromise]; + _promiseResolved = NO; + } + } + + return self; +} + +/* + * Legacy collection key that we provide for customers to disable Crash reporting. + * Customers can later turn on Crashlytics using Fabric.with if they choose to do so. + * + * This flag is unsupported for the "New SDK" + */ +- (BOOL)isLegacyDataCollectionKeyInPlist { + if ([_appInfo objectForKey:FIRCLSLegacyCrashlyticsCollectionKey]) { + return true; + } + + return false; +} + +// This functionality is called in the initializer before self is fully initialized, +// so a class method is used. The instance method below allows for a consistent clean API. ++ (BOOL)isCrashlyticsCollectionEnabledWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict { + FIRCLSDataCollectionSetting stickySetting = [FIRCLSDataCollectionArbiter stickySetting]; + if (stickySetting != FIRCLSDataCollectionSettingNotSet) { + return stickySetting == FIRCLSDataCollectionSettingEnabled; + } + + id firebaseCrashlyticsCollectionEnabled = [dict objectForKey:FIRCLSCrashlyticsCollectionKey]; + if ([firebaseCrashlyticsCollectionEnabled isKindOfClass:[NSString class]] || + [firebaseCrashlyticsCollectionEnabled isKindOfClass:[NSNumber class]]) { + return [firebaseCrashlyticsCollectionEnabled boolValue]; + } + + return [app isDataCollectionDefaultEnabled]; +} + +- (BOOL)isCrashlyticsCollectionEnabled { + return [FIRCLSDataCollectionArbiter isCrashlyticsCollectionEnabledWithApp:_app + withAppInfo:_appInfo]; +} + +- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled { + FIRCLSUserDefaults *userDefaults = [FIRCLSUserDefaults standardUserDefaults]; + FIRCLSDataCollectionSetting setting = + enabled ? FIRCLSDataCollectionSettingEnabled : FIRCLSDataCollectionSettingDisabled; + [userDefaults setInteger:setting forKey:FIRCLSDataCollectionEnabledKey]; + [userDefaults synchronize]; + + [_mutex lock]; + if (enabled) { + if (!_promiseResolved) { + [_dataCollectionEnabled fulfill:nil]; + _promiseResolved = YES; + } + } else { + if (_promiseResolved) { + _dataCollectionEnabled = [FBLPromise pendingPromise]; + _promiseResolved = NO; + } + } + [_mutex unlock]; +} + ++ (FIRCLSDataCollectionSetting)stickySetting { + FIRCLSUserDefaults *userDefaults = [FIRCLSUserDefaults standardUserDefaults]; + return [userDefaults integerForKey:FIRCLSDataCollectionEnabledKey]; +} + +- (FBLPromise *)waitForCrashlyticsCollectionEnabled { + FBLPromise *result = nil; + [_mutex lock]; + result = _dataCollectionEnabled; + [_mutex unlock]; + return result; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h new file mode 100644 index 000000000..4ab2bb661 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h @@ -0,0 +1,45 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * A FIRCLSDataCollectionToken represents having permission to upload data. A data collection token + * is either valid or nil. Every function that directly initiates a network operation that will + * result in data collection must check to make sure it has been passed a valid token. Tokens should + * only be created when either (1) automatic data collection is enabled, or (2) the user has + * explicitly given permission to collect data for a particular purpose, using the API. For all the + * functions in between, the data collection token getting passed as an argument helps to document + * and enforce the flow of data collection permission through the SDK. + */ +@interface FIRCLSDataCollectionToken : NSObject + +/** + * Creates a valid token. Only call this method when either (1) automatic data collection is + * enabled, or (2) the user has explicitly given permission to collect data for a particular + * purpose, using the API. + */ ++ (instancetype)validToken; + +/** + * Use this to verify that a token is valid. If this is called on a nil instance, it will return NO. + * @return YES. + */ +- (BOOL)isValid; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.m new file mode 100644 index 000000000..1a41ee1df --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.m @@ -0,0 +1,27 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSDataCollectionToken.h" + +@implementation FIRCLSDataCollectionToken + ++ (instancetype)validToken { + return [[FIRCLSDataCollectionToken alloc] init]; +} + +- (BOOL)isValid { + return YES; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.h new file mode 100644 index 000000000..fa53468c1 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.h @@ -0,0 +1,84 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import "FIRCLSURLSessionConfiguration.h" + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRCLSURLSession : NSObject { + id _delegate; + NSOperationQueue *_delegateQueue; + NSURLSessionConfiguration *_configuration; + NSMutableSet *_taskSet; + dispatch_queue_t _queue; + + NSString *_sessionDescription; +} + ++ (BOOL)NSURLSessionShouldBeUsed; + ++ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration; ++ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration + delegate:(nullable id)delegate + delegateQueue:(nullable NSOperationQueue *)queue; + +@property(nonatomic, readonly, retain) NSOperationQueue *delegateQueue; +@property(nonatomic, readonly, retain) id delegate; +@property(nonatomic, readonly, copy) NSURLSessionConfiguration *configuration; + +@property(nonatomic, copy) NSString *sessionDescription; + +- (void)getTasksWithCompletionHandler: + (void (^)(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks))completionHandler; + +// task creation - suitable for background operations +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL; + +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request; +- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url; + +// convenience methods (that are not available for background sessions +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + completionHandler:(nullable void (^)(NSData *data, + NSURLResponse *response, + NSError *error))completionHandler; +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request; + +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + completionHandler: + (nullable void (^)(NSURL *targetPath, + NSURLResponse *response, + NSError *error))completionHandler; + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + completionHandler: + (nullable void (^)(NSData *data, + NSURLResponse *response, + NSError *error))completionHandler; + +- (void)invalidateAndCancel; +- (void)finishTasksAndInvalidate; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.m new file mode 100644 index 000000000..981542368 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.m @@ -0,0 +1,346 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED +#import "FIRCLSURLSession.h" + +#import "FIRCLSURLSessionDataTask.h" +#import "FIRCLSURLSessionDataTask_PrivateMethods.h" +#import "FIRCLSURLSessionDownloadTask.h" +#import "FIRCLSURLSessionDownloadTask_PrivateMethods.h" +#import "FIRCLSURLSessionTask_PrivateMethods.h" +#import "FIRCLSURLSessionUploadTask.h" + +#define DELEGATE ((id)self->_delegate) + +@interface FIRCLSURLSession () + +@property(nonatomic, retain) NSOperationQueue *delegateQueue; +@property(nonatomic, retain) id delegate; +@property(nonatomic, copy) NSURLSessionConfiguration *configuration; + +@end + +@implementation FIRCLSURLSession + +@synthesize delegate = _delegate; +@synthesize delegateQueue = _delegateQueue; +@synthesize configuration = _configuration; +@synthesize sessionDescription = _sessionDescription; + ++ (BOOL)NSURLSessionShouldBeUsed { + if (!NSClassFromString(@"NSURLSession")) { + return NO; + } + + // We use this as a proxy to verify that we are on at least iOS 8 or 10.10. The first OSes that + // has NSURLSession were fairly unstable. + return [[NSURLSessionConfiguration class] + respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]; +} + ++ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration { + return [self sessionWithConfiguration:configuration delegate:nil delegateQueue:nil]; +} + ++ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration + delegate:(nullable id)delegate + delegateQueue:(nullable NSOperationQueue *)queue { + if ([self NSURLSessionShouldBeUsed]) { + return [NSURLSession sessionWithConfiguration:configuration + delegate:delegate + delegateQueue:queue]; + } + + if (!configuration) { + return nil; + } + +#if __has_feature(objc_arc) + FIRCLSURLSession *session = [self new]; +#else + FIRCLSURLSession *session = [[self new] autorelease]; +#endif + [session setDelegate:delegate]; + // When delegate exists, but delegateQueue is nil, create a serial queue like NSURLSession + // documents. + if (delegate && !queue) { + queue = [self newDefaultDelegateQueue]; + } + session.delegateQueue = queue; + session.configuration = configuration; + return (NSURLSession *)session; +} + ++ (NSOperationQueue *)newDefaultDelegateQueue { + NSOperationQueue *delegateQueue = [[NSOperationQueue alloc] init]; + delegateQueue.name = [NSString stringWithFormat:@"%@ %p", NSStringFromClass(self), self]; + delegateQueue.maxConcurrentOperationCount = 1; + return delegateQueue; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _queue = dispatch_queue_create("com.crashlytics.URLSession", 0); + + return self; +} + +#if !__has_feature(objc_arc) +- (void)dealloc { + [_taskSet release]; + [_delegate release]; + [_delegateQueue release]; + [_configuration release]; + +#if !OS_OBJECT_USE_OBJC + dispatch_release(_queue); +#endif + + [super dealloc]; +} +#endif + +#pragma mark - Managing the Session + +- (void)invalidateAndCancel { + dispatch_sync(_queue, ^{ + for (FIRCLSURLSessionTask *task in self->_taskSet) { + [task cancel]; + } + }); + + self.delegate = nil; +} + +- (void)finishTasksAndInvalidate { + self.delegate = nil; +} + +#pragma mark - + +- (void)getTasksWithCompletionHandler: + (void (^)(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks))completionHandler { + [[self delegateQueue] addOperationWithBlock:^{ + // TODO - this is totally wrong, but better than not calling back at all + completionHandler(@[], @[], @[]); + }]; +} + +- (void)removeTaskFromSet:(FIRCLSURLSessionTask *)task { + dispatch_async(_queue, ^{ + [self->_taskSet removeObject:task]; + }); +} + +- (void)configureTask:(FIRCLSURLSessionTask *)task + withRequest:(NSURLRequest *)request + block:(void (^)(NSMutableURLRequest *mutableRequest))block { + NSMutableURLRequest *modifiedRequest = [request mutableCopy]; + + dispatch_sync(_queue, ^{ + [self->_taskSet addObject:task]; + + // TODO: this isn't allowed to overwrite existing headers + for (NSString *key in [self->_configuration HTTPAdditionalHeaders]) { + [modifiedRequest addValue:[[self->_configuration HTTPAdditionalHeaders] objectForKey:key] + forHTTPHeaderField:key]; + } + }); + + if (block) { + block(modifiedRequest); + } + + [task setOriginalRequest:modifiedRequest]; + [task setDelegate:self]; + +#if !__has_feature(objc_arc) + [modifiedRequest release]; +#endif +} + +- (BOOL)shouldInvokeDelegateSelector:(SEL)selector forTask:(FIRCLSURLSessionTask *)task { + return [task invokesDelegate] && [_delegate respondsToSelector:selector]; +} + +#pragma mark Task Creation +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL { + return [self uploadTaskWithRequest:request fromFile:fileURL completionHandler:nil]; +} + +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request { + return [self downloadTaskWithRequest:request completionHandler:nil]; +} + +- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url { + return [self downloadTaskWithRequest:[NSURLRequest requestWithURL:url]]; +} + +#pragma mark Async Convenience Methods +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request + completionHandler:(nullable void (^)(NSData *data, + NSURLResponse *response, + NSError *error))completionHandler { + FIRCLSURLSessionDataTask *task = [FIRCLSURLSessionDataTask task]; + + if (completionHandler) { + [task setCompletionHandler:completionHandler]; + [task setInvokesDelegate:NO]; + } + + [self configureTask:task withRequest:request block:nil]; + + return (NSURLSessionDataTask *)task; +} + +- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request { + return [self dataTaskWithRequest:request completionHandler:nil]; +} + +- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request + fromFile:(NSURL *)fileURL + completionHandler: + (nullable void (^)(NSData *data, + NSURLResponse *response, + NSError *error))completionHandler { + FIRCLSURLSessionUploadTask *task = [FIRCLSURLSessionUploadTask task]; + + if (completionHandler) { + [task setCompletionHandler:completionHandler]; + [task setInvokesDelegate:NO]; + } + + [self configureTask:task + withRequest:request + block:^(NSMutableURLRequest *mutableRequest) { + // you cannot set up both of these, and we'll be using the stream here + [mutableRequest setHTTPBody:nil]; + [mutableRequest setHTTPBodyStream:[NSInputStream inputStreamWithURL:fileURL]]; + }]; + + return (NSURLSessionUploadTask *)task; +} + +- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request + completionHandler: + (nullable void (^)(NSURL *targetPath, + NSURLResponse *response, + NSError *error))completionHandler { + FIRCLSURLSessionDownloadTask *task = [FIRCLSURLSessionDownloadTask task]; + + if (completionHandler) { + [task setDownloadCompletionHandler:completionHandler]; + [task setInvokesDelegate:NO]; + } + + [self configureTask:task withRequest:request block:nil]; + + return (NSURLSessionDownloadTask *)task; +} + +#pragma mark FIRCLSURLSessionTaskDelegate +- (NSURLRequest *)task:(FIRCLSURLSessionTask *)task + willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request { + // just accept the proposed redirection + return request; +} + +- (void)task:(FIRCLSURLSessionTask *)task didCompleteWithError:(NSError *)error { + if (![self shouldInvokeDelegateSelector:@selector(URLSession:task:didCompleteWithError:) + forTask:task]) { + [self removeTaskFromSet:task]; + return; + } + + [_delegateQueue addOperationWithBlock:^{ + [DELEGATE URLSession:(NSURLSession *)self + task:(NSURLSessionTask *)task + didCompleteWithError:error]; + + // Note that you *cannot* clean up here, because this method could be run asynchronously with + // the delegate methods that care about the state of the task + [self removeTaskFromSet:task]; + }]; +} + +#pragma mark FIRCLSURLSessionDataTask +- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response { + if (![self shouldInvokeDelegateSelector:@selector + (URLSession:dataTask:didReceiveResponse:completionHandler:) + forTask:task]) { + return; + } + + [_delegateQueue addOperationWithBlock:^{ + [DELEGATE URLSession:(NSURLSession *)self + dataTask:(NSURLSessionDataTask *)task + didReceiveResponse:response + completionHandler:^(NSURLSessionResponseDisposition disposition){ + // nothing to do here + }]; + }]; +} + +- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveData:(NSData *)data { + if (![self shouldInvokeDelegateSelector:@selector(URLSession:dataTask:didReceiveData:) + forTask:task]) { + return; + } + + [_delegateQueue addOperationWithBlock:^{ + [DELEGATE URLSession:(NSURLSession *)self + dataTask:(NSURLSessionDataTask *)task + didReceiveData:data]; + }]; +} + +#pragma mark FIRCLSURLSessionDownloadDelegate +- (void)downloadTask:(FIRCLSURLSessionDownloadTask *)task didFinishDownloadingToURL:(NSURL *)url { + if (![self shouldInvokeDelegateSelector:@selector(URLSession: + downloadTask:didFinishDownloadingToURL:) + forTask:task]) { + // We have to be certain that we cleanup only once the delegate no longer cares about the state + // of the task being changed. In the case of download, this is either after the delegate method + // has been invoked, or here, if the delegate doesn't care. + [task cleanup]; + return; + } + + [_delegateQueue addOperationWithBlock:^{ + [DELEGATE URLSession:(NSURLSession *)self + downloadTask:(NSURLSessionDownloadTask *)task + didFinishDownloadingToURL:url]; + + // Cleanup for the download tasks is a little complex. As long as we do it only after + // the delegate has been informed of the completed download, we are ok. + [task cleanup]; + }]; +} + +@end + +#else + +INJECT_STRIP_SYMBOL(clsurlsession) + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionAvailability.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionAvailability.h new file mode 100644 index 000000000..9f3ead01b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionAvailability.h @@ -0,0 +1,28 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#define FIRCLSURLSESSION_REQUIRED (!TARGET_OS_WATCH && !TARGET_OS_TV) + +// These macros generate a function to force a symbol for the containing .o, to work around an issue +// where strip will not strip debug information without a symbol to strip. +#define CONCAT_EXPANDED(a, b) a##b +#define CONCAT(a, b) CONCAT_EXPANDED(a, b) +#define DUMMY_FUNCTION_NAME(x) CONCAT(fircls_strip_this_, x) +#define INJECT_STRIP_SYMBOL(x) \ + void DUMMY_FUNCTION_NAME(x)(void) { \ + } diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.h new file mode 100644 index 000000000..bda0f7083 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.h @@ -0,0 +1,42 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import + +@interface FIRCLSURLSessionConfiguration : NSObject { + NSDictionary *_additionalHeaders; + NSURLCache *_URLCache; + NSHTTPCookieAcceptPolicy _cookiePolicy; +} + ++ (NSURLSessionConfiguration *)defaultSessionConfiguration; ++ (NSURLSessionConfiguration *)ephemeralSessionConfiguration; ++ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier; ++ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier; + +@property(nonatomic, copy) NSDictionary *HTTPAdditionalHeaders; +@property(nonatomic, retain) NSURLCache *URLCache; +@property(nonatomic, assign) NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy; +@property(nonatomic, assign) BOOL sessionSendsLaunchEvents; +@property(nonatomic, assign) NSTimeInterval timeoutIntervalForRequest; +@property(nonatomic, assign) NSTimeInterval timeoutIntervalForResource; +@property(nonatomic, assign) BOOL allowsCellularAccess; + +@end + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.m new file mode 100644 index 000000000..177e7a6bf --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.m @@ -0,0 +1,92 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#import "FIRCLSURLSession.h" + +#if FIRCLSURLSESSION_REQUIRED +#import "FIRCLSURLSessionConfiguration.h" + +@implementation FIRCLSURLSessionConfiguration + +@synthesize URLCache = _URLCache; +@synthesize HTTPAdditionalHeaders = _additionalHeaders; +@synthesize HTTPCookieAcceptPolicy = _cookiePolicy; + ++ (NSURLSessionConfiguration *)defaultSessionConfiguration { + if ([FIRCLSURLSession NSURLSessionShouldBeUsed]) { + return [NSURLSessionConfiguration defaultSessionConfiguration]; + } + +#if __has_feature(objc_arc) + return [self new]; +#else + return [[self new] autorelease]; +#endif +} + ++ (NSURLSessionConfiguration *)ephemeralSessionConfiguration { + if ([FIRCLSURLSession NSURLSessionShouldBeUsed]) { + return [NSURLSessionConfiguration ephemeralSessionConfiguration]; + } + +#if __has_feature(objc_arc) + return [self new]; +#else + return [[self new] autorelease]; +#endif +} + ++ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier { + return [self backgroundSessionConfigurationWithIdentifier:identifier]; +} + ++ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier { + if (![FIRCLSURLSession NSURLSessionShouldBeUsed]) { + return nil; + } + + if ([[NSURLSessionConfiguration class] + respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) { + return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier]; + } + + return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier]; +} + +- (id)copyWithZone:(NSZone *)zone { + FIRCLSURLSessionConfiguration *configuration; + + configuration = [FIRCLSURLSessionConfiguration new]; + [configuration setHTTPAdditionalHeaders:[self HTTPAdditionalHeaders]]; + + return configuration; +} + +// This functionality is not supported by the wrapper, so we just stub it out +- (BOOL)sessionSendsLaunchEvents { + return NO; +} + +- (void)setSessionSendsLaunchEvents:(BOOL)sessionSendsLaunchEvents { +} + +@end + +#else + +INJECT_STRIP_SYMBOL(clsurlsessionconfiguration) + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession_PrivateMethods.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession_PrivateMethods.h new file mode 100644 index 000000000..84885e1d6 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession_PrivateMethods.h @@ -0,0 +1,27 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import + +@interface FIRCLSURLSession (PrivateMethods) + +- (void)runOnDelegateQueue:(void (^)(void))block; + +@end + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.h new file mode 100644 index 000000000..22099584e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.h @@ -0,0 +1,32 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import "FIRCLSURLSessionTask.h" + +@interface FIRCLSURLSessionDataTask : FIRCLSURLSessionTask { + void (^_completionHandler)(NSData *data, NSURLResponse *response, NSError *error); + NSURLConnection *_connection; + NSMutableData *_data; + NSString *_taskDescription; +} + +@property(nonatomic, copy) NSString *taskDescription; + +@end + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.m new file mode 100644 index 000000000..be40ab753 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.m @@ -0,0 +1,124 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED +#import "FIRCLSURLSessionDataTask.h" + +#import "FIRCLSURLSessionDataTask_PrivateMethods.h" + +#define DELEGATE ((id)[self delegate]) + +@interface FIRCLSURLSessionDataTask () +@end + +@implementation FIRCLSURLSessionDataTask + +@synthesize connection = _connection; +@synthesize completionHandler = _completionHandler; +@synthesize taskDescription = _taskDescription; + +#if !__has_feature(objc_arc) +- (void)dealloc { + [_connection release]; + [_completionHandler release]; + [_taskDescription release]; + [_data release]; + + [super dealloc]; +} +#endif + +- (void)resume { + dispatch_async([self queue], ^{ + NSURLConnection *connection; + + if ([self connection]) { + return; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + connection = [[NSURLConnection alloc] initWithRequest:[self originalRequest] + delegate:self + startImmediately:NO]; +#pragma clang diagnostic pop + + [self setConnection:connection]; + + // bummer we have to do this on a runloop, but other mechanisms require iOS 5 or 10.7 + [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; +#if !__has_feature(objc_arc) + [connection release]; +#endif + [connection start]; + }); +} + +- (void)complete { + // call completion handler first + if (_completionHandler) { + // this should go to another queue + _completionHandler(_data, [self response], [self error]); + } + + // and then finally, call the session delegate completion + [DELEGATE task:self didCompleteWithError:[self error]]; +} + +- (void)cancel { + [self.connection cancel]; +} + +#pragma mark NSURLConnectionDelegate +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { + dispatch_async([self queue], ^{ + [DELEGATE task:self didReceiveResponse:response]; + + [self setResponse:response]; + }); +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { + dispatch_async([self queue], ^{ + if (!self->_data) { + self->_data = [NSMutableData new]; + } + + [self->_data appendData:data]; + [DELEGATE task:self didReceiveData:data]; + }); +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { + dispatch_async([self queue], ^{ + [self setError:error]; + [self complete]; + }); +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection { + dispatch_async([self queue], ^{ + [self complete]; + }); +} + +@end + +#else + +INJECT_STRIP_SYMBOL(clsurlsessiondatatask) + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask_PrivateMethods.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask_PrivateMethods.h new file mode 100644 index 000000000..f85a3776f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask_PrivateMethods.h @@ -0,0 +1,43 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import + +#import "FIRCLSURLSessionTask_PrivateMethods.h" + +@protocol FIRCLSURLSessionDataDelegate; + +@interface FIRCLSURLSessionDataTask () + +@property(nonatomic, retain) NSURLConnection *connection; +@property(nonatomic, copy) void (^completionHandler) + (NSData *data, NSURLResponse *response, NSError *error); + +- (void)complete; + +@end + +@protocol FIRCLSURLSessionDataDelegate +@required + +- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response; +- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveData:(NSData *)data; + +@end + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.h new file mode 100644 index 000000000..314180f7d --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.h @@ -0,0 +1,31 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import "FIRCLSURLSessionDataTask.h" + +@protocol FIRCLSURLSessionDownloadDelegate; + +@interface FIRCLSURLSessionDownloadTask : FIRCLSURLSessionDataTask { + void (^_downloadCompletionHandler)(NSURL *targetPath, NSURLResponse *response, NSError *error); + NSOutputStream *_outputStream; + NSURL *_targetURL; +} + +@end + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.m new file mode 100644 index 000000000..a0c9b2a84 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.m @@ -0,0 +1,157 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED +#import "FIRCLSURLSessionDownloadTask.h" + +#import "FIRCLSURLSessionDownloadTask_PrivateMethods.h" + +#define DELEGATE ((id)[self delegate]) + +@interface FIRCLSURLSessionDownloadTask () +@end + +@implementation FIRCLSURLSessionDownloadTask + +@synthesize downloadCompletionHandler = _downloadCompletionHandler; + +- (id)init { + self = [super init]; + if (!self) { + return nil; + } + +#if __has_feature(objc_arc) + _targetURL = [self temporaryFileURL]; + _outputStream = [NSOutputStream outputStreamWithURL:_targetURL append:NO]; +#else + _targetURL = [[self temporaryFileURL] retain]; + _outputStream = [[NSOutputStream outputStreamWithURL:_targetURL append:NO] retain]; +#endif + + [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + [_outputStream setDelegate:self]; + + return self; +} + +#if !__has_feature(objc_arc) +- (void)dealloc { + [_downloadCompletionHandler release]; + [_targetURL release]; + [_outputStream release]; + + [super dealloc]; +} +#else +- (void)dealloc { + [_outputStream close]; + _outputStream.delegate = nil; +} +#endif + +- (NSURL *)temporaryFileURL { + NSString *tmpPath; + + tmpPath = [NSTemporaryDirectory() + stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]; + + // TODO: make this actually unique + return [NSURL fileURLWithPath:tmpPath isDirectory:NO]; +} + +- (void)cleanup { + // now, remove the temporary file + [[NSFileManager defaultManager] removeItemAtURL:_targetURL error:nil]; +} + +- (void)complete { + // This is an override of FIRCLSURLSessionDataTask's cleanup method + + // call completion handler first + if (_downloadCompletionHandler) { + _downloadCompletionHandler(_targetURL, [self response], [self error]); + } + + // followed by the session delegate, if there was no error + if (![self error]) { + [DELEGATE downloadTask:self didFinishDownloadingToURL:_targetURL]; + } + + // and then finally, call the session delegate completion + [DELEGATE task:self didCompleteWithError:[self error]]; +} + +- (void)writeDataToStream:(NSData *)data { + // open the stream first + if ([_outputStream streamStatus] == NSStreamStatusNotOpen) { + [_outputStream open]; + } + + if ([data respondsToSelector:@selector(enumerateByteRangesUsingBlock:)]) { + [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { + [self->_outputStream write:bytes maxLength:byteRange.length]; + }]; + + return; + } + + // fall back to the less-efficient mechanism for older OSes + [_outputStream write:[data bytes] maxLength:[data length]]; +} + +#pragma mark NSURLConnectionDelegate +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { + dispatch_async([self queue], ^{ + [self writeDataToStream:data]; + }); +} + +- (void)completeForError { + dispatch_async([self queue], ^{ + [self->_outputStream close]; + [self->_connection cancel]; + if (![self error]) { + [self setError:[NSError errorWithDomain:@"FIRCLSURLSessionDownloadTaskError" + code:-1 + userInfo:nil]]; + } + [self complete]; + }); +} + +#pragma mark NSStreamDelegate +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { + switch (eventCode) { + case NSStreamEventHasSpaceAvailable: + break; + case NSStreamEventErrorOccurred: + [self completeForError]; + break; + case NSStreamEventEndEncountered: + break; + default: + break; + } +} + +@end + +#else + +INJECT_STRIP_SYMBOL(clsurlsessiondownloadtask) + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask_PrivateMethods.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask_PrivateMethods.h new file mode 100644 index 000000000..7e8ee9d9f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask_PrivateMethods.h @@ -0,0 +1,39 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import + +#import "FIRCLSURLSessionDataTask_PrivateMethods.h" + +@protocol FIRCLSURLSessionDownloadDelegate; + +@interface FIRCLSURLSessionDownloadTask () + +@property(nonatomic, copy) void (^downloadCompletionHandler) + (NSURL *targetPath, NSURLResponse *response, NSError *error); + +@end + +@protocol FIRCLSURLSessionDownloadDelegate +@required + +- (void)downloadTask:(FIRCLSURLSessionDownloadTask *)task didFinishDownloadingToURL:(NSURL *)url; + +@end + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.h new file mode 100644 index 000000000..92316464d --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.h @@ -0,0 +1,38 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@protocol FIRCLSURLSessionTaskDelegate; + +@interface FIRCLSURLSessionTask : NSObject { + __unsafe_unretained id _delegate; + + NSURLRequest* _originalRequest; + NSURLRequest* _currentRequest; + NSURLResponse* _response; + NSError* _error; + dispatch_queue_t _queue; + BOOL _invokesDelegate; +} + +@property(nonatomic, readonly, copy) NSURLRequest* originalRequest; +@property(nonatomic, readonly, copy) NSURLRequest* currentRequest; +@property(nonatomic, readonly, copy) NSURLResponse* response; + +@property(nonatomic, readonly, copy) NSError* error; + +- (void)resume; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.m new file mode 100644 index 000000000..8eba2c185 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.m @@ -0,0 +1,95 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED +#import "FIRCLSURLSession.h" + +#import "FIRCLSURLSessionTask.h" + +#import "FIRCLSURLSessionTask_PrivateMethods.h" +#import "FIRCLSURLSession_PrivateMethods.h" + +@implementation FIRCLSURLSessionTask + ++ (instancetype)task { +#if __has_feature(objc_arc) + return [[self class] new]; + +#else + return [[[self class] new] autorelease]; +#endif +} + +@synthesize currentRequest = _currentRequest; +@synthesize originalRequest = _originalRequest; +@synthesize response = _response; +@synthesize error = _error; +@synthesize queue = _queue; +@synthesize invokesDelegate = _invokesDelegate; + +- (instancetype)init { + self = [super init]; + if (!self) { + return self; + } + + _queue = dispatch_queue_create("com.crashlytics.URLSessionTask", 0); + + _invokesDelegate = YES; + + return self; +} + +#if !__has_feature(objc_arc) +- (void)dealloc { + [_originalRequest release]; + [_currentRequest release]; + [_response release]; + [_error release]; + +#if !OS_OBJECT_USE_OBJC + dispatch_release(_queue); +#endif + + [super dealloc]; +} +#endif + +- (void)start { +#if DEBUG + assert(0 && "Must be implemented by FIRCLSURLSessionTask subclasses"); +#endif +} + +- (void)cancel { +#if DEBUG + assert(0 && "Must be implemented by FIRCLSURLSessionTask subclasses"); +#endif +} + +- (void)resume { +} + +- (void)cleanup { +} + +@end + +#else + +INJECT_STRIP_SYMBOL(clsurlsessiontask) + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask_PrivateMethods.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask_PrivateMethods.h new file mode 100644 index 000000000..784b3a3ce --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask_PrivateMethods.h @@ -0,0 +1,55 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import + +@protocol FIRCLSURLSessionTaskDelegate; + +@interface FIRCLSURLSessionTask () + ++ (instancetype)task; + +@property(nonatomic, assign) id delegate; + +@property(nonatomic, copy) NSURLRequest *originalRequest; +@property(nonatomic, copy) NSURLRequest *currentRequest; +@property(nonatomic, copy) NSURLResponse *response; + +@property(nonatomic, readonly) dispatch_queue_t queue; +@property(nonatomic, assign) BOOL invokesDelegate; + +- (void)cancel; + +@property(nonatomic, copy) NSError *error; + +- (void)cleanup; + +@end + +@protocol FIRCLSURLSessionTaskDelegate +@required + +- (NSURLRequest *)task:(FIRCLSURLSessionTask *)task + willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request; + +- (void)task:(FIRCLSURLSessionTask *)task didCompleteWithError:(NSError *)error; + +@end + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.h new file mode 100644 index 000000000..6c5ed8b4b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.h @@ -0,0 +1,25 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED + +#import "FIRCLSURLSessionDataTask.h" + +@interface FIRCLSURLSessionUploadTask : FIRCLSURLSessionDataTask + +@end + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.m new file mode 100644 index 000000000..df3df8382 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.m @@ -0,0 +1,28 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLSessionAvailability.h" + +#if FIRCLSURLSESSION_REQUIRED +#import "FIRCLSURLSessionUploadTask.h" + +@implementation FIRCLSURLSessionUploadTask + +@end + +#else + +INJECT_STRIP_SYMBOL(clsurlsessionuploadtask) + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.h new file mode 100644 index 000000000..6df153268 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.h @@ -0,0 +1,42 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +extern NSString *const FIRCLSUserDefaultsPathComponent; + +@interface FIRCLSUserDefaults : NSObject + ++ (instancetype)standardUserDefaults; + +- (id)objectForKey:(NSString *)key; +- (NSString *)stringForKey:(NSString *)key; +- (BOOL)boolForKey:(NSString *)key; +- (NSInteger)integerForKey:(NSString *)key; + +- (void)setObject:(id)object forKey:(NSString *)key; +- (void)setString:(NSString *)string forKey:(NSString *)key; +- (void)setBool:(BOOL)boolean forKey:(NSString *)key; +- (void)setInteger:(NSInteger)integer forKey:(NSString *)key; + +- (void)removeObjectForKey:(NSString *)key; +- (void)removeAllObjects; + +- (NSDictionary *)dictionaryRepresentation; + +- (void)migrateFromNSUserDefaults:(NSArray *)keysToMigrate; +- (id)objectForKeyByMigratingFromNSUserDefaults:(NSString *)keyToMigrateOrNil; +- (void)synchronize; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.m new file mode 100644 index 000000000..244dbe169 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.m @@ -0,0 +1,372 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSUserDefaults.h" + +#import "FIRCLSApplication.h" +#import "FIRCLSLogger.h" + +#define CLS_USER_DEFAULTS_SERIAL_DISPATCH_QUEUE "com.crashlytics.CLSUserDefaults.access" +#define CLS_USER_DEFAULTS_SYNC_QUEUE "com.crashlytics.CLSUserDefaults.io" + +#define CLS_TARGET_CAN_WRITE_TO_DISK !TARGET_OS_TV + +// These values are required to stay the same between versions of the SDK so +// that when end users upgrade, their crashlytics data is still saved on disk. +#if !CLS_TARGET_CAN_WRITE_TO_DISK +static NSString *const FIRCLSNSUserDefaultsDataDictionaryKey = + @"com.crashlytics.CLSUserDefaults.user-default-key.data-dictionary"; +#endif + +NSString *const FIRCLSUserDefaultsPathComponent = @"CLSUserDefaults"; + +/** + * This class is an isolated re-implementation of NSUserDefaults which isolates our storage + * from that of our customers. This solves a number of issues we have seen in production, firstly + * that customers often delete or clear NSUserDefaults, unintentionally deleting our data. + * Further, we have seen thread safety issues in production with NSUserDefaults, as well as a number + * of bugs related to accessing NSUserDefaults before the device has been unlocked due to the + * NSFileProtection of NSUserDefaults. + */ +@interface FIRCLSUserDefaults () +@property(nonatomic, readwrite) BOOL synchronizeWroteToDisk; +#if CLS_TARGET_CAN_WRITE_TO_DISK +@property(nonatomic, copy, readonly) NSURL *directoryURL; +@property(nonatomic, copy, readonly) NSURL *fileURL; +#endif +@property(nonatomic, copy, readonly) + NSDictionary *persistedDataDictionary; // May only be safely accessed on the DictionaryQueue +@property(nonatomic, copy, readonly) + NSMutableDictionary *dataDictionary; // May only be safely accessed on the DictionaryQueue +@property(nonatomic, readonly) dispatch_queue_t + serialDictionaryQueue; // The queue on which all access to the dataDictionary occurs. +@property(nonatomic, readonly) + dispatch_queue_t synchronizationQueue; // The queue on which all disk access occurs. + +@end + +@implementation FIRCLSUserDefaults + +#pragma mark - singleton + ++ (instancetype)standardUserDefaults { + static FIRCLSUserDefaults *standardUserDefaults = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + standardUserDefaults = [[super allocWithZone:NULL] init]; + }); + + return standardUserDefaults; +} + +- (id)copyWithZone:(NSZone *)zone { + return self; +} + +- (id)init { + if (self = [super init]) { + _serialDictionaryQueue = + dispatch_queue_create(CLS_USER_DEFAULTS_SERIAL_DISPATCH_QUEUE, DISPATCH_QUEUE_SERIAL); + _synchronizationQueue = + dispatch_queue_create(CLS_USER_DEFAULTS_SYNC_QUEUE, DISPATCH_QUEUE_SERIAL); + + dispatch_sync(self.serialDictionaryQueue, ^{ +#if CLS_TARGET_CAN_WRITE_TO_DISK + self->_directoryURL = [self generateDirectoryURL]; + self->_fileURL = [[self->_directoryURL + URLByAppendingPathComponent:FIRCLSUserDefaultsPathComponent + isDirectory:NO] URLByAppendingPathExtension:@"plist"]; +#endif + self->_persistedDataDictionary = [self loadDefaults]; + if (!self->_persistedDataDictionary) { + self->_persistedDataDictionary = [NSDictionary dictionary]; + } + self->_dataDictionary = [self->_persistedDataDictionary mutableCopy]; + }); + } + return self; +} + +- (NSURL *)generateDirectoryURL { + NSURL *directoryBaseURL = + [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory + inDomains:NSUserDomainMask] lastObject]; + NSString *hostAppBundleIdentifier = [self getEscapedAppBundleIdentifier]; + return [self generateDirectoryURLForBaseURL:directoryBaseURL + hostAppBundleIdentifier:hostAppBundleIdentifier]; +} + +- (NSURL *)generateDirectoryURLForBaseURL:(NSURL *)directoryBaseURL + hostAppBundleIdentifier:(NSString *)hostAppBundleIdentifier { + NSURL *directoryURL = directoryBaseURL; + // On iOS NSApplicationSupportDirectory is contained in the app's bundle. On OSX, it is not (it is + // ~/Library/Application Support/). On OSX we create a directory + // ~/Library/Application Support//com.crashlytics/ for storing files. + // Mac App Store review process requires files to be written to + // ~/Library/Application Support//, + // so ~/Library/Application Support/com.crashlytics// cannot be used. +#if !TARGET_OS_SIMULATOR && !TARGET_OS_EMBEDDED + if (hostAppBundleIdentifier) { + directoryURL = [directoryURL URLByAppendingPathComponent:hostAppBundleIdentifier]; + } +#endif + directoryURL = [directoryURL URLByAppendingPathComponent:@"com.crashlytics"]; + return directoryURL; +} + +- (NSString *)getEscapedAppBundleIdentifier { + return FIRCLSApplicationGetBundleIdentifier(); +} + +#pragma mark - fetch object + +- (id)objectForKey:(NSString *)key { + __block id result; + + dispatch_sync(self.serialDictionaryQueue, ^{ + result = [self->_dataDictionary objectForKey:key]; + }); + + return result; +} + +- (NSString *)stringForKey:(NSString *)key { + id result = [self objectForKey:key]; + + if (result != nil && [result isKindOfClass:[NSString class]]) { + return (NSString *)result; + } else { + return nil; + } +} + +- (BOOL)boolForKey:(NSString *)key { + id result = [self objectForKey:key]; + if (result != nil && [result isKindOfClass:[NSNumber class]]) { + return [(NSNumber *)result boolValue]; + } else { + return NO; + } +} + +// Defaults to 0 +- (NSInteger)integerForKey:(NSString *)key { + id result = [self objectForKey:key]; + if (result && [result isKindOfClass:[NSNumber class]]) { + return [(NSNumber *)result integerValue]; + } else { + return 0; + } +} + +#pragma mark - set object + +- (void)setObject:(id)object forKey:(NSString *)key { + dispatch_sync(self.serialDictionaryQueue, ^{ + [self->_dataDictionary setValue:object forKey:key]; + }); +} + +- (void)setString:(NSString *)string forKey:(NSString *)key { + [self setObject:string forKey:key]; +} + +- (void)setBool:(BOOL)boolean forKey:(NSString *)key { + [self setObject:[NSNumber numberWithBool:boolean] forKey:key]; +} + +- (void)setInteger:(NSInteger)integer forKey:(NSString *)key { + [self setObject:[NSNumber numberWithInteger:integer] forKey:key]; +} + +#pragma mark - removing objects + +- (void)removeObjectForKey:(NSString *)key { + dispatch_sync(self.serialDictionaryQueue, ^{ + [self->_dataDictionary removeObjectForKey:key]; + }); +} + +- (void)removeAllObjects { + dispatch_sync(self.serialDictionaryQueue, ^{ + [self->_dataDictionary removeAllObjects]; + }); +} + +#pragma mark - dictionary representation + +- (NSDictionary *)dictionaryRepresentation { + __block NSDictionary *result; + + dispatch_sync(self.serialDictionaryQueue, ^{ + result = [self->_dataDictionary copy]; + }); + + return result; +} + +#pragma mark - synchronization + +- (void)synchronize { + __block BOOL dirty = NO; + + // only write to the disk if the dictionaries have changed + dispatch_sync(self.serialDictionaryQueue, ^{ + dirty = ![self->_persistedDataDictionary isEqualToDictionary:self->_dataDictionary]; + }); + + _synchronizeWroteToDisk = dirty; + if (!dirty) { + return; + } + + NSDictionary *state = [self dictionaryRepresentation]; + dispatch_sync(self.synchronizationQueue, ^{ +#if CLS_TARGET_CAN_WRITE_TO_DISK + BOOL isDirectory = NO; + BOOL pathExists = [[NSFileManager defaultManager] fileExistsAtPath:[self->_directoryURL path] + isDirectory:&isDirectory]; + + if (!pathExists) { + NSError *error; + if (![[NSFileManager defaultManager] createDirectoryAtURL:self->_directoryURL + withIntermediateDirectories:YES + attributes:nil + error:&error]) { + FIRCLSErrorLog(@"Failed to create directory with error: %@", error); + } + } + + if (![state writeToURL:self->_fileURL atomically:YES]) { + FIRCLSErrorLog(@"Unable to open file for writing at path %@", [self->_fileURL path]); + } else { +#if TARGET_OS_IOS + // We disable NSFileProtection on our file in order to allow us to access it even if the + // device is locked. + NSError *error; + if (![[NSFileManager defaultManager] + setAttributes:@{NSFileProtectionKey : NSFileProtectionNone} + ofItemAtPath:[self->_fileURL path] + error:&error]) { + FIRCLSErrorLog(@"Error setting NSFileProtection: %@", error); + } +#endif + } +#else + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setObject:state forKey:FIRCLSNSUserDefaultsDataDictionaryKey]; + [defaults synchronize]; +#endif + }); + + dispatch_sync(self.serialDictionaryQueue, ^{ + self->_persistedDataDictionary = [self->_dataDictionary copy]; + }); +} + +- (NSDictionary *)loadDefaults { + __block NSDictionary *state = nil; + dispatch_sync(self.synchronizationQueue, ^{ +#if CLS_TARGET_CAN_WRITE_TO_DISK + BOOL isDirectory = NO; + BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[self->_fileURL path] + isDirectory:&isDirectory]; + + if (fileExists && !isDirectory) { + state = [NSDictionary dictionaryWithContentsOfURL:self->_fileURL]; + if (nil == state) { + FIRCLSErrorLog(@"Failed to read existing UserDefaults file"); + } + } else if (!fileExists) { + // No file found. This is expected on first launch. + } else if (fileExists && isDirectory) { + FIRCLSErrorLog(@"Found directory where file expected. Removing conflicting directory"); + + NSError *error; + if (![[NSFileManager defaultManager] removeItemAtURL:self->_fileURL error:&error]) { + FIRCLSErrorLog(@"Error removing conflicting directory: %@", error); + } + } +#else + state = [[NSUserDefaults standardUserDefaults] dictionaryForKey:FIRCLSNSUserDefaultsDataDictionaryKey]; +#endif + }); + return state; +} + +#pragma mark - migration + +// This method migrates all keys specified from NSUserDefaults to FIRCLSUserDefaults +// To do so, we copy all known key-value pairs into FIRCLSUserDefaults, synchronize it, then +// remove the keys from NSUserDefaults and synchronize it. +- (void)migrateFromNSUserDefaults:(NSArray *)keysToMigrate { + BOOL didFindKeys = NO; + + // First, copy all of the keysToMigrate which are stored NSUserDefaults + for (NSString *key in keysToMigrate) { + id oldValue = [[NSUserDefaults standardUserDefaults] objectForKey:(NSString *)key]; + if (nil != oldValue) { + didFindKeys = YES; + [self setObject:oldValue forKey:key]; + } + } + + if (didFindKeys) { + // First synchronize FIRCLSUserDefaults such that all keysToMigrate in NSUserDefaults are stored + // in FIRCLSUserDefaults. At this point, data is duplicated. + [[FIRCLSUserDefaults standardUserDefaults] synchronize]; + + for (NSString *key in keysToMigrate) { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:(NSString *)key]; + } + + // This should be our last interaction with NSUserDefaults. All data is migrated into + // FIRCLSUserDefaults + [[NSUserDefaults standardUserDefaults] synchronize]; + } +} + +// This method first queries FIRCLSUserDefaults to see if the key exist, and upon failure, +// searches for the key in NSUserDefaults, and migrates it if found. +- (id)objectForKeyByMigratingFromNSUserDefaults:(NSString *)keyToMigrateOrNil { + if (!keyToMigrateOrNil) { + return nil; + } + + id clsUserDefaultsValue = [self objectForKey:keyToMigrateOrNil]; + if (clsUserDefaultsValue != nil) { + return clsUserDefaultsValue; // if the value exists in FIRCLSUserDefaults, return it. + } + + id oldNSUserDefaultsValue = + [[NSUserDefaults standardUserDefaults] objectForKey:keyToMigrateOrNil]; + if (!oldNSUserDefaultsValue) { + return nil; // if the value also does not exist in NSUserDefaults, return nil. + } + + // Otherwise, the key exists in NSUserDefaults. Migrate it to FIRCLSUserDefaults + // and then return the associated value. + + // First store it in FIRCLSUserDefaults so in the event of a crash, data is not lost. + [self setObject:oldNSUserDefaultsValue forKey:keyToMigrateOrNil]; + [[FIRCLSUserDefaults standardUserDefaults] synchronize]; + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:keyToMigrateOrNil]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + return oldNSUserDefaultsValue; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults_private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults_private.h new file mode 100644 index 000000000..775bca24a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults_private.h @@ -0,0 +1,23 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "FIRCLSUserDefaults.h" + +@interface FIRCLSUserDefaults (Private) +- (BOOL)synchronizeWroteToDisk; +- (NSDictionary *)loadDefaults; +- (NSURL *)generateDirectoryURLForBaseURL:(NSURL *)directoryBaseURL + hostAppBundleIdentifier:(NSString *)hostAppBundleIdentifer; +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCrashlytics.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCrashlytics.m new file mode 100644 index 000000000..16e7a2f44 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCrashlytics.m @@ -0,0 +1,318 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#if __has_include() +#import +#else +#import "FBLPromises.h" +#endif + +#import "FIRCLSApplicationIdentifierModel.h" +#include "FIRCLSCrashedMarkerFile.h" +#import "FIRCLSDataCollectionArbiter.h" +#import "FIRCLSDefines.h" +#include "FIRCLSException.h" +#import "FIRCLSFileManager.h" +#include "FIRCLSGlobals.h" +#import "FIRCLSHost.h" +#include "FIRCLSProfiling.h" +#import "FIRCLSReport_Private.h" +#import "FIRCLSSettings.h" +#import "FIRCLSUserDefaults.h" +#include "FIRCLSUserLogging.h" +#include "FIRCLSUtility.h" + +#import "FIRCLSByteUtility.h" +#import "FIRCLSFABHost.h" +#import "FIRCLSLogger.h" + +#import "FIRCLSReportManager.h" + +#import +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "Interop/Analytics/Public/FIRAnalyticsInterop.h" + +#import +#import + +#if TARGET_OS_IPHONE +#import +#endif + +FIRCLSContext _firclsContext; +dispatch_queue_t _firclsLoggingQueue; +dispatch_queue_t _firclsBinaryImageQueue; +dispatch_queue_t _firclsExceptionQueue; + +static atomic_bool _hasInitializedInstance; + +NSString *const FIRCLSGoogleTransportMappingID = @"1206"; + +/// Empty protocol to register with FirebaseCore's component system. +@protocol FIRCrashlyticsInstanceProvider +@end + +@interface FIRCrashlytics () + +@property(nonatomic) BOOL didPreviouslyCrash; +@property(nonatomic, copy) NSString *googleAppID; +@property(nonatomic) FIRCLSDataCollectionArbiter *dataArbiter; +@property(nonatomic) FIRCLSFileManager *fileManager; +@property(nonatomic) FIRCLSReportManager *reportManager; +@property(nonatomic) GDTCORTransport *googleTransport; + +@end + +@implementation FIRCrashlytics + +#pragma mark - Singleton Support + +- (instancetype)initWithApp:(FIRApp *)app + appInfo:(NSDictionary *)appInfo + installations:(FIRInstallations *)installations + analytics:(id)analytics { + self = [super init]; + + if (self) { + bool expectedCalled = NO; + if (!atomic_compare_exchange_strong(&_hasInitializedInstance, &expectedCalled, YES)) { + FIRCLSErrorLog(@"Cannot instantiate more than one instance of Crashlytics."); + return nil; + } + + FIRCLSProfileMark mark = FIRCLSProfilingStart(); + + NSLog(@"[Firebase/Crashlytics] Version %@", @CLS_SDK_DISPLAY_VERSION); + + FIRCLSDeveloperLog("Crashlytics", @"Running on %@, %@ (%@)", FIRCLSHostModelInfo(), + FIRCLSHostOSDisplayVersion(), FIRCLSHostOSBuildVersion()); + + _googleTransport = [[GDTCORTransport alloc] initWithMappingID:FIRCLSGoogleTransportMappingID + transformers:nil + target:kGDTCORTargetCSH]; + + _fileManager = [[FIRCLSFileManager alloc] init]; + _googleAppID = app.options.googleAppID; + _dataArbiter = [[FIRCLSDataCollectionArbiter alloc] initWithApp:app withAppInfo:appInfo]; + + FIRCLSApplicationIdentifierModel *appModel = [[FIRCLSApplicationIdentifierModel alloc] init]; + FIRCLSSettings *settings = [[FIRCLSSettings alloc] initWithFileManager:_fileManager + appIDModel:appModel]; + + _reportManager = [[FIRCLSReportManager alloc] initWithFileManager:_fileManager + installations:installations + analytics:analytics + googleAppID:_googleAppID + dataArbiter:_dataArbiter + googleTransport:_googleTransport + appIDModel:appModel + settings:settings]; + + // Process did crash during previous execution + NSString *crashedMarkerFileName = [NSString stringWithUTF8String:FIRCLSCrashedMarkerFileName]; + NSString *crashedMarkerFileFullPath = + [[_fileManager rootPath] stringByAppendingPathComponent:crashedMarkerFileName]; + _didPreviouslyCrash = [_fileManager fileExistsAtPath:crashedMarkerFileFullPath]; + + if (_didPreviouslyCrash) { + // Delete the crash file marker in the background ensure start up is as fast as possible + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + [self.fileManager removeItemAtPath:crashedMarkerFileFullPath]; + }); + } + + [[[_reportManager startWithProfilingMark:mark] then:^id _Nullable(NSNumber *_Nullable value) { + if (![value boolValue]) { + FIRCLSErrorLog(@"Crash reporting could not be initialized"); + } + return value; + }] catch:^void(NSError *error) { + FIRCLSErrorLog(@"Crash reporting failed to initialize with error: %@", error); + }]; + } + + return self; +} + ++ (void)load { + [FIRApp registerInternalLibrary:(Class)self + withName:@"firebase-crashlytics" + withVersion:@CLS_SDK_DISPLAY_VERSION]; +} + ++ (NSArray *)componentsToRegister { + FIRDependency *analyticsDep = + [FIRDependency dependencyWithProtocol:@protocol(FIRAnalyticsInterop)]; + + FIRComponentCreationBlock creationBlock = + ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) { + if (!container.app.isDefaultApp) { + FIRCLSErrorLog(@"Crashlytics must be used with the default Firebase app."); + return nil; + } + + id analytics = FIR_COMPONENT(FIRAnalyticsInterop, container); + + FIRInstallations *installations = [FIRInstallations installationsWithApp:container.app]; + + *isCacheable = YES; + + return [[FIRCrashlytics alloc] initWithApp:container.app + appInfo:NSBundle.mainBundle.infoDictionary + installations:installations + analytics:analytics]; + }; + + FIRComponent *component = + [FIRComponent componentWithProtocol:@protocol(FIRCrashlyticsInstanceProvider) + instantiationTiming:FIRInstantiationTimingEagerInDefaultApp + dependencies:@[ analyticsDep ] + creationBlock:creationBlock]; + return @[ component ]; +} + ++ (instancetype)crashlytics { + // The container will return the same instance since isCacheable is set + + FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here. + + // Get the instance from the `FIRApp`'s container. This will create a new instance the + // first time it is called, and since `isCacheable` is set in the component creation + // block, it will return the existing instance on subsequent calls. + id instance = + FIR_COMPONENT(FIRCrashlyticsInstanceProvider, defaultApp.container); + + // In the component creation block, we return an instance of `FIRCrashlytics`. Cast it and + // return it. + return (FIRCrashlytics *)instance; +} + +- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled { + [self.dataArbiter setCrashlyticsCollectionEnabled:enabled]; +} + +- (BOOL)isCrashlyticsCollectionEnabled { + return [self.dataArbiter isCrashlyticsCollectionEnabled]; +} + +#pragma mark - API: didCrashDuringPreviousExecution + +- (BOOL)didCrashDuringPreviousExecution { + return self.didPreviouslyCrash; +} + +- (void)processDidCrashDuringPreviousExecution { + NSString *crashedMarkerFileName = [NSString stringWithUTF8String:FIRCLSCrashedMarkerFileName]; + NSString *crashedMarkerFileFullPath = + [[self.fileManager rootPath] stringByAppendingPathComponent:crashedMarkerFileName]; + self.didPreviouslyCrash = [self.fileManager fileExistsAtPath:crashedMarkerFileFullPath]; + + if (self.didPreviouslyCrash) { + // Delete the crash file marker in the background ensure start up is as fast as possible + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + [self.fileManager removeItemAtPath:crashedMarkerFileFullPath]; + }); + } +} + +#pragma mark - API: Logging +- (void)log:(NSString *)msg { + FIRCLSLog(@"%@", msg); +} + +- (void)logWithFormat:(NSString *)format, ... { + va_list args; + va_start(args, format); + [self logWithFormat:format arguments:args]; + va_end(args); +} + +- (void)logWithFormat:(NSString *)format arguments:(va_list)args { + [self log:[[NSString alloc] initWithFormat:format arguments:args]]; +} + +#pragma mark - API: Accessors + +- (void)checkForUnsentReportsWithCompletion:(void (^)(BOOL))completion { + [[self.reportManager checkForUnsentReports] then:^id _Nullable(NSNumber *_Nullable value) { + completion([value boolValue]); + return nil; + }]; +} + +- (void)sendUnsentReports { + [self.reportManager sendUnsentReports]; +} + +- (void)deleteUnsentReports { + [self.reportManager deleteUnsentReports]; +} + +#pragma mark - API: setUserID +- (void)setUserID:(NSString *)userID { + FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSUserIdentifierKey, userID); +} + +#pragma mark - API: setCustomValue + +- (void)setCustomValue:(id)value forKey:(NSString *)key { + FIRCLSUserLoggingRecordUserKeyValue(key, value); +} + +#pragma mark - API: Development Platform +// These two methods are depercated by our own API, so +// its ok to implement them +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" ++ (void)setDevelopmentPlatformName:(NSString *)name { + [[self crashlytics] setDevelopmentPlatformName:name]; +} + ++ (void)setDevelopmentPlatformVersion:(NSString *)version { + [[self crashlytics] setDevelopmentPlatformVersion:version]; +} +#pragma clang diagnostic pop + +- (NSString *)developmentPlatformName { + FIRCLSErrorLog(@"developmentPlatformName is write-only"); + return nil; +} + +- (void)setDevelopmentPlatformName:(NSString *)developmentPlatformName { + FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDevelopmentPlatformNameKey, + developmentPlatformName); +} + +- (NSString *)developmentPlatformVersion { + FIRCLSErrorLog(@"developmentPlatformVersion is write-only"); + return nil; +} + +- (void)setDevelopmentPlatformVersion:(NSString *)developmentPlatformVersion { + FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDevelopmentPlatformVersionKey, + developmentPlatformVersion); +} + +#pragma mark - API: Errors and Exceptions +- (void)recordError:(NSError *)error { + FIRCLSUserLoggingRecordError(error, nil); +} + +- (void)recordExceptionModel:(FIRExceptionModel *)exceptionModel { + FIRCLSExceptionRecordModel(exceptionModel); +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRExceptionModel.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRExceptionModel.m new file mode 100644 index 000000000..f0c4697a8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRExceptionModel.m @@ -0,0 +1,42 @@ +// Copyright 2020 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRExceptionModel.h" + +@interface FIRExceptionModel () + +@property(nonatomic, copy) NSString *name; +@property(nonatomic, copy) NSString *reason; + +@end + +@implementation FIRExceptionModel + +- (instancetype)initWithName:(NSString *)name reason:(NSString *)reason { + self = [super init]; + if (!self) { + return nil; + } + + _name = [name copy]; + _reason = [reason copy]; + + return self; +} + ++ (instancetype)exceptionModelWithName:(NSString *)name reason:(NSString *)reason { + return [[FIRExceptionModel alloc] initWithName:name reason:reason]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRStackFrame.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRStackFrame.m new file mode 100644 index 000000000..304491465 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRStackFrame.m @@ -0,0 +1,94 @@ +// Copyright 2020 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRStackFrame_Private.h" + +@interface FIRStackFrame () + +@property(nonatomic, copy, nullable) NSString *symbol; +@property(nonatomic, copy, nullable) NSString *rawSymbol; +@property(nonatomic, copy, nullable) NSString *library; +@property(nonatomic, copy, nullable) NSString *fileName; +@property(nonatomic, assign) uint32_t lineNumber; +@property(nonatomic, assign) uint64_t offset; +@property(nonatomic, assign) uint64_t address; + +@property(nonatomic, assign) BOOL isSymbolicated; + +@end + +@implementation FIRStackFrame + +#pragma mark - Public Methods + +- (instancetype)initWithSymbol:(NSString *)symbol file:(NSString *)file line:(NSInteger)line { + self = [super init]; + if (!self) { + return nil; + } + + _symbol = [symbol copy]; + _fileName = [file copy]; + _lineNumber = (uint32_t)line; + + _isSymbolicated = true; + + return self; +} + ++ (instancetype)stackFrameWithSymbol:(NSString *)symbol file:(NSString *)file line:(NSInteger)line { + return [[FIRStackFrame alloc] initWithSymbol:symbol file:file line:line]; +} + +#pragma mark - Internal Methods + ++ (instancetype)stackFrame { + return [[self alloc] init]; +} + ++ (instancetype)stackFrameWithAddress:(NSUInteger)address { + FIRStackFrame *frame = [self stackFrame]; + + [frame setAddress:address]; + + return frame; +} + ++ (instancetype)stackFrameWithSymbol:(NSString *)symbol { + FIRStackFrame *frame = [self stackFrame]; + + frame.symbol = symbol; + frame.rawSymbol = symbol; + + return frame; +} + +#pragma mark - Overrides + +- (NSString *)description { + if (self.isSymbolicated) { + return [NSString + stringWithFormat:@"{%@ - %@:%u}", [self fileName], [self symbol], [self lineNumber]]; + } + + if (self.fileName) { + return [NSString stringWithFormat:@"{[0x%llx] %@ - %@:%u}", [self address], [self fileName], + [self symbol], [self lineNumber]]; + } + + return [NSString + stringWithFormat:@"{[0x%llx + %u] %@}", [self address], [self lineNumber], [self symbol]]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.h new file mode 100644 index 000000000..61dac77c3 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.h @@ -0,0 +1,73 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#ifdef __OBJC__ +#import +@class FIRStackFrame; +@class FIRExceptionModel; +#endif + +#define CLS_EXCEPTION_STRING_LENGTH_MAX (1024 * 16) + +typedef enum { + FIRCLSExceptionTypeUnknown = 0, + FIRCLSExceptionTypeObjectiveC = 1, + FIRCLSExceptionTypeCpp = 2, + // 3 was FIRCLSExceptionTypeJavascript + // Keeping these numbers the same just to be safe + FIRCLSExceptionTypeCustom = 4 +} FIRCLSExceptionType; + +typedef struct { + const char* path; + + void (*originalTerminateHandler)(void); + +#if !TARGET_OS_IPHONE + void* originalNSApplicationReportException; +#endif + + uint32_t maxCustomExceptions; +} FIRCLSExceptionReadOnlyContext; + +typedef struct { + uint32_t customExceptionCount; +} FIRCLSExceptionWritableContext; + +__BEGIN_DECLS + +void FIRCLSExceptionInitialize(FIRCLSExceptionReadOnlyContext* roContext, + FIRCLSExceptionWritableContext* rwContext, + void* delegate); +void FIRCLSExceptionCheckHandlers(void* delegate); + +void FIRCLSExceptionRaiseTestObjCException(void) __attribute((noreturn)); +void FIRCLSExceptionRaiseTestCppException(void) __attribute((noreturn)); + +#ifdef __OBJC__ +void FIRCLSExceptionRecordModel(FIRExceptionModel* exceptionModel); +void FIRCLSExceptionRecordNSException(NSException* exception); +void FIRCLSExceptionRecord(FIRCLSExceptionType type, + const char* name, + const char* reason, + NSArray* frames, + BOOL attemptDelivery); +#endif + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm new file mode 100644 index 000000000..6f2099045 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm @@ -0,0 +1,410 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#include "FIRCLSException.h" + +#import "FIRExceptionModel_Private.h" +#import "FIRStackFrame_Private.h" + +#include "FIRCLSApplication.h" +#include "FIRCLSFile.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSHandler.h" +#import "FIRCLSLogger.h" +#include "FIRCLSProcess.h" +#import "FIRCLSUserLogging.h" +#import "FIRCLSUtility.h" + +#include "FIRCLSDemangleOperation.h" +#import "FIRCLSReportManager_Private.h" + +// C++/Objective-C exception handling +#include +#include +#include +#include + +#if !TARGET_OS_IPHONE +#import +#import +#endif + +#pragma mark Prototypes +static void FIRCLSTerminateHandler(void); +#if !TARGET_OS_IPHONE +void FIRCLSNSApplicationReportException(id self, SEL cmd, NSException *exception); + +typedef void (*NSApplicationReportExceptionFunction)(id, SEL, NSException *); + +static BOOL FIRCLSIsNSApplicationCrashOnExceptionsEnabled(void); +static NSApplicationReportExceptionFunction FIRCLSOriginalNSExceptionReportExceptionFunction(void); +static Method FIRCLSGetNSApplicationReportExceptionMethod(void); + +#endif + +#pragma mark - API +void FIRCLSExceptionInitialize(FIRCLSExceptionReadOnlyContext *roContext, + FIRCLSExceptionWritableContext *rwContext, + void *delegate) { + if (!FIRCLSUnlinkIfExists(roContext->path)) { + FIRCLSSDKLog("Unable to reset the exception file %s\n", strerror(errno)); + } + + roContext->originalTerminateHandler = std::set_terminate(FIRCLSTerminateHandler); + +#if !TARGET_OS_IPHONE + // If FIRCLSApplicationSharedInstance is null, we don't need this + if (FIRCLSIsNSApplicationCrashOnExceptionsEnabled() && FIRCLSApplicationSharedInstance()) { + Method m = FIRCLSGetNSApplicationReportExceptionMethod(); + + roContext->originalNSApplicationReportException = + (void *)method_setImplementation(m, (IMP)FIRCLSNSApplicationReportException); + } +#endif + + rwContext->customExceptionCount = 0; +} + +void FIRCLSExceptionRecordModel(FIRExceptionModel *exceptionModel) { + const char *name = [[exceptionModel.name copy] UTF8String]; + const char *reason = [[exceptionModel.reason copy] UTF8String]; + + FIRCLSExceptionRecord(FIRCLSExceptionTypeCustom, name, reason, [exceptionModel.stackTrace copy], + NO); +} + +void FIRCLSExceptionRecordNSException(NSException *exception) { + FIRCLSSDKLog("Recording an NSException\n"); + + NSArray *returnAddresses = [exception callStackReturnAddresses]; + + NSString *name = [exception name]; + NSString *reason = [exception reason]; + + // It's tempting to try to make use of callStackSymbols here. But, the output + // of that function is not intended to be machine-readible. We could parse it, + // but that isn't really worthwhile, considering that address-based symbolication + // needs to work anyways. + + // package our frames up into the appropriate format + NSMutableArray *frames = [NSMutableArray new]; + + for (NSNumber *address in returnAddresses) { + [frames addObject:[FIRStackFrame stackFrameWithAddress:[address unsignedIntegerValue]]]; + } + + FIRCLSExceptionRecord(FIRCLSExceptionTypeObjectiveC, [name UTF8String], [reason UTF8String], + frames, YES); +} + +static void FIRCLSExceptionRecordFrame(FIRCLSFile *file, FIRStackFrame *frame) { + FIRCLSFileWriteHashStart(file); + + FIRCLSFileWriteHashEntryUint64(file, "pc", [frame address]); + + NSString *string = [frame symbol]; + if (string) { + FIRCLSFileWriteHashEntryHexEncodedString(file, "symbol", [string UTF8String]); + } + + FIRCLSFileWriteHashEntryUint64(file, "offset", [frame offset]); + + string = [frame library]; + if (string) { + FIRCLSFileWriteHashEntryHexEncodedString(file, "library", [string UTF8String]); + } + + string = [frame fileName]; + if (string) { + FIRCLSFileWriteHashEntryHexEncodedString(file, "file", [string UTF8String]); + } + + FIRCLSFileWriteHashEntryUint64(file, "line", [frame lineNumber]); + + FIRCLSFileWriteHashEnd(file); +} + +static bool FIRCLSExceptionIsNative(FIRCLSExceptionType type) { + return type == FIRCLSExceptionTypeObjectiveC || type == FIRCLSExceptionTypeCpp; +} + +static const char *FIRCLSExceptionNameForType(FIRCLSExceptionType type) { + switch (type) { + case FIRCLSExceptionTypeObjectiveC: + return "objective-c"; + case FIRCLSExceptionTypeCpp: + return "c++"; + case FIRCLSExceptionTypeCustom: + return "custom"; + default: + break; + } + + return "unknown"; +} + +void FIRCLSExceptionWrite(FIRCLSFile *file, + FIRCLSExceptionType type, + const char *name, + const char *reason, + NSArray *frames) { + FIRCLSFileWriteSectionStart(file, "exception"); + + FIRCLSFileWriteHashStart(file); + + FIRCLSFileWriteHashEntryString(file, "type", FIRCLSExceptionNameForType(type)); + FIRCLSFileWriteHashEntryHexEncodedString(file, "name", name); + FIRCLSFileWriteHashEntryHexEncodedString(file, "reason", reason); + FIRCLSFileWriteHashEntryUint64(file, "time", time(NULL)); + + if ([frames count]) { + FIRCLSFileWriteHashKey(file, "frames"); + FIRCLSFileWriteArrayStart(file); + + for (FIRStackFrame *frame in frames) { + FIRCLSExceptionRecordFrame(file, frame); + } + + FIRCLSFileWriteArrayEnd(file); + } + + FIRCLSFileWriteHashEnd(file); + + FIRCLSFileWriteSectionEnd(file); +} + +void FIRCLSExceptionRecord(FIRCLSExceptionType type, + const char *name, + const char *reason, + NSArray *frames, + BOOL attemptDelivery) { + if (!FIRCLSContextIsInitialized()) { + return; + } + + bool native = FIRCLSExceptionIsNative(type); + + FIRCLSSDKLog("Recording an exception structure (%d, %d)\n", attemptDelivery, native); + + // exceptions can happen on multiple threads at the same time + if (native) { + dispatch_sync(_firclsExceptionQueue, ^{ + const char *path = _firclsContext.readonly->exception.path; + FIRCLSFile file; + + if (!FIRCLSFileInitWithPath(&file, path, false)) { + FIRCLSSDKLog("Unable to open exception file\n"); + return; + } + + FIRCLSExceptionWrite(&file, type, name, reason, frames); + + // We only want to do this work if we have the expectation that we'll actually crash + FIRCLSHandler(&file, mach_thread_self(), NULL); + + FIRCLSFileClose(&file); + + // disallow immediate delivery for non-native exceptions + if (attemptDelivery) { + FIRCLSHandlerAttemptImmediateDelivery(); + } + }); + } else { + FIRCLSUserLoggingWriteAndCheckABFiles( + &_firclsContext.readonly->logging.customExceptionStorage, + &_firclsContext.writable->logging.activeCustomExceptionPath, ^(FIRCLSFile *file) { + FIRCLSExceptionWrite(file, type, name, reason, frames); + }); + } + + FIRCLSSDKLog("Finished recording an exception structure\n"); +} + +// Ignore this message here, because we know that this call will not leak. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winvalid-noreturn" +void FIRCLSExceptionRaiseTestObjCException(void) { + [NSException raise:@"CrashlyticsTestException" + format:@"This is an Objective-C exception using for testing."]; +} + +void FIRCLSExceptionRaiseTestCppException(void) { + throw "Crashlytics C++ Test Exception"; +} +#pragma clang diagnostic pop + +static const char *FIRCLSExceptionDemangle(const char *symbol) { + return [[FIRCLSDemangleOperation demangleCppSymbol:symbol] UTF8String]; +} + +static void FIRCLSCatchAndRecordActiveException(std::type_info *typeInfo) { + if (!FIRCLSIsValidPointer(typeInfo)) { + FIRCLSSDKLog("Error: invalid parameter\n"); + return; + } + + const char *name = typeInfo->name(); + FIRCLSSDKLog("Recording exception of type '%s'\n", name); + + // This is a funny technique to get the exception object. The inner @try + // has the ability to capture NSException-derived objects. It seems that + // c++ trys can do that in some cases, but I was warned by the WWDC labs + // that there are cases where that will not work (like for NSException subclasses). + try { + @try { + // This could potentially cause a call to std::terminate() if there is actually no active + // exception. + throw; + } @catch (NSException *exception) { +#if TARGET_OS_IPHONE + FIRCLSExceptionRecordNSException(exception); +#else + // There's no need to record this here, because we're going to get + // the value forward to us by AppKit + FIRCLSSDKLog("Skipping ObjC exception at this point\n"); +#endif + } + } catch (const char *exc) { + FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, "const char *", exc, nil, YES); + } catch (const std::string &exc) { + FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, "std::string", exc.c_str(), nil, YES); + } catch (const std::exception &exc) { + FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, FIRCLSExceptionDemangle(name), exc.what(), nil, + YES); + } catch (const std::exception *exc) { + FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, FIRCLSExceptionDemangle(name), exc->what(), nil, + YES); + } catch (const std::bad_alloc &exc) { + // it is especially important to avoid demangling in this case, because the expetation at this + // point is that all allocations could fail + FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, "std::bad_alloc", exc.what(), nil, YES); + } catch (...) { + FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, FIRCLSExceptionDemangle(name), "", nil, YES); + } +} + +#pragma mark - Handlers +static void FIRCLSTerminateHandler(void) { + FIRCLSSDKLog("C++ terminate handler invoked\n"); + + void (*handler)(void) = _firclsContext.readonly->exception.originalTerminateHandler; + if (handler == FIRCLSTerminateHandler) { + FIRCLSSDKLog("Error: original handler was set recursively\n"); + handler = NULL; + } + + // Restore pre-existing handler, if any. Do this early, so that + // if std::terminate is called while we are executing here, we do not recurse. + if (handler) { + FIRCLSSDKLog("restoring pre-existing handler\n"); + + // To prevent infinite recursion in this function, check that we aren't resetting the terminate + // handler to the same function again, which would be this function in the event that we can't + // actually change the handler during a terminate. + if (std::set_terminate(handler) == handler) { + FIRCLSSDKLog("handler has already been restored, aborting\n"); + abort(); + } + } + + // we can use typeInfo to record the type of the exception, + // but we must use a catch to get the value + std::type_info *typeInfo = __cxxabiv1::__cxa_current_exception_type(); + if (typeInfo) { + FIRCLSCatchAndRecordActiveException(typeInfo); + } else { + FIRCLSSDKLog("no active exception\n"); + } + + // only do this if there was a pre-existing handler + if (handler) { + FIRCLSSDKLog("invoking pre-existing handler\n"); + handler(); + } + + FIRCLSSDKLog("aborting\n"); + abort(); +} + +void FIRCLSExceptionCheckHandlers(void *delegate) { +#if !TARGET_OS_IPHONE + // Check this on OS X all the time, even if the debugger is attached. This is a common + // source of errors, so we want to be extra verbose in this case. + if (FIRCLSApplicationSharedInstance()) { + if (!FIRCLSIsNSApplicationCrashOnExceptionsEnabled()) { + FIRCLSWarningLog(@"Warning: NSApplicationCrashOnExceptions is not set. This will " + @"result in poor top-level uncaught exception reporting."); + } + } +#endif + + if (_firclsContext.readonly->debuggerAttached) { + return; + } + + void *ptr = NULL; + + ptr = (void *)std::get_terminate(); + if (ptr != FIRCLSTerminateHandler) { + FIRCLSLookupFunctionPointer(ptr, ^(const char *name, const char *lib) { + FIRCLSWarningLog(@"Warning: std::get_terminate is '%s' in '%s'", name, lib); + }); + } + +#if TARGET_OS_IPHONE + ptr = (void *)NSGetUncaughtExceptionHandler(); + if (ptr) { + FIRCLSLookupFunctionPointer(ptr, ^(const char *name, const char *lib) { + FIRCLSWarningLog(@"Warning: NSUncaughtExceptionHandler is '%s' in '%s'", name, lib); + }); + } +#else + if (FIRCLSApplicationSharedInstance() && FIRCLSIsNSApplicationCrashOnExceptionsEnabled()) { + // In this case, we *might* be able to intercept exceptions. But, verify we've still + // swizzled the method. + Method m = FIRCLSGetNSApplicationReportExceptionMethod(); + + if (method_getImplementation(m) != (IMP)FIRCLSNSApplicationReportException) { + FIRCLSWarningLog( + @"Warning: top-level NSApplication-reported exceptions cannot be intercepted"); + } + } +#endif +} + +#pragma mark - AppKit Handling +#if !TARGET_OS_IPHONE +static BOOL FIRCLSIsNSApplicationCrashOnExceptionsEnabled(void) { + return [[NSUserDefaults standardUserDefaults] boolForKey:@"NSApplicationCrashOnExceptions"]; +} + +static Method FIRCLSGetNSApplicationReportExceptionMethod(void) { + return class_getInstanceMethod(NSClassFromString(@"NSApplication"), @selector(reportException:)); +} + +static NSApplicationReportExceptionFunction FIRCLSOriginalNSExceptionReportExceptionFunction(void) { + return (NSApplicationReportExceptionFunction) + _firclsContext.readonly->exception.originalNSApplicationReportException; +} + +void FIRCLSNSApplicationReportException(id self, SEL cmd, NSException *exception) { + FIRCLSExceptionRecordNSException(exception); + + // Call the original implementation + FIRCLSOriginalNSExceptionReportExceptionFunction()(self, cmd, exception); +} + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.h new file mode 100644 index 000000000..7ce03e416 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.h @@ -0,0 +1,26 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include "FIRCLSFile.h" + +__BEGIN_DECLS + +void FIRCLSHandler(FIRCLSFile* file, thread_t crashedThread, void* uapVoid); +void FIRCLSHandlerAttemptImmediateDelivery(void); + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.m new file mode 100644 index 000000000..918e4265e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.m @@ -0,0 +1,61 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSHandler.h" + +#include "FIRCLSCrashedMarkerFile.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSHost.h" +#include "FIRCLSProcess.h" +#include "FIRCLSUtility.h" + +#import "FIRCLSReportManager_Private.h" + +void FIRCLSHandler(FIRCLSFile* file, thread_t crashedThread, void* uapVoid) { + FIRCLSProcess process; + + FIRCLSProcessInit(&process, crashedThread, uapVoid); + + FIRCLSProcessSuspendAllOtherThreads(&process); + + FIRCLSProcessRecordAllThreads(&process, file); + + FIRCLSProcessRecordRuntimeInfo(&process, file); + // Get dispatch queue and thread names. Note that getting the thread names + // can hang, so let's do that last + FIRCLSProcessRecordDispatchQueueNames(&process, file); + FIRCLSProcessRecordThreadNames(&process, file); + + // this stuff isn't super important, but we can try + FIRCLSProcessRecordStats(&process, file); + FIRCLSHostWriteDiskUsage(file); + + // This is the first common point where various crash handlers call into + // Store a crash file marker to indicate that a crash has occured + FIRCLSCreateCrashedMarkerFile(); + + FIRCLSProcessResumeAllOtherThreads(&process); + + // clean up after ourselves + FIRCLSProcessDestroy(&process); +} + +void FIRCLSHandlerAttemptImmediateDelivery(void) { + // now, attempt to relay the event to the delegate + FIRCLSReportManager* manager = (__bridge id)_firclsContext.readonly->delegate; + + if ([manager respondsToSelector:@selector(potentiallySubmittableCrashOccurred)]) { + [manager potentiallySubmittableCrashOccurred]; + } +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c new file mode 100644 index 000000000..3ec4e9790 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c @@ -0,0 +1,533 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSDefines.h" +#include "FIRCLSFeatures.h" + +#if CLS_MACH_EXCEPTION_SUPPORTED + +#include "FIRCLSGlobals.h" +#include "FIRCLSHandler.h" +#include "FIRCLSMachException.h" +#include "FIRCLSProcess.h" +#include "FIRCLSSignal.h" +#include "FIRCLSUtility.h" + +#include +#include +#include +#include + +#pragma mark Prototypes +static exception_mask_t FIRCLSMachExceptionMask(void); +static void* FIRCLSMachExceptionServer(void* argument); +static bool FIRCLSMachExceptionThreadStart(FIRCLSMachExceptionReadContext* context); +static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context, + MachExceptionMessage* message); +static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context, + MachExceptionMessage* message); +static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context, + MachExceptionMessage* message, + kern_return_t result); +static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context, + exception_mask_t ignoreMask); +static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts, + exception_mask_t mask); +static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context, + MachExceptionMessage* message); + +#pragma mark - Initialization +void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context, exception_mask_t ignoreMask) { + if (!FIRCLSUnlinkIfExists(context->path)) { + FIRCLSSDKLog("Unable to reset the mach exception file %s\n", strerror(errno)); + } + + if (!FIRCLSMachExceptionRegister(context, ignoreMask)) { + FIRCLSSDKLog("Unable to register mach exception handler\n"); + return; + } + + if (!FIRCLSMachExceptionThreadStart(context)) { + FIRCLSSDKLog("Unable to start thread\n"); + FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask); + } +} + +void FIRCLSMachExceptionCheckHandlers(void) { + if (_firclsContext.readonly->debuggerAttached) { + return; + } + + // It isn't really critical that this be done, as its extremely uncommon to run into + // preexisting handlers. + // Can use task_get_exception_ports for this. +} + +static exception_mask_t FIRCLSMachExceptionMask(void) { + exception_mask_t mask; + + // EXC_BAD_ACCESS + // EXC_BAD_INSTRUCTION + // EXC_ARITHMETIC + // EXC_EMULATION - non-failure + // EXC_SOFTWARE - non-failure + // EXC_BREAKPOINT - trap instructions, from the debugger and code. Needs special treatment. + // EXC_SYSCALL - non-failure + // EXC_MACH_SYSCALL - non-failure + // EXC_RPC_ALERT - non-failure + // EXC_CRASH - see below + // EXC_RESOURCE - non-failure, happens when a process exceeds a resource limit + // EXC_GUARD - see below + // + // EXC_CRASH is a special kind of exception. It is handled by launchd, and treated special by + // the kernel. Seems that we cannot safely catch it - our handler will never be called. This + // is a confirmed kernel bug. Lacking access to EXC_CRASH means we must use signal handlers to + // cover all types of crashes. + // EXC_GUARD is relatively new, and isn't available on all OS versions. You have to be careful, + // becuase you cannot succesfully register hanlders if there are any unrecognized masks. We've + // dropped support for old OS versions that didn't have EXC_GUARD (iOS 5 and below, macOS 10.6 and + // below) so we always add it now + + mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | + EXC_MASK_BREAKPOINT | EXC_MASK_GUARD; + + return mask; +} + +static bool FIRCLSMachExceptionThreadStart(FIRCLSMachExceptionReadContext* context) { + pthread_attr_t attr; + + if (pthread_attr_init(&attr) != 0) { + FIRCLSSDKLog("pthread_attr_init %s\n", strerror(errno)); + return false; + } + + if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) { + FIRCLSSDKLog("pthread_attr_setdetachstate %s\n", strerror(errno)); + return false; + } + + // Use to pre-allocate a stack for this thread + // The stack must be page-aligned + if (pthread_attr_setstack(&attr, _firclsContext.readonly->machStack, + CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE) != 0) { + FIRCLSSDKLog("pthread_attr_setstack %s\n", strerror(errno)); + return false; + } + + if (pthread_create(&context->thread, &attr, FIRCLSMachExceptionServer, context) != 0) { + FIRCLSSDKLog("pthread_create %s\n", strerror(errno)); + return false; + } + + pthread_attr_destroy(&attr); + + return true; +} + +exception_mask_t FIRCLSMachExceptionMaskForSignal(int signal) { + switch (signal) { + case SIGTRAP: + return EXC_MASK_BREAKPOINT; + case SIGSEGV: + return EXC_MASK_BAD_ACCESS; + case SIGBUS: + return EXC_MASK_BAD_ACCESS; + case SIGILL: + return EXC_MASK_BAD_INSTRUCTION; + case SIGABRT: + return EXC_MASK_CRASH; + case SIGSYS: + return EXC_MASK_CRASH; + case SIGFPE: + return EXC_MASK_ARITHMETIC; + } + + return 0; +} + +#pragma mark - Message Handling +static void* FIRCLSMachExceptionServer(void* argument) { + FIRCLSMachExceptionReadContext* context = argument; + + pthread_setname_np("com.google.firebase.crashlytics.MachExceptionServer"); + + while (1) { + MachExceptionMessage message; + + // read the exception message + if (!FIRCLSMachExceptionReadMessage(context, &message)) { + break; + } + + // handle it, and possibly forward + kern_return_t result = FIRCLSMachExceptionDispatchMessage(context, &message); + + // and now, reply + if (!FIRCLSMachExceptionReply(context, &message, result)) { + break; + } + } + + FIRCLSSDKLog("Mach exception server thread exiting\n"); + + return NULL; +} + +static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context, + MachExceptionMessage* message) { + mach_msg_return_t r; + + memset(message, 0, sizeof(MachExceptionMessage)); + + r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionMessage), + context->port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) { + FIRCLSSDKLog("Error receving mach_msg (%d)\n", r); + return false; + } + + FIRCLSSDKLog("Accepted mach exception message\n"); + + return true; +} + +static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context, + MachExceptionMessage* message) { + FIRCLSSDKLog("Mach exception: 0x%x, count: %d, code: 0x%llx 0x%llx\n", message->exception, + message->codeCnt, message->codeCnt > 0 ? message->code[0] : -1, + message->codeCnt > 1 ? message->code[1] : -1); + + // This will happen if a child process raises an exception, as the exception ports are + // inherited. + if (message->task.name != mach_task_self()) { + FIRCLSSDKLog("Mach exception task mis-match, returning failure\n"); + return KERN_FAILURE; + } + + FIRCLSSDKLog("Unregistering handler\n"); + if (!FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask)) { + FIRCLSSDKLog("Failed to unregister\n"); + return KERN_FAILURE; + } + + FIRCLSSDKLog("Restoring original signal handlers\n"); + if (!FIRCLSSignalSafeInstallPreexistingHandlers(&_firclsContext.readonly->signal)) { + FIRCLSSDKLog("Failed to restore signal handlers\n"); + return KERN_FAILURE; + } + + FIRCLSSDKLog("Recording mach exception\n"); + if (!FIRCLSMachExceptionRecord(context, message)) { + FIRCLSSDKLog("Failed to record mach exception\n"); + return KERN_FAILURE; + } + + return KERN_SUCCESS; +} + +static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context, + MachExceptionMessage* message, + kern_return_t result) { + MachExceptionReply reply; + mach_msg_return_t r; + + // prepare the reply + reply.head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(message->head.msgh_bits), 0); + reply.head.msgh_remote_port = message->head.msgh_remote_port; + reply.head.msgh_size = (mach_msg_size_t)sizeof(MachExceptionReply); + reply.head.msgh_local_port = MACH_PORT_NULL; + reply.head.msgh_id = message->head.msgh_id + 100; + + reply.NDR = NDR_record; + + reply.retCode = result; + + FIRCLSSDKLog("Sending exception reply\n"); + + // send it + r = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0, MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (r != MACH_MSG_SUCCESS) { + FIRCLSSDKLog("mach_msg reply failed (%d)\n", r); + return false; + } + + FIRCLSSDKLog("Exception reply delivered\n"); + + return true; +} + +#pragma mark - Registration +static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context, + exception_mask_t ignoreMask) { + mach_port_t task = mach_task_self(); + + kern_return_t kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &context->port); + if (kr != KERN_SUCCESS) { + FIRCLSSDKLog("Error: mach_port_allocate failed %d\n", kr); + return false; + } + + kr = mach_port_insert_right(task, context->port, context->port, MACH_MSG_TYPE_MAKE_SEND); + if (kr != KERN_SUCCESS) { + FIRCLSSDKLog("Error: mach_port_insert_right failed %d\n", kr); + mach_port_deallocate(task, context->port); + return false; + } + + // Get the desired mask, which covers all the mach exceptions we are capable of handling, + // but clear out any that are in our ignore list. We do this by ANDing with the bitwise + // negation. Because we are only clearing bits, there's no way to set an incorrect mask + // using ignoreMask. + context->mask = FIRCLSMachExceptionMask() & ~ignoreMask; + + // ORing with MACH_EXCEPTION_CODES will produce 64-bit exception data + kr = task_swap_exception_ports(task, context->mask, context->port, + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE, + context->originalPorts.masks, &context->originalPorts.count, + context->originalPorts.ports, context->originalPorts.behaviors, + context->originalPorts.flavors); + if (kr != KERN_SUCCESS) { + FIRCLSSDKLog("Error: task_swap_exception_ports %d\n", kr); + return false; + } + + for (int i = 0; i < context->originalPorts.count; ++i) { + FIRCLSSDKLog("original 0x%x 0x%x 0x%x 0x%x\n", context->originalPorts.ports[i], + context->originalPorts.masks[i], context->originalPorts.behaviors[i], + context->originalPorts.flavors[i]); + } + + return true; +} + +static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts, + exception_mask_t mask) { + kern_return_t kr; + + // Re-register all the old ports. + for (mach_msg_type_number_t i = 0; i < originalPorts->count; ++i) { + // clear the bits from this original mask + mask &= ~originalPorts->masks[i]; + + kr = + task_set_exception_ports(mach_task_self(), originalPorts->masks[i], originalPorts->ports[i], + originalPorts->behaviors[i], originalPorts->flavors[i]); + if (kr != KERN_SUCCESS) { + FIRCLSSDKLog("unable to restore original port: %d", originalPorts->ports[i]); + } + } + + // Finally, mark any masks we registered for that do not have an original port as unused. + kr = task_set_exception_ports(mach_task_self(), mask, MACH_PORT_NULL, + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); + if (kr != KERN_SUCCESS) { + FIRCLSSDKLog("unable to unset unregistered mask: 0x%x", mask); + return false; + } + + return true; +} + +#pragma mark - Recording +static void FIRCLSMachExceptionNameLookup(exception_type_t number, + mach_exception_data_type_t code, + const char** name, + const char** codeName) { + if (!name || !codeName) { + return; + } + + *name = NULL; + *codeName = NULL; + + switch (number) { + case EXC_BAD_ACCESS: + *name = "EXC_BAD_ACCESS"; + switch (code) { + case KERN_INVALID_ADDRESS: + *codeName = "KERN_INVALID_ADDRESS"; + break; + case KERN_PROTECTION_FAILURE: + *codeName = "KERN_PROTECTION_FAILURE"; + break; + } + + break; + case EXC_BAD_INSTRUCTION: + *name = "EXC_BAD_INSTRUCTION"; +#if CLS_CPU_X86 + *codeName = "EXC_I386_INVOP"; +#endif + break; + case EXC_ARITHMETIC: + *name = "EXC_ARITHMETIC"; +#if CLS_CPU_X86 + switch (code) { + case EXC_I386_DIV: + *codeName = "EXC_I386_DIV"; + break; + case EXC_I386_INTO: + *codeName = "EXC_I386_INTO"; + break; + case EXC_I386_NOEXT: + *codeName = "EXC_I386_NOEXT"; + break; + case EXC_I386_EXTOVR: + *codeName = "EXC_I386_EXTOVR"; + break; + case EXC_I386_EXTERR: + *codeName = "EXC_I386_EXTERR"; + break; + case EXC_I386_EMERR: + *codeName = "EXC_I386_EMERR"; + break; + case EXC_I386_BOUND: + *codeName = "EXC_I386_BOUND"; + break; + case EXC_I386_SSEEXTERR: + *codeName = "EXC_I386_SSEEXTERR"; + break; + } +#endif + break; + case EXC_BREAKPOINT: + *name = "EXC_BREAKPOINT"; +#if CLS_CPU_X86 + switch (code) { + case EXC_I386_DIVERR: + *codeName = "EXC_I386_DIVERR"; + break; + case EXC_I386_SGLSTP: + *codeName = "EXC_I386_SGLSTP"; + break; + case EXC_I386_NMIFLT: + *codeName = "EXC_I386_NMIFLT"; + break; + case EXC_I386_BPTFLT: + *codeName = "EXC_I386_BPTFLT"; + break; + case EXC_I386_INTOFLT: + *codeName = "EXC_I386_INTOFLT"; + break; + case EXC_I386_BOUNDFLT: + *codeName = "EXC_I386_BOUNDFLT"; + break; + case EXC_I386_INVOPFLT: + *codeName = "EXC_I386_INVOPFLT"; + break; + case EXC_I386_NOEXTFLT: + *codeName = "EXC_I386_NOEXTFLT"; + break; + case EXC_I386_EXTOVRFLT: + *codeName = "EXC_I386_EXTOVRFLT"; + break; + case EXC_I386_INVTSSFLT: + *codeName = "EXC_I386_INVTSSFLT"; + break; + case EXC_I386_SEGNPFLT: + *codeName = "EXC_I386_SEGNPFLT"; + break; + case EXC_I386_STKFLT: + *codeName = "EXC_I386_STKFLT"; + break; + case EXC_I386_GPFLT: + *codeName = "EXC_I386_GPFLT"; + break; + case EXC_I386_PGFLT: + *codeName = "EXC_I386_PGFLT"; + break; + case EXC_I386_EXTERRFLT: + *codeName = "EXC_I386_EXTERRFLT"; + break; + case EXC_I386_ALIGNFLT: + *codeName = "EXC_I386_ALIGNFLT"; + break; + case EXC_I386_ENDPERR: + *codeName = "EXC_I386_ENDPERR"; + break; + case EXC_I386_ENOEXTFLT: + *codeName = "EXC_I386_ENOEXTFLT"; + break; + } +#endif + break; + case EXC_GUARD: + *name = "EXC_GUARD"; + break; + } +} + +static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context, + MachExceptionMessage* message) { + if (!context || !message) { + return false; + } + + if (FIRCLSContextMarkAndCheckIfCrashed()) { + FIRCLSSDKLog("Error: aborting mach exception handler because crash has already occurred\n"); + exit(1); + return false; + } + + FIRCLSFile file; + + if (!FIRCLSFileInitWithPath(&file, context->path, false)) { + FIRCLSSDKLog("Unable to open mach exception file\n"); + return false; + } + + FIRCLSFileWriteSectionStart(&file, "mach_exception"); + + FIRCLSFileWriteHashStart(&file); + + FIRCLSFileWriteHashEntryUint64(&file, "exception", message->exception); + + // record the codes + FIRCLSFileWriteHashKey(&file, "codes"); + FIRCLSFileWriteArrayStart(&file); + for (mach_msg_type_number_t i = 0; i < message->codeCnt; ++i) { + FIRCLSFileWriteArrayEntryUint64(&file, message->code[i]); + } + FIRCLSFileWriteArrayEnd(&file); + + const char* name = NULL; + const char* codeName = NULL; + + FIRCLSMachExceptionNameLookup(message->exception, message->codeCnt > 0 ? message->code[0] : 0, + &name, &codeName); + + FIRCLSFileWriteHashEntryString(&file, "name", name); + FIRCLSFileWriteHashEntryString(&file, "code_name", codeName); + + FIRCLSFileWriteHashEntryUint64(&file, "original_ports", context->originalPorts.count); + FIRCLSFileWriteHashEntryUint64(&file, "time", time(NULL)); + + FIRCLSFileWriteHashEnd(&file); + + FIRCLSFileWriteSectionEnd(&file); + + FIRCLSHandler(&file, message->thread.name, NULL); + + FIRCLSFileClose(&file); + + return true; +} + +#else + +INJECT_STRIP_SYMBOL(cls_mach_exception) + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h new file mode 100644 index 000000000..b19881a7a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h @@ -0,0 +1,78 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSFeatures.h" + +#pragma once + +#if CLS_MACH_EXCEPTION_SUPPORTED + +#include +#include +#include + +// must be at least PTHREAD_STACK_MIN size +#define CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE (256 * 1024) + +#pragma mark Structures +#pragma pack(push, 4) +typedef struct { + mach_msg_header_t head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; + mach_msg_trailer_t trailer; +} MachExceptionMessage; + +typedef struct { + mach_msg_header_t head; + NDR_record_t NDR; + kern_return_t retCode; +} MachExceptionReply; +#pragma pack(pop) + +typedef struct { + mach_msg_type_number_t count; + exception_mask_t masks[EXC_TYPES_COUNT]; + exception_handler_t ports[EXC_TYPES_COUNT]; + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; +} FIRCLSMachExceptionOriginalPorts; + +typedef struct { + mach_port_t port; + pthread_t thread; + const char* path; + + exception_mask_t mask; + FIRCLSMachExceptionOriginalPorts originalPorts; +} FIRCLSMachExceptionReadContext; + +#pragma mark - API +void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context, exception_mask_t ignoreMask); +exception_mask_t FIRCLSMachExceptionMaskForSignal(int signal); + +void FIRCLSMachExceptionCheckHandlers(void); + +#else + +#define CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE 0 + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.c new file mode 100644 index 000000000..5fa302d7c --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.c @@ -0,0 +1,318 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSSignal.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSHandler.h" +#include "FIRCLSUtility.h" + +#include +#include + +static const int FIRCLSFatalSignals[FIRCLSSignalCount] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, + SIGSEGV, SIGSYS, SIGTRAP}; + +#if CLS_USE_SIGALTSTACK +static void FIRCLSSignalInstallAltStack(FIRCLSSignalReadContext *roContext); +#endif +static void FIRCLSSignalInstallHandlers(FIRCLSSignalReadContext *roContext); +static void FIRCLSSignalHandler(int signal, siginfo_t *info, void *uapVoid); + +void FIRCLSSignalInitialize(FIRCLSSignalReadContext *roContext) { + if (!FIRCLSUnlinkIfExists(roContext->path)) { + FIRCLSSDKLog("Unable to reset the signal log file %s\n", strerror(errno)); + } + +#if CLS_USE_SIGALTSTACK + FIRCLSSignalInstallAltStack(roContext); +#endif + FIRCLSSignalInstallHandlers(roContext); +#if TARGET_IPHONE_SIMULATOR + // prevent the OpenGL stack (by way of OpenGLES.framework/libLLVMContainer.dylib) from installing + // signal handlers that do not chain back + // TODO: I don't believe this is necessary as of recent iOS releases + bool *ptr = dlsym(RTLD_DEFAULT, "_ZN4llvm23DisablePrettyStackTraceE"); + if (ptr) { + *ptr = true; + } +#endif +} + +void FIRCLSSignalEnumerateHandledSignals(void (^block)(int idx, int signal)) { + for (int i = 0; i < FIRCLSSignalCount; ++i) { + block(i, FIRCLSFatalSignals[i]); + } +} + +#if CLS_USE_SIGALTSTACK + +static void FIRCLSSignalInstallAltStack(FIRCLSSignalReadContext *roContext) { + stack_t signalStack; + stack_t originalStack; + + signalStack.ss_sp = _firclsContext.readonly->signalStack; + signalStack.ss_size = CLS_SIGNAL_HANDLER_STACK_SIZE; + signalStack.ss_flags = 0; + + if (sigaltstack(&signalStack, &originalStack) != 0) { + FIRCLSSDKLog("Unable to setup stack %s\n", strerror(errno)); + + return; + } + + roContext->originalStack.ss_sp = NULL; + roContext->originalStack = originalStack; +} + +#endif + +static void FIRCLSSignalInstallHandlers(FIRCLSSignalReadContext *roContext) { + FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) { + struct sigaction action; + struct sigaction previousAction; + + action.sa_sigaction = FIRCLSSignalHandler; + // SA_RESETHAND seems like it would be great, but it doesn't appear to + // work correctly. After taking a signal, causing another identical signal in + // the handler will *not* cause the default handler to be invokved (which should + // terminate the process). I've found some evidence that others have seen this + // behavior on MAC OS X. + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + + sigemptyset(&action.sa_mask); + + previousAction.sa_sigaction = NULL; + if (sigaction(signal, &action, &previousAction) != 0) { + FIRCLSSDKLog("Unable to install handler for %d (%s)\n", signal, strerror(errno)); + } + + // store the last action, so it can be recalled + roContext->originalActions[idx].sa_sigaction = NULL; + + if (previousAction.sa_sigaction) { + roContext->originalActions[idx] = previousAction; + } + }); +} + +void FIRCLSSignalCheckHandlers(void) { + if (_firclsContext.readonly->debuggerAttached) { + return; + } + + FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) { + struct sigaction previousAction; + Dl_info info; + void *ptr; + + if (sigaction(signal, 0, &previousAction) != 0) { + fprintf(stderr, "Unable to read signal handler\n"); + return; + } + + ptr = previousAction.__sigaction_u.__sa_handler; + const char *signalName = NULL; + const char *codeName = NULL; + + FIRCLSSignalNameLookup(signal, 0, &signalName, &codeName); + + if (ptr == FIRCLSSignalHandler) { + return; + } + + const char *name = NULL; + if (dladdr(ptr, &info) != 0) { + name = info.dli_sname; + } + + fprintf(stderr, + "[Crashlytics] The signal %s has a non-Crashlytics handler (%s). This will interfere " + "with reporting.\n", + signalName, name); + }); +} + +void FIRCLSSignalSafeRemoveHandlers(bool includingAbort) { + for (int i = 0; i < FIRCLSSignalCount; ++i) { + struct sigaction sa; + + if (!includingAbort && (FIRCLSFatalSignals[i] == SIGABRT)) { + continue; + } + + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + + if (sigaction(FIRCLSFatalSignals[i], &sa, NULL) != 0) + FIRCLSSDKLog("Unable to set default handler for %d (%s)\n", i, strerror(errno)); + } +} + +bool FIRCLSSignalSafeInstallPreexistingHandlers(FIRCLSSignalReadContext *roContext) { + bool success; + + FIRCLSSignalSafeRemoveHandlers(true); + +#if CLS_USE_SIGALTSTACK + + // re-install the original stack, if needed + if (roContext->originalStack.ss_sp) { + if (sigaltstack(&roContext->originalStack, 0) != 0) { + FIRCLSSDKLog("Unable to setup stack %s\n", strerror(errno)); + + return false; + } + } + +#endif + + // re-install the original handlers, if any + success = true; + for (int i = 0; i < FIRCLSSignalCount; ++i) { + if (roContext->originalActions[i].sa_sigaction == NULL) { + continue; + } + + if (sigaction(FIRCLSFatalSignals[i], &roContext->originalActions[i], 0) != 0) { + FIRCLSSDKLog("Unable to install handler for %d (%s)\n", i, strerror(errno)); + success = false; + } + } + + return success; +} + +void FIRCLSSignalNameLookup(int number, int code, const char **name, const char **codeName) { + if (!name || !codeName) { + return; + } + + *codeName = NULL; + + switch (number) { + case SIGABRT: + *name = "SIGABRT"; + *codeName = "ABORT"; + break; + case SIGBUS: + *name = "SIGBUS"; + break; + case SIGFPE: + *name = "SIGFPE"; + break; + case SIGILL: + *name = "SIGILL"; + break; + case SIGSEGV: + *name = "SIGSEGV"; + break; + case SIGSYS: + *name = "SIGSYS"; + break; + case SIGTRAP: + *name = "SIGTRAP"; + break; + default: + *name = "UNKNOWN"; + break; + } +} + +static void FIRCLSSignalRecordSignal(int savedErrno, siginfo_t *info, void *uapVoid) { + if (!_firclsContext.readonly) { + return; + } + + if (FIRCLSContextMarkAndCheckIfCrashed()) { + FIRCLSSDKLog("Error: aborting signal handler because crash has already occurred"); + exit(1); + return; + } + + FIRCLSFile file; + + if (!FIRCLSFileInitWithPath(&file, _firclsContext.readonly->signal.path, false)) { + FIRCLSSDKLog("Unable to open signal file\n"); + return; + } + + FIRCLSFileWriteSectionStart(&file, "signal"); + + FIRCLSFileWriteHashStart(&file); + + if (FIRCLSIsValidPointer(info)) { + FIRCLSFileWriteHashEntryUint64(&file, "number", info->si_signo); + FIRCLSFileWriteHashEntryUint64(&file, "code", info->si_code); + FIRCLSFileWriteHashEntryUint64(&file, "address", (uint64_t)info->si_addr); + + const char *name = NULL; + const char *codeName = NULL; + + FIRCLSSignalNameLookup(info->si_signo, info->si_code, &name, &codeName); + + FIRCLSFileWriteHashEntryString(&file, "name", name); + FIRCLSFileWriteHashEntryString(&file, "code_name", codeName); + } + + FIRCLSFileWriteHashEntryUint64(&file, "errno", savedErrno); + FIRCLSFileWriteHashEntryUint64(&file, "time", time(NULL)); + + FIRCLSFileWriteHashEnd(&file); + + FIRCLSFileWriteSectionEnd(&file); + + FIRCLSHandler(&file, mach_thread_self(), uapVoid); + + FIRCLSFileClose(&file); +} + +static void FIRCLSSignalHandler(int signal, siginfo_t *info, void *uapVoid) { + int savedErrno; + sigset_t set; + + // save errno, both because it is interesting, and so we can restore it afterwards + savedErrno = errno; + errno = 0; + + FIRCLSSDKLog("Signal: %d\n", signal); + + // it is important to do this before unmasking signals, otherwise we can get + // called in a loop + FIRCLSSignalSafeRemoveHandlers(true); + + sigfillset(&set); + if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { + FIRCLSSDKLog("Unable to unmask signals - we risk infinite recursion here\n"); + } + + // check info and uapVoid, and set them to appropriate values if invalid. This can happen + // if we have been called without the SA_SIGINFO flag set + if (!FIRCLSIsValidPointer(info)) { + info = NULL; + } + + if (!FIRCLSIsValidPointer(uapVoid)) { + uapVoid = NULL; + } + + FIRCLSSignalRecordSignal(savedErrno, info, uapVoid); + + // re-install original handlers + if (_firclsContext.readonly) { + FIRCLSSignalSafeInstallPreexistingHandlers(&_firclsContext.readonly->signal); + } + + // restore errno + errno = savedErrno; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.h new file mode 100644 index 000000000..3b6b1b4e2 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.h @@ -0,0 +1,51 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "FIRCLSFeatures.h" +#include "FIRCLSFile.h" + +#include +#include + +#define FIRCLSSignalCount (7) + +// per man sigaltstack, MINSIGSTKSZ is the minimum *overhead* needed to support +// a signal stack. The actual stack size must be larger. Let's pick the recommended +// size. +#if CLS_USE_SIGALTSTACK +#define CLS_SIGNAL_HANDLER_STACK_SIZE (SIGSTKSZ * 2) +#else +#define CLS_SIGNAL_HANDLER_STACK_SIZE 0 +#endif + +typedef struct { + const char* path; + struct sigaction originalActions[FIRCLSSignalCount]; + +#if CLS_USE_SIGALTSTACK + stack_t originalStack; +#endif +} FIRCLSSignalReadContext; + +void FIRCLSSignalInitialize(FIRCLSSignalReadContext* roContext); +void FIRCLSSignalCheckHandlers(void); + +void FIRCLSSignalSafeRemoveHandlers(bool includingAbort); +bool FIRCLSSignalSafeInstallPreexistingHandlers(FIRCLSSignalReadContext* roContext); + +void FIRCLSSignalNameLookup(int number, int code, const char** name, const char** codeName); + +void FIRCLSSignalEnumerateHandledSignals(void (^block)(int idx, int signal)); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.h new file mode 100644 index 000000000..f0de9fed9 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.h @@ -0,0 +1,23 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIRAEvent_Internal_h +#define FIRAEvent_Internal_h + +#import "FIRAEvent.h" +#import "FIRAValue.h" + +NSString* FIRCLSFIRAEventDictionaryToJSON(NSDictionary* eventAsDictionary); + +#endif /* FIRAEvent_Internal_h */ diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.m new file mode 100644 index 000000000..4591e29f1 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.m @@ -0,0 +1,42 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRAEvent+Internal.h" + +#import "FIRCLSUtility.h" + +NSString* FIRCLSFIRAEventDictionaryToJSON(NSDictionary* eventAsDictionary) { + NSError* error = nil; + + if (eventAsDictionary == nil) { + return nil; + } + + if (![NSJSONSerialization isValidJSONObject:eventAsDictionary]) { + FIRCLSSDKLog("Firebase Analytics event is not valid JSON"); + return nil; + } + + NSData* jsonData = [NSJSONSerialization dataWithJSONObject:eventAsDictionary + options:0 + error:&error]; + + if (error == nil) { + NSString* json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + return json; + } else { + FIRCLSSDKLog("Unable to convert Firebase Analytics event to json"); + return nil; + } +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent.h new file mode 100644 index 000000000..9fcbd6602 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent.h @@ -0,0 +1,79 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRAPBEvent; + +/// An application event. +@interface FIRAEvent : NSObject + +/// Origin of this event (eg "app" or "auto"). +@property(nonatomic, readonly) NSString *origin; + +/// Name of this event. +@property(nonatomic, readonly) NSString *name; + +/// Timestamp of when this event was fired. +@property(nonatomic, readonly) NSTimeInterval timestamp; + +/// Timestamp of the previous time an event with this name was fired, if any. +@property(nonatomic, readonly) NSTimeInterval previousTimestamp; + +/// The event's parameters as {NSString : NSString} or {NSString : NSNumber}. +@property(nonatomic, readonly) NSDictionary *parameters; + +/// Indicates whether the event has the conversion parameter. Setting to YES adds the conversion +/// parameter if not already present. Setting to NO removes the conversion parameter and adds an +/// error. +@property(nonatomic, getter=isConversion) BOOL conversion; + +/// Indicates whether the event has the real-time parameter. Setting to YES adds the real-time +/// parameter if not already present. Setting to NO removes the real-time parameter. +@property(nonatomic, getter=isRealtime) BOOL realtime; + +/// Indicates whether the event has debug parameter. Setting to YES adds the debug parameter if +/// not already present. Setting to NO removes the debug parameter. +@property(nonatomic, getter=isDebug) BOOL debug; + +/// The populated FIRAPBEvent for proto. +@property(nonatomic, readonly) FIRAPBEvent *protoEvent; + +/// Creates an event with the given parameters. Parameters will be copied and normalized. Returns +/// nil if the name does not meet length requirements. +/// If |parameters| contains the "_o" parameter, its value will be overwritten with the value of +/// |origin|. +- (instancetype)initWithOrigin:(NSString *)origin + isPublic:(BOOL)isPublic + name:(NSString *)name + timestamp:(NSTimeInterval)timestamp + previousTimestamp:(NSTimeInterval)previousTimestamp + parameters:(NSDictionary *)parameters NS_DESIGNATED_INITIALIZER; + +- (instancetype)init NS_UNAVAILABLE; + +/// Returns a new event object with the given previousTimestamp. +- (instancetype)copyWithPreviousTimestamp:(NSTimeInterval)previousTimestamp; + +/// Returns a new event object with the new parameters. +- (instancetype)copyWithParameters:(NSDictionary *)parameters; + +/// Returns YES if all parameters in screenParameters were added to the event object. Returns NO if +/// screenParameters is nil/empty or the event already contains any of the screen parameter keys. +/// Performs internal validation on the screen parameter values and converts them to FIRAValue +/// objects if they aren't already. screenParameters should be a dictionary of +/// { NSString : NSString | NSNumber } or { NSString : FIRAValue }. +- (BOOL)addScreenParameters:(NSDictionary *)screenParameters; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAValue.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAValue.h new file mode 100644 index 000000000..7d10ec308 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAValue.h @@ -0,0 +1,69 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +typedef NS_ENUM(NSInteger, FIRAValueType) { + kFIRAValueTypeDouble = 0, + kFIRAValueTypeInteger, + kFIRAValueTypeString, +}; + +@interface FIRAValue : NSObject + +/// The type of the value. +@property(nonatomic, readonly) FIRAValueType valueType; + +#pragma mark - Double type. + +/// Indicates whether the FIRAValue instance is a floating point. +@property(nonatomic, readonly) BOOL isDouble; + +/// Float value. Check valueType to see if this attribute has float value. +@property(nonatomic, readonly) double doubleValue; + +#pragma mark - Integer type. + +/// Indicates whether the FIRAValue instance is an integer. +@property(nonatomic, readonly) BOOL isInt64; + +/// Int64 value. Check valueType to see if this attribute has int64 value. +@property(nonatomic, readonly) int64_t int64Value; + +#pragma mark - String type. + +/// Indicates whether the FIRAValue instance is a string. +@property(nonatomic, readonly) BOOL isString; + +/// String value. Check valueType to see if this attribute has string value. +@property(nonatomic, readonly) NSString *stringValue; + +#pragma mark - Initializers. + +/// Creates a @c FIRAValue if |object| is of type NSString or NSNumber. Returns |object| if it's +/// already a FIRAValue. Returns nil otherwise. ++ (instancetype)valueFromObject:(id)object; + +/// Creates a @c FIRAValue with double value. +- (instancetype)initWithDouble:(double)value; + +/// Creates a @c FIRAValue with int64 value. +- (instancetype)initWithInt64:(int64_t)value; + +/// Creates a @c FIRAValue with string value. +- (instancetype)initWithString:(NSString *)stringValue; + +- (instancetype)init NS_UNAVAILABLE; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.c new file mode 100644 index 000000000..febafd2ab --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.c @@ -0,0 +1,238 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "FIRCLSAllocate.h" +#include "FIRCLSHost.h" +#include "FIRCLSUtility.h" + +#include +#include +#include +#include +#include +#include +#include + +void* FIRCLSAllocatorSafeAllocateFromRegion(FIRCLSAllocationRegion* region, size_t size); + +FIRCLSAllocatorRef FIRCLSAllocatorCreate(size_t writableSpace, size_t readableSpace) { + FIRCLSAllocatorRef allocator; + FIRCLSAllocationRegion writableRegion; + FIRCLSAllocationRegion readableRegion; + size_t allocationSize; + vm_size_t pageSize; + void* buffer; + + // | GUARD | WRITABLE_REGION | GUARD | READABLE_REGION | GUARD | + + pageSize = FIRCLSHostGetPageSize(); + + readableSpace += sizeof(FIRCLSAllocator); // add the space for our allocator itself + + // we can only protect at the page level, so we need all of our regions to be + // exact multples of pages. But, we don't need anything in the special-case of zero. + + writableRegion.size = 0; + if (writableSpace > 0) { + writableRegion.size = ((writableSpace / pageSize) + 1) * pageSize; + } + + readableRegion.size = 0; + if (readableSpace > 0) { + readableRegion.size = ((readableSpace / pageSize) + 1) * pageSize; + } + + // Make one big, continous allocation, adding additional pages for our guards. Note + // that we cannot use malloc (or valloc) in this case, because we need to assert full + // ownership over these allocations. mmap is a much better choice. We also mark these + // pages as MAP_NOCACHE. + allocationSize = writableRegion.size + readableRegion.size + pageSize * 3; + buffer = + mmap(0, allocationSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_NOCACHE, -1, 0); + if (buffer == MAP_FAILED) { + FIRCLSSDKLogError("Mapping failed %s\n", strerror(errno)); + return NULL; + } + + // move our cursors into position + writableRegion.cursor = (void*)((uintptr_t)buffer + pageSize); + readableRegion.cursor = (void*)((uintptr_t)buffer + pageSize + writableRegion.size + pageSize); + writableRegion.start = writableRegion.cursor; + readableRegion.start = readableRegion.cursor; + + FIRCLSSDKLogInfo("Mapping: %p %p %p, total: %zu K\n", buffer, writableRegion.start, + readableRegion.start, allocationSize / 1024); + + // protect first guard page + if (mprotect(buffer, pageSize, PROT_NONE) != 0) { + FIRCLSSDKLogError("First guard protection failed %s\n", strerror(errno)); + return NULL; + } + + // middle guard + if (mprotect((void*)((uintptr_t)buffer + pageSize + writableRegion.size), pageSize, PROT_NONE) != + 0) { + FIRCLSSDKLogError("Middle guard protection failed %s\n", strerror(errno)); + return NULL; + } + + // end guard + if (mprotect((void*)((uintptr_t)buffer + pageSize + writableRegion.size + pageSize + + readableRegion.size), + pageSize, PROT_NONE) != 0) { + FIRCLSSDKLogError("Last guard protection failed %s\n", strerror(errno)); + return NULL; + } + + // now, perform our first "allocation", which is to place our allocator into the read-only region + allocator = FIRCLSAllocatorSafeAllocateFromRegion(&readableRegion, sizeof(FIRCLSAllocator)); + + // set up its data structure + allocator->buffer = buffer; + allocator->protectionEnabled = false; + allocator->readableRegion = readableRegion; + allocator->writeableRegion = writableRegion; + + FIRCLSSDKLogDebug("Allocator successfully created %p", allocator); + + return allocator; +} + +void FIRCLSAllocatorDestroy(FIRCLSAllocatorRef allocator) { + if (allocator) { + } +} + +bool FIRCLSAllocatorProtect(FIRCLSAllocatorRef allocator) { + void* address; + + if (!FIRCLSIsValidPointer(allocator)) { + FIRCLSSDKLogError("Invalid allocator"); + return false; + } + + if (allocator->protectionEnabled) { + FIRCLSSDKLogWarn("Write protection already enabled"); + return true; + } + + // This has to be done first + allocator->protectionEnabled = true; + + vm_size_t pageSize = FIRCLSHostGetPageSize(); + + // readable region + address = + (void*)((uintptr_t)allocator->buffer + pageSize + allocator->writeableRegion.size + pageSize); + + return mprotect(address, allocator->readableRegion.size, PROT_READ) == 0; +} + +bool FIRCLSAllocatorUnprotect(FIRCLSAllocatorRef allocator) { + size_t bufferSize; + + if (!allocator) { + return false; + } + + vm_size_t pageSize = FIRCLSHostGetPageSize(); + + bufferSize = (uintptr_t)allocator->buffer + pageSize + allocator->writeableRegion.size + + pageSize + allocator->readableRegion.size + pageSize; + + allocator->protectionEnabled = + !(mprotect(allocator->buffer, bufferSize, PROT_READ | PROT_WRITE) == 0); + + return allocator->protectionEnabled; +} + +void* FIRCLSAllocatorSafeAllocateFromRegion(FIRCLSAllocationRegion* region, size_t size) { + void* newCursor; + void* originalCursor; + + // Here's the idea + // - read the current cursor + // - compute what our new cursor should be + // - attempt a swap + // if the swap fails, some other thread has modified stuff, and we have to start again + // if the swap works, everything has been updated correctly and we are done + do { + originalCursor = region->cursor; + + // this shouldn't happen unless we make a mistake with our size pre-computations + if ((uintptr_t)originalCursor - (uintptr_t)region->start + size > region->size) { + FIRCLSSDKLog("Unable to allocate sufficient memory, falling back to malloc\n"); + void* ptr = malloc(size); + if (!ptr) { + FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocateFromRegion\n"); + return NULL; + } + return ptr; + } + + newCursor = (void*)((uintptr_t)originalCursor + size); + } while (!atomic_compare_exchange_strong(®ion->cursor, &originalCursor, newCursor)); + + return originalCursor; +} + +void* FIRCLSAllocatorSafeAllocate(FIRCLSAllocatorRef allocator, + size_t size, + FIRCLSAllocationType type) { + FIRCLSAllocationRegion* region; + + if (!allocator) { + // fall back to malloc in this case + FIRCLSSDKLog("Allocator invalid, falling back to malloc\n"); + void* ptr = malloc(size); + if (!ptr) { + FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocate\n"); + return NULL; + } + return ptr; + } + + if (allocator->protectionEnabled) { + FIRCLSSDKLog("Allocator already protected, falling back to malloc\n"); + void* ptr = malloc(size); + if (!ptr) { + FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocate\n"); + return NULL; + } + return ptr; + } + + switch (type) { + case CLS_READONLY: + region = &allocator->readableRegion; + break; + case CLS_READWRITE: + region = &allocator->writeableRegion; + break; + default: + return NULL; + } + + return FIRCLSAllocatorSafeAllocateFromRegion(region, size); +} + +void FIRCLSAllocatorFree(FIRCLSAllocatorRef allocator, void* ptr) { + if (!allocator) { + free(ptr); + } + + // how do we do deallocations? +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.h new file mode 100644 index 000000000..02b526a87 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.h @@ -0,0 +1,48 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSFeatures.h" + +#pragma once + +#include +#include + +typedef enum { CLS_READONLY = 0, CLS_READWRITE = 1 } FIRCLSAllocationType; + +typedef struct { + size_t size; + void* start; + _Atomic(void*) volatile cursor; +} FIRCLSAllocationRegion; + +typedef struct { + void* buffer; + bool protectionEnabled; + FIRCLSAllocationRegion writeableRegion; + FIRCLSAllocationRegion readableRegion; +} FIRCLSAllocator; +typedef FIRCLSAllocator* FIRCLSAllocatorRef; + +FIRCLSAllocatorRef FIRCLSAllocatorCreate(size_t writableSpace, size_t readableSpace); +void FIRCLSAllocatorDestroy(FIRCLSAllocatorRef allocator); + +bool FIRCLSAllocatorProtect(FIRCLSAllocatorRef allocator); +bool FIRCLSAllocatorUnprotect(FIRCLSAllocatorRef allocator); + +void* FIRCLSAllocatorSafeAllocate(FIRCLSAllocatorRef allocator, + size_t size, + FIRCLSAllocationType type); +const char* FIRCLSAllocatorSafeStrdup(FIRCLSAllocatorRef allocator, const char* string); +void FIRCLSAllocatorFree(FIRCLSAllocatorRef allocator, void* ptr); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h new file mode 100644 index 000000000..f309ae07a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h @@ -0,0 +1,81 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "TargetConditionals.h" + +// macro trickiness +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#define CONCAT_EXPANDED(a, b) a##b +#define CONCAT(a, b) CONCAT_EXPANDED(a, b) + +// These macros generate a function to force a symbol for the containing .o, to work around an issue +// where strip will not strip debug information without a symbol to strip. +#define DUMMY_FUNCTION_NAME(x) CONCAT(fircls_strip_this_, x) +#define INJECT_STRIP_SYMBOL(x) \ + void DUMMY_FUNCTION_NAME(x)(void) { \ + } + +// These make some target os types available to previous versions of xcode that do not yet have them +// in their SDKs +#ifndef TARGET_OS_IOS +#define TARGET_OS_IOS TARGET_OS_IPHONE +#endif + +#ifndef TARGET_OS_WATCH +#define TARGET_OS_WATCH 0 +#endif + +#ifndef TARGET_OS_TV +#define TARGET_OS_TV 0 +#endif + +// These help compile based on availability of technologies/frameworks. +#define CLS_TARGET_OS_OSX (TARGET_OS_MAC && !TARGET_OS_IPHONE) +#define CLS_TARGET_OS_HAS_UIKIT (TARGET_OS_IOS || TARGET_OS_TV) + +#define CLS_SDK_DISPLAY_VERSION STR(DISPLAY_VERSION) + +#define CLS_SDK_GENERATOR_NAME (STR(CLS_SDK_NAME) "/" CLS_SDK_DISPLAY_VERSION) + +// arch definitions +#if defined(__arm__) || defined(__arm64__) || defined(__arm64e__) +#include +#endif + +#if defined(__arm__) +#define CLS_CPU_ARM 1 +#endif +#if defined(__arm64__) || defined(__arm64e__) +#define CLS_CPU_ARM64 1 +#endif +#if defined(__ARM_ARCH_7S__) +#define CLS_CPU_ARMV7S 1 +#endif +#if defined(_ARM_ARCH_7) +#define CLS_CPU_ARMV7 1 +#endif +#if defined(_ARM_ARCH_6) +#define CLS_CPU_ARMV6 1 +#endif +#if defined(__i386__) +#define CLS_CPU_I386 1 +#endif +#if defined(__x86_64__) +#define CLS_CPU_X86_64 1 +#endif +#define CLS_CPU_X86 (CLS_CPU_I386 || CLS_CPU_X86_64) +#define CLS_CPU_64BIT (CLS_CPU_X86_64 || CLS_CPU_ARM64) diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.h new file mode 100644 index 000000000..098833f61 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.h @@ -0,0 +1,32 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRCLSSettings; +@protocol FIRAnalyticsInterop; +@protocol FIRAnalyticsInteropListener; + +@interface FIRCLSFCRAnalytics : NSObject + +/** Logs a Crashlytics crash session in Firebase Analytics. + * @param crashTimeStamp The time stamp of the crash to be logged. + */ ++ (void)logCrashWithTimeStamp:(NSTimeInterval)crashTimeStamp + toAnalytics:(id)analytics; + ++ (void)registerEventListener:(id)eventListener + toAnalytics:(id)analytics; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.m new file mode 100644 index 000000000..56dd33b98 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.m @@ -0,0 +1,78 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSFCRAnalytics.h" + +#import "FIRCLSInternalLogging.h" +#import "FIRCLSSettings.h" + +#import "Interop/Analytics/Public/FIRAnalyticsInterop.h" + +// Origin for events and user properties generated by Crashlytics. +static NSString *const kFIREventOriginCrash = @"clx"; + +// App exception event name. +static NSString *const kFIREventAppException = @"_ae"; + +// Timestamp key for the event payload. +static NSString *const kFIRParameterTimestamp = @"timestamp"; + +// Fatal key for the event payload. +static NSString *const kFIRParameterFatal = @"fatal"; + +FOUNDATION_STATIC_INLINE NSNumber *timeIntervalInMillis(NSTimeInterval timeInterval) { + return @(llrint(timeInterval * 1000.0)); +} + +@implementation FIRCLSFCRAnalytics + ++ (void)logCrashWithTimeStamp:(NSTimeInterval)crashTimeStamp + toAnalytics:(id)analytics { + if (analytics == nil) { + return; + } + + FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports:Event", "Sending event."); + NSDictionary *params = [self buildLogParamsFromCrash:crashTimeStamp]; + [analytics logEventWithOrigin:kFIREventOriginCrash name:kFIREventAppException parameters:params]; +} + ++ (void)registerEventListener:(id)eventListener + toAnalytics:(id)analytics { + if (analytics == nil) { + return; + } + + [analytics registerAnalyticsListener:eventListener withOrigin:kFIREventOriginCrash]; + + FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports:Event", + "Registered Firebase Analytics event listener"); +} + +/** + * Builds a dictionary of params to be sent to Analytics using the crash object. + * + * @param crashTimeStamp The time stamp of the crash to be logged. + * + * @return An NSDictionary containing the time the crash occured and the fatal + * flag to be fed into Firebase Analytics. + */ ++ (NSDictionary *)buildLogParamsFromCrash:(NSTimeInterval)crashTimeStamp { + return @{ + kFIRParameterTimestamp : timeIntervalInMillis(crashTimeStamp), + kFIRParameterFatal : @(INT64_C(1)) + }; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h new file mode 100644 index 000000000..4810a5d9b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h @@ -0,0 +1,31 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "FIRCLSDefines.h" + +#define CLS_MEMORY_PROTECTION_ENABLED 1 +#define CLS_COMPACT_UNWINDED_ENABLED 1 +#define CLS_DWARF_UNWINDING_ENABLED 1 + +#define CLS_USE_SIGALTSTACK (!TARGET_OS_WATCH && !TARGET_OS_TV) +#define CLS_CAN_SUSPEND_THREADS !TARGET_OS_WATCH +#define CLS_MACH_EXCEPTION_SUPPORTED (!TARGET_OS_WATCH && !TARGET_OS_TV) + +#define CLS_COMPACT_UNWINDING_SUPPORTED \ + ((CLS_CPU_I386 || CLS_CPU_X86_64 || CLS_CPU_ARM64) && CLS_COMPACT_UNWINDED_ENABLED) + +#define CLS_DWARF_UNWINDING_SUPPORTED \ + (CLS_COMPACT_UNWINDING_SUPPORTED && CLS_DWARF_UNWINDING_ENABLED) diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.h new file mode 100644 index 000000000..1714ac12e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.h @@ -0,0 +1,109 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +// Required for 1P builds +#include +#include +#include + +#if defined(__OBJC__) +#import +#endif + +__BEGIN_DECLS + +typedef struct { + int fd; + int collectionDepth; + bool needComma; + + bool bufferWrites; + char* writeBuffer; + size_t writeBufferLength; + + off_t writtenLength; +} FIRCLSFile; +typedef FIRCLSFile* FIRCLSFileRef; + +#define CLS_FILE_MAX_STRING_LENGTH (10240) +#define CLS_FILE_HEX_BUFFER \ + (32) // must be at least 2, and should be even (to account for 2 chars per hex value) +#define CLS_FILE_MAX_WRITE_ATTEMPTS (50) + +extern const size_t FIRCLSWriteBufferLength; + +// make sure to stop work if either FIRCLSFileInit... method returns false, because the FIRCLSFile +// struct will contain garbage data! +bool FIRCLSFileInitWithPath(FIRCLSFile* file, const char* path, bool bufferWrites); +bool FIRCLSFileInitWithPathMode(FIRCLSFile* file, + const char* path, + bool appendMode, + bool bufferWrites); + +void FIRCLSFileFlushWriteBuffer(FIRCLSFile* file); +bool FIRCLSFileClose(FIRCLSFile* file); +bool FIRCLSFileCloseWithOffset(FIRCLSFile* file, off_t* finalSize); +bool FIRCLSFileIsOpen(FIRCLSFile* file); + +bool FIRCLSFileLoopWithWriteBlock(const void* buffer, + size_t length, + ssize_t (^writeBlock)(const void* partialBuffer, + size_t partialLength)); +bool FIRCLSFileWriteWithRetries(int fd, const void* buffer, size_t length); + +// writing +void FIRCLSFileWriteSectionStart(FIRCLSFile* file, const char* name); +void FIRCLSFileWriteSectionEnd(FIRCLSFile* file); + +void FIRCLSFileWriteHashStart(FIRCLSFile* file); +void FIRCLSFileWriteHashEnd(FIRCLSFile* file); +void FIRCLSFileWriteHashKey(FIRCLSFile* file, const char* key); +void FIRCLSFileWriteHashEntryUint64(FIRCLSFile* file, const char* key, uint64_t value); +void FIRCLSFileWriteHashEntryInt64(FIRCLSFile* file, const char* key, int64_t value); +void FIRCLSFileWriteHashEntryString(FIRCLSFile* file, const char* key, const char* value); +#if defined(__OBJC__) +void FIRCLSFileWriteHashEntryNSString(FIRCLSFile* file, const char* key, NSString* string); +void FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(FIRCLSFile* file, + const char* key, + NSString* string); +#endif +void FIRCLSFileWriteHashEntryHexEncodedString(FIRCLSFile* file, const char* key, const char* value); +void FIRCLSFileWriteHashEntryBoolean(FIRCLSFile* file, const char* key, bool value); + +void FIRCLSFileWriteArrayStart(FIRCLSFile* file); +void FIRCLSFileWriteArrayEnd(FIRCLSFile* file); +void FIRCLSFileWriteArrayEntryUint64(FIRCLSFile* file, uint64_t value); +void FIRCLSFileWriteArrayEntryString(FIRCLSFile* file, const char* value); +void FIRCLSFileWriteArrayEntryHexEncodedString(FIRCLSFile* file, const char* value); + +void FIRCLSFileFDWriteUInt64(int fd, uint64_t number, bool hex); +void FIRCLSFileFDWriteInt64(int fd, int64_t number); +void FIRCLSFileWriteUInt64(FIRCLSFile* file, uint64_t number, bool hex); +void FIRCLSFileWriteInt64(FIRCLSFile* file, int64_t number); + +#if defined(__OBJC__) && TARGET_OS_MAC +NSArray* FIRCLSFileReadSections(const char* path, + bool deleteOnFailure, + NSObject* (^transformer)(id obj)); +NSString* FIRCLSFileHexEncodeString(const char* string); +NSString* FIRCLSFileHexDecodeString(const char* string); +#endif + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.m new file mode 100644 index 000000000..10b96d339 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.m @@ -0,0 +1,702 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSFile.h" + +#include "FIRCLSByteUtility.h" +#include "FIRCLSUtility.h" + +#if TARGET_OS_MAC +#include +#endif + +#include + +#include +#include + +#include + +// uint64_t should only have max 19 chars in base 10, and less in base 16 +static const size_t FIRCLSUInt64StringBufferLength = 21; +static const size_t FIRCLSStringBufferLength = 16; +const size_t FIRCLSWriteBufferLength = 1000; + +static bool FIRCLSFileInit(FIRCLSFile* file, int fdm, bool appendMode, bool bufferWrites); + +static void FIRCLSFileWriteToFileDescriptorOrBuffer(FIRCLSFile* file, + const char* string, + size_t length); +static void FIRCLSFileWriteToBuffer(FIRCLSFile* file, const char* string, size_t length); +static void FIRCLSFileWriteToFileDescriptor(FIRCLSFile* file, const char* string, size_t length); + +short FIRCLSFilePrepareUInt64(char* buffer, uint64_t number, bool hex); + +static void FIRCLSFileWriteString(FIRCLSFile* file, const char* string); +static void FIRCLSFileWriteHexEncodedString(FIRCLSFile* file, const char* string); +static void FIRCLSFileWriteBool(FIRCLSFile* file, bool value); + +static void FIRCLSFileWriteCollectionStart(FIRCLSFile* file, const char openingChar); +static void FIRCLSFileWriteCollectionEnd(FIRCLSFile* file, const char closingChar); +static void FIRCLSFileWriteColletionEntryProlog(FIRCLSFile* file); +static void FIRCLSFileWriteColletionEntryEpilog(FIRCLSFile* file); + +#define CLS_FILE_DEBUG_LOGGING 0 + +#pragma mark - File Structure +static bool FIRCLSFileInit(FIRCLSFile* file, int fd, bool appendMode, bool bufferWrites) { + if (!file) { + FIRCLSSDKLog("Error: file is null\n"); + return false; + } + + if (fd < 0) { + FIRCLSSDKLog("Error: file descriptor invalid\n"); + return false; + } + + memset(file, 0, sizeof(FIRCLSFile)); + + file->fd = fd; + + file->bufferWrites = bufferWrites; + if (bufferWrites) { + file->writeBuffer = malloc(FIRCLSWriteBufferLength * sizeof(char)); + if (!file->writeBuffer) { + FIRCLSErrorLog(@"Unable to malloc in FIRCLSFileInit"); + return false; + } + + file->writeBufferLength = 0; + } + + file->writtenLength = 0; + if (appendMode) { + struct stat fileStats; + fstat(fd, &fileStats); + off_t currentFileSize = fileStats.st_size; + if (currentFileSize > 0) { + file->writtenLength += currentFileSize; + } + } + + return true; +} + +bool FIRCLSFileInitWithPath(FIRCLSFile* file, const char* path, bool bufferWrites) { + return FIRCLSFileInitWithPathMode(file, path, true, bufferWrites); +} + +bool FIRCLSFileInitWithPathMode(FIRCLSFile* file, + const char* path, + bool appendMode, + bool bufferWrites) { + if (!file) { + FIRCLSSDKLog("Error: file is null\n"); + return false; + } + + int mask = O_WRONLY | O_CREAT; + + if (appendMode) { + mask |= O_APPEND; + } else { + mask |= O_TRUNC; + } + + // make sure to call FIRCLSFileInit no matter what + int fd = -1; + if (path) { +#if TARGET_OS_IPHONE + /* + * data-protected non-portable open(2) : + * int open_dprotected_np(user_addr_t path, int flags, int class, int dpflags, int mode) + */ + fd = open_dprotected_np(path, mask, 4, 0, 0644); +#else + fd = open(path, mask, 0644); +#endif + + if (fd < 0) { + FIRCLSSDKLog("Error: Unable to open file %s\n", strerror(errno)); + } + } + + return FIRCLSFileInit(file, fd, appendMode, bufferWrites); +} + +bool FIRCLSFileClose(FIRCLSFile* file) { + return FIRCLSFileCloseWithOffset(file, NULL); +} + +bool FIRCLSFileCloseWithOffset(FIRCLSFile* file, off_t* finalSize) { + if (!FIRCLSIsValidPointer(file)) { + return false; + } + + if (file->bufferWrites && FIRCLSIsValidPointer(file->writeBuffer)) { + if (file->writeBufferLength > 0) { + FIRCLSFileFlushWriteBuffer(file); + } + free(file->writeBuffer); + } + + if (FIRCLSIsValidPointer(finalSize)) { + *finalSize = file->writtenLength; + } + + if (close(file->fd) != 0) { + FIRCLSSDKLog("Error: Unable to close file %s\n", strerror(errno)); + return false; + } + + memset(file, 0, sizeof(FIRCLSFile)); + file->fd = -1; + + return true; +} + +bool FIRCLSFileIsOpen(FIRCLSFile* file) { + if (!FIRCLSIsValidPointer(file)) { + return false; + } + + return file->fd > -1; +} + +#pragma mark - Core Writing API +void FIRCLSFileFlushWriteBuffer(FIRCLSFile* file) { + if (!FIRCLSIsValidPointer(file)) { + return; + } + + if (!file->bufferWrites) { + return; + } + + FIRCLSFileWriteToFileDescriptor(file, file->writeBuffer, file->writeBufferLength); + file->writeBufferLength = 0; +} + +static void FIRCLSFileWriteToFileDescriptorOrBuffer(FIRCLSFile* file, + const char* string, + size_t length) { + if (file->bufferWrites) { + if (file->writeBufferLength + length > FIRCLSWriteBufferLength - 1) { + // fill remaining space in buffer + size_t remainingSpace = FIRCLSWriteBufferLength - file->writeBufferLength - 1; + FIRCLSFileWriteToBuffer(file, string, remainingSpace); + FIRCLSFileFlushWriteBuffer(file); + + // write remainder of string to newly-emptied buffer + size_t remainingLength = length - remainingSpace; + FIRCLSFileWriteToFileDescriptorOrBuffer(file, string + remainingSpace, remainingLength); + } else { + FIRCLSFileWriteToBuffer(file, string, length); + } + } else { + FIRCLSFileWriteToFileDescriptor(file, string, length); + } +} + +static void FIRCLSFileWriteToFileDescriptor(FIRCLSFile* file, const char* string, size_t length) { + if (!FIRCLSFileWriteWithRetries(file->fd, string, length)) { + return; + } + + file->writtenLength += length; +} + +// Beware calling this method directly: it will truncate the input string if it's longer +// than the remaining space in the buffer. It's safer to call through +// FIRCLSFileWriteToFileDescriptorOrBuffer. +static void FIRCLSFileWriteToBuffer(FIRCLSFile* file, const char* string, size_t length) { + size_t writeLength = length; + if (file->writeBufferLength + writeLength > FIRCLSWriteBufferLength - 1) { + writeLength = FIRCLSWriteBufferLength - file->writeBufferLength - 1; + } + strncpy(file->writeBuffer + file->writeBufferLength, string, writeLength); + file->writeBufferLength += writeLength; + file->writeBuffer[file->writeBufferLength] = '\0'; +} + +bool FIRCLSFileLoopWithWriteBlock(const void* buffer, + size_t length, + ssize_t (^writeBlock)(const void* buf, size_t len)) { + for (size_t count = 0; length > 0 && count < CLS_FILE_MAX_WRITE_ATTEMPTS; ++count) { + // try to write all that is left + ssize_t ret = writeBlock(buffer, length); + if (ret >= 0 && ret == length) { + return true; + } + + // Write was unsuccessful (out of space, etc) + if (ret < 0) { + return false; + } + + // We wrote more bytes than we expected, abort + if (ret > length) { + return false; + } + + // wrote a portion of the data, adjust and keep trying + if (ret > 0) { + length -= ret; + buffer += ret; + continue; + } + + // return value is <= 0, which is an error + break; + } + + return false; +} + +bool FIRCLSFileWriteWithRetries(int fd, const void* buffer, size_t length) { + return FIRCLSFileLoopWithWriteBlock(buffer, length, + ^ssize_t(const void* partialBuffer, size_t partialLength) { + return write(fd, partialBuffer, partialLength); + }); +} + +#pragma mark - Strings + +static void FIRCLSFileWriteUnbufferedStringWithSuffix(FIRCLSFile* file, + const char* string, + size_t length, + char suffix) { + char suffixBuffer[2]; + + // collaspe the quote + suffix into one single write call, for a small performance win + suffixBuffer[0] = '"'; + suffixBuffer[1] = suffix; + + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "\"", 1); + FIRCLSFileWriteToFileDescriptorOrBuffer(file, string, length); + FIRCLSFileWriteToFileDescriptorOrBuffer(file, suffixBuffer, suffix == 0 ? 1 : 2); +} + +static void FIRCLSFileWriteStringWithSuffix(FIRCLSFile* file, + const char* string, + size_t length, + char suffix) { + // 2 for quotes, 1 for suffix (if present) and 1 more for null character + const size_t maxStringSize = FIRCLSStringBufferLength - (suffix == 0 ? 3 : 4); + + if (length >= maxStringSize) { + FIRCLSFileWriteUnbufferedStringWithSuffix(file, string, length, suffix); + return; + } + + // we are trying to achieve this in one write call + // <"><"> + + char buffer[FIRCLSStringBufferLength]; + + buffer[0] = '"'; + + strncpy(buffer + 1, string, length); + + buffer[length + 1] = '"'; + length += 2; + + if (suffix) { + buffer[length] = suffix; + length += 1; + } + + // Always add the terminator. strncpy above would copy the terminator, if we supplied length + 1, + // but since we do this suffix adjustment here, it's easier to just fix it up in both cases. + buffer[length + 1] = 0; + + FIRCLSFileWriteToFileDescriptorOrBuffer(file, buffer, length); +} + +void FIRCLSFileWriteString(FIRCLSFile* file, const char* string) { + if (!string) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "null", 4); + return; + } + + FIRCLSFileWriteStringWithSuffix(file, string, strlen(string), 0); +} + +void FIRCLSFileWriteHexEncodedString(FIRCLSFile* file, const char* string) { + if (!file) { + return; + } + + if (!string) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "null", 4); + return; + } + + char buffer[CLS_FILE_HEX_BUFFER]; + + memset(buffer, 0, sizeof(buffer)); + + size_t length = strlen(string); + + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "\"", 1); + + int bufferIndex = 0; + for (int i = 0; i < length; ++i) { + FIRCLSHexFromByte(string[i], &buffer[bufferIndex]); + + bufferIndex += 2; // 1 char => 2 hex values at a time + + // we can continue only if we have enough space for two more hex + // characters *and* a terminator. So, we need three total chars + // of space + if (bufferIndex >= CLS_FILE_HEX_BUFFER) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, buffer, CLS_FILE_HEX_BUFFER); + bufferIndex = 0; + } + } + + // Copy the remainder, which could even be the entire string, if it + // fit into the buffer completely. Be careful with bounds checking here. + // The string needs to be non-empty, and we have to have copied at least + // one pair of hex characters in. + if (bufferIndex > 0 && length > 0) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, buffer, bufferIndex); + } + + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "\"", 1); +} + +#pragma mark - Integers +void FIRCLSFileWriteUInt64(FIRCLSFile* file, uint64_t number, bool hex) { + char buffer[FIRCLSUInt64StringBufferLength]; + short i = FIRCLSFilePrepareUInt64(buffer, number, hex); + char* beginning = &buffer[i]; // Write from a pointer to the begining of the string. + FIRCLSFileWriteToFileDescriptorOrBuffer(file, beginning, strlen(beginning)); +} + +void FIRCLSFileFDWriteUInt64(int fd, uint64_t number, bool hex) { + char buffer[FIRCLSUInt64StringBufferLength]; + short i = FIRCLSFilePrepareUInt64(buffer, number, hex); + char* beginning = &buffer[i]; // Write from a pointer to the begining of the string. + FIRCLSFileWriteWithRetries(fd, beginning, strlen(beginning)); +} + +void FIRCLSFileWriteInt64(FIRCLSFile* file, int64_t number) { + if (number < 0) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "-", 1); + number *= -1; // make it positive + } + + FIRCLSFileWriteUInt64(file, number, false); +} + +void FIRCLSFileFDWriteInt64(int fd, int64_t number) { + if (number < 0) { + FIRCLSFileWriteWithRetries(fd, "-", 1); + number *= -1; // make it positive + } + + FIRCLSFileFDWriteUInt64(fd, number, false); +} + +short FIRCLSFilePrepareUInt64(char* buffer, uint64_t number, bool hex) { + uint32_t base = hex ? 16 : 10; + + // zero it out, which will add a terminator + memset(buffer, 0, FIRCLSUInt64StringBufferLength); + + // TODO: look at this closer + // I'm pretty sure there is a bug in this code that + // can result in numbers with leading zeros. Technically, + // those are not valid json. + + // Set current index. + short i = FIRCLSUInt64StringBufferLength - 1; + + // Loop through filling in the chars from the end. + do { + char value = number % base + '0'; + if (value > '9') { + value += 'a' - '9' - 1; + } + + buffer[--i] = value; + } while ((number /= base) > 0 && i > 0); + + // returns index pointing to the beginning of the string. + return i; +} + +void FIRCLSFileWriteBool(FIRCLSFile* file, bool value) { + if (value) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "true", 4); + } else { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "false", 5); + } +} + +void FIRCLSFileWriteSectionStart(FIRCLSFile* file, const char* name) { + FIRCLSFileWriteHashStart(file); + FIRCLSFileWriteHashKey(file, name); +} + +void FIRCLSFileWriteSectionEnd(FIRCLSFile* file) { + FIRCLSFileWriteHashEnd(file); + FIRCLSFileWriteToFileDescriptorOrBuffer(file, "\n", 1); +} + +void FIRCLSFileWriteCollectionStart(FIRCLSFile* file, const char openingChar) { + char string[2]; + + string[0] = ','; + string[1] = openingChar; + + if (file->needComma) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, string, 2); // write the seperator + opening char + } else { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, &string[1], 1); // write only the opening char + } + + file->collectionDepth++; + + file->needComma = false; +} + +void FIRCLSFileWriteCollectionEnd(FIRCLSFile* file, const char closingChar) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, &closingChar, 1); + + if (file->collectionDepth <= 0) { + // FIRCLSSafeLog("Collection depth invariant violated\n"); + return; + } + + file->collectionDepth--; + + file->needComma = file->collectionDepth > 0; +} + +void FIRCLSFileWriteColletionEntryProlog(FIRCLSFile* file) { + if (file->needComma) { + FIRCLSFileWriteToFileDescriptorOrBuffer(file, ",", 1); + } +} + +void FIRCLSFileWriteColletionEntryEpilog(FIRCLSFile* file) { + file->needComma = true; +} + +void FIRCLSFileWriteHashStart(FIRCLSFile* file) { + FIRCLSFileWriteCollectionStart(file, '{'); +} + +void FIRCLSFileWriteHashEnd(FIRCLSFile* file) { + FIRCLSFileWriteCollectionEnd(file, '}'); +} + +void FIRCLSFileWriteHashKey(FIRCLSFile* file, const char* key) { + FIRCLSFileWriteColletionEntryProlog(file); + + FIRCLSFileWriteStringWithSuffix(file, key, strlen(key), ':'); + + file->needComma = false; +} + +void FIRCLSFileWriteHashEntryUint64(FIRCLSFile* file, const char* key, uint64_t value) { + // no prolog needed because it comes from the key + + FIRCLSFileWriteHashKey(file, key); + FIRCLSFileWriteUInt64(file, value, false); + + FIRCLSFileWriteColletionEntryEpilog(file); +} + +void FIRCLSFileWriteHashEntryInt64(FIRCLSFile* file, const char* key, int64_t value) { + // prolog from key + FIRCLSFileWriteHashKey(file, key); + FIRCLSFileWriteInt64(file, value); + + FIRCLSFileWriteColletionEntryEpilog(file); +} + +void FIRCLSFileWriteHashEntryString(FIRCLSFile* file, const char* key, const char* value) { + FIRCLSFileWriteHashKey(file, key); + FIRCLSFileWriteString(file, value); + + FIRCLSFileWriteColletionEntryEpilog(file); +} + +void FIRCLSFileWriteHashEntryNSString(FIRCLSFile* file, const char* key, NSString* string) { + FIRCLSFileWriteHashEntryString(file, key, [string UTF8String]); +} + +void FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(FIRCLSFile* file, + const char* key, + NSString* string) { + if ([string length] > 0) { + FIRCLSFileWriteHashEntryString(file, key, [string UTF8String]); + } +} + +void FIRCLSFileWriteHashEntryHexEncodedString(FIRCLSFile* file, + const char* key, + const char* value) { + FIRCLSFileWriteHashKey(file, key); + FIRCLSFileWriteHexEncodedString(file, value); + + FIRCLSFileWriteColletionEntryEpilog(file); +} + +void FIRCLSFileWriteHashEntryBoolean(FIRCLSFile* file, const char* key, bool value) { + FIRCLSFileWriteHashKey(file, key); + FIRCLSFileWriteBool(file, value); + + FIRCLSFileWriteColletionEntryEpilog(file); +} + +void FIRCLSFileWriteArrayStart(FIRCLSFile* file) { + FIRCLSFileWriteCollectionStart(file, '['); +} + +void FIRCLSFileWriteArrayEnd(FIRCLSFile* file) { + FIRCLSFileWriteCollectionEnd(file, ']'); +} + +void FIRCLSFileWriteArrayEntryUint64(FIRCLSFile* file, uint64_t value) { + FIRCLSFileWriteColletionEntryProlog(file); + + FIRCLSFileWriteUInt64(file, value, false); + + FIRCLSFileWriteColletionEntryEpilog(file); +} + +void FIRCLSFileWriteArrayEntryString(FIRCLSFile* file, const char* value) { + FIRCLSFileWriteColletionEntryProlog(file); + + FIRCLSFileWriteString(file, value); + + FIRCLSFileWriteColletionEntryEpilog(file); +} + +void FIRCLSFileWriteArrayEntryHexEncodedString(FIRCLSFile* file, const char* value) { + FIRCLSFileWriteColletionEntryProlog(file); + + FIRCLSFileWriteHexEncodedString(file, value); + + FIRCLSFileWriteColletionEntryEpilog(file); +} + +NSArray* FIRCLSFileReadSections(const char* path, + bool deleteOnFailure, + NSObject* (^transformer)(id obj)) { + if (!FIRCLSIsValidPointer(path)) { + FIRCLSSDKLogError("Error: input path is invalid\n"); + return nil; + } + + NSString* pathString = [NSString stringWithUTF8String:path]; + NSString* contents = [NSString stringWithContentsOfFile:pathString + encoding:NSUTF8StringEncoding + error:nil]; + NSArray* components = [contents componentsSeparatedByString:@"\n"]; + + if (!components) { + if (deleteOnFailure) { + unlink(path); + } + + FIRCLSSDKLog("Unable to read file %s\n", path); + return nil; + } + + NSMutableArray* array = [NSMutableArray array]; + + // loop through all the entires, and + for (NSString* component in components) { + NSData* data = [component dataUsingEncoding:NSUTF8StringEncoding]; + + id obj = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + if (!obj) { + continue; + } + + if (transformer) { + obj = transformer(obj); + } + + if (!obj) { + continue; + } + + [array addObject:obj]; + } + + return array; +} + +NSString* FIRCLSFileHexEncodeString(const char* string) { + size_t length = strlen(string); + char* encodedBuffer = malloc(length * 2 + 1); + + if (!encodedBuffer) { + FIRCLSErrorLog(@"Unable to malloc in FIRCLSFileHexEncodeString"); + return nil; + } + + memset(encodedBuffer, 0, length * 2 + 1); + + int bufferIndex = 0; + for (int i = 0; i < length; ++i) { + FIRCLSHexFromByte(string[i], &encodedBuffer[bufferIndex]); + + bufferIndex += 2; // 1 char => 2 hex values at a time + } + + NSString* stringObject = [NSString stringWithUTF8String:encodedBuffer]; + + free(encodedBuffer); + + return stringObject; +} + +NSString* FIRCLSFileHexDecodeString(const char* string) { + size_t length = strlen(string); + char* decodedBuffer = malloc(length); // too long, but safe + if (!decodedBuffer) { + FIRCLSErrorLog(@"Unable to malloc in FIRCLSFileHexDecodeString"); + return nil; + } + + memset(decodedBuffer, 0, length); + + for (int i = 0; i < length / 2; ++i) { + size_t index = i * 2; + + uint8_t hiNybble = FIRCLSNybbleFromChar(string[index]); + uint8_t lowNybble = FIRCLSNybbleFromChar(string[index + 1]); + + if (hiNybble == FIRCLSInvalidCharNybble || lowNybble == FIRCLSInvalidCharNybble) { + // char is invalid, abort loop + break; + } + + decodedBuffer[i] = (hiNybble << 4) | lowNybble; + } + + NSString* strObject = [NSString stringWithUTF8String:decodedBuffer]; + + free(decodedBuffer); + + return strObject; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.c new file mode 100644 index 000000000..d9e3daa09 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.c @@ -0,0 +1,96 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSInternalLogging.h" +#include "FIRCLSContext.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSUtility.h" + +void FIRCLSSDKFileLog(FIRCLSInternalLogLevel level, const char* format, ...) { + if (!_firclsContext.readonly || !_firclsContext.writable) { + return; + } + + const char* path = _firclsContext.readonly->logPath; + if (!FIRCLSIsValidPointer(path)) { + return; + } + + if (_firclsContext.writable->internalLogging.logLevel > level) { + return; + } + + if (_firclsContext.writable->internalLogging.logFd == -1) { + _firclsContext.writable->internalLogging.logFd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644); + } + + const int fd = _firclsContext.writable->internalLogging.logFd; + if (fd < 0) { + return; + } + + va_list args; + va_start(args, format); + +#if DEBUG && 0 + // It's nice to use printf here, so all the formatting works. However, its possible to hit a + // deadlock if you call vfprintf in a crash handler. So, this code is handy to keep, just in case, + // if there's a really tough thing to debug. + FILE* file = fopen(path, "a+"); + vfprintf(file, format, args); + fclose(file); +#else + size_t formatLength = strlen(format); + for (size_t idx = 0; idx < formatLength; ++idx) { + if (format[idx] != '%') { + write(fd, &format[idx], 1); + continue; + } + + idx++; // move to the format char + switch (format[idx]) { + case 'd': { + int value = va_arg(args, int); + FIRCLSFileFDWriteInt64(fd, value); + } break; + case 'u': { + uint32_t value = va_arg(args, uint32_t); + FIRCLSFileFDWriteUInt64(fd, value, false); + } break; + case 'p': { + uintptr_t value = va_arg(args, uintptr_t); + write(fd, "0x", 2); + FIRCLSFileFDWriteUInt64(fd, value, true); + } break; + case 's': { + const char* string = va_arg(args, const char*); + if (!string) { + string = "(null)"; + } + + write(fd, string, strlen(string)); + } break; + case 'x': { + unsigned int value = va_arg(args, unsigned int); + FIRCLSFileFDWriteUInt64(fd, value, true); + } break; + default: + // unhandled, back up to write out the percent + the format char + write(fd, &format[idx - 1], 2); + break; + } + } +#endif + va_end(args); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.h new file mode 100644 index 000000000..4ff164453 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.h @@ -0,0 +1,57 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#if __OBJC__ +#import "FIRCLSLogger.h" +#define FIRCLSDeveloperLog(label, __FORMAT__, ...) \ + FIRCLSDebugLog(@"[" label "] " __FORMAT__, ##__VA_ARGS__); +#endif + +typedef enum { + FIRCLSInternalLogLevelUnknown = 0, + FIRCLSInternalLogLevelDebug = 1, + FIRCLSInternalLogLevelInfo = 2, + FIRCLSInternalLogLevelWarn = 3, + FIRCLSInternalLogLevelError = 4 +} FIRCLSInternalLogLevel; + +typedef struct { + int logFd; + FIRCLSInternalLogLevel logLevel; +} FIRCLSInternalLoggingWritableContext; + +#define FIRCLSSDKLogDebug(__FORMAT__, ...) \ + FIRCLSSDKFileLog(FIRCLSInternalLogLevelDebug, "DEBUG [%s:%d] " __FORMAT__, __FUNCTION__, \ + __LINE__, ##__VA_ARGS__) +#define FIRCLSSDKLogInfo(__FORMAT__, ...) \ + FIRCLSSDKFileLog(FIRCLSInternalLogLevelInfo, "INFO [%s:%d] " __FORMAT__, __FUNCTION__, \ + __LINE__, ##__VA_ARGS__) +#define FIRCLSSDKLogWarn(__FORMAT__, ...) \ + FIRCLSSDKFileLog(FIRCLSInternalLogLevelWarn, "WARN [%s:%d] " __FORMAT__, __FUNCTION__, \ + __LINE__, ##__VA_ARGS__) +#define FIRCLSSDKLogError(__FORMAT__, ...) \ + FIRCLSSDKFileLog(FIRCLSInternalLogLevelError, "ERROR [%s:%d] " __FORMAT__, __FUNCTION__, \ + __LINE__, ##__VA_ARGS__) + +#define FIRCLSSDKLog FIRCLSSDKLogWarn + +__BEGIN_DECLS + +void FIRCLSSDKFileLog(FIRCLSInternalLogLevel level, const char* format, ...) __printflike(2, 3); + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h new file mode 100644 index 000000000..e03d99a9e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h @@ -0,0 +1,24 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +__BEGIN_DECLS + +void FIRCLSDebugLog(NSString *message, ...); +void FIRCLSInfoLog(NSString *message, ...); +void FIRCLSWarningLog(NSString *message, ...); +void FIRCLSErrorLog(NSString *message, ...); + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSLogger.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSLogger.m new file mode 100644 index 000000000..50bd1922f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSLogger.m @@ -0,0 +1,52 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSLogger.h" + +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" + +FIRLoggerService kFIRLoggerCrashlytics = @"[Firebase/Crashlytics]"; + +NSString *const CrashlyticsMessageCode = @"I-CLS000000"; + +void FIRCLSDebugLog(NSString *message, ...) { + va_list args_ptr; + va_start(args_ptr, message); + FIRLogBasic(FIRLoggerLevelDebug, kFIRLoggerCrashlytics, CrashlyticsMessageCode, message, + args_ptr); + va_end(args_ptr); +} + +void FIRCLSInfoLog(NSString *message, ...) { + va_list args_ptr; + va_start(args_ptr, message); + FIRLogBasic(FIRLoggerLevelInfo, kFIRLoggerCrashlytics, CrashlyticsMessageCode, message, args_ptr); + va_end(args_ptr); +} + +void FIRCLSWarningLog(NSString *message, ...) { + va_list args_ptr; + va_start(args_ptr, message); + FIRLogBasic(FIRLoggerLevelWarning, kFIRLoggerCrashlytics, CrashlyticsMessageCode, message, + args_ptr); + va_end(args_ptr); +} + +void FIRCLSErrorLog(NSString *message, ...) { + va_list args_ptr; + va_start(args_ptr, message); + FIRLogBasic(FIRLoggerLevelError, kFIRLoggerCrashlytics, CrashlyticsMessageCode, message, + args_ptr); + va_end(args_ptr); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSProfiling.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSProfiling.c new file mode 100644 index 000000000..df5008023 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSProfiling.c @@ -0,0 +1,47 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSProfiling.h" + +#include +#include + +FIRCLSProfileMark FIRCLSProfilingStart(void) { + return mach_absolute_time(); +} + +double FIRCLSProfileEnd(FIRCLSProfileMark mark) { + uint64_t duration = mach_absolute_time() - mark; + + mach_timebase_info_data_t info; + mach_timebase_info(&info); + + if (info.denom == 0) { + return 0.0; + } + + // Convert to nanoseconds + duration *= info.numer; + duration /= info.denom; + + return (double)duration / (double)NSEC_PER_MSEC; // return time in milliseconds +} + +void FIRCLSProfileBlock(const char* label, void (^block)(void)) { + FIRCLSProfileMark mark = FIRCLSProfilingStart(); + + block(); + + fprintf(stderr, "[Profile] %s: %f ms\n", label, FIRCLSProfileEnd(mark)); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSProfiling.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSProfiling.h new file mode 100644 index 000000000..5cc312f4e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSProfiling.h @@ -0,0 +1,29 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +typedef uint64_t FIRCLSProfileMark; + +__BEGIN_DECLS + +// high-resolution timing, returning the results in seconds +FIRCLSProfileMark FIRCLSProfilingStart(void); +double FIRCLSProfileEnd(FIRCLSProfileMark mark); + +void FIRCLSProfileBlock(const char* label, void (^block)(void)); + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.c new file mode 100644 index 000000000..44a300282 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.c @@ -0,0 +1,147 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSThreadState.h" +#include "FIRCLSDefines.h" +#include "FIRCLSUtility.h" + +#if defined(__arm__) || defined(__arm64__) +#include +#include +#endif + +#if CLS_CPU_X86_64 +#define GET_IP_REGISTER(r) (r->__ss.__rip) +#define GET_FP_REGISTER(r) (r->__ss.__rbp) +#define GET_SP_REGISTER(r) (r->__ss.__rsp) +#define GET_LR_REGISTER(r) 0 +#define SET_IP_REGISTER(r, v) (r->__ss.__rip = v) +#define SET_FP_REGISTER(r, v) (r->__ss.__rbp = v) +#define SET_SP_REGISTER(r, v) (r->__ss.__rsp = v) +#define SET_LR_REGISTER(r, v) +#elif CLS_CPU_I386 +#define GET_IP_REGISTER(r) (r->__ss.__eip) +#define GET_FP_REGISTER(r) (r->__ss.__ebp) +#define GET_SP_REGISTER(r) (r->__ss.__esp) +#define GET_LR_REGISTER(r) 0 +#define SET_IP_REGISTER(r, v) (r->__ss.__eip = v) +#define SET_FP_REGISTER(r, v) (r->__ss.__ebp = v) +#define SET_SP_REGISTER(r, v) (r->__ss.__esp = v) +#define SET_LR_REGISTER(r, v) +#elif CLS_CPU_ARM64 +// The arm_thread_state64_get_* macros translate down to the AUTIA and AUTIB instructions which +// authenticate the address, but don't clear the upper bits. From the docs: +// "If the authentication passes, the upper bits of the address are restored to enable +// subsequent use of the address. the authentication fails, the upper bits are corrupted and +// any subsequent use of the address results in a Translation fault." +// Since we only want the address (with the metadata in the upper bits masked out), we used the +// ptrauth_strip macro to clear the upper bits. +// +// We found later that ptrauth_strip doesn't seem to do anything. In many cases, the upper bits were +// already stripped, so for most non-system-library code, Crashlytics would still symbolicate. But +// for system libraries, the upper bits were being left in even when we called ptrauth_strip. +// Instead, we're bit masking and only allowing the latter 36 bits. +#define CLS_PTRAUTH_STRIP(pointer) ((uintptr_t)pointer & 0x0000000FFFFFFFFF) +#define GET_IP_REGISTER(r) (CLS_PTRAUTH_STRIP(arm_thread_state64_get_pc(r->__ss))) +#define GET_FP_REGISTER(r) (CLS_PTRAUTH_STRIP(arm_thread_state64_get_fp(r->__ss))) +#define GET_SP_REGISTER(r) (CLS_PTRAUTH_STRIP(arm_thread_state64_get_sp(r->__ss))) +#define GET_LR_REGISTER(r) (CLS_PTRAUTH_STRIP(arm_thread_state64_get_lr(r->__ss))) +#define SET_IP_REGISTER(r, v) arm_thread_state64_set_pc_fptr(r->__ss, (void*)v) +#define SET_FP_REGISTER(r, v) arm_thread_state64_set_fp(r->__ss, v) +#define SET_SP_REGISTER(r, v) arm_thread_state64_set_sp(r->__ss, v) +#define SET_LR_REGISTER(r, v) arm_thread_state64_set_lr_fptr(r->__ss, (void*)v) +#elif CLS_CPU_ARM +#define GET_IP_REGISTER(r) (r->__ss.__pc) +#define GET_FP_REGISTER(r) (r->__ss.__r[7]) +#define GET_SP_REGISTER(r) (r->__ss.__sp) +#define GET_LR_REGISTER(r) (r->__ss.__lr) +#define SET_IP_REGISTER(r, v) (r->__ss.__pc = v) +#define SET_FP_REGISTER(r, v) (r->__ss.__r[7] = v) +#define SET_SP_REGISTER(r, v) (r->__ss.__sp = v) +#define SET_LR_REGISTER(r, v) (r->__ss.__lr = v) +#else +#error "Architecture Unsupported" +#endif + +uintptr_t FIRCLSThreadContextGetPC(FIRCLSThreadContext* registers) { + if (!registers) { + return 0; + } + + return GET_IP_REGISTER(registers); +} + +uintptr_t FIRCLSThreadContextGetStackPointer(const FIRCLSThreadContext* registers) { + if (!registers) { + return 0; + } + + return GET_SP_REGISTER(registers); +} + +bool FIRCLSThreadContextSetStackPointer(FIRCLSThreadContext* registers, uintptr_t value) { + if (!FIRCLSIsValidPointer(registers)) { + return false; + } + + SET_SP_REGISTER(registers, value); + + return true; +} + +uintptr_t FIRCLSThreadContextGetLinkRegister(const FIRCLSThreadContext* registers) { + if (!FIRCLSIsValidPointer(registers)) { + return 0; + } + + return GET_LR_REGISTER(registers); +} + +bool FIRCLSThreadContextSetLinkRegister(FIRCLSThreadContext* registers, uintptr_t value) { + if (!FIRCLSIsValidPointer(registers)) { + return false; + } + + SET_LR_REGISTER(registers, value); + + return true; +} + +bool FIRCLSThreadContextSetPC(FIRCLSThreadContext* registers, uintptr_t value) { + if (!registers) { + return false; + } + + SET_IP_REGISTER(registers, value); + + return true; +} + +uintptr_t FIRCLSThreadContextGetFramePointer(const FIRCLSThreadContext* registers) { + if (!FIRCLSIsValidPointer(registers)) { + return 0; + } + + return GET_FP_REGISTER(registers); +} + +bool FIRCLSThreadContextSetFramePointer(FIRCLSThreadContext* registers, uintptr_t value) { + if (!FIRCLSIsValidPointer(registers)) { + return false; + } + + SET_FP_REGISTER(registers, value); + + return true; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.h new file mode 100644 index 000000000..f281f665c --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.h @@ -0,0 +1,57 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#if CLS_CPU_ARM +#define FIRCLSThreadStateCount ARM_THREAD_STATE_COUNT +#define FIRCLSThreadState ARM_THREAD_STATE +#elif CLS_CPU_ARM64 +#define FIRCLSThreadStateCount ARM_THREAD_STATE64_COUNT +#define FIRCLSThreadState ARM_THREAD_STATE64 +#elif CLS_CPU_I386 +#define FIRCLSThreadStateCount x86_THREAD_STATE32_COUNT +#define FIRCLSThreadState x86_THREAD_STATE32 +#elif CLS_CPU_X86_64 +#define FIRCLSThreadStateCount x86_THREAD_STATE64_COUNT +#define FIRCLSThreadState x86_THREAD_STATE64 +#endif + +// _STRUCT_MCONTEXT was fixed to point to the right thing on ARM in the iOS 7.1 SDK +typedef _STRUCT_MCONTEXT FIRCLSThreadContext; + +// I'm not entirely sure what happened when, but this appears to have disappeared from +// the SDKs... +#if !defined(_STRUCT_UCONTEXT64) +typedef _STRUCT_UCONTEXT _STRUCT_UCONTEXT64; +#endif + +#pragma mark Register Access + +uintptr_t FIRCLSThreadContextGetPC(FIRCLSThreadContext* registers); +uintptr_t FIRCLSThreadContextGetStackPointer(const FIRCLSThreadContext* registers); +uintptr_t FIRCLSThreadContextGetFramePointer(const FIRCLSThreadContext* registers); + +bool FIRCLSThreadContextSetPC(FIRCLSThreadContext* registers, uintptr_t value); +bool FIRCLSThreadContextSetStackPointer(FIRCLSThreadContext* registers, uintptr_t value); +bool FIRCLSThreadContextSetFramePointer(FIRCLSThreadContext* registers, uintptr_t value); + +// The link register only exists on ARM platforms. +#if CLS_CPU_ARM || CLS_CPU_ARM64 +uintptr_t FIRCLSThreadContextGetLinkRegister(const FIRCLSThreadContext* registers); +bool FIRCLSThreadContextSetLinkRegister(FIRCLSThreadContext* registers, uintptr_t value); +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h new file mode 100644 index 000000000..5a50f0fd3 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h @@ -0,0 +1,54 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include +#include "FIRCLSGlobals.h" + +#define FIRCLSIsValidPointer(x) ((uintptr_t)x >= 4096) +#define FIRCLSInvalidCharNybble (255) + +__BEGIN_DECLS + +void FIRCLSLookupFunctionPointer(void* ptr, void (^block)(const char* name, const char* lib)); + +void FIRCLSHexFromByte(uint8_t c, char output[]); +uint8_t FIRCLSNybbleFromChar(char c); + +bool FIRCLSReadMemory(vm_address_t src, void* dest, size_t len); +bool FIRCLSReadString(vm_address_t src, char** dest, size_t maxlen); + +const char* FIRCLSDupString(const char* string); + +bool FIRCLSUnlinkIfExists(const char* path); + +#if __OBJC__ +void FIRCLSDispatchAfter(float timeInSeconds, dispatch_queue_t queue, dispatch_block_t block); + +NSString* FIRCLSNormalizeUUID(NSString* value); +NSString* FIRCLSGenerateNormalizedUUID(void); + +NSString* FIRCLSNSDataToNSString(NSData* data); + +void FIRCLSAddOperationAfter(float timeInSeconds, NSOperationQueue* queue, void (^block)(void)); +#endif + +#if DEBUG +void FIRCLSPrintAUUID(const uint8_t* value); +#endif + +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSUtility.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSUtility.m new file mode 100644 index 000000000..520640f3c --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSUtility.m @@ -0,0 +1,218 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSUtility.h" + +#include + +#include + +#include "FIRCLSFeatures.h" +#include "FIRCLSFile.h" +#include "FIRCLSGlobals.h" + +#import "FIRCLSByteUtility.h" +#import "FIRCLSUUID.h" + +#import + +void FIRCLSLookupFunctionPointer(void* ptr, void (^block)(const char* name, const char* lib)) { + Dl_info info; + + if (dladdr(ptr, &info) == 0) { + block(NULL, NULL); + return; + } + + const char* name = "unknown"; + const char* lib = "unknown"; + + if (info.dli_sname) { + name = info.dli_sname; + } + + if (info.dli_fname) { + lib = info.dli_fname; + } + + block(name, lib); +} + +uint8_t FIRCLSNybbleFromChar(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + + return FIRCLSInvalidCharNybble; +} + +bool FIRCLSReadMemory(vm_address_t src, void* dest, size_t len) { + if (!FIRCLSIsValidPointer(src)) { + return false; + } + + vm_size_t readSize = len; + + return vm_read_overwrite(mach_task_self(), src, len, (pointer_t)dest, &readSize) == KERN_SUCCESS; +} + +bool FIRCLSReadString(vm_address_t src, char** dest, size_t maxlen) { + char c; + vm_address_t address; + + if (!dest) { + return false; + } + + // Walk the entire string. Not certain this is perfect... + for (address = src; address < src + maxlen; ++address) { + if (!FIRCLSReadMemory(address, &c, 1)) { + return false; + } + + if (c == 0) { + break; + } + } + + *dest = (char*)src; + + return true; +} + +const char* FIRCLSDupString(const char* string) { +#if CLS_MEMORY_PROTECTION_ENABLED + char* buffer; + size_t length; + + if (!string) { + return NULL; + } + + length = strlen(string); + buffer = FIRCLSAllocatorSafeAllocate(_firclsContext.allocator, length + 1, CLS_READONLY); + + memcpy(buffer, string, length); + + buffer[length] = 0; // null-terminate + + return buffer; +#else + return strdup(string); +#endif +} + +void FIRCLSDispatchAfter(float timeInSeconds, dispatch_queue_t queue, dispatch_block_t block) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeInSeconds * NSEC_PER_SEC)), queue, + block); +} + +bool FIRCLSUnlinkIfExists(const char* path) { + if (unlink(path) != 0) { + if (errno != ENOENT) { + return false; + } + } + + return true; +} + +/* +NSString* FIRCLSGenerateUUID(void) { + NSString* string; + + CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); + string = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid)); + CFRelease(uuid); + + return string; +} +*/ + +NSString* FIRCLSNormalizeUUID(NSString* value) { + return [[value stringByReplacingOccurrencesOfString:@"-" withString:@""] lowercaseString]; +} + +NSString* FIRCLSGenerateNormalizedUUID(void) { + return FIRCLSNormalizeUUID(FIRCLSGenerateUUID()); +} + +NSString* FIRCLSNSDataToNSString(NSData* data) { + NSString* string; + char* buffer; + size_t size; + NSUInteger length; + + // we need 2 hex char for every byte of data, plus one more spot for a + // null terminator + length = [data length]; + size = (length * 2) + 1; + buffer = malloc(sizeof(char) * size); + + if (!buffer) { + FIRCLSErrorLog(@"Unable to malloc in FIRCLSNSDataToNSString"); + return nil; + } + + FIRCLSSafeHexToString([data bytes], length, buffer); + + string = [NSString stringWithUTF8String:buffer]; + + free(buffer); + + return string; +} + +/* +NSString* FIRCLSHashBytes(const void* bytes, size_t length) { + uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0}; + CC_SHA1(bytes, (CC_LONG)length, digest); + + NSData* result = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]; + + return FIRCLSNSDataToNSString(result); +} + +NSString* FIRCLSHashNSData(NSData* data) { + return FIRCLSHashBytes([data bytes], [data length]); +} +*/ + +void FIRCLSAddOperationAfter(float timeInSeconds, NSOperationQueue* queue, void (^block)(void)) { + dispatch_queue_t afterQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + FIRCLSDispatchAfter(timeInSeconds, afterQueue, ^{ + [queue addOperationWithBlock:block]; + }); +} + +#if DEBUG +void FIRCLSPrintAUUID(const uint8_t* value) { + CFUUIDRef uuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *(CFUUIDBytes*)value); + + NSString* string = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid)); + + CFRelease(uuid); + + FIRCLSDebugLog(@"%@", [[string stringByReplacingOccurrencesOfString:@"-" + withString:@""] lowercaseString]); +} +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.h new file mode 100644 index 000000000..41a489672 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.h @@ -0,0 +1,33 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class is a model to identify a single execution of the app + */ +@interface FIRCLSExecutionIdentifierModel : NSObject + +/** + * Returns the launch identifier. This is a unique id that will remain constant until this process + * is relaunched. This value is useful for correlating events across kits and/or across reports at + * the process-lifecycle level. + */ +@property(nonatomic, readonly) NSString *executionID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.m new file mode 100644 index 000000000..e312d46f8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.m @@ -0,0 +1,33 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSExecutionIdentifierModel.h" + +#import "FIRCLSUUID.h" + +@implementation FIRCLSExecutionIdentifierModel + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _executionID = [[FIRCLSGenerateUUID() stringByReplacingOccurrencesOfString:@"-" + withString:@""] lowercaseString]; + + return self; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSFileManager.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSFileManager.h new file mode 100644 index 000000000..dd988a950 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSFileManager.h @@ -0,0 +1,73 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRCLSInternalReport; + +@interface FIRCLSFileManager : NSObject + +- (instancetype)init NS_DESIGNATED_INITIALIZER; + +@property(nonatomic, readonly) NSFileManager *underlyingFileManager; + +/** + * Returns the folder containing the settings file + */ +@property(nonatomic, readonly) NSString *settingsDirectoryPath; + +/** + * Returns the path to the settings file + */ +@property(nonatomic, readonly) NSString *settingsFilePath; + +/** + * Path to the file that holds the ttl and keys that invalidate settings + */ +@property(nonatomic, readonly) NSString *settingsCacheKeyPath; + +@property(nonatomic, readonly) NSString *rootPath; +@property(nonatomic, readonly) NSString *structurePath; +@property(nonatomic, readonly) NSString *activePath; +@property(nonatomic, readonly) NSString *processingPath; +@property(nonatomic, readonly) NSString *pendingPath; +@property(nonatomic, readonly) NSString *preparedPath; +@property(nonatomic, readonly) NSString *legacyPreparedPath; +@property(nonatomic, readonly) NSArray *activePathContents; +@property(nonatomic, readonly) NSArray *legacyPreparedPathContents; +@property(nonatomic, readonly) NSArray *preparedPathContents; +@property(nonatomic, readonly) NSArray *processingPathContents; + +- (BOOL)fileExistsAtPath:(NSString *)path; +- (BOOL)createFileAtPath:(NSString *)path + contents:(NSData *)data + attributes:(NSDictionary *)attr; +- (BOOL)createDirectoryAtPath:(NSString *)path; +- (BOOL)removeItemAtPath:(NSString *)path; +- (BOOL)removeContentsOfDirectoryAtPath:(NSString *)path; +- (BOOL)moveItemAtPath:(NSString *)path toDirectory:(NSString *)destDir; +- (void)enumerateFilesInDirectory:(NSString *)directory + usingBlock:(void (^)(NSString *filePath, NSString *extension))block; +- (NSNumber *)fileSizeAtPath:(NSString *)path; +- (NSArray *)contentsOfDirectory:(NSString *)path; + +// logic of managing files/directories +- (BOOL)createReportDirectories; +- (NSString *)setupNewPathForExecutionIdentifier:(NSString *)identifier; + +- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error; + +- (NSData *)dataWithContentsOfFile:(NSString *)path; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSFileManager.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSFileManager.m new file mode 100644 index 000000000..040dcf5b8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSFileManager.m @@ -0,0 +1,279 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSFileManager.h" + +#import "FIRCLSApplication.h" +#import "FIRCLSInternalReport.h" +#import "FIRCLSLogger.h" + +NSString *const FIRCLSCacheDirectoryName = @"com.crashlytics.data"; +NSString *const FIRCLSCacheVersion = @"v5"; + +@interface FIRCLSFileManager () { + NSString *_rootPath; +} + +@end + +@implementation FIRCLSFileManager + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _underlyingFileManager = [NSFileManager defaultManager]; + + NSString *path = + [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; + path = [path stringByAppendingPathComponent:FIRCLSCacheDirectoryName]; + path = [path stringByAppendingPathComponent:[self pathNamespace]]; + _rootPath = [path copy]; + + return self; +} + +#pragma mark - Core API + +- (BOOL)fileExistsAtPath:(NSString *)path { + return [_underlyingFileManager fileExistsAtPath:path]; +} + +- (BOOL)createFileAtPath:(NSString *)path + contents:(nullable NSData *)data + attributes:(nullable NSDictionary *)attr { + return [_underlyingFileManager createFileAtPath:path contents:data attributes:attr]; +} + +- (BOOL)createDirectoryAtPath:(NSString *)path { + NSDictionary *attributes; + NSError *error; + + attributes = @{NSFilePosixPermissions : [NSNumber numberWithShort:0755]}; + error = nil; + + if (![[self underlyingFileManager] createDirectoryAtPath:path + withIntermediateDirectories:YES + attributes:attributes + error:&error]) { + FIRCLSErrorLog(@"Unable to create directory %@", error); + return NO; + } + + return YES; +} + +- (BOOL)removeItemAtPath:(NSString *)path { + NSError *error; + + error = nil; + if (![[self underlyingFileManager] removeItemAtPath:path error:&error] || !path) { + FIRCLSErrorLog(@"Failed to remove file %@: %@", path, error); + + return NO; + } + + return YES; +} + +- (BOOL)removeContentsOfDirectoryAtPath:(NSString *)path { + __block BOOL success = YES; + + // only return true if we were able to remove every item in the directory (or it was empty) + + [self enumerateFilesInDirectory:path + usingBlock:^(NSString *filePath, NSString *extension) { + success = [self removeItemAtPath:filePath] && success; + }]; + + return success; +} + +- (BOOL)moveItemAtPath:(NSString *)path toDirectory:(NSString *)destDir { + NSString *destPath; + NSError *error; + + destPath = [destDir stringByAppendingPathComponent:[path lastPathComponent]]; + error = nil; + + if (!path || !destPath) { + FIRCLSErrorLog(@"Failed to move file, inputs invalid"); + + return NO; + } + + if (![[self underlyingFileManager] moveItemAtPath:path toPath:destPath error:&error]) { + FIRCLSErrorLog(@"Failed to move file: %@", error); + + return NO; + } + + return YES; +} + +- (void)enumerateFilesInDirectory:(NSString *)directory + usingBlock:(void (^)(NSString *filePath, NSString *extension))block { + for (NSString *path in [[self underlyingFileManager] contentsOfDirectoryAtPath:directory + error:nil]) { + NSString *extension; + NSString *fullPath; + + // Skip files that start with a dot. This is important, because if you try to move a .DS_Store + // file, it will fail if the target directory also has a .DS_Store file in it. Plus, its + // wasteful, because we don't care about dot files. + if ([path hasPrefix:@"."]) { + continue; + } + + extension = [path pathExtension]; + fullPath = [directory stringByAppendingPathComponent:path]; + if (block) { + block(fullPath, extension); + } + } +} + +- (NSNumber *)fileSizeAtPath:(NSString *)path { + NSError *error = nil; + NSDictionary *attrs = [[self underlyingFileManager] attributesOfItemAtPath:path error:&error]; + + if (!attrs) { + FIRCLSErrorLog(@"Unable to read file size: %@", error); + return nil; + } + + return [attrs objectForKey:NSFileSize]; +} + +- (NSArray *)contentsOfDirectory:(NSString *)path { + NSMutableArray *array = [NSMutableArray array]; + + [self enumerateFilesInDirectory:path + usingBlock:^(NSString *filePath, NSString *extension) { + [array addObject:filePath]; + }]; + + return [array copy]; +} + +#pragma - Properties +- (NSString *)pathNamespace { + return FIRCLSApplicationGetBundleIdentifier(); +} + +- (NSString *)versionedPath { + return [[self rootPath] stringByAppendingPathComponent:FIRCLSCacheVersion]; +} + +#pragma - Settings Paths + +// This path should be different than the structurePath because the +// settings download operations will delete the settings directory, +// which would delete crash reports if these were the same +- (NSString *)settingsDirectoryPath { + return [[self versionedPath] stringByAppendingPathComponent:@"settings"]; +} + +- (NSString *)settingsFilePath { + return [[self settingsDirectoryPath] stringByAppendingPathComponent:@"settings.json"]; +} + +- (NSString *)settingsCacheKeyPath { + return [[self settingsDirectoryPath] stringByAppendingPathComponent:@"cache-key.json"]; +} + +#pragma - Report Paths +- (NSString *)structurePath { + return [[self versionedPath] stringByAppendingPathComponent:@"reports"]; +} + +- (NSString *)activePath { + return [[self structurePath] stringByAppendingPathComponent:@"active"]; +} + +- (NSString *)pendingPath { + return [[self structurePath] stringByAppendingPathComponent:@"pending"]; +} + +- (NSString *)processingPath { + return [[self structurePath] stringByAppendingPathComponent:@"processing"]; +} + +- (NSString *)legacyPreparedPath { + return [[self structurePath] stringByAppendingPathComponent:@"prepared-legacy"]; +} + +- (NSString *)preparedPath { + return [[self structurePath] stringByAppendingPathComponent:@"prepared"]; +} + +- (NSArray *)activePathContents { + return [self contentsOfDirectory:[self activePath]]; +} + +- (NSArray *)legacyPreparedPathContents { + return [self contentsOfDirectory:[self legacyPreparedPath]]; +} + +- (NSArray *)preparedPathContents { + return [self contentsOfDirectory:[self preparedPath]]; +} + +- (NSArray *)processingPathContents { + return [self contentsOfDirectory:[self processingPath]]; +} + +#pragma mark - Logic +- (BOOL)createReportDirectories { + if (![self createDirectoryAtPath:[self activePath]]) { + return NO; + } + + if (![self createDirectoryAtPath:[self processingPath]]) { + return NO; + } + + if (![self createDirectoryAtPath:[self legacyPreparedPath]]) { + return NO; + } + + if (![self createDirectoryAtPath:[self preparedPath]]) { + return NO; + } + + return YES; +} + +- (NSString *)setupNewPathForExecutionIdentifier:(NSString *)identifier { + NSString *path = [[self activePath] stringByAppendingPathComponent:identifier]; + + if (![self createDirectoryAtPath:path]) { + return nil; + } + + return path; +} + +- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error { + return [self.underlyingFileManager moveItemAtPath:srcPath toPath:dstPath error:error]; +} + +// Wrapper over NSData so the method can be mocked for unit tests +- (NSData *)dataWithContentsOfFile:(NSString *)path { + return [NSData dataWithContentsOfFile:path]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h new file mode 100644 index 000000000..6100c8a97 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h @@ -0,0 +1,46 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRInstallations; + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class is a model for identifying an installation of an app + */ +@interface FIRCLSInstallIdentifierModel : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +- (instancetype)initWithInstallations:(FIRInstallations *)instanceID NS_DESIGNATED_INITIALIZER; + +/** + * Returns the backwards compatible Crashlytics Installation UUID + */ +@property(nonatomic, readonly) NSString *installID; + +/** + * To support end-users rotating Install IDs, this will check and rotate the Install ID, + * which is a costly operation performance-wise. To keep the startup time impact down, call this in + * a background thread. + * + * The block will be called on a background thread. + */ +- (void)regenerateInstallIDIfNeededWithBlock:(void (^)(BOOL didRotate))callback; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.m new file mode 100644 index 000000000..2af2d7dbc --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.m @@ -0,0 +1,161 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSInstallIdentifierModel.h" + +#import + +#import "FIRCLSByteUtility.h" +#import "FIRCLSLogger.h" +#import "FIRCLSUUID.h" +#import "FIRCLSUserDefaults.h" + +static NSString *const FIRCLSInstallationUUIDKey = @"com.crashlytics.iuuid"; +static NSString *const FIRCLSInstallationIIDHashKey = @"com.crashlytics.install.iid"; + +// Legacy key that is automatically removed +static NSString *const FIRCLSInstallationADIDKey = @"com.crashlytics.install.adid"; + +@interface FIRCLSInstallIdentifierModel () + +@property(nonatomic, copy) NSString *installID; + +@property(nonatomic, readonly) FIRInstallations *installations; + +@end + +@implementation FIRCLSInstallIdentifierModel + +// This needs to be synthesized so we can set without using the setter in the constructor and +// overridden setters and getters +@synthesize installID = _installID; + +- (instancetype)initWithInstallations:(FIRInstallations *)installations { + self = [super init]; + if (!self) { + return nil; + } + + // capture the install ID information + _installID = [self readInstallationUUID].copy; + _installations = installations; + + if (!_installID) { + FIRCLSDebugLog(@"Generating Install ID"); + _installID = [self generateInstallationUUID].copy; + + FIRCLSUserDefaults *defaults = [FIRCLSUserDefaults standardUserDefaults]; + [defaults synchronize]; + } + + return self; +} + +- (NSString *)installID { + @synchronized(self) { + return _installID; + } +} + +- (void)setInstallID:(NSString *)installID { + @synchronized(self) { + _installID = installID; + } +} + +/** + * Reads installation UUID stored in persistent storage. + * If the installation UUID is stored in legacy key, migrates it over to the new key. + */ +- (NSString *)readInstallationUUID { + return [[FIRCLSUserDefaults standardUserDefaults] objectForKey:FIRCLSInstallationUUIDKey]; +} + +/** + * Generates a new UUID and saves it in persistent storage. + * Does not sychronize the user defaults (to allow optimized + * batching of user default synchronizing) + */ +- (NSString *)generateInstallationUUID { + NSString *UUID = FIRCLSGenerateUUID(); + FIRCLSUserDefaults *userDefaults = [FIRCLSUserDefaults standardUserDefaults]; + [userDefaults setObject:UUID forKey:FIRCLSInstallationUUIDKey]; + return UUID; +} + +#pragma mark Privacy Shield + +/** + * To support privacy shield we need to regenerate the install id when the IID changes. + * + * This is a blocking, slow call that must be called on a background thread. + */ +- (void)regenerateInstallIDIfNeededWithBlock:(void (^)(BOOL didRotate))callback { + // This callback is on the main thread + [self.installations + installationIDWithCompletion:^(NSString *_Nullable currentIID, NSError *_Nullable error) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + BOOL didRotate = [self rotateCrashlyticsInstallUUIDWithIID:currentIID error:error]; + callback(didRotate); + }); + }]; +} + +- (BOOL)rotateCrashlyticsInstallUUIDWithIID:(NSString *_Nullable)currentIID + error:(NSError *_Nullable)error { + BOOL didRotate = NO; + + FIRCLSUserDefaults *defaults = [FIRCLSUserDefaults standardUserDefaults]; + + // Remove the legacy ID + NSString *adID = [defaults objectForKey:FIRCLSInstallationADIDKey]; + if (adID.length != 0) { + [defaults removeObjectForKey:FIRCLSInstallationADIDKey]; + [defaults synchronize]; + } + + if (error != nil) { + FIRCLSErrorLog(@"Failed to get Firebase Instance ID: %@", error); + return didRotate; + } + + if (currentIID.length == 0) { + FIRCLSErrorLog(@"Firebase Instance ID was empty when checked for changes"); + return didRotate; + } + + NSString *currentIIDHash = + FIRCLS256HashNSData([currentIID dataUsingEncoding:NSUTF8StringEncoding]); + NSString *lastIIDHash = [defaults objectForKey:FIRCLSInstallationIIDHashKey]; + + // If the IDs are the same, we never regenerate + if ([lastIIDHash isEqualToString:currentIIDHash]) { + return didRotate; + } + + // If we had an FIID saved, we know it's not an upgrade scenario, so we can regenerate + if (lastIIDHash.length != 0) { + FIRCLSDebugLog(@"Regenerating Install ID"); + self.installID = [self generateInstallationUUID].copy; + didRotate = YES; + } + + // Write the new FIID to UserDefaults + [defaults setObject:currentIIDHash forKey:FIRCLSInstallationIIDHashKey]; + [defaults synchronize]; + + return didRotate; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h new file mode 100644 index 000000000..a02123ffd --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h @@ -0,0 +1,117 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#include "FIRCLSFeatures.h" + +extern NSString *const FIRCLSReportBinaryImageFile; +extern NSString *const FIRCLSReportExceptionFile; +extern NSString *const FIRCLSReportCustomExceptionAFile; +extern NSString *const FIRCLSReportCustomExceptionBFile; +extern NSString *const FIRCLSReportSignalFile; +#if CLS_MACH_EXCEPTION_SUPPORTED +extern NSString *const FIRCLSReportMachExceptionFile; +#endif +extern NSString *const FIRCLSReportErrorAFile; +extern NSString *const FIRCLSReportErrorBFile; +extern NSString *const FIRCLSReportLogAFile; +extern NSString *const FIRCLSReportLogBFile; +extern NSString *const FIRCLSReportMetadataFile; +extern NSString *const FIRCLSReportInternalIncrementalKVFile; +extern NSString *const FIRCLSReportInternalCompactedKVFile; +extern NSString *const FIRCLSReportUserIncrementalKVFile; +extern NSString *const FIRCLSReportUserCompactedKVFile; + +@class FIRCLSFileManager; + +@interface FIRCLSInternalReport : NSObject + ++ (instancetype)reportWithPath:(NSString *)path; +- (instancetype)initWithPath:(NSString *)path + executionIdentifier:(NSString *)identifier NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithPath:(NSString *)path; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + ++ (NSArray *)crashFileNames; + +@property(nonatomic, copy, readonly) NSString *directoryName; +@property(nonatomic, copy) NSString *path; +@property(nonatomic, assign, readonly) BOOL needsToBeSubmitted; + +// content paths +@property(nonatomic, copy, readonly) NSString *binaryImagePath; +@property(nonatomic, copy, readonly) NSString *metadataPath; + +- (void)enumerateSymbolicatableFilesInContent:(void (^)(NSString *path))block; + +- (NSString *)pathForContentFile:(NSString *)name; + +// Metadata Helpers + +/** + * Returns the org id for the report. + **/ +@property(nonatomic, copy, readonly) NSString *orgID; + +/** + * Returns the Install UUID for the report. + **/ +@property(nonatomic, copy, readonly) NSString *installID; + +/** + * Returns YES if report contains a signal, mach exception or unhandled exception record, NO + * otherwise. + **/ +@property(nonatomic, assign, readonly) BOOL isCrash; + +/** + * Returns the session identifier for the report. + **/ +@property(nonatomic, copy, readonly) NSString *identifier; + +/** + * Returns the custom key value data for the report. + **/ +@property(nonatomic, copy, readonly) NSDictionary *customKeys; + +/** + * Returns the CFBundleVersion of the application that generated the report. + **/ +@property(nonatomic, copy, readonly) NSString *bundleVersion; + +/** + * Returns the CFBundleShortVersionString of the application that generated the report. + **/ +@property(nonatomic, copy, readonly) NSString *bundleShortVersionString; + +/** + * Returns the date that the report was created. + **/ +@property(nonatomic, copy, readonly) NSDate *dateCreated; + +@property(nonatomic, copy, readonly) NSDate *crashedOnDate; + +/** + * Returns the os version that the application crashed on. + **/ +@property(nonatomic, copy, readonly) NSString *OSVersion; + +/** + * Returns the os build version that the application crashed on. + **/ +@property(nonatomic, copy, readonly) NSString *OSBuildVersion; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInternalReport.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInternalReport.m new file mode 100644 index 000000000..74ac9975b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSInternalReport.m @@ -0,0 +1,253 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO: Remove this class after the uploading of reports via GoogleDataTransport is no longer an +// experiment + +#import "FIRCLSInternalReport.h" + +#import "FIRCLSFile.h" +#import "FIRCLSFileManager.h" +#import "FIRCLSLogger.h" + +NSString *const FIRCLSReportBinaryImageFile = @"binary_images.clsrecord"; +NSString *const FIRCLSReportExceptionFile = @"exception.clsrecord"; +NSString *const FIRCLSReportCustomExceptionAFile = @"custom_exception_a.clsrecord"; +NSString *const FIRCLSReportCustomExceptionBFile = @"custom_exception_b.clsrecord"; +NSString *const FIRCLSReportSignalFile = @"signal.clsrecord"; +#if CLS_MACH_EXCEPTION_SUPPORTED +NSString *const FIRCLSReportMachExceptionFile = @"mach_exception.clsrecord"; +#endif +NSString *const FIRCLSReportMetadataFile = @"metadata.clsrecord"; +NSString *const FIRCLSReportErrorAFile = @"errors_a.clsrecord"; +NSString *const FIRCLSReportErrorBFile = @"errors_b.clsrecord"; +NSString *const FIRCLSReportLogAFile = @"log_a.clsrecord"; +NSString *const FIRCLSReportLogBFile = @"log_b.clsrecord"; +NSString *const FIRCLSReportInternalIncrementalKVFile = @"internal_incremental_kv.clsrecord"; +NSString *const FIRCLSReportInternalCompactedKVFile = @"internal_compacted_kv.clsrecord"; +NSString *const FIRCLSReportUserIncrementalKVFile = @"user_incremental_kv.clsrecord"; +NSString *const FIRCLSReportUserCompactedKVFile = @"user_compacted_kv.clsrecord"; + +@interface FIRCLSInternalReport () { + NSString *_identifier; + NSString *_path; + NSArray *_metadataSections; +} + +@end + +@implementation FIRCLSInternalReport + ++ (instancetype)reportWithPath:(NSString *)path { + return [[self alloc] initWithPath:path]; +} + +#pragma mark - Initialization +/** + * Initializes a new report, i.e. one without metadata on the file system yet. + */ +- (instancetype)initWithPath:(NSString *)path executionIdentifier:(NSString *)identifier { + self = [super init]; + if (!self) { + return self; + } + + if (!path || !identifier) { + return nil; + } + + [self setPath:path]; + + _identifier = [identifier copy]; + + return self; +} + +/** + * Initializes a pre-existing report, i.e. one with metadata on the file system. + */ +- (instancetype)initWithPath:(NSString *)path { + NSString *metadataPath = [path stringByAppendingPathComponent:FIRCLSReportMetadataFile]; + NSString *identifier = [[[[self.class readFIRCLSFileAtPath:metadataPath] objectAtIndex:0] + objectForKey:@"identity"] objectForKey:@"session_id"]; + if (!identifier) { + FIRCLSErrorLog(@"Unable to read identifier at path %@", path); + } + return [self initWithPath:path executionIdentifier:identifier]; +} + +#pragma mark - Path Helpers +- (NSString *)directoryName { + return self.path.lastPathComponent; +} + +- (NSString *)pathForContentFile:(NSString *)name { + return [[self path] stringByAppendingPathComponent:name]; +} + +- (NSString *)metadataPath { + return [[self path] stringByAppendingPathComponent:FIRCLSReportMetadataFile]; +} + +- (NSString *)binaryImagePath { + return [self pathForContentFile:FIRCLSReportBinaryImageFile]; +} + +#pragma mark - Processing Methods +- (BOOL)needsToBeSubmitted { + NSArray *reportFiles = @[ + FIRCLSReportExceptionFile, FIRCLSReportSignalFile, FIRCLSReportCustomExceptionAFile, + FIRCLSReportCustomExceptionBFile, +#if CLS_MACH_EXCEPTION_SUPPORTED + FIRCLSReportMachExceptionFile, +#endif + FIRCLSReportErrorAFile, FIRCLSReportErrorBFile + ]; + return [self checkExistenceOfAtLeastOnceFileInArray:reportFiles]; +} + +// These are purposefully in order of precedence. If duplicate data exists +// in any crash file, the exception file's contents take precedence over the +// rest, for example +// +// Do not change the order of this. +// ++ (NSArray *)crashFileNames { + static NSArray *files; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + files = @[ + FIRCLSReportExceptionFile, +#if CLS_MACH_EXCEPTION_SUPPORTED + FIRCLSReportMachExceptionFile, +#endif + FIRCLSReportSignalFile + ]; + }); + return files; +} + +- (BOOL)isCrash { + NSArray *crashFiles = [FIRCLSInternalReport crashFileNames]; + return [self checkExistenceOfAtLeastOnceFileInArray:crashFiles]; +} + +- (BOOL)checkExistenceOfAtLeastOnceFileInArray:(NSArray *)files { + NSFileManager *manager = [NSFileManager defaultManager]; + + for (NSString *fileName in files) { + NSString *path = [self pathForContentFile:fileName]; + + if ([manager fileExistsAtPath:path]) { + return YES; + } + } + + return NO; +} + +- (void)enumerateSymbolicatableFilesInContent:(void (^)(NSString *path))block { + for (NSString *fileName in [FIRCLSInternalReport crashFileNames]) { + NSString *path = [self pathForContentFile:fileName]; + + block(path); + } +} + +#pragma mark - Metadata helpers ++ (NSArray *)readFIRCLSFileAtPath:(NSString *)path { + NSArray *sections = FIRCLSFileReadSections([path fileSystemRepresentation], false, nil); + + if ([sections count] == 0) { + return nil; + } + + return sections; +} + +- (NSArray *)metadataSections { + if (!_metadataSections) { + _metadataSections = [self.class readFIRCLSFileAtPath:self.metadataPath]; + } + return _metadataSections; +} + +- (NSString *)orgID { + return + [[[self.metadataSections objectAtIndex:0] objectForKey:@"identity"] objectForKey:@"org_id"]; +} + +- (NSDictionary *)customKeys { + return nil; +} + +- (NSString *)bundleVersion { + return [[[self.metadataSections objectAtIndex:2] objectForKey:@"application"] + objectForKey:@"build_version"]; +} + +- (NSString *)bundleShortVersionString { + return [[[self.metadataSections objectAtIndex:2] objectForKey:@"application"] + objectForKey:@"display_version"]; +} + +- (NSDate *)dateCreated { + NSUInteger unixtime = [[[[self.metadataSections objectAtIndex:0] objectForKey:@"identity"] + objectForKey:@"started_at"] unsignedIntegerValue]; + + return [NSDate dateWithTimeIntervalSince1970:unixtime]; +} + +- (NSDate *)crashedOnDate { + if (!self.isCrash) { + return nil; + } + +#if CLS_MACH_EXCEPTION_SUPPORTED + // try the mach exception first, because it is more common + NSDate *date = [self timeFromCrashContentFile:FIRCLSReportMachExceptionFile + sectionName:@"mach_exception"]; + if (date) { + return date; + } +#endif + + return [self timeFromCrashContentFile:FIRCLSReportSignalFile sectionName:@"signal"]; +} + +- (NSDate *)timeFromCrashContentFile:(NSString *)fileName sectionName:(NSString *)sectionName { + // This works because both signal and mach exception files have the same structure to extract + // the "time" component + NSString *path = [self pathForContentFile:fileName]; + + NSNumber *timeValue = [[[[self.class readFIRCLSFileAtPath:path] objectAtIndex:0] + objectForKey:sectionName] objectForKey:@"time"]; + if (timeValue == nil) { + return nil; + } + + return [NSDate dateWithTimeIntervalSince1970:[timeValue unsignedIntegerValue]]; +} + +- (NSString *)OSVersion { + return [[[self.metadataSections objectAtIndex:1] objectForKey:@"host"] + objectForKey:@"os_display_version"]; +} + +- (NSString *)OSBuildVersion { + return [[[self.metadataSections objectAtIndex:1] objectForKey:@"host"] + objectForKey:@"os_build_version"]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport.h new file mode 100644 index 000000000..464dff736 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport.h @@ -0,0 +1,110 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate + * changes for details. + **/ +@protocol FIRCLSCrashReport + +@property(nonatomic, copy, readonly) NSString *identifier; +@property(nonatomic, copy, readonly) NSDictionary *customKeys; +@property(nonatomic, copy, readonly) NSString *bundleVersion; +@property(nonatomic, copy, readonly) NSString *bundleShortVersionString; +@property(nonatomic, readonly, nullable) NSDate *crashedOnDate; +@property(nonatomic, copy, readonly) NSString *OSVersion; +@property(nonatomic, copy, readonly) NSString *OSBuildVersion; + +@end + +/** + * The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can + * use this class to get information about the event, and can also set some values after the + * event has occurred. + **/ +@interface FIRCLSReport : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + * Returns the session identifier for the report. + **/ +@property(nonatomic, copy, readonly) NSString *identifier; + +/** + * Returns the custom key value data for the report. + **/ +@property(nonatomic, copy, readonly) NSDictionary *customKeys; + +/** + * Returns the CFBundleVersion of the application that generated the report. + **/ +@property(nonatomic, copy, readonly) NSString *bundleVersion; + +/** + * Returns the CFBundleShortVersionString of the application that generated the report. + **/ +@property(nonatomic, copy, readonly) NSString *bundleShortVersionString; + +/** + * Returns the date that the report was created. + **/ +@property(nonatomic, copy, readonly) NSDate *dateCreated; + +/** + * Returns the os version that the application crashed on. + **/ +@property(nonatomic, copy, readonly) NSString *OSVersion; + +/** + * Returns the os build version that the application crashed on. + **/ +@property(nonatomic, copy, readonly) NSString *OSBuildVersion; + +/** + * Returns YES if the report contains any crash information, otherwise returns NO. + **/ +@property(nonatomic, assign, readonly) BOOL isCrash; + +/** + * You can use this method to set, after the event, additional custom keys. The rules + * and semantics for this method are the same as those documented in FIRCrashlytics.h. Be aware + * that the maximum size and count of custom keys is still enforced, and you can overwrite keys + * and/or cause excess keys to be deleted by using this method. + **/ +- (void)setObjectValue:(nullable id)value forKey:(NSString *)key; + +/** + * Record an application-specific user identifier. See FIRCrashlytics.h for details. + **/ +@property(nonatomic, copy, nullable) NSString *userIdentifier; + +/** + * Record a user name. See FIRCrashlytics.h for details. + **/ +@property(nonatomic, copy, nullable) NSString *userName; + +/** + * Record a user email. See FIRCrashlytics.h for details. + **/ +@property(nonatomic, copy, nullable) NSString *userEmail; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport.m new file mode 100644 index 000000000..1b4bade64 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport.m @@ -0,0 +1,241 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSContext.h" +#import "FIRCLSFile.h" +#import "FIRCLSGlobals.h" +#import "FIRCLSInternalReport.h" +#import "FIRCLSReport_Private.h" +#import "FIRCLSUserLogging.h" + +@interface FIRCLSReport () { + FIRCLSInternalReport *_internalReport; + uint32_t _internalKVCounter; + uint32_t _userKVCounter; + + NSString *_internalCompactedKVFile; + NSString *_internalIncrementalKVFile; + NSString *_userCompactedKVFile; + NSString *_userIncrementalKVFile; + + BOOL _readOnly; + + // cached values, to ensure that their contents remain valid + // even if the report is deleted + NSString *_identifer; + NSString *_bundleVersion; + NSString *_bundleShortVersionString; + NSDate *_dateCreated; + NSDate *_crashedOnDate; + NSString *_OSVersion; + NSString *_OSBuildVersion; + NSNumber *_isCrash; + NSDictionary *_customKeys; +} + +@end + +@implementation FIRCLSReport + +- (instancetype)initWithInternalReport:(FIRCLSInternalReport *)report + prefetchData:(BOOL)shouldPrefetch { + self = [super init]; + if (!self) { + return nil; + } + + _internalReport = report; + + // TODO: correct kv accounting + // The internal report will have non-zero compacted and incremental keys. The right thing to do + // is count them, so we can kick off compactions/pruning at the right times. By + // setting this value to zero, we're allowing more entries to be made than there really + // should be. Not the end of the world, but we should do better eventually. + _internalKVCounter = 0; + _userKVCounter = 0; + + _internalCompactedKVFile = + [self.internalReport pathForContentFile:FIRCLSReportInternalCompactedKVFile]; + _internalIncrementalKVFile = + [self.internalReport pathForContentFile:FIRCLSReportInternalIncrementalKVFile]; + _userCompactedKVFile = [self.internalReport pathForContentFile:FIRCLSReportUserCompactedKVFile]; + _userIncrementalKVFile = + [self.internalReport pathForContentFile:FIRCLSReportUserIncrementalKVFile]; + + _readOnly = shouldPrefetch; + + if (shouldPrefetch) { + _identifer = report.identifier; + _bundleVersion = report.bundleVersion; + _bundleShortVersionString = report.bundleShortVersionString; + _dateCreated = report.dateCreated; + _crashedOnDate = report.crashedOnDate; + _OSVersion = report.OSVersion; + _OSBuildVersion = report.OSBuildVersion; + _isCrash = [NSNumber numberWithBool:report.isCrash]; + + _customKeys = [self readCustomKeys]; + } + + return self; +} + +- (instancetype)initWithInternalReport:(FIRCLSInternalReport *)report { + return [self initWithInternalReport:report prefetchData:NO]; +} + +#pragma mark - Helpers +- (FIRCLSUserLoggingKVStorage)internalKVStorage { + FIRCLSUserLoggingKVStorage storage; + + storage.maxCount = _firclsContext.readonly->logging.internalKVStorage.maxCount; + storage.maxIncrementalCount = + _firclsContext.readonly->logging.internalKVStorage.maxIncrementalCount; + storage.compactedPath = [_internalCompactedKVFile fileSystemRepresentation]; + storage.incrementalPath = [_internalIncrementalKVFile fileSystemRepresentation]; + + return storage; +} + +- (FIRCLSUserLoggingKVStorage)userKVStorage { + FIRCLSUserLoggingKVStorage storage; + + storage.maxCount = _firclsContext.readonly->logging.userKVStorage.maxCount; + storage.maxIncrementalCount = _firclsContext.readonly->logging.userKVStorage.maxIncrementalCount; + storage.compactedPath = [_userCompactedKVFile fileSystemRepresentation]; + storage.incrementalPath = [_userIncrementalKVFile fileSystemRepresentation]; + + return storage; +} + +- (BOOL)canRecordNewValues { + return !_readOnly && FIRCLSContextIsInitialized(); +} + +- (void)recordValue:(id)value forInternalKey:(NSString *)key { + if (!self.canRecordNewValues) { + return; + } + + FIRCLSUserLoggingKVStorage storage = [self internalKVStorage]; + + FIRCLSUserLoggingRecordKeyValue(key, value, &storage, &_internalKVCounter); +} + +- (void)recordValue:(id)value forUserKey:(NSString *)key { + if (!self.canRecordNewValues) { + return; + } + + FIRCLSUserLoggingKVStorage storage = [self userKVStorage]; + + FIRCLSUserLoggingRecordKeyValue(key, value, &storage, &_userKVCounter); +} + +- (NSDictionary *)readCustomKeys { + FIRCLSUserLoggingKVStorage storage = [self userKVStorage]; + + // return decoded entries + return FIRCLSUserLoggingGetCompactedKVEntries(&storage, true); +} + +#pragma mark - Metadata helpers + +- (NSString *)identifier { + if (!_identifer) { + _identifer = self.internalReport.identifier; + } + + return _identifer; +} + +- (NSDictionary *)customKeys { + if (!_customKeys) { + _customKeys = [self readCustomKeys]; + } + + return _customKeys; +} + +- (NSString *)bundleVersion { + if (!_bundleVersion) { + _bundleVersion = self.internalReport.bundleVersion; + } + + return _bundleVersion; +} + +- (NSString *)bundleShortVersionString { + if (!_bundleShortVersionString) { + _bundleShortVersionString = self.internalReport.bundleShortVersionString; + } + + return _bundleShortVersionString; +} + +- (NSDate *)dateCreated { + if (!_dateCreated) { + _dateCreated = self.internalReport.dateCreated; + } + + return _dateCreated; +} + +// for compatibility with the CLSCrashReport Protocol +- (NSDate *)crashedOnDate { + if (!_crashedOnDate) { + _crashedOnDate = self.internalReport.crashedOnDate; + } + + return _crashedOnDate; +} + +- (NSString *)OSVersion { + if (!_OSVersion) { + _OSVersion = self.internalReport.OSVersion; + } + + return _OSVersion; +} + +- (NSString *)OSBuildVersion { + if (!_OSBuildVersion) { + _OSBuildVersion = self.internalReport.OSBuildVersion; + } + + return _OSBuildVersion; +} + +- (BOOL)isCrash { + if (_isCrash == nil) { + _isCrash = [NSNumber numberWithBool:self.internalReport.isCrash]; + } + + return [_isCrash boolValue]; +} + +#pragma mark - Public Read/Write Methods +- (void)setObjectValue:(id)value forKey:(NSString *)key { + [self recordValue:value forUserKey:key]; +} + +- (NSString *)userIdentifier { + return nil; +} + +- (void)setUserIdentifier:(NSString *)userIdentifier { + [self recordValue:userIdentifier forInternalKey:FIRCLSUserIdentifierKey]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport_Private.h new file mode 100644 index 000000000..0d8b67c6b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSReport_Private.h @@ -0,0 +1,27 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSReport.h" + +@class FIRCLSInternalReport; + +@interface FIRCLSReport () + +- (instancetype)initWithInternalReport:(FIRCLSInternalReport *)report + prefetchData:(BOOL)shouldPrefetch NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithInternalReport:(FIRCLSInternalReport *)report; + +@property(nonatomic, strong, readonly) FIRCLSInternalReport *internalReport; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSettings.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSettings.h new file mode 100644 index 000000000..f45b45fd0 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSettings.h @@ -0,0 +1,134 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#if __has_include() +#import +#else +#import "FBLPromises.h" +#endif + +@class FIRCLSApplicationIdentifierModel; +@class FIRCLSFileManager; + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRCLSSettings : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager + appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel + NS_DESIGNATED_INITIALIZER; + +/** + * Recreates the settings dictionary by re-reading the settings file from persistent storage. This + * should be called before any settings values are read, as it will populate the underlying + * settingsDictionary. If the Google App ID has changed or there is an error, delete the cache file + * and settingsDictionary. If the cache has expired, set `isCacheExpired` to true so that settings + * are re-fetched, but do not delete any values. + */ +- (void)reloadFromCacheWithGoogleAppID:(NSString *)googleAppID + currentTimestamp:(NSTimeInterval)currentTimestamp; + +/** + * Stores a separate file with the settings expiration and Google App ID it was saved with + * so that we can later determine that the settings have expired. + * + * This should be called in a background thread right after the settings.json file has been + * downloaded. + */ +- (void)cacheSettingsWithGoogleAppID:(NSString *)googleAppID + currentTimestamp:(NSTimeInterval)currentTimestamp; + +/** + * Returns true when Settings should be fetched from the server again + */ +@property(nonatomic, readonly) BOOL isCacheExpired; + +/** + * Determines how long these Settings should be respected until the SDK should fetch again + */ +@property(nonatomic, readonly) uint32_t cacheDurationSeconds; + +/** + * The Crashlytics Organization identifier of the app. Allows data continuity between + * old and new Crashlytics SDKs. + */ +@property(nonatomic, nullable, readonly) NSString *orgID; + +/** + * The backend bundle identifier of the app. Crashlytics can in some cases have + * a different bundle identifier than the app itself (eg. Crashlytics will always downcase + * the bundle ID). + */ +@property(nonatomic, nullable, readonly) NSString *fetchedBundleID; + +/** + * Indicates whether the app needs onboarding + */ +@property(nonatomic, readonly) BOOL appNeedsOnboarding; + +/** + * Indicates whether the app needs an update + */ +@property(nonatomic, readonly) BOOL appUpdateRequired; + +/** + * When this is false, Crashlytics will not start up + */ +@property(nonatomic, readonly) BOOL collectReportsEnabled; + +/** + * When this is false, Crashlytics will not collect non-fatal errors and errors + * from the custom exception / record error APIs + */ +@property(nonatomic, readonly) BOOL errorReportingEnabled; + +/** + * When this is false, Crashlytics will not collect custom exceptions from the API + */ +@property(nonatomic, readonly) BOOL customExceptionsEnabled; + +/** + * Determine if the SDK should use the new endpoint for uploading reports + */ +@property(nonatomic, readonly) BOOL shouldUseNewReportEndpoint; + +/** + * Returns the maximum number of custom exception events that will be + * recorded in a session. + */ +@property(nonatomic, readonly) uint32_t errorLogBufferSize; + +/** + * Returns the maximum size of the log buffer in bytes + */ +@property(nonatomic, readonly) uint32_t logBufferSize; + +/** + * Returns the maximum number of custom exceptions that will be collected + * in a session. + */ +@property(nonatomic, readonly) uint32_t maxCustomExceptions; + +/** + * Returns the maximum number of custom key-value pair keys (not bytes). + */ +@property(nonatomic, readonly) uint32_t maxCustomKeys; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSettings.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSettings.m new file mode 100644 index 000000000..7d1347f4e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSettings.m @@ -0,0 +1,357 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSSettings.h" + +#if __has_include() +#import +#else +#import "FBLPromises.h" +#endif + +#import "FIRCLSApplicationIdentifierModel.h" +#import "FIRCLSConstants.h" +#import "FIRCLSFileManager.h" +#import "FIRCLSLogger.h" +#import "FIRCLSURLBuilder.h" + +NSString *const CreatedAtKey = @"created_at"; +NSString *const GoogleAppIDKey = @"google_app_id"; +NSString *const BuildInstanceID = @"build_instance_id"; +NSString *const AppVersion = @"app_version"; + +@interface FIRCLSSettings () + +@property(nonatomic, strong) FIRCLSFileManager *fileManager; +@property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel; + +@property(nonatomic, strong) NSDictionary *settingsDictionary; + +@property(nonatomic) BOOL isCacheKeyExpired; + +@end + +@implementation FIRCLSSettings + +- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager + appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel { + self = [super init]; + if (!self) { + return nil; + } + + _fileManager = fileManager; + _appIDModel = appIDModel; + + _settingsDictionary = nil; + _isCacheKeyExpired = NO; + + return self; +} + +#pragma mark - Public Methods + +- (void)reloadFromCacheWithGoogleAppID:(NSString *)googleAppID + currentTimestamp:(NSTimeInterval)currentTimestamp { + NSString *settingsFilePath = self.fileManager.settingsFilePath; + + NSData *data = [self.fileManager dataWithContentsOfFile:settingsFilePath]; + + if (!data) { + FIRCLSDebugLog(@"[Crashlytics:Settings] No settings were cached"); + + return; + } + + NSError *error = nil; + @synchronized(self) { + _settingsDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + } + + if (!_settingsDictionary) { + FIRCLSErrorLog(@"Could not load settings file data with error: %@", error.localizedDescription); + + // Attempt to remove it, in case it's messed up + [self deleteCachedSettings]; + return; + } + + NSDictionary *cacheKey = [self loadCacheKey]; + if (!cacheKey) { + FIRCLSErrorLog(@"Could not load settings cache key"); + + [self deleteCachedSettings]; + return; + } + + NSString *cachedGoogleAppID = cacheKey[GoogleAppIDKey]; + if (![cachedGoogleAppID isEqualToString:googleAppID]) { + FIRCLSDebugLog( + @"[Crashlytics:Settings] Invalidating settings cache because Google App ID changed"); + + [self deleteCachedSettings]; + return; + } + + NSTimeInterval cacheCreatedAt = [cacheKey[CreatedAtKey] unsignedIntValue]; + NSTimeInterval cacheDurationSeconds = self.cacheDurationSeconds; + if (currentTimestamp > (cacheCreatedAt + cacheDurationSeconds)) { + FIRCLSDebugLog(@"[Crashlytics:Settings] Settings TTL expired"); + + @synchronized(self) { + self.isCacheKeyExpired = YES; + } + } + + NSString *cacheBuildInstanceID = cacheKey[BuildInstanceID]; + if (![cacheBuildInstanceID isEqualToString:self.appIDModel.buildInstanceID]) { + FIRCLSDebugLog(@"[Crashlytics:Settings] Settings expired because build instance changed"); + + @synchronized(self) { + self.isCacheKeyExpired = YES; + } + } + + NSString *cacheAppVersion = cacheKey[AppVersion]; + if (![cacheAppVersion isEqualToString:self.appIDModel.synthesizedVersion]) { + FIRCLSDebugLog(@"[Crashlytics:Settings] Settings expired because app version changed"); + + @synchronized(self) { + self.isCacheKeyExpired = YES; + } + } +} + +- (void)cacheSettingsWithGoogleAppID:(NSString *)googleAppID + currentTimestamp:(NSTimeInterval)currentTimestamp { + NSNumber *createdAtTimestamp = [NSNumber numberWithDouble:currentTimestamp]; + NSDictionary *cacheKey = @{ + CreatedAtKey : createdAtTimestamp, + GoogleAppIDKey : googleAppID, + BuildInstanceID : self.appIDModel.buildInstanceID, + AppVersion : self.appIDModel.synthesizedVersion, + }; + + NSError *error = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:cacheKey + options:kNilOptions + error:&error]; + + if (!jsonData) { + FIRCLSErrorLog(@"Could not create settings cache key with error: %@", + error.localizedDescription); + + return; + } + + if ([self.fileManager fileExistsAtPath:self.fileManager.settingsCacheKeyPath]) { + [self.fileManager removeItemAtPath:self.fileManager.settingsCacheKeyPath]; + } + [self.fileManager createFileAtPath:self.fileManager.settingsCacheKeyPath + contents:jsonData + attributes:nil]; + + // If Settings were expired before, they should no longer be expired after this. + // This may be set back to YES if reloading from the cache fails + @synchronized(self) { + self.isCacheKeyExpired = NO; + } + + [self reloadFromCacheWithGoogleAppID:googleAppID currentTimestamp:currentTimestamp]; +} + +#pragma mark - Convenience Methods + +- (NSDictionary *)loadCacheKey { + NSData *cacheKeyData = + [self.fileManager dataWithContentsOfFile:self.fileManager.settingsCacheKeyPath]; + + if (!cacheKeyData) { + return nil; + } + + NSError *error = nil; + NSDictionary *cacheKey = [NSJSONSerialization JSONObjectWithData:cacheKeyData + options:NSJSONReadingAllowFragments + error:&error]; + return cacheKey; +} + +- (void)deleteCachedSettings { + __weak FIRCLSSettings *weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + __strong FIRCLSSettings *strongSelf = weakSelf; + if ([strongSelf.fileManager fileExistsAtPath:strongSelf.fileManager.settingsFilePath]) { + [strongSelf.fileManager removeItemAtPath:strongSelf.fileManager.settingsFilePath]; + } + if ([strongSelf.fileManager fileExistsAtPath:strongSelf.fileManager.settingsCacheKeyPath]) { + [strongSelf.fileManager removeItemAtPath:strongSelf.fileManager.settingsCacheKeyPath]; + } + }); + + @synchronized(self) { + self.isCacheKeyExpired = YES; + _settingsDictionary = nil; + } +} + +- (NSDictionary *)settingsDictionary { + @synchronized(self) { + return _settingsDictionary; + } +} + +#pragma mark - Settings Groups + +- (NSDictionary *)appSettings { + return self.settingsDictionary[@"app"]; +} + +- (NSDictionary *)sessionSettings { + return self.settingsDictionary[@"session"]; +} + +- (NSDictionary *)featuresSettings { + return self.settingsDictionary[@"features"]; +} + +- (NSDictionary *)fabricSettings { + return self.settingsDictionary[@"fabric"]; +} + +#pragma mark - Caching + +- (BOOL)isCacheExpired { + if (!self.settingsDictionary) { + return YES; + } + + @synchronized(self) { + return self.isCacheKeyExpired; + } +} + +- (uint32_t)cacheDurationSeconds { + id fetchedCacheDuration = self.settingsDictionary[@"cache_duration"]; + if (fetchedCacheDuration) { + return [fetchedCacheDuration unsignedIntValue]; + } + + return 60 * 60; +} + +#pragma mark - Identifiers + +- (nullable NSString *)orgID { + return self.fabricSettings[@"org_id"]; +} + +- (nullable NSString *)fetchedBundleID { + return self.fabricSettings[@"bundle_id"]; +} + +#pragma mark - Onboarding / Update + +- (NSString *)appStatus { + return self.appSettings[@"status"]; +} + +- (BOOL)appNeedsOnboarding { + return [self.appStatus isEqualToString:@"new"]; +} + +- (BOOL)appUpdateRequired { + return [[self.appSettings objectForKey:@"update_required"] boolValue]; +} + +#pragma mark - On / Off Switches + +- (BOOL)errorReportingEnabled { + NSNumber *value = [self featuresSettings][@"collect_logged_exceptions"]; + + if (value != nil) { + return [value boolValue]; + } + + return YES; +} + +- (BOOL)customExceptionsEnabled { + // Right now, recording custom exceptions from the API and + // automatically capturing non-fatal errors go hand in hand + return [self errorReportingEnabled]; +} + +- (BOOL)collectReportsEnabled { + NSNumber *value = [self featuresSettings][@"collect_reports"]; + + if (value != nil) { + return value.boolValue; + } + + return YES; +} + +- (BOOL)shouldUseNewReportEndpoint { + NSNumber *value = [self appSettings][@"report_upload_variant"]; + + // Default to use the new endpoint when settings were not successfully fetched + // or there's an unexpected issue + if (value == nil) { + return YES; + } + + // 0 - Unknown + // 1 - Legacy + // 2 - New + return value.intValue == 2; +} + +#pragma mark - Optional Limit Overrides + +- (uint32_t)errorLogBufferSize { + return [self logBufferSize]; +} + +- (uint32_t)logBufferSize { + NSNumber *value = [self sessionSettings][@"log_buffer_size"]; + + if (value != nil) { + return value.unsignedIntValue; + } + + return 64 * 1000; +} + +- (uint32_t)maxCustomExceptions { + NSNumber *value = [self sessionSettings][@"max_custom_exception_events"]; + + if (value != nil) { + return value.unsignedIntValue; + } + + return 8; +} + +- (uint32_t)maxCustomKeys { + NSNumber *value = [self sessionSettings][@"max_custom_key_value_pairs"]; + + if (value != nil) { + return value.unsignedIntValue; + } + + return 64; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h new file mode 100644 index 000000000..a18e60467 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h @@ -0,0 +1,26 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRStackFrame; + +@interface FIRCLSSymbolResolver : NSObject + +- (BOOL)loadBinaryImagesFromFile:(NSString *)path; + +- (FIRStackFrame *)frameForAddress:(uint64_t)address; +- (BOOL)updateStackFrame:(FIRStackFrame *)frame; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.m new file mode 100644 index 000000000..522a14ec5 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.m @@ -0,0 +1,175 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSSymbolResolver.h" + +#include + +#include "FIRCLSBinaryImage.h" +#include "FIRCLSFile.h" +#import "FIRCLSLogger.h" +#import "FIRStackFrame_Private.h" + +@interface FIRCLSSymbolResolver () { + NSMutableArray* _binaryImages; +} + +@end + +@implementation FIRCLSSymbolResolver + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _binaryImages = [NSMutableArray array]; + + return self; +} + +- (BOOL)loadBinaryImagesFromFile:(NSString*)path { + if ([path length] == 0) { + return NO; + } + + NSArray* sections = FIRCLSFileReadSections([path fileSystemRepresentation], false, nil); + + if ([sections count] == 0) { + FIRCLSErrorLog(@"Failed to read binary image file %@", path); + return NO; + } + + // filter out unloads, as well as loads with invalid entries + for (NSDictionary* entry in sections) { + NSDictionary* details = [entry objectForKey:@"load"]; + if (!details) { + continue; + } + + // This does happen occationally and causes a crash. I'm really not sure there + // is anything sane we can do in this case. + if (![details objectForKey:@"base"] || ![details objectForKey:@"size"]) { + continue; + } + + if ([details objectForKey:@"base"] == (id)[NSNull null] || + [details objectForKey:@"size"] == (id)[NSNull null]) { + continue; + } + + [_binaryImages addObject:details]; + } + + [_binaryImages sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { + NSNumber* base1 = [obj1 objectForKey:@"base"]; + NSNumber* base2 = [obj2 objectForKey:@"base"]; + + return [base1 compare:base2]; + }]; + + return YES; +} + +- (NSDictionary*)loadedBinaryImageForPC:(uintptr_t)pc { + NSUInteger index = + [_binaryImages indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL* stop) { + uintptr_t base = [[obj objectForKey:@"base"] unsignedIntegerValue]; + uintptr_t size = [[obj objectForKey:@"size"] unsignedIntegerValue]; + + return pc >= base && pc < (base + size); + }]; + + if (index == NSNotFound) { + return nil; + } + + return [_binaryImages objectAtIndex:index]; +} + +- (BOOL)fillInImageDetails:(FIRCLSBinaryImageDetails*)details forUUID:(NSString*)uuid { + if (!details || !uuid) { + return NO; + } + + return FIRCLSBinaryImageFindImageForUUID([uuid UTF8String], details); +} + +- (FIRStackFrame*)frameForAddress:(uint64_t)address { + FIRStackFrame* frame = [FIRStackFrame stackFrameWithAddress:(NSUInteger)address]; + + if (![self updateStackFrame:frame]) { + return nil; + } + + return frame; +} + +- (BOOL)updateStackFrame:(FIRStackFrame*)frame { + uint64_t address = [frame address]; + if (address == 0) { + return NO; + } + + NSDictionary* binaryImage = [self loadedBinaryImageForPC:(uintptr_t)address]; + + FIRCLSBinaryImageDetails imageDetails; + + if (![self fillInImageDetails:&imageDetails forUUID:[binaryImage objectForKey:@"uuid"]]) { +#if DEBUG + FIRCLSDebugLog(@"Image not found"); +#endif + return NO; + } + + uintptr_t addr = (uintptr_t)address - + (uintptr_t)[[binaryImage objectForKey:@"base"] unsignedIntegerValue] + + (uintptr_t)imageDetails.node.baseAddress; + Dl_info dlInfo; + + if (dladdr((void*)addr, &dlInfo) == 0) { +#if DEBUG + FIRCLSDebugLog(@"Could not look up address"); +#endif + return NO; + } + + if (addr - (uintptr_t)dlInfo.dli_saddr == 0) { + addr -= 2; + if (dladdr((void*)addr, &dlInfo) == 0) { +#if DEBUG + FIRCLSDebugLog(@"Could not look up address"); +#endif + return NO; + } + } + + if (dlInfo.dli_sname) { + NSString* symbol = [NSString stringWithUTF8String:dlInfo.dli_sname]; + + frame.symbol = symbol; + frame.rawSymbol = symbol; + } + + if (addr > (uintptr_t)dlInfo.dli_saddr) { + [frame setOffset:addr - (uintptr_t)dlInfo.dli_saddr]; + } + + [frame setLibrary:[[binaryImage objectForKey:@"path"] lastPathComponent]]; + + return YES; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordApplication.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordApplication.h new file mode 100644 index 000000000..d17bdce72 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordApplication.h @@ -0,0 +1,24 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSRecordBase.h" + +@interface FIRCLSRecordApplication : FIRCLSRecordBase + +@property(nonatomic, copy) NSString *build_version; +@property(nonatomic, copy) NSString *display_version; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordApplication.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordApplication.m new file mode 100644 index 000000000..f587e1014 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordApplication.m @@ -0,0 +1,30 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSRecordApplication.h" + +@implementation FIRCLSRecordApplication + +- (instancetype)initWithDict:(NSDictionary *)dict { + self = [super initWithDict:dict]; + if (self) { + _display_version = dict[@"display_version"]; + _build_version = dict[@"build_version"]; + } + return self; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordBase.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordBase.h new file mode 100644 index 000000000..a3cb6e2f8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordBase.h @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +/** + * This is the base class to represent the data in the persisted crash (.clsrecord) files. + * The properties these subclasses are nullable on purpose. If there is an issue reading values + * from the crash files, continue as if those fields are optional so a report can still be uploaded. + * That way the issue can potentially be monitored through the backend. + **/ +@interface FIRCLSRecordBase : NSObject + +/** + * Mark the default initializer as unavailable so the subclasses do not have to add the same line + **/ +- (instancetype)init NS_UNAVAILABLE; + +/** + * All subclasses should define an initializer taking in a dictionary + **/ +- (instancetype)initWithDict:(NSDictionary *)dict; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordBase.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordBase.m new file mode 100644 index 000000000..7d591f539 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordBase.m @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSRecordBase.h" + +@implementation FIRCLSRecordBase + +- (instancetype)initWithDict:(NSDictionary *)dict { + return [super init]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordHost.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordHost.h new file mode 100644 index 000000000..652d283c1 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordHost.h @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSRecordBase.h" + +@interface FIRCLSRecordHost : FIRCLSRecordBase + +@property(nonatomic, copy) NSString *platform; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordHost.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordHost.m new file mode 100644 index 000000000..67184ae68 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordHost.m @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSRecordHost.h" + +@implementation FIRCLSRecordHost + +- (instancetype)initWithDict:(NSDictionary *)dict { + self = [super initWithDict:dict]; + if (self) { + _platform = dict[@"platform"]; + } + return self; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordIdentity.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordIdentity.h new file mode 100644 index 000000000..9f13fdd16 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordIdentity.h @@ -0,0 +1,24 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSRecordBase.h" + +@interface FIRCLSRecordIdentity : FIRCLSRecordBase + +@property(nonatomic, copy) NSString *build_version; +@property(nonatomic, copy) NSString *install_id; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordIdentity.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordIdentity.m new file mode 100644 index 000000000..b4b547e63 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSRecordIdentity.m @@ -0,0 +1,30 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSRecordIdentity.h" + +@implementation FIRCLSRecordIdentity + +- (instancetype)initWithDict:(NSDictionary *)dict { + self = [super initWithDict:dict]; + if (self) { + _build_version = dict[@"build_version"]; + _install_id = dict[@"install_id"]; + } + return self; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.h new file mode 100644 index 000000000..eea894814 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.h @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#include "crashlytics.nanopb.h" + +#import + +/// This class is responsible for reading the persisted crash reports from disk and converting them +/// the information into the nanopb model to be used with GoogleDataTransport +@interface FIRCLSReportAdapter : NSObject + +- (instancetype)init NS_UNAVAILABLE; + +/// Initializer +/// @param folderPath Path where the persisted crash files reside +- (instancetype)initWithPath:(NSString *)folderPath googleAppId:(NSString *)googleAppID; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.m new file mode 100644 index 000000000..3c74691b8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.m @@ -0,0 +1,250 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSReportAdapter.h" +#import "FIRCLSReportAdapter_Private.h" + +#import "FIRCLSInternalReport.h" +#import "FIRCLSLogger.h" + +#import "FIRCLSUserLogging.h" + +#import +#import +#import + +@implementation FIRCLSReportAdapter + +- (instancetype)initWithPath:(NSString *)folderPath googleAppId:(NSString *)googleAppID { + self = [super init]; + if (self) { + _folderPath = folderPath; + _googleAppID = googleAppID; + + [self loadMetaDataFile]; + + _report = [self protoReport]; + } + return self; +} + +- (void)dealloc { + pb_release(google_crashlytics_Report_fields, &_report); +} + +// +// MARK: Load from persisted crash files +// + +/// Reads from metadata.clsrecord +- (void)loadMetaDataFile { + NSString *path = [self.folderPath stringByAppendingPathComponent:FIRCLSReportMetadataFile]; + NSDictionary *dict = [FIRCLSReportAdapter combinedDictionariesFromFilePath:path]; + + self.identity = [[FIRCLSRecordIdentity alloc] initWithDict:dict[@"identity"]]; + self.host = [[FIRCLSRecordHost alloc] initWithDict:dict[@"host"]]; + self.application = [[FIRCLSRecordApplication alloc] initWithDict:dict[@"application"]]; +} + +/// Return the persisted crash file as a combined dictionary that way lookups can occur with a key +/// (to avoid ordering dependency) +/// @param filePath Persisted crash file path ++ (NSDictionary *)combinedDictionariesFromFilePath:(NSString *)filePath { + NSMutableDictionary *joinedDict = [[NSMutableDictionary alloc] init]; + for (NSDictionary *dict in [self dictionariesFromEachLineOfFile:filePath]) { + [joinedDict addEntriesFromDictionary:dict]; + } + return joinedDict; +} + +/// The persisted crash files contains JSON on separate lines. Read each line and return the JSON +/// data as a dictionary. +/// @param filePath Persisted crash file path ++ (NSArray *)dictionariesFromEachLineOfFile:(NSString *)filePath { + NSString *content = [[NSString alloc] initWithContentsOfFile:filePath + encoding:NSUTF8StringEncoding + error:nil]; + NSArray *lines = + [content componentsSeparatedByCharactersInSet:NSCharacterSet.newlineCharacterSet]; + + NSMutableArray *array = [[NSMutableArray alloc] init]; + + int lineNum = 0; + for (NSString *line in lines) { + lineNum++; + + if (line.length == 0) { + // Likely newline at the end of the file + continue; + } + + NSError *error; + NSDictionary *dict = + [NSJSONSerialization JSONObjectWithData:[line dataUsingEncoding:NSUTF8StringEncoding] + options:0 + error:&error]; + + if (error) { + FIRCLSErrorLog(@"Failed to read JSON from file (%@) line (%d) with error: %@", filePath, + lineNum, error); + } else { + [array addObject:dict]; + } + } + + return array; +} + +// +// MARK: GDTCOREventDataObject +// + +- (NSData *)transportBytes { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + + // Encode 1 time to determine the size. + if (!pb_encode(&sizestream, google_crashlytics_Report_fields, &_report)) { + FIRCLSErrorLog(@"Error in nanopb encoding for size: %s", PB_GET_ERROR(&sizestream)); + } + + // Encode a 2nd time to actually get the bytes from it. + size_t bufferSize = sizestream.bytes_written; + CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize); + CFDataSetLength(dataRef, bufferSize); + pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize); + if (!pb_encode(&ostream, google_crashlytics_Report_fields, &_report)) { + FIRCLSErrorLog(@"Error in nanopb encoding for bytes: %s", PB_GET_ERROR(&ostream)); + } + + return CFBridgingRelease(dataRef); +} + +// +// MARK: NanoPB conversions +// + +- (google_crashlytics_Report)protoReport { + google_crashlytics_Report report = google_crashlytics_Report_init_default; + report.sdk_version = FIRCLSEncodeString(self.identity.build_version); + report.gmp_app_id = FIRCLSEncodeString(self.googleAppID); + report.platform = [self protoPlatformFromString:self.host.platform]; + report.installation_uuid = FIRCLSEncodeString(self.identity.install_id); + report.build_version = FIRCLSEncodeString(self.application.build_version); + report.display_version = FIRCLSEncodeString(self.application.display_version); + report.apple_payload = [self protoFilesPayload]; + return report; +} + +- (google_crashlytics_FilesPayload)protoFilesPayload { + google_crashlytics_FilesPayload apple_payload = google_crashlytics_FilesPayload_init_default; + + NSArray *clsRecords = [self clsRecordFilePaths]; + google_crashlytics_FilesPayload_File *files = + malloc(sizeof(google_crashlytics_FilesPayload_File) * clsRecords.count); + + if (files == NULL) { + // files and files_count are initialized to NULL and 0 by default. + return apple_payload; + } + for (NSUInteger i = 0; i < clsRecords.count; i++) { + google_crashlytics_FilesPayload_File file = google_crashlytics_FilesPayload_File_init_default; + file.filename = FIRCLSEncodeString(clsRecords[i].lastPathComponent); + + NSError *error; + file.contents = FIRCLSEncodeData([NSData dataWithContentsOfFile:clsRecords[i] + options:0 + error:&error]); + if (error) { + FIRCLSErrorLog(@"Failed to read from %@ with error: %@", clsRecords[i], error); + } + + files[i] = file; + } + + apple_payload.files = files; + apple_payload.files_count = (pb_size_t)clsRecords.count; + + return apple_payload; +} + +- (NSArray *)clsRecordFilePaths { + NSMutableArray *clsRecords = [[NSMutableArray alloc] init]; + + NSError *error; + NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.folderPath + error:&error]; + + if (error) { + FIRCLSErrorLog(@"Failed to find .clsrecords from %@ with error: %@", self.folderPath, error); + return clsRecords; + } + + [files enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + NSString *filename = (NSString *)obj; + NSString *lowerExtension = filename.pathExtension.lowercaseString; + if ([lowerExtension isEqualToString:@"clsrecord"] || + [lowerExtension isEqualToString:@"symbolicated"]) { + [clsRecords addObject:[self.folderPath stringByAppendingPathComponent:filename]]; + } + }]; + + return [clsRecords sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; +} + +- (google_crashlytics_Platforms)protoPlatformFromString:(NSString *)str { + NSString *platform = str.lowercaseString; + + if ([platform isEqualToString:@"ios"]) { + return google_crashlytics_Platforms_IOS; + } else if ([platform isEqualToString:@"mac"]) { + return google_crashlytics_Platforms_MAC_OS_X; + } else if ([platform isEqualToString:@"tvos"]) { + return google_crashlytics_Platforms_TVOS; + } else { + return google_crashlytics_Platforms_UNKNOWN_PLATFORM; + } +} + +/** Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array. + * @note Memory needs to be freed manually, through pb_free or pb_release. + * @param string The string to encode as pb_bytes. + */ +pb_bytes_array_t *FIRCLSEncodeString(NSString *string) { + if ([string isMemberOfClass:[NSNull class]]) { + FIRCLSErrorLog(@"Expected encodable string, but found NSNull instead. " + @"Set a symbolic breakpoint at FIRCLSEncodeString to debug."); + string = nil; + } + NSString *stringToEncode = string ? string : @""; + NSData *stringBytes = [stringToEncode dataUsingEncoding:NSUTF8StringEncoding]; + return FIRCLSEncodeData(stringBytes); +} + +/** Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array. + * @note Memory needs to be free manually, through pb_free or pb_release. + * @param data The data to copy into the new bytes array. + */ +pb_bytes_array_t *FIRCLSEncodeData(NSData *data) { + pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length)); + if (pbBytes == NULL) { + return NULL; + } + memcpy(pbBytes->bytes, [data bytes], data.length); + pbBytes->size = (pb_size_t)data.length; + return pbBytes; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter_Private.h new file mode 100644 index 000000000..e9f2c1606 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter_Private.h @@ -0,0 +1,43 @@ +/* + * Copyright 2020 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FIRCLSReportAdapter.h" + +#import "FIRCLSRecordApplication.h" +#import "FIRCLSRecordHost.h" +#import "FIRCLSRecordIdentity.h" + +pb_bytes_array_t *FIRCLSEncodeString(NSString *string); +pb_bytes_array_t *FIRCLSEncodeData(NSData *data); + +@interface FIRCLSReportAdapter () + +@property(nonatomic, readonly) BOOL hasCrashed; + +@property(nonatomic, strong) NSString *folderPath; +@property(nonatomic, strong) NSString *googleAppID; + +// From metadata.clsrecord +@property(nonatomic, strong) FIRCLSRecordIdentity *identity; +@property(nonatomic, strong) FIRCLSRecordHost *host; +@property(nonatomic, strong) FIRCLSRecordApplication *application; + +@property(nonatomic) google_crashlytics_Report report; + +- (google_crashlytics_Report)protoReport; +- (NSArray *)clsRecordFilePaths; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation.h new file mode 100644 index 000000000..5636d3d09 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation.h @@ -0,0 +1,23 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +typedef void (^FIRCLSAsyncOperationCompletionBlock)(NSError* error); + +@interface FIRCLSAsyncOperation : NSOperation + +@property(copy, nonatomic) FIRCLSAsyncOperationCompletionBlock asyncCompletion; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation.m new file mode 100644 index 000000000..94415f14e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation.m @@ -0,0 +1,135 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSAsyncOperation.h" + +@interface FIRCLSAsyncOperation () { + BOOL _internalExecuting; + BOOL _internalFinished; +} + +@property(nonatomic, strong) NSRecursiveLock *lock; + +@end + +@implementation FIRCLSAsyncOperation + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _internalExecuting = NO; + _internalFinished = NO; + + self.lock = [[NSRecursiveLock alloc] init]; + self.lock.name = @"com.crashlytics.async-operation-lock"; + + return self; +} + +#pragma mark - NSOperation Overrides +- (BOOL)isConcurrent { + return YES; +} + +- (BOOL)isAsynchronous { + return YES; +} + +- (BOOL)isExecuting { + [self.lock lock]; + BOOL result = _internalExecuting; + [self.lock unlock]; + + return result; +} + +- (BOOL)isFinished { + [self.lock lock]; + BOOL result = _internalFinished; + [self.lock unlock]; + + return result; +} + +- (void)start { + if ([self checkForCancellation]) { + return; + } + + [self markStarted]; + + [self main]; +} + +#pragma mark - Utilities +- (void)changeValueForKey:(NSString *)key inBlock:(void (^)(void))block { + [self willChangeValueForKey:key]; + block(); + [self didChangeValueForKey:key]; +} + +- (void)lock:(void (^)(void))block { + [self.lock lock]; + block(); + [self.lock unlock]; +} + +- (BOOL)checkForCancellation { + if ([self isCancelled]) { + [self markDone]; + return YES; + } + + return NO; +} + +#pragma mark - State Management +- (void)unlockedMarkFinished { + [self changeValueForKey:@"isFinished" + inBlock:^{ + self->_internalFinished = YES; + }]; +} + +- (void)unlockedMarkStarted { + [self changeValueForKey:@"isExecuting" + inBlock:^{ + self->_internalExecuting = YES; + }]; +} + +- (void)unlockedMarkComplete { + [self changeValueForKey:@"isExecuting" + inBlock:^{ + self->_internalExecuting = NO; + }]; +} + +- (void)markStarted { + [self lock:^{ + [self unlockedMarkStarted]; + }]; +} + +- (void)markDone { + [self lock:^{ + [self unlockedMarkComplete]; + [self unlockedMarkFinished]; + }]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation_Private.h new file mode 100644 index 000000000..1135ed75b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/FIRCLSAsyncOperation_Private.h @@ -0,0 +1,24 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSAsyncOperation.h" + +@interface FIRCLSAsyncOperation (Private) + +- (void)markStarted; +- (void)markDone; + +- (BOOL)checkForCancellation; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSPackageReportOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSPackageReportOperation.h new file mode 100644 index 000000000..5822e7527 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSPackageReportOperation.h @@ -0,0 +1,35 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRCLSInternalReport; +@class FIRCLSFileManager; +@class FIRCLSSettings; + +@interface FIRCLSPackageReportOperation : NSOperation + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)initWithReport:(FIRCLSInternalReport *)report + fileManager:(FIRCLSFileManager *)fileManager + settings:(FIRCLSSettings *)settings NS_DESIGNATED_INITIALIZER; + +@property(nonatomic, readonly) FIRCLSInternalReport *report; +@property(nonatomic, readonly) FIRCLSFileManager *fileManager; +@property(nonatomic, readonly) FIRCLSSettings *settings; + +@property(nonatomic, copy, readonly) NSString *finalPath; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSPackageReportOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSPackageReportOperation.m new file mode 100644 index 000000000..aaf4c2a16 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSPackageReportOperation.m @@ -0,0 +1,210 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSPackageReportOperation.h" + +#include +#include + +#import "FIRCLSFileManager.h" +#import "FIRCLSInternalReport.h" + +#import "FIRCLSUtility.h" + +#import "FIRCLSByteUtility.h" +#import "FIRCLSMultipartMimeStreamEncoder.h" + +#import "FIRCLSSettings.h" + +@interface FIRCLSPackageReportOperation () + +@property(nonatomic, copy) NSString *finalPath; + +@end + +@implementation FIRCLSPackageReportOperation + +- (instancetype)initWithReport:(FIRCLSInternalReport *)report + fileManager:(FIRCLSFileManager *)fileManager + settings:(FIRCLSSettings *)settings { + self = [super init]; + if (!self) { + return nil; + } + + _report = report; + _fileManager = fileManager; + _settings = settings; + + return self; +} + +- (BOOL)compressData:(NSData *)data toPath:(NSString *)path { + gzFile file = gzopen([path fileSystemRepresentation], "w"); + if (file == Z_NULL) { + FIRCLSSDKLogError("Error: unable to open file for compression %s\n", strerror(errno)); + return NO; + } + + __block BOOL success = [data length] > 0; + + FIRCLSEnumerateByteRangesOfNSDataUsingBlock( + data, ^(const void *bytes, NSRange byteRange, BOOL *stop) { + size_t length = byteRange.length; + + if (![self writeBytes:bytes length:length toGZFile:file]) { + *stop = YES; + success = NO; + } + }); + + gzclose(file); + + return success; +} + +- (BOOL)writeBytes:(const void *)buffer length:(size_t)length toGZFile:(gzFile)file { + return FIRCLSFileLoopWithWriteBlock( + buffer, length, ^ssize_t(const void *partialBuffer, size_t partialLength) { + errno = 0; + int ret = gzwrite(file, buffer, (unsigned int)length); + + if (ret == 0) { + int zerror = 0; + const char *errorString = gzerror(file, &zerror); + + FIRCLSSDKLogError("Error: failed to write compressed bytes %d, %s, %s \n", zerror, + errorString, strerror(errno)); + } + + return ret; + }); +} + +- (NSString *)reportPath { + return [self.report path]; +} + +- (NSString *)destinationDirectory { + return [self.fileManager legacyPreparedPath]; +} + +- (NSString *)packagedPathWithName:(NSString *)name { + // the output file will use the boundary as the filename, and "multipartmime" as the extension + return [[self.destinationDirectory stringByAppendingPathComponent:name] + stringByAppendingPathExtension:@"multipartmime"]; +} + +- (void)main { + NSString *reportOrgID = self.settings.orgID; + if (!reportOrgID) { + FIRCLSDebugLog( + @"[Crashlytics:PackageReport] Skipping packaging of report with id '%@' this run of the " + @"app because Organization ID was nil. Report will upload once settings are download " + @"successfully", + self.report.identifier); + + return; + } + + self.finalPath = nil; + + NSString *boundary = [FIRCLSMultipartMimeStreamEncoder generateBoundary]; + NSString *destPath = [self packagedPathWithName:boundary]; + + // try to read the metadata file, which could always fail + NSString *reportSessionId = self.report.identifier; + + NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:destPath append:NO]; + + FIRCLSMultipartMimeStreamEncoder *encoder = + [FIRCLSMultipartMimeStreamEncoder encoderWithStream:stream andBoundary:boundary]; + if (!encoder) { + return; + } + + [encoder encode:^{ + [encoder addValue:reportOrgID fieldName:@"org_id"]; + + if (reportSessionId) { + [encoder addValue:reportSessionId fieldName:@"report_id"]; + } + + [self.fileManager + enumerateFilesInDirectory:self.reportPath + usingBlock:^(NSString *filePath, NSString *extension) { + if (self.cancelled) { + return; + } + + // Do not package or include already gz'ed files. These can get left over + // from previously-submitted reports. There's an opportinity here to avoid + // compressed certain types of files that cannot be changed. + if ([extension isEqualToString:@"gz"]) { + return; + } + + NSData *data = [NSData dataWithContentsOfFile:filePath + options:0 + error:nil]; + if ([data length] == 0) { + const char *filename = [[filePath lastPathComponent] UTF8String]; + + FIRCLSSDKLogError("Error: unable to read data for compression: %s\n", + filename); + return; + } + + [self encode:encoder data:data fromPath:filePath]; + }]; + }]; + + if (self.cancelled) { + [self.fileManager removeItemAtPath:destPath]; + return; + } + + self.finalPath = destPath; +} + +- (void)encode:(FIRCLSMultipartMimeStreamEncoder *)encoder + data:(NSData *)data + fromPath:(NSString *)path { + // must be non-nil and > 0 length + if ([path length] == 0) { + FIRCLSSDKLogError("Error: path is invalid\n"); + return; + } + + NSString *uploadPath = [path stringByAppendingPathExtension:@"gz"]; + NSString *fieldname = [path lastPathComponent]; + NSString *filename = [uploadPath lastPathComponent]; + NSString *mimeType = @"application/x-gzip"; + + // first, attempt to compress + if (![self compressData:data toPath:uploadPath]) { + FIRCLSSDKLogError("Error: compression failed for %s\n", [filename UTF8String]); + + // attempt the upload without compression + mimeType = @"text/plain"; + uploadPath = path; + } + + [encoder addFile:[NSURL fileURLWithPath:uploadPath] + fileName:filename + mimeType:mimeType + fieldName:fieldname]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.h new file mode 100644 index 000000000..1e9028636 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.h @@ -0,0 +1,30 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRCLSInternalReport; +@class FIRCLSSymbolResolver; + +@interface FIRCLSProcessReportOperation : NSOperation + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +- (instancetype)initWithReport:(FIRCLSInternalReport *)report + resolver:(FIRCLSSymbolResolver *)resolver NS_DESIGNATED_INITIALIZER; + +@property(nonatomic, readonly) FIRCLSSymbolResolver *symbolResolver; +@property(nonatomic, readonly) FIRCLSInternalReport *report; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.m new file mode 100644 index 000000000..12ce601d6 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.m @@ -0,0 +1,113 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSProcessReportOperation.h" + +#import "FIRCLSDemangleOperation.h" +#import "FIRCLSFile.h" +#import "FIRCLSInternalReport.h" +#import "FIRCLSSerializeSymbolicatedFramesOperation.h" +#import "FIRCLSSymbolResolver.h" +#import "FIRCLSSymbolicationOperation.h" +#import "FIRStackFrame_Private.h" + +@implementation FIRCLSProcessReportOperation + +- (instancetype)initWithReport:(FIRCLSInternalReport *)report + resolver:(FIRCLSSymbolResolver *)resolver { + self = [super init]; + if (!self) { + return nil; + } + + _report = report; + _symbolResolver = resolver; + + return self; +} + +- (NSString *)binaryImagePath { + return self.report.binaryImagePath; +} + +- (NSArray *)threadArrayFromFile:(NSString *)path { + NSArray *threads = + FIRCLSFileReadSections([path fileSystemRepresentation], false, ^NSObject *(id obj) { + // use this to select out the one entry that has a "threads" top-level entry + return [obj objectForKey:@"threads"]; + }); + + if ([threads count] == 0) { + return nil; + } + + // threads is actually an array of arrays + threads = [threads objectAtIndex:0]; + if (!threads) { + return nil; + } + + NSMutableArray *threadArray = [NSMutableArray array]; + + for (NSDictionary *threadDetails in threads) { + NSMutableArray *frameArray = [NSMutableArray array]; + + for (NSNumber *pc in [threadDetails objectForKey:@"stacktrace"]) { + FIRStackFrame *frame = [FIRStackFrame stackFrameWithAddress:[pc unsignedIntegerValue]]; + + [frameArray addObject:frame]; + } + + [threadArray addObject:frameArray]; + } + + return threadArray; +} + +- (BOOL)symbolicateFile:(NSString *)path withResolver:(FIRCLSSymbolResolver *)resolver { + NSArray *threadArray = [self threadArrayFromFile:path]; + if (!threadArray) { + return NO; + } + + FIRCLSSymbolicationOperation *symbolicationOp = [[FIRCLSSymbolicationOperation alloc] init]; + [symbolicationOp setThreadArray:threadArray]; + [symbolicationOp setSymbolResolver:resolver]; + + FIRCLSDemangleOperation *demangleOp = [[FIRCLSDemangleOperation alloc] init]; + [demangleOp setThreadArray:threadArray]; + + FIRCLSSerializeSymbolicatedFramesOperation *serializeOp = + [[FIRCLSSerializeSymbolicatedFramesOperation alloc] init]; + [serializeOp setThreadArray:threadArray]; + [serializeOp setOutputPath:[path stringByAppendingPathExtension:@"symbolicated"]]; + + [symbolicationOp start]; + [demangleOp start]; + [serializeOp start]; + + return YES; +} + +- (void)main { + if (![self.symbolResolver loadBinaryImagesFromFile:self.binaryImagePath]) { + return; + } + + [self.report enumerateSymbolicatableFilesInContent:^(NSString *path) { + [self symbolicateFile:path withResolver:self.symbolResolver]; + }]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.h new file mode 100644 index 000000000..b26b87cd7 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.h @@ -0,0 +1,24 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSThreadArrayOperation.h" + +@interface FIRCLSDemangleOperation : FIRCLSThreadArrayOperation + ++ (NSString *)demangleSymbol:(const char *)symbol; ++ (NSString *)demangleCppSymbol:(const char *)symbol; + +- (NSString *)demangleSymbol:(const char *)symbol; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.mm b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.mm new file mode 100644 index 000000000..364a6ed6d --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSDemangleOperation.mm @@ -0,0 +1,96 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSDemangleOperation.h" +#include "FIRStackFrame_Private.h" + +#import + +@implementation FIRCLSDemangleOperation + ++ (NSString *)demangleSymbol:(const char *)symbol { + if (!symbol) { + return nil; + } + + if (strncmp(symbol, "_Z", 2) == 0) { + return [self demangleCppSymbol:symbol]; + } else if (strncmp(symbol, "__Z", 3) == 0) { + return [self demangleBlockInvokeCppSymbol:symbol]; + } + + return nil; +} + ++ (NSString *)demangleBlockInvokeCppSymbol:(const char *)symbol { + NSString *string = [NSString stringWithUTF8String:symbol]; + + // search backwards, because this string should be at the end + NSRange range = [string rangeOfString:@"_block_invoke" options:NSBackwardsSearch]; + + if (range.location == NSNotFound) { + return nil; + } + + // we need at least a "_Z..." for a valid C++ symbol, so make sure of that + if (range.location < 5) { + return nil; + } + + // extract the mangled C++ symbol from the string + NSString *cppSymbol = [string substringWithRange:NSMakeRange(1, range.location - 1)]; + cppSymbol = [self demangleSymbol:[cppSymbol UTF8String]]; + if (!cppSymbol) { + return nil; + } + + // extract out just the "_block_invoke..." part + string = + [string substringWithRange:NSMakeRange(range.location, [string length] - range.location)]; + + // and glue that onto the end + return [cppSymbol stringByAppendingString:string]; +} + ++ (NSString *)demangleCppSymbol:(const char *)symbol { + int status; + char *buffer = NULL; + + buffer = __cxxabiv1::__cxa_demangle(symbol, buffer, NULL, &status); + if (!buffer) { + return nil; + } + + NSString *result = [NSString stringWithUTF8String:buffer]; + + free(buffer); + + return result; +} + +- (NSString *)demangleSymbol:(const char *)symbol { + return [[self class] demangleSymbol:symbol]; +} + +- (void)main { + [self enumerateFramesWithBlock:^(FIRStackFrame *frame) { + NSString *demangedSymbol = [self demangleSymbol:[[frame rawSymbol] UTF8String]]; + + if (demangedSymbol) { + [frame setSymbol:demangedSymbol]; + } + }]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.h new file mode 100644 index 000000000..b73c67cdc --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.h @@ -0,0 +1,21 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSThreadArrayOperation.h" + +@interface FIRCLSSerializeSymbolicatedFramesOperation : FIRCLSThreadArrayOperation + +@property(nonatomic, copy) NSString *outputPath; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.m new file mode 100644 index 000000000..b8701d261 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSerializeSymbolicatedFramesOperation.m @@ -0,0 +1,63 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSSerializeSymbolicatedFramesOperation.h" + +#import "FIRCLSFile.h" +#import "FIRCLSLogger.h" +#import "FIRStackFrame_Private.h" + +@implementation FIRCLSSerializeSymbolicatedFramesOperation + +- (void)main { + FIRCLSFile file; + + // Make sure not to open in append mode, so we can overwrite any pre-existing symbolication + // files. + if (!FIRCLSFileInitWithPathMode(&file, [self.outputPath fileSystemRepresentation], false, + false)) { + FIRCLSErrorLog(@"Failed to create output file"); + return; + } + + FIRCLSFileWriteSectionStart(&file, "threads"); + FIRCLSFileWriteArrayStart(&file); + + for (NSArray *frameArray in self.threadArray) { + FIRCLSFileWriteArrayStart(&file); + + for (FIRStackFrame *frame in frameArray) { + FIRCLSFileWriteHashStart(&file); + FIRCLSFileWriteHashEntryString(&file, "symbol", [[frame symbol] UTF8String]); + + // only include this field if it is present and different + if (![[frame rawSymbol] isEqualToString:[frame symbol]]) { + FIRCLSFileWriteHashEntryString(&file, "raw_symbol", [[frame rawSymbol] UTF8String]); + } + + FIRCLSFileWriteHashEntryUint64(&file, "offset", [frame offset]); + FIRCLSFileWriteHashEntryString(&file, "library", [[frame library] UTF8String]); + + FIRCLSFileWriteHashEnd(&file); + } + + FIRCLSFileWriteArrayEnd(&file); + } + + FIRCLSFileWriteArrayEnd(&file); + FIRCLSFileWriteSectionEnd(&file); + FIRCLSFileClose(&file); +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.h new file mode 100644 index 000000000..7c63e20e9 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.h @@ -0,0 +1,23 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSThreadArrayOperation.h" + +@class FIRCLSSymbolResolver; + +@interface FIRCLSSymbolicationOperation : FIRCLSThreadArrayOperation + +@property(nonatomic, strong) FIRCLSSymbolResolver *symbolResolver; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.m new file mode 100644 index 000000000..d15005bbe --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSSymbolicationOperation.m @@ -0,0 +1,27 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSSymbolicationOperation.h" + +#import "FIRCLSSymbolResolver.h" + +@implementation FIRCLSSymbolicationOperation + +- (void)main { + [self enumerateFramesWithBlock:^(FIRStackFrame *frame) { + [self.symbolResolver updateStackFrame:frame]; + }]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.h new file mode 100644 index 000000000..0c2a1df54 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.h @@ -0,0 +1,25 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRStackFrame; + +@interface FIRCLSThreadArrayOperation : NSOperation + +@property(nonatomic, strong) NSArray *threadArray; + +- (void)enumerateFramesWithBlock:(void (^)(FIRStackFrame *frame))block; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.m new file mode 100644 index 000000000..3f7509e7e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Operations/Symbolication/FIRCLSThreadArrayOperation.m @@ -0,0 +1,31 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSThreadArrayOperation.h" + +@implementation FIRCLSThreadArrayOperation + +- (void)enumerateFramesWithBlock:(void (^)(FIRStackFrame *frame))block { + for (NSArray *frameArray in self.threadArray) { + for (FIRStackFrame *frame in frameArray) { + block(frame); + + if ([self isCancelled]) { + break; + } + } + } +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Private/FIRExceptionModel_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Private/FIRExceptionModel_Private.h new file mode 100644 index 000000000..7cd161a95 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Private/FIRExceptionModel_Private.h @@ -0,0 +1,33 @@ +// Copyright 2020 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIRExceptionModel_Private_h +#define FIRExceptionModel_Private_h + +#import + +#import "FIRExceptionModel.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRExceptionModel (Private) + +@property(nonatomic, copy) NSString *name; +@property(nonatomic, copy) NSString *reason; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* FIRExceptionModel_Private_h */ diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Private/FIRStackFrame_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Private/FIRStackFrame_Private.h new file mode 100644 index 000000000..d4c0a44c9 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Private/FIRStackFrame_Private.h @@ -0,0 +1,44 @@ +// Copyright 2020 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "FIRStackFrame.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class is used in conjunction with recordExceptionModel to record information about + * non-ObjC/C++ exceptions. All information included here will be displayed in the Crashlytics UI, + * and can influence crash grouping. Be particularly careful with the use of the address property. + *If set, Crashlytics will attempt symbolication and could overwrite other properities in the + *process. + **/ +@interface FIRStackFrame (Private) + ++ (instancetype)stackFrame; ++ (instancetype)stackFrameWithAddress:(NSUInteger)address; ++ (instancetype)stackFrameWithSymbol:(NSString *)symbol; + +@property(nonatomic, copy, nullable) NSString *symbol; +@property(nonatomic, copy, nullable) NSString *rawSymbol; +@property(nonatomic, copy, nullable) NSString *library; +@property(nonatomic, copy, nullable) NSString *fileName; +@property(nonatomic, assign) uint32_t lineNumber; +@property(nonatomic, assign) uint64_t offset; +@property(nonatomic, assign) uint64_t address; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRCrashlytics.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRCrashlytics.h new file mode 100644 index 000000000..9f651537b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRCrashlytics.h @@ -0,0 +1,192 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "FIRExceptionModel.h" + +#if __has_include() +#warning "FirebaseCrashlytics and Crashlytics are not compatible \ +in the same app because including multiple crash reporters can \ +cause problems when registering exception handlers." +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + * The Firebase Crashlytics API provides methods to annotate and manage fatal and + * non-fatal reports captured and reported to Firebase Crashlytics. + * + * By default, Firebase Crashlytics is initialized with `[FIRApp configure]`. + * + * Note: The Crashlytics class cannot be subclassed. If this makes testing difficult, + * we suggest using a wrapper class or a protocol extension. + */ +NS_SWIFT_NAME(Crashlytics) +@interface FIRCrashlytics : NSObject + +/** :nodoc: */ +- (instancetype)init NS_UNAVAILABLE; + +/** + * Accesses the singleton Crashlytics instance. + * + * @return The singleton Crashlytics instance. + */ ++ (instancetype)crashlytics NS_SWIFT_NAME(crashlytics()); + +/** + * Adds logging that is sent with your crash data. The logging does not appear in the + * system.log and is only visible in the Crashlytics dashboard. + * + * @param msg Message to log + */ +- (void)log:(NSString *)msg; + +/** + * Adds logging that is sent with your crash data. The logging does not appear in the + * system.log and is only visible in the Crashlytics dashboard. + * + * @param format Format of string + * @param ... A comma-separated list of arguments to substitute into format + */ +- (void)logWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); + +/** + * Adds logging that is sent with your crash data. The logging does not appear in the + * system.log and is only visible in the Crashlytics dashboard. + * + * @param format Format of string + * @param args Arguments to substitute into format + */ +- (void)logWithFormat:(NSString *)format + arguments:(va_list)args NS_SWIFT_NAME(log(format:arguments:)); + +/** + * Sets a custom key and value to be associated with subsequent fatal and non-fatal reports. + * When setting an object value, the object is converted to a string. This is + * typically done by calling "-[NSObject description]". + * + * @param value The value to be associated with the key + * @param key A unique key + */ +- (void)setCustomValue:(id)value forKey:(NSString *)key; + +/** + * Records a user ID (identifier) that's associated with subsequent fatal and non-fatal reports. + * + * If you want to associate a crash with a specific user, we recommend specifying an arbitrary + * string (e.g., a database, ID, hash, or other value that you can index and query, but is + * meaningless to a third-party observer). This allows you to facilitate responses for support + * requests and reach out to users for more information. + * + * @param userID An arbitrary user identifier string that associates a user to a record in your + * system. + */ +- (void)setUserID:(NSString *)userID; + +/** + * Records a non-fatal event described by an NSError object. The events are + * grouped and displayed similarly to crashes. Keep in mind that this method can be expensive. + * The total number of NSErrors that can be recorded during your app's life-cycle is limited by a + * fixed-size circular buffer. If the buffer is overrun, the oldest data is dropped. Errors are + * relayed to Crashlytics on a subsequent launch of your application. + * + * @param error Non-fatal error to be recorded + */ +- (void)recordError:(NSError *)error NS_SWIFT_NAME(record(error:)); + +/** + * Records an Exception Model described by an FIRExceptionModel object. The events are + * grouped and displayed similarly to crashes. Keep in mind that this method can be expensive. + * The total number of FIRExceptionModels that can be recorded during your app's life-cycle is + * limited by a fixed-size circular buffer. If the buffer is overrun, the oldest data is dropped. + * Exception Models are relayed to Crashlytics on a subsequent launch of your application. + * + * @param exceptionModel Instance of the FIRExceptionModel to be recorded + */ +- (void)recordExceptionModel:(FIRExceptionModel *)exceptionModel + NS_SWIFT_NAME(record(exceptionModel:)); + +/** + * Returns whether the app crashed during the previous execution. + */ +- (BOOL)didCrashDuringPreviousExecution; + +/** + * Enables/disables automatic data collection. + * + * Calling this method overrides both the FirebaseCrashlyticsCollectionEnabled flag in your + * App's Info.plist and FIRApp's isDataCollectionDefaultEnabled flag. + * + * When you set a value for this method, it persists across runs of the app. + * + * The value does not apply until the next run of the app. If you want to disable data + * collection without rebooting, add the FirebaseCrashlyticsCollectionEnabled flag to your app's + * Info.plist. + * * + * @param enabled Determines whether automatic data collection is enabled + */ +- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled; + +/** + * Indicates whether or not automatic data collection is enabled + * + * This method uses three ways to decide whether automatic data collection is enabled, + * in order of priority: + * - If setCrashlyticsCollectionEnabled iscalled with a value, use it + * - If the FirebaseCrashlyticsCollectionEnabled key is in your app's Info.plist, use it + * - Otherwise, use the default isDataCollectionDefaultEnabled in FIRApp + */ +- (BOOL)isCrashlyticsCollectionEnabled; + +/** + * Determines whether there are any unsent crash reports cached on the device, then calls the given + * callback. + * + * The callback only executes if automatic data collection is disabled. You can use + * the callback to get one-time consent from a user upon a crash, and then call + * sendUnsentReports or deleteUnsentReports, depending on whether or not the user gives consent. + * + * Disable automatic collection by: + * - Adding the FirebaseCrashlyticsCollectionEnabled: NO key to your App's Info.plist + * - Calling [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:NO] in your app + * - Setting FIRApp's isDataCollectionDefaultEnabled to NO + * + * @param completion The callback that's executed once Crashlytics finishes checking for unsent + * reports. The callback is set to YES if there are unsent reports on disk. + */ +- (void)checkForUnsentReportsWithCompletion:(void (^)(BOOL))completion + NS_SWIFT_NAME(checkForUnsentReports(completion:)); + +/** + * Enqueues any unsent reports on the device to upload to Crashlytics. + * + * This method only applies if automatic data collection is disabled. + * + * When automatic data collection is enabled, Crashlytics automatically uploads and deletes reports + * at startup, so this method is ignored. + */ +- (void)sendUnsentReports; + +/** + * Deletes any unsent reports on the device. + * + * This method only applies if automatic data collection is disabled. + */ +- (void)deleteUnsentReports; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRExceptionModel.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRExceptionModel.h new file mode 100644 index 000000000..a0ee1579e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRExceptionModel.h @@ -0,0 +1,57 @@ +// Copyright 2020 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "FIRStackFrame.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The Firebase Crashlytics Exception Model provides a way to report custom exceptions + * to Crashlytics that came from a runtime environment outside of the native + * platform Crashlytics is running in. + */ +NS_SWIFT_NAME(ExceptionModel) +@interface FIRExceptionModel : NSObject + +/** :nodoc: */ +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes an Exception Model model with the given required fields. + * + * @param name - typically the type of the Exception class + * @param reason - the human-readable reason the issue occurred + */ +- (instancetype)initWithName:(NSString *)name reason:(NSString *)reason; + +/** + * Creates an Exception Model model with the given required fields. + * + * @param name - typically the type of the Exception class + * @param reason - the human-readable reason the issue occurred + */ ++ (instancetype)exceptionModelWithName:(NSString *)name + reason:(NSString *)reason NS_SWIFT_UNAVAILABLE(""); + +/** + * A list of Stack Frames that make up the stack trace. The order of the stack trace is top-first, + * so typically the "main" function is the last element in this list. + */ +@property(nonatomic, copy) NSArray *stackTrace; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRStackFrame.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRStackFrame.h new file mode 100644 index 000000000..ef9746fbb --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FIRStackFrame.h @@ -0,0 +1,53 @@ +// Copyright 2020 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * The Firebase Crashlytics Stack Frame provides a way to construct the lines of + * a stack trace for reporting along with a recorded Exception Model. + */ +NS_SWIFT_NAME(StackFrame) +@interface FIRStackFrame : NSObject + +/** :nodoc: */ +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes a symbolicated Stack Frame with the given required fields. Symbolicated + * Stack Frames will appear in the Crashlytics dashboard as reported in these fields. + * + * @param symbol - The function or method name + * @param file - the file where the exception occurred + * @param line - the line number + */ +- (instancetype)initWithSymbol:(NSString *)symbol file:(NSString *)file line:(NSInteger)line; + +/** + * Creates a symbolicated Stack Frame with the given required fields. Symbolicated + * Stack Frames will appear in the Crashlytics dashboard as reported in these fields. * + * + * @param symbol - The function or method name + * @param file - the file where the exception occurred + * @param line - the line number + */ ++ (instancetype)stackFrameWithSymbol:(NSString *)symbol + file:(NSString *)file + line:(NSInteger)line NS_SWIFT_UNAVAILABLE(""); + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Folly/folly/lang/ColdClass.cpp b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FirebaseCrashlytics.h similarity index 74% rename from ios/Pods/Folly/folly/lang/ColdClass.cpp rename to ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FirebaseCrashlytics.h index 47bf67c21..9022811ba 100644 --- a/ios/Pods/Folly/folly/lang/ColdClass.cpp +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Public/FirebaseCrashlytics.h @@ -1,11 +1,11 @@ /* - * Copyright 2017-present Facebook, Inc. + * Copyright 2019 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include -folly::cold_detail::ColdClass::ColdClass() noexcept {} +#import "FIRCrashlytics.h" +#import "FIRExceptionModel.h" +#import "FIRStackFrame.h" diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/FIRCLSSettingsOnboardingManager.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/FIRCLSSettingsOnboardingManager.h new file mode 100644 index 000000000..2708d0952 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/FIRCLSSettingsOnboardingManager.h @@ -0,0 +1,59 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#if __has_include() +#import +#else +#import "FBLPromises.h" +#endif + +@class FIRCLSApplicationIdentifierModel; +@class FIRCLSDataCollectionToken; +@class FIRCLSFileManager; +@class FIRCLSInstallIdentifierModel; +@class FIRCLSSettings; + +NS_ASSUME_NONNULL_BEGIN + +/** + * Use this class to retrieve remote settings for the application from crashlytics backend, and + * onboard the application on the server. + */ +@interface FIRCLSSettingsOnboardingManager : NSObject + +/** + * Designated Initializer. + */ +- (instancetype)initWithAppIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel + installIDModel:(FIRCLSInstallIdentifierModel *)installIDModel + settings:(FIRCLSSettings *)settings + fileManager:(FIRCLSFileManager *)fileManager + googleAppID:(NSString *)googleAppID NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + * This method kicks off downloading settings and onboarding for the app. + * @param googleAppID (required) GMP id for the app. + * @param token (required) Data collection token signifying we can make network calls + */ +- (void)beginSettingsAndOnboardingWithGoogleAppId:(NSString *)googleAppID + token:(FIRCLSDataCollectionToken *)token + waitForCompletion:(BOOL)waitForCompletion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/FIRCLSSettingsOnboardingManager.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/FIRCLSSettingsOnboardingManager.m new file mode 100644 index 000000000..c820d2dcb --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/FIRCLSSettingsOnboardingManager.m @@ -0,0 +1,243 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSSettingsOnboardingManager.h" + +#import "FIRCLSApplicationIdentifierModel.h" +#import "FIRCLSConstants.h" +#import "FIRCLSDataCollectionToken.h" +#import "FIRCLSDefines.h" +#import "FIRCLSDownloadAndSaveSettingsOperation.h" +#import "FIRCLSFABNetworkClient.h" +#import "FIRCLSFileManager.h" +#import "FIRCLSInstallIdentifierModel.h" +#import "FIRCLSLogger.h" +#import "FIRCLSOnboardingOperation.h" +#import "FIRCLSSettings.h" +#import "FIRCLSURLBuilder.h" + +@interface FIRCLSSettingsOnboardingManager () + +@property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel; +@property(nonatomic, strong) FIRCLSInstallIdentifierModel *installIDModel; + +@property(nonatomic, strong) FIRCLSSettings *settings; + +@property(nonatomic, nullable, strong) FIRCLSOnboardingOperation *onboardingOperation; +@property(nonatomic, strong) FIRCLSFileManager *fileManager; + +// set to YES once onboarding call has been made. +@property(nonatomic) BOOL hasAttemptedAppConfigure; + +@property(nonatomic) NSDictionary *configuration; +@property(nonatomic) NSDictionary *defaultConfiguration; +@property(nonatomic, copy) NSString *googleAppID; +@property(nonatomic, copy) NSDictionary *kitVersionsByKitBundleIdentifier; +@property(nonatomic, readonly) FIRCLSFABNetworkClient *networkClient; + +@end + +@implementation FIRCLSSettingsOnboardingManager + +- (instancetype)initWithAppIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel + installIDModel:(FIRCLSInstallIdentifierModel *)installIDModel + settings:(FIRCLSSettings *)settings + fileManager:(FIRCLSFileManager *)fileManager + googleAppID:(NSString *)googleAppID { + self = [super init]; + if (!self) { + return nil; + } + + _appIDModel = appIDModel; + _installIDModel = installIDModel; + _settings = settings; + _fileManager = fileManager; + _googleAppID = googleAppID; + + _networkClient = [[FIRCLSFABNetworkClient alloc] initWithQueue:nil]; + + return self; +} + +- (void)beginSettingsAndOnboardingWithGoogleAppId:(NSString *)googleAppID + token:(FIRCLSDataCollectionToken *)token + waitForCompletion:(BOOL)waitForCompletion { + NSParameterAssert(googleAppID); + + self.googleAppID = googleAppID; + + // This map helps us determine what versions of the SDK + // are out there. We're keeping the Fabric value in there for + // backwards compatibility + // TODO(b/141747635) + self.kitVersionsByKitBundleIdentifier = @{ + FIRCLSApplicationGetSDKBundleID() : @CLS_SDK_DISPLAY_VERSION, + }; + + [self beginSettingsDownload:token waitForCompletion:waitForCompletion]; +} + +#pragma mark Helper methods + +/** + * Makes a settings download request. If the request fails, the error is handled silently(with a log + * statement). If the server response indicates onboarding is needed, an onboarding request is sent + * to the server. If the onboarding request fails, the error is handled silently(with a log + * statement). + */ +- (void)beginSettingsDownload:(FIRCLSDataCollectionToken *)token + waitForCompletion:(BOOL)waitForCompletion { + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + + FIRCLSDownloadAndSaveSettingsOperation *operation = nil; + operation = [[FIRCLSDownloadAndSaveSettingsOperation alloc] + initWithGoogleAppID:self.googleAppID + delegate:self + settingsURL:self.settingsURL + settingsDirectoryPath:self.fileManager.settingsDirectoryPath + settingsFilePath:self.fileManager.settingsFilePath + installIDModel:self.installIDModel + networkClient:self.networkClient + token:token]; + + if (waitForCompletion) { + operation.asyncCompletion = ^(NSError *error) { + dispatch_semaphore_signal(semaphore); + }; + } + + [operation startWithToken:token]; + + if (waitForCompletion) { + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + } +} + +- (void)beginOnboarding:(BOOL)appCreate + endpointString:(NSString *)endpoint + token:(FIRCLSDataCollectionToken *)token { + [self.onboardingOperation cancel]; + + self.onboardingOperation = + [[FIRCLSOnboardingOperation alloc] initWithDelegate:self + shouldCreate:appCreate + googleAppID:self.googleAppID + kitVersionsByKitBundleIdentifier:self.kitVersionsByKitBundleIdentifier + appIdentifierModel:self.appIDModel + endpointString:endpoint + networkClient:self.networkClient + token:token + settings:self.settings]; + + [self.onboardingOperation startWithToken:token]; +} + +- (void)finishNetworkingSession { + [self.networkClient invalidateAndCancel]; +} + +#pragma mark FIRCLSOnboardingOperationDelegate methods + +- (void)onboardingOperation:(FIRCLSOnboardingOperation *)operation + didCompleteAppCreationWithError:(nullable NSError *)error { + if (error) { + FIRCLSErrorLog(@"Unable to complete application configure: %@", error); + [self finishNetworkingSession]; + return; + } + self.onboardingOperation = nil; + FIRCLSDebugLog(@"Completed configure"); + + // now, go get settings, as they can change (and it completes the onboarding process) + [self beginSettingsDownload:operation.token waitForCompletion:NO]; +} + +- (void)onboardingOperation:(FIRCLSOnboardingOperation *)operation + didCompleteAppUpdateWithError:(nullable NSError *)error { + [self finishNetworkingSession]; + if (error) { + FIRCLSErrorLog(@"Unable to complete application update: %@", error); + return; + } + self.onboardingOperation = nil; + FIRCLSDebugLog(@"Completed application update"); +} + +#pragma mark FIRCLSDownloadAndSaveSettingsOperationDelegate methods + +- (void)operation:(FIRCLSDownloadAndSaveSettingsOperation *)operation + didDownloadAndSaveSettingsWithError:(nullable NSError *)error { + if (error) { + FIRCLSErrorLog(@"Failed to download settings %@", error); + [self finishNetworkingSession]; + return; + } + + FIRCLSDebugLog(@"Settings downloaded successfully"); + + NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate]; + [self.settings cacheSettingsWithGoogleAppID:self.googleAppID currentTimestamp:currentTimestamp]; + + // only try this once + if (self.hasAttemptedAppConfigure) { + FIRCLSDebugLog(@"App already onboarded in this run of the app"); + [self finishNetworkingSession]; + return; + } + + // Onboarding is still needed in Firebase, here are the backend app states - + // 1. When the app is created in the Firebase console, app state: built (client settings call + // returns app status: new) + // 2. After onboarding call is made, app state: build_configured + // 3. Another settings call is triggered after onboarding, app state: activated + if ([self.settings appNeedsOnboarding]) { + FIRCLSDebugLog(@"Starting onboarding with app create"); + self.hasAttemptedAppConfigure = YES; + [self beginOnboarding:YES endpointString:FIRCLSConfigureEndpoint token:operation.token]; + return; + } + + if ([self.settings appUpdateRequired]) { + FIRCLSDebugLog(@"Starting onboarding with app update"); + self.hasAttemptedAppConfigure = YES; + [self beginOnboarding:NO endpointString:FIRCLSConfigureEndpoint token:operation.token]; + return; + } + + // we're all set! + [self finishNetworkingSession]; +} + +- (NSURL *)settingsURL { + // GET + // /spi/v2/platforms/:platform/apps/:identifier/settings?build_version=1234&display_version=abc&instance=xyz&source=1 + FIRCLSURLBuilder *url = [FIRCLSURLBuilder URLWithBase:FIRCLSSettingsEndpoint]; + + [url appendComponent:@"/spi/v2/platforms/"]; + [url escapeAndAppendComponent:self.appIDModel.platform]; + [url appendComponent:@"/gmp/"]; + [url escapeAndAppendComponent:self.googleAppID]; + [url appendComponent:@"/settings"]; + + [url appendValue:self.appIDModel.buildVersion forQueryParam:@"build_version"]; + [url appendValue:self.appIDModel.displayVersion forQueryParam:@"display_version"]; + [url appendValue:self.appIDModel.buildInstanceID forQueryParam:@"instance"]; + [url appendValue:@(self.appIDModel.installSource) forQueryParam:@"source"]; + // TODO: find the right param name for KitVersions and add them here + return url.URL; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h new file mode 100644 index 000000000..3bc75630b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h @@ -0,0 +1,71 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "FIRCLSApplication.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * This class is a model for identifiers related to the application binary. + * It is thread-safe. + */ +@interface FIRCLSApplicationIdentifierModel : NSObject + +@property(nonatomic, readonly, nullable) NSString* bundleID; + +/** + * Returns the user-facing app name + */ +@property(nonatomic, readonly, nullable) NSString* displayName; + +@property(nonatomic, readonly, nullable) NSString* platform; +@property(nonatomic, readonly, nullable) NSString* buildVersion; +@property(nonatomic, readonly, nullable) NSString* displayVersion; + +/** + * Returns the synthesized app version, similar to how the backend does it + * () + */ +@property(nonatomic, readonly, nullable) NSString* synthesizedVersion; + +@property(nonatomic, readonly) FIRCLSApplicationInstallationSourceType installSource; + +/** + * A mapping between all supported architectures and their UUIDs + */ +@property(nonatomic, readonly) NSDictionary* architectureUUIDMap; + +/** + * Returns the linked OS SDK + */ +@property(nonatomic, readonly) NSString* builtSDKString; + +/** + * Returns the min supported OS + */ +@property(nonatomic, readonly) NSString* minimumSDKString; + +/** + * The unique identifier for this instance of the version of app running Crashlytics. This is + * computed by hashing the app itself. + * + * On Android, this is called the Build ID + */ +@property(nonatomic, readonly) NSString* buildInstanceID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.m new file mode 100644 index 000000000..3429df4d8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.m @@ -0,0 +1,138 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSApplicationIdentifierModel.h" + +#import "FIRCLSApplication.h" +#import "FIRCLSByteUtility.h" +#import "FIRCLSDefines.h" +#import "FIRCLSMachO.h" +#import "FIRCLSUUID.h" + +@interface FIRCLSApplicationIdentifierModel () + +@property(nonatomic, copy, readwrite) NSDictionary *architectureUUIDMap; +@property(nonatomic, copy, readwrite) NSString *buildInstanceID; +@property(nonatomic, readonly) FIRCLSMachOVersion builtSDK; +@property(nonatomic, readonly) FIRCLSMachOVersion minimumSDK; + +@end + +@implementation FIRCLSApplicationIdentifierModel + +- (nullable instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + if (![self computeExecutableInfo]) { + return nil; + } + + [self computeInstanceIdentifier]; + + return self; +} + +- (NSString *)bundleID { + return FIRCLSApplicationGetBundleIdentifier(); +} + +- (NSString *)displayName { + return FIRCLSApplicationGetName(); +} + +- (NSString *)platform { + return FIRCLSApplicationGetPlatform(); +} + +- (NSString *)buildVersion { + return FIRCLSApplicationGetBundleVersion(); +} + +- (NSString *)displayVersion { + return FIRCLSApplicationGetShortBundleVersion(); +} + +- (NSString *)synthesizedVersion { + return [NSString stringWithFormat:@"%@ (%@)", self.displayVersion, self.buildVersion]; +} + +- (FIRCLSApplicationInstallationSourceType)installSource { + return FIRCLSApplicationInstallationSource(); +} + +- (NSString *)builtSDKString { + return FIRCLSMachOFormatVersion(&_builtSDK); +} + +- (NSString *)minimumSDKString { + return FIRCLSMachOFormatVersion(&_minimumSDK); +} + +- (BOOL)computeExecutableInfo { + struct FIRCLSMachOFile file; + + if (!FIRCLSMachOFileInitWithCurrent(&file)) { + return NO; + } + + NSMutableDictionary *executables = [NSMutableDictionary dictionary]; + + FIRCLSMachOFileEnumerateSlices(&file, ^(FIRCLSMachOSliceRef fileSlice) { + NSString *arch; + + arch = [NSString stringWithUTF8String:FIRCLSMachOSliceGetArchitectureName(fileSlice)]; + + FIRCLSMachOSliceEnumerateLoadCommands( + fileSlice, ^(uint32_t type, uint32_t size, const struct load_command *cmd) { + if (type == LC_UUID) { + const uint8_t *uuid; + + uuid = FIRCLSMachOGetUUID(cmd); + + [executables setObject:FIRCLSUUIDToNSString(uuid) forKey:arch]; + } else if (type == LC_VERSION_MIN_MACOSX || type == LC_VERSION_MIN_IPHONEOS) { + self->_minimumSDK = FIRCLSMachOGetMinimumOSVersion(cmd); + self->_builtSDK = FIRCLSMachOGetLinkedSDKVersion(cmd); + } + }); + }); + + FIRCLSMachOFileDestroy(&file); + + _architectureUUIDMap = executables; + + return YES; +} + +- (void)computeInstanceIdentifier { + // build up the components of the instance identifier + NSMutableString *string = [NSMutableString string]; + + // first, the uuids, sorted by architecture name + for (NSString *key in + [[_architectureUUIDMap allKeys] sortedArrayUsingSelector:@selector(compare:)]) { + [string appendString:[self.architectureUUIDMap objectForKey:key]]; + } + + // TODO: the instance identifier calculation needs to match Beta's expectation. So, we have to + // continue generating a less-correct value for now. One day, we should encorporate a hash of the + // Info.plist and icon data. + + _buildInstanceID = FIRCLSHashNSData([string dataUsingEncoding:NSUTF8StringEncoding]); +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSDownloadAndSaveSettingsOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSDownloadAndSaveSettingsOperation.h new file mode 100644 index 000000000..318089ff9 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSDownloadAndSaveSettingsOperation.h @@ -0,0 +1,80 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "FIRCLSNetworkOperation.h" + +@class FIRCLSDownloadAndSaveSettingsOperation; +@class FIRCLSFABNetworkClient; +@class FIRCLSInstallIdentifierModel; + +NS_ASSUME_NONNULL_BEGIN + +/** + * This is the protocol that a delegate of FIRCLSDownloadAndSaveSettingsOperation needs to follow. + */ +@protocol FIRCLSDownloadAndSaveSettingsOperationDelegate + +@required + +/** + * Method that is called when settings have been downloaded and saved, or an error has occurred + * during the operation. This method may be called on an arbitrary background thread. + */ +- (void)operation:(FIRCLSDownloadAndSaveSettingsOperation *)operation + didDownloadAndSaveSettingsWithError:(nullable NSError *)error; + +@end + +/** + * This operation downloads settings from the backend servers, and saves them in file on disk. + */ +@interface FIRCLSDownloadAndSaveSettingsOperation : FIRCLSNetworkOperation + +- (instancetype)initWithGoogleAppID:(NSString *)googleAppID + token:(FIRCLSDataCollectionToken *)token NS_UNAVAILABLE; + +/** + * @param googleAppID must NOT be nil + * @param delegate gets a callback after settings have been downloaded or an error occurs. + * @param settingsURL must NOT be nil. This is the URL to which a download request is made. + * @param settingsDirectoryPath must NOT be nil. This is the directory on disk where the settings + * are persisted. + * @param settingsFilePath must NOT be nil. It is the full file path(including file name) in which + * settings will be persisted on disk. + * @param installIDModel must NOT be nil. This value is sent back to the backend to uniquely + * identify the app install. + */ +- (instancetype)initWithGoogleAppID:(NSString *)googleAppID + delegate:(id)delegate + settingsURL:(NSURL *)settingsURL + settingsDirectoryPath:(NSString *)settingsDirectoryPath + settingsFilePath:(NSString *)settingsFilePath + installIDModel:(FIRCLSInstallIdentifierModel *)installIDModel + networkClient:(FIRCLSFABNetworkClient *)networkClient + token:(FIRCLSDataCollectionToken *)token NS_DESIGNATED_INITIALIZER; + +/** + * Delegate of this operation. + */ +@property(nonatomic, readonly, weak) id delegate; + +/** + * When an error occurs during this operation, it is made available in this property. + */ +@property(nonatomic, readonly) NSError *error; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSDownloadAndSaveSettingsOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSDownloadAndSaveSettingsOperation.m new file mode 100644 index 000000000..9d32f9150 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSDownloadAndSaveSettingsOperation.m @@ -0,0 +1,132 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSDownloadAndSaveSettingsOperation.h" + +#import "FIRCLSConstants.h" +#import "FIRCLSFABHost.h" +#import "FIRCLSFABNetworkClient.h" +#import "FIRCLSInstallIdentifierModel.h" +#import "FIRCLSLogger.h" + +@interface FIRCLSDownloadAndSaveSettingsOperation () + +/** + * Method called to fetch the URL from where settings have to be downloaded. + */ +@property(readonly, nonatomic) NSURL *settingsURL; +/** + * File manager which will be used to save settings on disk. + */ +@property(readonly, nonatomic) NSFileManager *fileManager; + +/** + * Directory path on which settings file will be saved + */ +@property(readonly, nonatomic) NSString *settingsDirectoryPath; +/** + * Complete file path on which settings file will be saved + */ +@property(readonly, nonatomic) NSString *settingsFilePath; +/** + * App install identifier. + */ +@property(strong, readonly, nonatomic) FIRCLSInstallIdentifierModel *installIDModel; + +@property(weak, readonly, nonatomic) FIRCLSFABNetworkClient *networkClient; + +@end + +@implementation FIRCLSDownloadAndSaveSettingsOperation + +- (instancetype)initWithGoogleAppID:(NSString *)googleAppID + delegate:(id)delegate + settingsURL:(NSURL *)settingsURL + settingsDirectoryPath:(NSString *)settingsDirectoryPath + settingsFilePath:(NSString *)settingsFilePath + installIDModel:(FIRCLSInstallIdentifierModel *)installIDModel + networkClient:(FIRCLSFABNetworkClient *)networkClient + token:(FIRCLSDataCollectionToken *)token { + NSParameterAssert(settingsURL); + NSParameterAssert(settingsDirectoryPath); + NSParameterAssert(settingsFilePath); + NSParameterAssert(installIDModel); + + self = [super initWithGoogleAppID:googleAppID token:token]; + if (self) { + _delegate = delegate; + _settingsURL = settingsURL.copy; + _settingsDirectoryPath = settingsDirectoryPath.copy; + _settingsFilePath = settingsFilePath.copy; + _fileManager = [[NSFileManager alloc] init]; + _installIDModel = installIDModel; + _networkClient = networkClient; + } + return self; +} + +- (NSMutableURLRequest *)mutableRequestWithDefaultHTTPHeaderFieldsAndTimeoutForURL:(NSURL *)url { + NSMutableURLRequest *request = + [super mutableRequestWithDefaultHTTPHeaderFieldsAndTimeoutForURL:url]; + request.HTTPMethod = @"GET"; + [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; + [request setValue:self.installIDModel.installID + forHTTPHeaderField:@"X-Crashlytics-Installation-ID"]; + [request setValue:FIRCLSHostModelInfo() forHTTPHeaderField:@"X-Crashlytics-Device-Model"]; + [request setValue:FIRCLSHostOSBuildVersion() + forHTTPHeaderField:@"X-Crashlytics-OS-Build-Version"]; + [request setValue:FIRCLSHostOSDisplayVersion() + forHTTPHeaderField:@"X-Crashlytics-OS-Display-Version"]; + [request setValue:FIRCLSVersion forHTTPHeaderField:@"X-Crashlytics-API-Client-Version"]; + + return request; +} + +- (void)main { + NSMutableURLRequest *request = + [self mutableRequestWithDefaultHTTPHeaderFieldsAndTimeoutForURL:self.settingsURL]; + + [self.networkClient + startDownloadTaskWithRequest:request + retryLimit:1 + completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { + if (error) { + self->_error = error; + [self.delegate operation:self didDownloadAndSaveSettingsWithError:self.error]; + [self finishWithError:error]; + return; + } + // This move needs to happen synchronously, because after this method completes, + // the file will not be available. + NSError *moveError = nil; + + // this removal will frequently fail, and we don't want the warning + [self.fileManager removeItemAtPath:self.settingsDirectoryPath error:nil]; + + [self.fileManager createDirectoryAtPath:self.settingsDirectoryPath + withIntermediateDirectories:YES + attributes:nil + error:nil]; + if (![self.fileManager moveItemAtPath:location.path + toPath:self.settingsFilePath + error:&moveError]) { + FIRCLSErrorLog(@"Unable to complete settings download %@", moveError); + self->_error = moveError; + } + [self.delegate operation:self didDownloadAndSaveSettingsWithError:self.error]; + [self finishWithError:self.error]; + }]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSNetworkOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSNetworkOperation.h new file mode 100644 index 000000000..a449903f7 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSNetworkOperation.h @@ -0,0 +1,55 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "FIRCLSOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@class FIRCLSDataCollectionToken; +@class FIRCLSSettings; + +/** + * This is a base class for network based operations. + */ +@interface FIRCLSNetworkOperation : FIRCLSFABAsyncOperation + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +/** + * Designated initializer. All parameters are mandatory and must not be nil. + */ +- (instancetype)initWithGoogleAppID:(NSString *)googleAppID + token:(FIRCLSDataCollectionToken *)token NS_DESIGNATED_INITIALIZER; + +- (void)start NS_UNAVAILABLE; +- (void)startWithToken:(FIRCLSDataCollectionToken *)token; + +/** + * Creates a mutable request for posting to Crashlytics backend with a default timeout. + */ +- (NSMutableURLRequest *)mutableRequestWithDefaultHTTPHeaderFieldsAndTimeoutForURL:(NSURL *)url; + +/** + * Creates a mutable request for posting to Crashlytics backend with given timeout. + */ +- (NSMutableURLRequest *)mutableRequestWithDefaultHTTPHeadersForURL:(NSURL *)url + timeout:(NSTimeInterval)timeout; + +@property(nonatomic, strong, readonly) FIRCLSDataCollectionToken *token; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSNetworkOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSNetworkOperation.m new file mode 100644 index 000000000..52b77c19c --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSNetworkOperation.m @@ -0,0 +1,92 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSNetworkOperation.h" + +#import "FIRCLSApplication.h" +#import "FIRCLSConstants.h" +#import "FIRCLSDataCollectionToken.h" +#import "FIRCLSDefines.h" +#import "FIRCLSLogger.h" + +@interface FIRCLSNetworkOperation () + +@property(nonatomic, strong, readonly) NSString *googleAppID; + +@end + +@implementation FIRCLSNetworkOperation + +- (instancetype)initWithGoogleAppID:(NSString *)googleAppID + token:(FIRCLSDataCollectionToken *)token { + NSParameterAssert(googleAppID); + if (!googleAppID) { + return nil; + } + + self = [super init]; + if (self) { + _googleAppID = googleAppID; + _token = token; + } + return self; +} + +- (void)startWithToken:(FIRCLSDataCollectionToken *)token { + // Settings and Onboarding are considered data collection, so we must only + // call this with a valid token + if (![token isValid]) { + FIRCLSErrorLog(@"Skipping network operation with invalid data collection token"); + return; + } + + [super start]; +} + +- (NSMutableURLRequest *)mutableRequestWithDefaultHTTPHeaderFieldsAndTimeoutForURL:(NSURL *)url { + return [self mutableRequestWithDefaultHTTPHeadersForURL:url timeout:10.0]; +} + +- (NSMutableURLRequest *)mutableRequestWithDefaultHTTPHeadersForURL:(NSURL *)url + timeout:(NSTimeInterval)timeout { + NSMutableURLRequest *request = + [NSMutableURLRequest requestWithURL:url + cachePolicy:NSURLRequestReloadIgnoringLocalCacheData + timeoutInterval:timeout]; + + NSString *localeId = self.localeIdentifier; + + [request setValue:self.userAgentString forHTTPHeaderField:FIRCLSNetworkUserAgent]; + [request setValue:FIRCLSNetworkUTF8 forHTTPHeaderField:FIRCLSNetworkAcceptCharset]; + [request setValue:localeId forHTTPHeaderField:FIRCLSNetworkAcceptLanguage]; + [request setValue:localeId forHTTPHeaderField:FIRCLSNetworkContentLanguage]; + [request setValue:FIRCLSDeveloperToken forHTTPHeaderField:FIRCLSNetworkCrashlyticsDeveloperToken]; + [request setValue:FIRCLSApplicationGetSDKBundleID() + forHTTPHeaderField:FIRCLSNetworkCrashlyticsAPIClientId]; + [request setValue:FIRCLSVersion + forHTTPHeaderField:FIRCLSNetworkCrashlyticsAPIClientDisplayVersion]; + [request setValue:self.googleAppID forHTTPHeaderField:FIRCLSNetworkCrashlyticsGoogleAppId]; + + return request; +} + +- (NSString *)userAgentString { + return [NSString stringWithFormat:@"%@/%@", FIRCLSApplicationGetSDKBundleID(), FIRCLSVersion]; +} + +- (NSString *)localeIdentifier { + return NSLocale.currentLocale.localeIdentifier; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSOnboardingOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSOnboardingOperation.h new file mode 100644 index 000000000..14d56ed64 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSOnboardingOperation.h @@ -0,0 +1,84 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "FIRCLSApplicationIdentifierModel.h" +#import "FIRCLSDataCollectionToken.h" +#import "FIRCLSNetworkOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +extern NSString *const FIRCLSOnboardingErrorDomain; + +@class FIRCLSOnboardingOperation; +@class FIRCLSFABNetworkClient; +@class FIRCLSSettings; + +/** + * This is the protocol that a delegate of FIRCLSOnboardingOperation should follow. + */ +@protocol FIRCLSOnboardingOperationDelegate +@required + +/** + * This callback is for the delegate to know that app update has completed with/without an error. + */ +- (void)onboardingOperation:(FIRCLSOnboardingOperation *)operation + didCompleteAppUpdateWithError:(nullable NSError *)error; +/** + * This callback is for the delegate to know that app creation has completed with/without an error. + */ +- (void)onboardingOperation:(FIRCLSOnboardingOperation *)operation + didCompleteAppCreationWithError:(nullable NSError *)error; + +@end + +/** + * This class onboards the app, by making request to the backend servers. + */ +@interface FIRCLSOnboardingOperation : FIRCLSNetworkOperation + +/** + * When an error occurs during this operation, it is made available in this property. + */ +@property(nonatomic, readonly) NSError *error; + +- (instancetype)initWithGoogleAppID:(NSString *)googleAppID + token:(FIRCLSDataCollectionToken *)token NS_UNAVAILABLE; + +/** + * Designated initializer. + * @param delegate may be nil. Gets callbacks when app creation or updation succeeds or gets errored + * out. + * @param googleAppID must NOT be nil. + * @param kitVersionsByKitBundleIdentifier may be nil. Maps Kit bundle identifier to kit version + * being used in the app. + * @param appIdentifierModel must NOT be nil. Used to get information required in the onboarding + * network call. + * @param appEndPoint must NOT be nil. Endpoint which needs to be hit with the onboarding request. + * @param settings which are used to fetch the organization identifier. + */ +- (instancetype)initWithDelegate:(id)delegate + shouldCreate:(BOOL)shouldCreate + googleAppID:(NSString *)googleAppID + kitVersionsByKitBundleIdentifier:(NSDictionary *)kitVersionsByKitBundleIdentifier + appIdentifierModel:(FIRCLSApplicationIdentifierModel *)appIdentifierModel + endpointString:(NSString *)appEndPoint + networkClient:(FIRCLSFABNetworkClient *)networkClient + token:(FIRCLSDataCollectionToken *)token + settings:(FIRCLSSettings *)settings NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSOnboardingOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSOnboardingOperation.m new file mode 100644 index 000000000..ce2e58b5c --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Settings/Operations/FIRCLSOnboardingOperation.m @@ -0,0 +1,208 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSOnboardingOperation.h" + +#import "FIRCLSByteUtility.h" +#import "FIRCLSConstants.h" +#import "FIRCLSFABNetworkClient.h" +#import "FIRCLSLogger.h" +#import "FIRCLSMachO.h" +#import "FIRCLSMultipartMimeStreamEncoder.h" +#import "FIRCLSSettings.h" +#import "FIRCLSURLBuilder.h" + +// The SPIv1/v2 onboarding flow looks something like this: +// - get settings +// - settings says we're good, nothing to do +// - settings says update +// - do an update +// - settings says new +// - do a create +// - get settings again (and do *not* take action after that) + +NSString *const FIRCLSOnboardingErrorDomain = @"FIRCLSOnboardingErrorDomain"; + +typedef NS_ENUM(NSInteger, FIRCLSOnboardingError) { + FIRCLSOnboardingErrorMultipartMimeConfiguration +}; + +@interface FIRCLSOnboardingOperation () + +@property(nonatomic) BOOL shouldCreate; +@property(nonatomic, readonly) FIRCLSApplicationIdentifierModel *appIdentifierModel; +@property(nonatomic, readonly) NSString *appEndpoint; +@property(nonatomic, readonly, unsafe_unretained) id delegate; +@property(nonatomic, weak, readonly) FIRCLSFABNetworkClient *networkClient; +@property(nonatomic, readonly) NSDictionary *kitVersionsByKitBundleIdentifier; +@property(nonatomic, readonly) FIRCLSSettings *settings; +@end + +@implementation FIRCLSOnboardingOperation + +#pragma mark lifecycle methods + +- (instancetype)initWithDelegate:(id)delegate + shouldCreate:(BOOL)shouldCreate + googleAppID:(NSString *)googleAppID + kitVersionsByKitBundleIdentifier:(NSDictionary *)kitVersionsByKitBundleIdentifier + appIdentifierModel:(FIRCLSApplicationIdentifierModel *)appIdentifierModel + endpointString:(NSString *)appEndPoint + networkClient:(FIRCLSFABNetworkClient *)networkClient + token:(FIRCLSDataCollectionToken *)token + settings:(FIRCLSSettings *)settings { + NSParameterAssert(appIdentifierModel); + NSParameterAssert(appEndPoint); + + self = [super initWithGoogleAppID:googleAppID token:token]; + if (self) { + _shouldCreate = shouldCreate; + _delegate = delegate; + _appIdentifierModel = appIdentifierModel; + _appEndpoint = appEndPoint; + _networkClient = networkClient; + _kitVersionsByKitBundleIdentifier = kitVersionsByKitBundleIdentifier.copy; + _settings = settings; + } + return self; +} + +- (void)main { + [self beginAppConfigure]; +} + +- (void)beginAppConfigure { + NSOutputStream *stream = [[NSOutputStream alloc] initToMemory]; + NSString *boundary = [FIRCLSMultipartMimeStreamEncoder generateBoundary]; + + FIRCLSMultipartMimeStreamEncoder *encoder = + [FIRCLSMultipartMimeStreamEncoder encoderWithStream:stream andBoundary:boundary]; + if (!encoder) { + FIRCLSErrorLog(@"Configure failed during onboarding"); + [self finishWithError:[self errorForCode:FIRCLSOnboardingErrorMultipartMimeConfiguration + userInfo:@{ + NSLocalizedDescriptionKey : @"Multipart mime encoder was nil" + }]]; + return; + } + + NSString *orgID = [self.settings orgID]; + if (!orgID) { + FIRCLSErrorLog(@"Could not onboard app with missing Organization ID"); + [self finishWithError:[self errorForCode:FIRCLSOnboardingErrorMultipartMimeConfiguration + userInfo:@{ + NSLocalizedDescriptionKey : @"Organization ID was nil" + }]]; + return; + } + + [encoder encode:^{ + [encoder addValue:orgID fieldName:@"org_id"]; + + [encoder addValue:self.settings.fetchedBundleID fieldName:@"app[identifier]"]; + [encoder addValue:self.appIdentifierModel.buildInstanceID + fieldName:@"app[instance_identifier]"]; + [encoder addValue:self.appIdentifierModel.displayName fieldName:@"app[name]"]; + [encoder addValue:self.appIdentifierModel.buildVersion fieldName:@"app[build_version]"]; + [encoder addValue:self.appIdentifierModel.displayVersion fieldName:@"app[display_version]"]; + [encoder addValue:@(self.appIdentifierModel.installSource) fieldName:@"app[source]"]; + [encoder addValue:self.appIdentifierModel.minimumSDKString + fieldName:@"app[minimum_sdk_version]"]; + [encoder addValue:self.appIdentifierModel.builtSDKString fieldName:@"app[built_sdk_version]"]; + [self.kitVersionsByKitBundleIdentifier + enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + NSString *formKey = [NSString stringWithFormat:@"%@[%@]", @"app[build][libraries]", key]; + [encoder addValue:obj fieldName:formKey]; + }]; + + [self.appIdentifierModel.architectureUUIDMap + enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + [encoder addValue:key fieldName:@"app[slices][][arch]"]; + [encoder addValue:obj fieldName:@"app[slices][][uuid]"]; + }]; + }]; + + NSMutableURLRequest *request = [self onboardingRequestForAppCreate:self.shouldCreate]; + [request setValue:orgID forHTTPHeaderField:FIRCLSNetworkCrashlyticsOrgId]; + + [request setValue:encoder.contentTypeHTTPHeaderValue forHTTPHeaderField:@"Content-Type"]; + [request setValue:encoder.contentLengthHTTPHeaderValue forHTTPHeaderField:@"Content-Length"]; + [request setHTTPBody:[stream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]]; + + // Retry only when onboarding an app for the first time, otherwise it'll overwhelm our servers + NSUInteger retryLimit = self.shouldCreate ? 10 : 1; + + [self.networkClient + startDataTaskWithRequest:request + retryLimit:retryLimit + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + self->_error = error; + if (!self.shouldCreate) { + [self.delegate onboardingOperation:self didCompleteAppUpdateWithError:error]; + } else { + [self.delegate onboardingOperation:self didCompleteAppCreationWithError:error]; + } + [self finishWithError:error]; + }]; +} + +#pragma mark private methods + +- (NSError *)errorForCode:(NSUInteger)code userInfo:(NSDictionary *)userInfo { + return [NSError errorWithDomain:FIRCLSOnboardingErrorDomain code:code userInfo:userInfo]; +} + +- (NSURL *)appCreateURL { + // https://update.crashlytics.com/spi/v1/platforms/mac/apps/com.crashlytics.mac + + FIRCLSURLBuilder *url = [FIRCLSURLBuilder URLWithBase:self.appEndpoint]; + + [url appendComponent:@"/spi/v1/platforms/"]; + [url escapeAndAppendComponent:self.appIdentifierModel.platform]; + [url appendComponent:@"/apps"]; + + return url.URL; +} + +- (NSURL *)appUpdateURL { + // https://update.crashlytics.com/spi/v1/platforms/mac/apps/com.crashlytics.mac + + FIRCLSURLBuilder *url = [FIRCLSURLBuilder URLWithBase:[self appEndpoint]]; + + [url appendComponent:@"/spi/v1/platforms/"]; + [url escapeAndAppendComponent:self.appIdentifierModel.platform]; + [url appendComponent:@"/apps/"]; + [url escapeAndAppendComponent:self.settings.fetchedBundleID]; + + return url.URL; +} + +- (NSMutableURLRequest *)onboardingRequestForAppCreate:(BOOL)shouldCreate { + const NSTimeInterval timeout = 10.0; + NSURL *url = nil; + NSString *httpVerb = nil; + if (shouldCreate) { + httpVerb = @"POST"; + url = self.appCreateURL; + } else { + httpVerb = @"PUT"; + url = self.appUpdateURL; + } + NSMutableURLRequest *request = [self mutableRequestWithDefaultHTTPHeadersForURL:url + timeout:timeout]; + request.HTTPMethod = httpVerb; + return request; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind.c new file mode 100644 index 000000000..1875f9812 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind.c @@ -0,0 +1,404 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSCompactUnwind_Private.h" +#include "FIRCLSDataParsing.h" +#include "FIRCLSDefines.h" +#include "FIRCLSDwarfUnwind.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSUnwind.h" +#include "FIRCLSUtility.h" + +#include + +#if CLS_COMPACT_UNWINDING_SUPPORTED + +#pragma mark Parsing +bool FIRCLSCompactUnwindInit(FIRCLSCompactUnwindContext* context, + const void* unwindInfo, + const void* ehFrame, + uintptr_t loadAddress) { + if (!FIRCLSIsValidPointer(context)) { + FIRCLSSDKLog("Error: invalid context passed to compact unwind init"); + return false; + } + if (!FIRCLSIsValidPointer(unwindInfo)) { + FIRCLSSDKLog("Error: invalid unwind info passed to compact unwind init"); + return false; + } + if (!FIRCLSIsValidPointer(loadAddress)) { + FIRCLSSDKLog("Error: invalid load address passed to compact unwind init"); + return false; + } + + memset(context, 0, sizeof(FIRCLSCompactUnwindContext)); + + if (!FIRCLSReadMemory((vm_address_t)unwindInfo, &context->unwindHeader, + sizeof(struct unwind_info_section_header))) { + FIRCLSSDKLog("Error: could not read memory contents of unwindInfo\n"); + return false; + } + + if (context->unwindHeader.version != UNWIND_SECTION_VERSION) { + FIRCLSSDKLog("Error: bad unwind_info structure version (%d != %d)\n", + context->unwindHeader.version, UNWIND_SECTION_VERSION); + return false; + } + + // copy in the values + context->unwindInfo = unwindInfo; + context->ehFrame = ehFrame; + context->loadAddress = loadAddress; + + return true; +} + +void* FIRCLSCompactUnwindGetIndexData(FIRCLSCompactUnwindContext* context) { + return (void*)((uintptr_t)context->unwindInfo + + (uintptr_t)context->unwindHeader.indexSectionOffset); +} + +compact_unwind_encoding_t* FIRCLSCompactUnwindGetCommonEncodings( + FIRCLSCompactUnwindContext* context) { + return (compact_unwind_encoding_t*)((uintptr_t)context->unwindInfo + + (uintptr_t) + context->unwindHeader.commonEncodingsArraySectionOffset); +} + +void* FIRCLSCompactUnwindGetSecondLevelData(FIRCLSCompactUnwindContext* context) { + return (void*)((uintptr_t)context->unwindInfo + + context->indexHeader.secondLevelPagesSectionOffset); +} + +uintptr_t FIRCLSCompactUnwindGetIndexFunctionOffset(FIRCLSCompactUnwindContext* context) { + return context->loadAddress + context->indexHeader.functionOffset; +} +uintptr_t FIRCLSCompactUnwindGetTargetAddress(FIRCLSCompactUnwindContext* context, uintptr_t pc) { + uintptr_t offset = FIRCLSCompactUnwindGetIndexFunctionOffset(context); + + if (pc <= offset) { + FIRCLSSDKLog("Error: PC is invalid\n"); + return 0; + } + + return pc - offset; +} + +#pragma mark - Parsing and Lookup +bool FIRCLSCompactUnwindLookupFirstLevel(FIRCLSCompactUnwindContext* context, uintptr_t address) { + if (!context) { + return false; + } + + // In practice, it appears that there always one more first level entry + // than required. This actually makes sense, since we have to use this + // info to check if we are in range. This implies there must be + // at least 2 indices at a minimum. + + uint32_t indexCount = context->unwindHeader.indexCount; + if (indexCount < 2) { + return false; + } + + // make sure our address is valid + if (address < context->loadAddress) { + return false; + } + + struct unwind_info_section_header_index_entry* indexEntries = + FIRCLSCompactUnwindGetIndexData(context); + if (!indexEntries) { + return false; + } + + address -= context->loadAddress; // search relative to zero + + // minus one because of the extra entry - see comment above + for (uint32_t index = 0; index < indexCount - 1; ++index) { + uint32_t value = indexEntries[index].functionOffset; + uint32_t nextValue = indexEntries[index + 1].functionOffset; + + if (address >= value && address < nextValue) { + context->firstLevelNextFunctionOffset = nextValue; + context->indexHeader = indexEntries[index]; + return true; + } + } + + return false; +} + +uint32_t FIRCLSCompactUnwindGetSecondLevelPageKind(FIRCLSCompactUnwindContext* context) { + if (!context) { + return 0; + } + + return *(uint32_t*)FIRCLSCompactUnwindGetSecondLevelData(context); +} + +bool FIRCLSCompactUnwindLookupSecondLevelRegular(FIRCLSCompactUnwindContext* context, + uintptr_t pc, + FIRCLSCompactUnwindResult* result) { + FIRCLSSDKLog("Encountered a regular second-level page\n"); + return false; +} + +// this only works for compressed entries right now +bool FIRCLSCompactUnwindBinarySearchSecondLevel(uintptr_t address, + uint32_t* index, + uint16_t entryCount, + uint32_t* entryArray) { + if (!index || !entryArray) { + return false; + } + + if (entryCount == 0) { + return false; + } + + if (address == 0) { + return false; + } + + uint32_t highIndex = entryCount; + *index = 0; + + while (*index < highIndex) { + uint32_t midIndex = (*index + highIndex) / 2; + + // FIRCLSSDKLog("%u %u %u\n", *index, midIndex, highIndex); + + uintptr_t value = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entryArray[midIndex]); + + if (value > address) { + if (highIndex == midIndex) { + return false; + } + + highIndex = midIndex; + continue; + } + + *index = midIndex; + + // are we at the end of the array? + if (midIndex == entryCount - 1) { + return false; + } + + uintptr_t nextValue = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entryArray[midIndex + 1]); + if (nextValue > address) { + // we've found it + break; + } + + *index += 1; + } + + // check to make sure we're still within bounds + return *index < entryCount; +} + +bool FIRCLSCompactUnwindLookupSecondLevelCompressed(FIRCLSCompactUnwindContext* context, + uintptr_t pc, + FIRCLSCompactUnwindResult* result) { + if (!context || !result) { + return false; + } + + void* ptr = FIRCLSCompactUnwindGetSecondLevelData(context); + + if (!ptr) { + return false; + } + + memset(result, 0, sizeof(FIRCLSCompactUnwindResult)); + + struct unwind_info_compressed_second_level_page_header* header = + (struct unwind_info_compressed_second_level_page_header*)ptr; + + // adjust address + uintptr_t targetAddress = FIRCLSCompactUnwindGetTargetAddress(context, pc); + + uint32_t* entryArray = ptr + header->entryPageOffset; + + uint32_t index = 0; + + if (!FIRCLSCompactUnwindBinarySearchSecondLevel(targetAddress, &index, header->entryCount, + entryArray)) { + FIRCLSSDKLogInfo("Unable to find PC in second level\n"); + return false; + } + + uint32_t entry = entryArray[index]; + + // Computing the fuction start address is easy + result->functionStart = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) + + FIRCLSCompactUnwindGetIndexFunctionOffset(context); + + // Computing the end is more complex, because we could be on the last entry. In that case, we + // cannot use the next value as the end. + result->functionEnd = context->loadAddress; + if (index < header->entryCount - 1) { + result->functionEnd += UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entryArray[index + 1]) + + context->indexHeader.functionOffset; + } else { + result->functionEnd += context->firstLevelNextFunctionOffset; + } + + // FIRCLSSDKLog("Located %lx => %lx %lx\n", pc, result->functionStart, result->functionEnd); + + if ((pc < result->functionStart) || (pc >= result->functionEnd)) { + FIRCLSSDKLog("PC does not match computed function range\n"); + return false; + } + + uint32_t encodingIndex = UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry); + + // encoding could be in the common array + if (encodingIndex < context->unwindHeader.commonEncodingsArrayCount) { + result->encoding = FIRCLSCompactUnwindGetCommonEncodings(context)[encodingIndex]; + + // FIRCLSSDKLog("Entry has common encoding: 0x%x\n", result->encoding); + } else { + encodingIndex = encodingIndex - context->unwindHeader.commonEncodingsArrayCount; + + compact_unwind_encoding_t* encodings = ptr + header->encodingsPageOffset; + + result->encoding = encodings[encodingIndex]; + + // FIRCLSSDKLog("Entry has compressed encoding: 0x%x\n", result->encoding); + } + + if (result->encoding == 0) { + FIRCLSSDKLogInfo("Entry has has no unwind info\n"); + return false; + } + + return true; +} + +bool FIRCLSCompactUnwindLookupSecondLevel(FIRCLSCompactUnwindContext* context, + uintptr_t pc, + FIRCLSCompactUnwindResult* result) { + switch (FIRCLSCompactUnwindGetSecondLevelPageKind(context)) { + case UNWIND_SECOND_LEVEL_REGULAR: + FIRCLSSDKLogInfo("Found a second level regular header\n"); + if (FIRCLSCompactUnwindLookupSecondLevelRegular(context, pc, result)) { + return true; + } + break; + case UNWIND_SECOND_LEVEL_COMPRESSED: + FIRCLSSDKLogInfo("Found a second level compressed header\n"); + if (FIRCLSCompactUnwindLookupSecondLevelCompressed(context, pc, result)) { + return true; + } + break; + default: + FIRCLSSDKLogError("Unrecognized header kind - unable to continue\n"); + break; + } + + return false; +} + +bool FIRCLSCompactUnwindLookup(FIRCLSCompactUnwindContext* context, + uintptr_t pc, + FIRCLSCompactUnwindResult* result) { + if (!context || !result) { + return false; + } + + // step 1 - find the pc in the first-level index + if (!FIRCLSCompactUnwindLookupFirstLevel(context, pc)) { + FIRCLSSDKLogWarn("Unable to find pc in first level\n"); + return false; + } + + FIRCLSSDKLogDebug("Found first level (second => %u)\n", + context->indexHeader.secondLevelPagesSectionOffset); + + // step 2 - use that info to find the second-level information + // that second actually has the encoding info we're looking for. + if (!FIRCLSCompactUnwindLookupSecondLevel(context, pc, result)) { + FIRCLSSDKLogInfo("Second-level PC lookup failed\n"); + return false; + } + + return true; +} + +#pragma mark - Unwinding +bool FIRCLSCompactUnwindLookupAndCompute(FIRCLSCompactUnwindContext* context, + FIRCLSThreadContext* registers) { + if (!context || !registers) { + return false; + } + + uintptr_t pc = FIRCLSThreadContextGetPC(registers); + + // little sanity check + if (pc < context->loadAddress) { + return false; + } + + FIRCLSCompactUnwindResult result; + + memset(&result, 0, sizeof(result)); + + if (!FIRCLSCompactUnwindLookup(context, pc, &result)) { + FIRCLSSDKLogInfo("Unable to lookup compact unwind for pc %p\n", (void*)pc); + return false; + } + + // Ok, armed with the encoding, we can actually attempt to modify the registers. Because + // the encoding is arch-specific, this function has to be defined per-arch. + if (!FIRCLSCompactUnwindComputeRegisters(context, &result, registers)) { + FIRCLSSDKLogError("Failed to compute registers\n"); + return false; + } + + return true; +} + +#if CLS_DWARF_UNWINDING_SUPPORTED +bool FIRCLSCompactUnwindDwarfFrame(FIRCLSCompactUnwindContext* context, + uintptr_t dwarfOffset, + FIRCLSThreadContext* registers) { + if (!context || !registers) { + return false; + } + + // Everyone's favorite! Dwarf unwinding! + FIRCLSSDKLogInfo("Trying to read dwarf data with offset %lx\n", dwarfOffset); + + FIRCLSDwarfCFIRecord record; + + if (!FIRCLSDwarfParseCFIFromFDERecordOffset(&record, context->ehFrame, dwarfOffset)) { + FIRCLSSDKLogError("Unable to init FDE\n"); + return false; + } + + if (!FIRCLSDwarfUnwindComputeRegisters(&record, registers)) { + FIRCLSSDKLogError("Failed to compute DWARF registers\n"); + return false; + } + + return true; +} +#endif + +#else +INJECT_STRIP_SYMBOL(compact_unwind) +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind.h new file mode 100644 index 000000000..1698e7585 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind.h @@ -0,0 +1,68 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include "FIRCLSFeatures.h" +#include "FIRCLSThreadState.h" + +// We have to pack the arrays defined in this header, so +// we can reason about pointer math. +#pragma pack(push) +#pragma pack(1) +#include +#pragma pack(pop) + +// First masks out the value, and then shifts the value by the number +// of zeros in the mask. __builtin_ctz returns the number of trailing zeros. +// Its output is undefined if the input is zero. +#define GET_BITS_WITH_MASK(value, mask) ((value & mask) >> (mask == 0 ? 0 : __builtin_ctz(mask))) + +typedef struct { + const void* unwindInfo; + const void* ehFrame; + uintptr_t loadAddress; + + struct unwind_info_section_header unwindHeader; + struct unwind_info_section_header_index_entry indexHeader; + uint32_t firstLevelNextFunctionOffset; +} FIRCLSCompactUnwindContext; + +typedef struct { + compact_unwind_encoding_t encoding; + uintptr_t functionStart; + uintptr_t functionEnd; + uintptr_t lsda; + uintptr_t personality; + +} FIRCLSCompactUnwindResult; + +bool FIRCLSCompactUnwindInit(FIRCLSCompactUnwindContext* context, + const void* unwindInfo, + const void* ehFrame, + uintptr_t loadAddress); +void* FIRCLSCompactUnwindGetIndexData(FIRCLSCompactUnwindContext* context); +void* FIRCLSCompactUnwindGetSecondLevelData(FIRCLSCompactUnwindContext* context); +bool FIRCLSCompactUnwindFindFirstLevelIndex(FIRCLSCompactUnwindContext* context, + uintptr_t pc, + uint32_t* index); + +bool FIRCLSCompactUnwindDwarfFrame(FIRCLSCompactUnwindContext* context, + uintptr_t dwarfOffset, + FIRCLSThreadContext* registers); +bool FIRCLSCompactUnwindLookupAndCompute(FIRCLSCompactUnwindContext* context, + FIRCLSThreadContext* registers); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind_Private.h new file mode 100644 index 000000000..1dd01562f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Compact/FIRCLSCompactUnwind_Private.h @@ -0,0 +1,28 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "FIRCLSCompactUnwind.h" +#pragma pack(push, 1) +#include +#pragma pack(pop) + +bool FIRCLSCompactUnwindLookup(FIRCLSCompactUnwindContext* context, + uintptr_t pc, + FIRCLSCompactUnwindResult* result); + +bool FIRCLSCompactUnwindComputeRegisters(FIRCLSCompactUnwindContext* context, + FIRCLSCompactUnwindResult* result, + FIRCLSThreadContext* registers); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDataParsing.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDataParsing.c new file mode 100644 index 000000000..871fd3007 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDataParsing.c @@ -0,0 +1,238 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSDataParsing.h" +#include "FIRCLSDefines.h" +#include "FIRCLSUtility.h" +#include "dwarf.h" + +#include + +#if CLS_DWARF_UNWINDING_SUPPORTED + +uint8_t FIRCLSParseUint8AndAdvance(const void** cursor) { + uint8_t tmp = **(uint8_t**)cursor; + + *cursor += sizeof(uint8_t); + + return tmp; +} + +uint16_t FIRCLSParseUint16AndAdvance(const void** cursor) { + uint16_t tmp = **(uint16_t**)cursor; + + *cursor += sizeof(uint16_t); + + return tmp; +} + +int16_t FIRCLSParseInt16AndAdvance(const void** cursor) { + int16_t tmp = **(int16_t**)cursor; + + *cursor += sizeof(int16_t); + + return tmp; +} + +uint32_t FIRCLSParseUint32AndAdvance(const void** cursor) { + uint32_t tmp = **(uint32_t**)cursor; + + *cursor += sizeof(uint32_t); + + return tmp; +} + +int32_t FIRCLSParseInt32AndAdvance(const void** cursor) { + int32_t tmp = **(int32_t**)cursor; + + *cursor += sizeof(int32_t); + + return tmp; +} + +uint64_t FIRCLSParseUint64AndAdvance(const void** cursor) { + uint64_t tmp = **(uint64_t**)cursor; + + *cursor += sizeof(uint64_t); + + return tmp; +} + +int64_t FIRCLSParseInt64AndAdvance(const void** cursor) { + int64_t tmp = **(int64_t**)cursor; + + *cursor += sizeof(int64_t); + + return tmp; +} + +uintptr_t FIRCLSParsePointerAndAdvance(const void** cursor) { + uintptr_t tmp = **(uintptr_t**)cursor; + + *cursor += sizeof(uintptr_t); + + return tmp; +} + +// Signed and Unsigned LEB128 decoding algorithms taken from Wikipedia - +// http://en.wikipedia.org/wiki/LEB128 +uint64_t FIRCLSParseULEB128AndAdvance(const void** cursor) { + uint64_t result = 0; + char shift = 0; + + for (int i = 0; i < sizeof(uint64_t); ++i) { + char byte; + + byte = **(uint8_t**)cursor; + + *cursor += 1; + + result |= ((0x7F & byte) << shift); + if ((0x80 & byte) == 0) { + break; + } + + shift += 7; + } + + return result; +} + +int64_t FIRCLSParseLEB128AndAdvance(const void** cursor) { + uint64_t result = 0; + char shift = 0; + char size = sizeof(int64_t) * 8; + char byte = 0; + + for (int i = 0; i < sizeof(uint64_t); ++i) { + byte = **(uint8_t**)cursor; + + *cursor += 1; + + result |= ((0x7F & byte) << shift); + shift += 7; + + /* sign bit of byte is second high order bit (0x40) */ + if ((0x80 & byte) == 0) { + break; + } + } + + if ((shift < size) && (0x40 & byte)) { + // sign extend + result |= -(1 << shift); + } + + return result; +} + +const char* FIRCLSParseStringAndAdvance(const void** cursor) { + const char* string; + + string = (const char*)(*cursor); + + // strlen doesn't include the null character, which we need to advance past + *cursor += strlen(string) + 1; + + return string; +} + +uint64_t FIRCLSParseRecordLengthAndAdvance(const void** cursor) { + uint64_t length; + + length = FIRCLSParseUint32AndAdvance(cursor); + if (length == DWARF_EXTENDED_LENGTH_FLAG) { + length = FIRCLSParseUint64AndAdvance(cursor); + } + + return length; +} + +uintptr_t FIRCLSParseAddressWithEncodingAndAdvance(const void** cursor, uint8_t encoding) { + if (encoding == DW_EH_PE_omit) { + return 0; + } + + if (!cursor) { + return CLS_INVALID_ADDRESS; + } + + if (!*cursor) { + return CLS_INVALID_ADDRESS; + } + + intptr_t inputAddr = (intptr_t)*cursor; + intptr_t addr; + + switch (encoding & DW_EH_PE_VALUE_MASK) { + case DW_EH_PE_ptr: + // 32 or 64 bits + addr = FIRCLSParsePointerAndAdvance(cursor); + break; + case DW_EH_PE_uleb128: + addr = (intptr_t)FIRCLSParseULEB128AndAdvance(cursor); + break; + case DW_EH_PE_udata2: + addr = FIRCLSParseUint16AndAdvance(cursor); + break; + case DW_EH_PE_udata4: + addr = FIRCLSParseUint32AndAdvance(cursor); + break; + case DW_EH_PE_udata8: + addr = (intptr_t)FIRCLSParseUint64AndAdvance(cursor); + break; + case DW_EH_PE_sleb128: + addr = (intptr_t)FIRCLSParseLEB128AndAdvance(cursor); + break; + case DW_EH_PE_sdata2: + addr = FIRCLSParseInt16AndAdvance(cursor); + break; + case DW_EH_PE_sdata4: + addr = FIRCLSParseInt32AndAdvance(cursor); + break; + case DW_EH_PE_sdata8: + addr = (intptr_t)FIRCLSParseInt64AndAdvance(cursor); + break; + default: + FIRCLSSDKLog("Unhandled: encoding 0x%02x\n", encoding); + return CLS_INVALID_ADDRESS; + } + + // and now apply the relative offset + switch (encoding & DW_EH_PE_RELATIVE_OFFSET_MASK) { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + addr += inputAddr; + break; + default: + FIRCLSSDKLog("Unhandled: relative encoding 0x%02x\n", encoding); + return CLS_INVALID_ADDRESS; + } + + // Here's a crazy one. It seems this encoding means you actually look up + // the value of the address using the result address itself + if (encoding & DW_EH_PE_indirect) { + if (!addr) { + return CLS_INVALID_ADDRESS; + } + + addr = *(uintptr_t*)addr; + } + + return addr; +} +#else +INJECT_STRIP_SYMBOL(data_parsing) +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDataParsing.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDataParsing.h new file mode 100644 index 000000000..8a6e96617 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDataParsing.h @@ -0,0 +1,46 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include "FIRCLSFeatures.h" + +#if CLS_DWARF_UNWINDING_SUPPORTED + +#if CLS_CPU_64BIT +#define CLS_INVALID_ADDRESS (0xffffffffffffffff) +#else +#define CLS_INVALID_ADDRESS (0xffffffff) +#endif + +// basic data types +uint8_t FIRCLSParseUint8AndAdvance(const void** cursor); +uint16_t FIRCLSParseUint16AndAdvance(const void** cursor); +int16_t FIRCLSParseInt16AndAdvance(const void** cursor); +uint32_t FIRCLSParseUint32AndAdvance(const void** cursor); +int32_t FIRCLSParseInt32AndAdvance(const void** cursor); +uint64_t FIRCLSParseUint64AndAdvance(const void** cursor); +int64_t FIRCLSParseInt64AndAdvance(const void** cursor); +uintptr_t FIRCLSParsePointerAndAdvance(const void** cursor); +uint64_t FIRCLSParseULEB128AndAdvance(const void** cursor); +int64_t FIRCLSParseLEB128AndAdvance(const void** cursor); +const char* FIRCLSParseStringAndAdvance(const void** cursor); + +// FDE/CIE-specifc structures +uint64_t FIRCLSParseRecordLengthAndAdvance(const void** cursor); +uintptr_t FIRCLSParseAddressWithEncodingAndAdvance(const void** cursor, uint8_t encoding); + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfExpressionMachine.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfExpressionMachine.c new file mode 100644 index 000000000..ea308f13a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfExpressionMachine.c @@ -0,0 +1,453 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSDwarfExpressionMachine.h" +#include "FIRCLSDataParsing.h" +#include "FIRCLSDefines.h" +#include "FIRCLSDwarfUnwindRegisters.h" +#include "FIRCLSUnwind_arch.h" +#include "FIRCLSUtility.h" +#include "dwarf.h" + +#if CLS_DWARF_UNWINDING_SUPPORTED + +static bool FIRCLSDwarfExpressionMachineExecute_bregN(FIRCLSDwarfExpressionMachine *machine, + uint8_t opcode); +static bool FIRCLSDwarfExpressionMachineExecute_deref(FIRCLSDwarfExpressionMachine *machine); +static bool FIRCLSDwarfExpressionMachineExecute_plus_uconst(FIRCLSDwarfExpressionMachine *machine); +static bool FIRCLSDwarfExpressionMachineExecute_and(FIRCLSDwarfExpressionMachine *machine); +static bool FIRCLSDwarfExpressionMachineExecute_plus(FIRCLSDwarfExpressionMachine *machine); +static bool FIRCLSDwarfExpressionMachineExecute_dup(FIRCLSDwarfExpressionMachine *machine); +static bool FIRCLSDwarfExpressionMachineExecute_swap(FIRCLSDwarfExpressionMachine *machine); +static bool FIRCLSDwarfExpressionMachineExecute_deref_size(FIRCLSDwarfExpressionMachine *machine); +static bool FIRCLSDwarfExpressionMachineExecute_ne(FIRCLSDwarfExpressionMachine *machine); +static bool FIRCLSDwarfExpressionMachineExecute_litN(FIRCLSDwarfExpressionMachine *machine, + uint8_t opcode); + +#pragma mark - +#pragma mark Stack Implementation +void FIRCLSDwarfExpressionStackInit(FIRCLSDwarfExpressionStack *stack) { + if (!FIRCLSIsValidPointer(stack)) { + return; + } + + memset(stack, 0, sizeof(FIRCLSDwarfExpressionStack)); + + stack->pointer = stack->buffer; +} + +bool FIRCLSDwarfExpressionStackIsValid(FIRCLSDwarfExpressionStack *stack) { + if (!FIRCLSIsValidPointer(stack)) { + return false; + } + + // check for valid stack pointer + if (stack->pointer < stack->buffer) { + return false; + } + + if (stack->pointer > stack->buffer + CLS_DWARF_EXPRESSION_STACK_SIZE) { + return false; + } + + return true; +} + +bool FIRCLSDwarfExpressionStackPush(FIRCLSDwarfExpressionStack *stack, intptr_t value) { + if (!FIRCLSDwarfExpressionStackIsValid(stack)) { + return false; + } + + if (stack->pointer == stack->buffer + CLS_DWARF_EXPRESSION_STACK_SIZE) { + // overflow + stack->pointer = NULL; + return false; + } + + *(stack->pointer) = value; + stack->pointer += 1; + + return true; +} + +intptr_t FIRCLSDwarfExpressionStackPeek(FIRCLSDwarfExpressionStack *stack) { + if (!FIRCLSDwarfExpressionStackIsValid(stack)) { + return 0; + } + + if (stack->pointer == stack->buffer) { + // underflow + stack->pointer = NULL; + return 0; + } + + return *(stack->pointer - 1); +} + +intptr_t FIRCLSDwarfExpressionStackPop(FIRCLSDwarfExpressionStack *stack) { + if (!FIRCLSDwarfExpressionStackIsValid(stack)) { + return 0; + } + + if (stack->pointer == stack->buffer) { + // underflow + stack->pointer = NULL; + return 0; + } + + stack->pointer -= 1; + + return *(stack->pointer); +} + +#pragma mark - +#pragma mark Machine API +bool FIRCLSDwarfExpressionMachineInit(FIRCLSDwarfExpressionMachine *machine, + const void *cursor, + const FIRCLSThreadContext *registers, + intptr_t stackValue) { + if (!FIRCLSIsValidPointer(machine)) { + return false; + } + + memset(machine, 0, sizeof(FIRCLSDwarfExpressionMachine)); + + if (!FIRCLSIsValidPointer(cursor)) { + return false; + } + + machine->dataCursor = cursor; + machine->registers = registers; + + FIRCLSDwarfExpressionStackInit(&machine->stack); + + return FIRCLSDwarfExpressionStackPush(&machine->stack, stackValue); +} + +bool FIRCLSDwarfExpressionMachinePrepareForExecution(FIRCLSDwarfExpressionMachine *machine) { + if (!FIRCLSIsValidPointer(machine)) { + FIRCLSSDKLog("Error: invalid inputs\n"); + return false; + } + + uint64_t expressionLength = FIRCLSParseULEB128AndAdvance(&machine->dataCursor); + + if (expressionLength == 0) { + FIRCLSSDKLog("Error: DWARF expression length is zero\n"); + return false; + } + + machine->endAddress = machine->dataCursor + expressionLength; + + return true; +} + +bool FIRCLSDwarfExpressionMachineIsFinished(FIRCLSDwarfExpressionMachine *machine) { + if (!FIRCLSIsValidPointer(machine)) { + FIRCLSSDKLog("Error: invalid inputs\n"); + return true; + } + + if (!FIRCLSIsValidPointer(machine->endAddress) || !FIRCLSIsValidPointer(machine->dataCursor)) { + FIRCLSSDKLog("Error: DWARF machine pointers invalid\n"); + return true; + } + + if (!FIRCLSDwarfExpressionStackIsValid(&machine->stack)) { + FIRCLSSDKLog("Error: DWARF machine stack invalid\n"); + return true; + } + + return machine->dataCursor >= machine->endAddress; +} + +bool FIRCLSDwarfExpressionMachineGetResult(FIRCLSDwarfExpressionMachine *machine, + intptr_t *result) { + if (!FIRCLSIsValidPointer(machine) || !FIRCLSIsValidPointer(result)) { + return false; + } + + if (machine->dataCursor != machine->endAddress) { + FIRCLSSDKLog("Error: DWARF expression hasn't completed execution\n"); + return false; + } + + *result = FIRCLSDwarfExpressionStackPeek(&machine->stack); + + return FIRCLSDwarfExpressionStackIsValid(&machine->stack); +} + +bool FIRCLSDwarfExpressionMachineExecuteNextOpcode(FIRCLSDwarfExpressionMachine *machine) { + if (!FIRCLSIsValidPointer(machine)) { + return false; + } + + const uint8_t opcode = FIRCLSParseUint8AndAdvance(&machine->dataCursor); + + bool success = false; + + switch (opcode) { + case DW_OP_deref: + success = FIRCLSDwarfExpressionMachineExecute_deref(machine); + break; + case DW_OP_dup: + success = FIRCLSDwarfExpressionMachineExecute_dup(machine); + break; + case DW_OP_and: + success = FIRCLSDwarfExpressionMachineExecute_and(machine); + break; + case DW_OP_plus: + success = FIRCLSDwarfExpressionMachineExecute_plus(machine); + break; + case DW_OP_swap: + success = FIRCLSDwarfExpressionMachineExecute_swap(machine); + break; + case DW_OP_plus_uconst: + success = FIRCLSDwarfExpressionMachineExecute_plus_uconst(machine); + break; + case DW_OP_ne: + success = FIRCLSDwarfExpressionMachineExecute_ne(machine); + break; + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + success = FIRCLSDwarfExpressionMachineExecute_litN(machine, opcode); + break; + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + success = FIRCLSDwarfExpressionMachineExecute_bregN(machine, opcode); + break; + case DW_OP_deref_size: + success = FIRCLSDwarfExpressionMachineExecute_deref_size(machine); + break; + default: + FIRCLSSDKLog("Error: Unrecognized DWARF expression opcode 0x%x\n", opcode); + return false; + } + + return success; +} + +#pragma mark - +#pragma mark Helpers +static intptr_t FIRCLSDwarfExpressionMachineStackPop(FIRCLSDwarfExpressionMachine *machine) { + return FIRCLSDwarfExpressionStackPop(&machine->stack); +} + +static bool FIRCLSDwarfExpressionMachineStackPush(FIRCLSDwarfExpressionMachine *machine, + intptr_t value) { + return FIRCLSDwarfExpressionStackPush(&machine->stack, value); +} + +#pragma mark - +#pragma mark Opcode Implementations +static bool FIRCLSDwarfExpressionMachineExecute_bregN(FIRCLSDwarfExpressionMachine *machine, + uint8_t opcode) { + // find the register number, compute offset value, push + const uint8_t regNum = opcode - DW_OP_breg0; + + if (regNum > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: DW_OP_breg invalid register number\n"); + return false; + } + + int64_t offset = FIRCLSParseLEB128AndAdvance(&machine->dataCursor); + + FIRCLSSDKLog("DW_OP_breg %d value %d\n", regNum, (int)offset); + + const intptr_t value = + FIRCLSDwarfUnwindGetRegisterValue(machine->registers, regNum) + (intptr_t)offset; + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +static bool FIRCLSDwarfExpressionMachineExecute_deref(FIRCLSDwarfExpressionMachine *machine) { + // pop stack, dereference, push result + intptr_t value = FIRCLSDwarfExpressionMachineStackPop(machine); + + FIRCLSSDKLog("DW_OP_deref value %p\n", (void *)value); + + if (!FIRCLSReadMemory(value, &value, sizeof(value))) { + FIRCLSSDKLog("Error: DW_OP_deref failed to read memory\n"); + return false; + } + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +static bool FIRCLSDwarfExpressionMachineExecute_plus_uconst(FIRCLSDwarfExpressionMachine *machine) { + // pop stack, add constant, push result + intptr_t value = FIRCLSDwarfExpressionMachineStackPop(machine); + + value += FIRCLSParseULEB128AndAdvance(&machine->dataCursor); + + FIRCLSSDKLog("DW_OP_plus_uconst value %lu\n", value); + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +static bool FIRCLSDwarfExpressionMachineExecute_and(FIRCLSDwarfExpressionMachine *machine) { + FIRCLSSDKLog("DW_OP_plus_and\n"); + + intptr_t value = FIRCLSDwarfExpressionMachineStackPop(machine); + + value = value & FIRCLSDwarfExpressionMachineStackPop(machine); + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +static bool FIRCLSDwarfExpressionMachineExecute_plus(FIRCLSDwarfExpressionMachine *machine) { + FIRCLSSDKLog("DW_OP_plus\n"); + + intptr_t value = FIRCLSDwarfExpressionMachineStackPop(machine); + + value = value + FIRCLSDwarfExpressionMachineStackPop(machine); + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +static bool FIRCLSDwarfExpressionMachineExecute_dup(FIRCLSDwarfExpressionMachine *machine) { + // duplicate top of stack + intptr_t value = FIRCLSDwarfExpressionStackPeek(&machine->stack); + + FIRCLSSDKLog("DW_OP_dup value %lu\n", value); + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +static bool FIRCLSDwarfExpressionMachineExecute_swap(FIRCLSDwarfExpressionMachine *machine) { + // swap top two values on the stack + intptr_t valueA = FIRCLSDwarfExpressionMachineStackPop(machine); + intptr_t valueB = FIRCLSDwarfExpressionMachineStackPop(machine); + + FIRCLSSDKLog("DW_OP_swap\n"); + + if (!FIRCLSDwarfExpressionMachineStackPush(machine, valueA)) { + return false; + } + + return FIRCLSDwarfExpressionMachineStackPush(machine, valueB); +} + +static bool FIRCLSDwarfExpressionMachineExecute_deref_size(FIRCLSDwarfExpressionMachine *machine) { + // pop stack, dereference variable sized value, push result + const void *address = (const void *)FIRCLSDwarfExpressionMachineStackPop(machine); + const uint8_t readSize = FIRCLSParseUint8AndAdvance(&machine->dataCursor); + intptr_t value = 0; + + FIRCLSSDKLog("DW_OP_deref_size %p size %u\n", address, readSize); + + switch (readSize) { + case 1: + value = FIRCLSParseUint8AndAdvance(&address); + break; + case 2: + value = FIRCLSParseUint16AndAdvance(&address); + break; + case 4: + value = FIRCLSParseUint32AndAdvance(&address); + break; + case 8: + // this is a little funky, as an 8 here really doesn't make sense for 32-bit platforms + value = (intptr_t)FIRCLSParseUint64AndAdvance(&address); + break; + default: + FIRCLSSDKLog("Error: unrecognized DW_OP_deref_size argument %x\n", readSize); + return false; + } + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +static bool FIRCLSDwarfExpressionMachineExecute_ne(FIRCLSDwarfExpressionMachine *machine) { + FIRCLSSDKLog("DW_OP_ne\n"); + + intptr_t value = FIRCLSDwarfExpressionMachineStackPop(machine); + + value = value != FIRCLSDwarfExpressionMachineStackPop(machine); + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +static bool FIRCLSDwarfExpressionMachineExecute_litN(FIRCLSDwarfExpressionMachine *machine, + uint8_t opcode) { + const uint8_t value = opcode - DW_OP_lit0; + + FIRCLSSDKLog("DW_OP_lit %u\n", value); + + return FIRCLSDwarfExpressionMachineStackPush(machine, value); +} + +#else +INJECT_STRIP_SYMBOL(dwarf_expression_machine) +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfExpressionMachine.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfExpressionMachine.h new file mode 100644 index 000000000..7dd70f847 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfExpressionMachine.h @@ -0,0 +1,55 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include "FIRCLSFeatures.h" +#include "FIRCLSThreadState.h" + +#define CLS_DWARF_EXPRESSION_STACK_SIZE (100) + +#if CLS_DWARF_UNWINDING_SUPPORTED + +typedef struct { + intptr_t buffer[CLS_DWARF_EXPRESSION_STACK_SIZE]; + intptr_t *pointer; +} FIRCLSDwarfExpressionStack; + +typedef struct { + FIRCLSDwarfExpressionStack stack; + const void *dataCursor; + const void *endAddress; + const FIRCLSThreadContext *registers; +} FIRCLSDwarfExpressionMachine; + +void FIRCLSDwarfExpressionStackInit(FIRCLSDwarfExpressionStack *stack); +bool FIRCLSDwarfExpressionStackIsValid(FIRCLSDwarfExpressionStack *stack); +bool FIRCLSDwarfExpressionStackPush(FIRCLSDwarfExpressionStack *stack, intptr_t value); +intptr_t FIRCLSDwarfExpressionStackPeek(FIRCLSDwarfExpressionStack *stack); +intptr_t FIRCLSDwarfExpressionStackPop(FIRCLSDwarfExpressionStack *stack); + +bool FIRCLSDwarfExpressionMachineInit(FIRCLSDwarfExpressionMachine *machine, + const void *cursor, + const FIRCLSThreadContext *registers, + intptr_t stackValue); +bool FIRCLSDwarfExpressionMachinePrepareForExecution(FIRCLSDwarfExpressionMachine *machine); +bool FIRCLSDwarfExpressionMachineIsFinished(FIRCLSDwarfExpressionMachine *machine); +bool FIRCLSDwarfExpressionMachineGetResult(FIRCLSDwarfExpressionMachine *machine, intptr_t *result); + +bool FIRCLSDwarfExpressionMachineExecuteNextOpcode(FIRCLSDwarfExpressionMachine *machine); + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwind.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwind.c new file mode 100644 index 000000000..665e8aaa9 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwind.c @@ -0,0 +1,1002 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSDwarfUnwind.h" +#include "FIRCLSDataParsing.h" +#include "FIRCLSDefines.h" +#include "FIRCLSDwarfExpressionMachine.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSUnwind_arch.h" +#include "FIRCLSUtility.h" +#include "dwarf.h" + +#include + +#if CLS_DWARF_UNWINDING_SUPPORTED + +#define FIRCLSDwarfLog(__FORMAT__, ...) FIRCLSSDKLog(__FORMAT__, ##__VA_ARGS__) + +#define CLS_DWARF_EXPRESSION_STACK_SIZE (100) + +#pragma mark Prototypes +static bool FIRCLSDwarfParseAndProcessAugmentation(DWARFCIERecord* record, const void** ptr); + +#pragma mark - Record Parsing +bool FIRCLSDwarfParseCIERecord(DWARFCIERecord* cie, const void* ptr) { + if (!cie || !ptr) { + return false; + } + + memset(cie, 0, sizeof(DWARFCIERecord)); + + cie->length = FIRCLSParseRecordLengthAndAdvance(&ptr); + if (cie->length == 0) { + FIRCLSSDKLog("Error: CIE length invalid\n"); + return false; + } + + // the length does not include the length field(s) themselves + const void* endAddress = ptr + cie->length; + + if (FIRCLSParseUint32AndAdvance(&ptr) != DWARF_CIE_ID_CIE_FLAG) { + FIRCLSSDKLog("Error: CIE flag not found\n"); + } + + cie->version = FIRCLSParseUint8AndAdvance(&ptr); + if (cie->version != 1 && cie->version != 3) { + FIRCLSSDKLog("Error: CIE version %u unsupported\n", cie->version); + } + + cie->pointerEncoding = DW_EH_PE_absptr; + cie->lsdaEncoding = DW_EH_PE_absptr; + + cie->augmentation = FIRCLSParseStringAndAdvance(&ptr); + cie->codeAlignFactor = FIRCLSParseULEB128AndAdvance(&ptr); + cie->dataAlignFactor = FIRCLSParseLEB128AndAdvance(&ptr); + + switch (cie->version) { + case 1: + cie->returnAddressRegister = FIRCLSParseUint8AndAdvance(&ptr); + break; + case 3: + cie->returnAddressRegister = FIRCLSParseULEB128AndAdvance(&ptr); + break; + default: + FIRCLSSDKLog("Error: CIE version %u unsupported\n", cie->version); + return false; + } + + if (!FIRCLSDwarfParseAndProcessAugmentation(cie, &ptr)) { + return false; + } + + cie->instructions.data = ptr; + cie->instructions.length = (uint32_t)(endAddress - ptr); + + return true; +} + +static bool FIRCLSDwarfParseAndProcessAugmentation(DWARFCIERecord* record, const void** ptr) { + if (!record || !ptr) { + return false; + } + + if (!record->augmentation) { + return false; + } + + if (record->augmentation[0] == 0) { + return true; + } + + if (record->augmentation[0] != 'z') { + FIRCLSSDKLog("Error: Unimplemented: augmentation string %s\n", record->augmentation); + return false; + } + + size_t stringLength = strlen(record->augmentation); + + uint64_t dataLength = FIRCLSParseULEB128AndAdvance(ptr); + const void* ending = *ptr + dataLength; + + // start at 1 because we know the first character is a 'z' + for (size_t i = 1; i < stringLength; ++i) { + switch (record->augmentation[i]) { + case 'L': + // There is an LSDA pointer encoding present. The actual address of the LSDA + // is in the FDE + record->lsdaEncoding = FIRCLSParseUint8AndAdvance(ptr); + break; + case 'R': + // There is a pointer encoding present, used for all addresses in an FDE. + record->pointerEncoding = FIRCLSParseUint8AndAdvance(ptr); + break; + case 'P': + // Two arguments. A pointer encoding, and a pointer to a personality function encoded + // with that value. + record->personalityEncoding = FIRCLSParseUint8AndAdvance(ptr); + record->personalityFunction = + FIRCLSParseAddressWithEncodingAndAdvance(ptr, record->personalityEncoding); + if (record->personalityFunction == CLS_INVALID_ADDRESS) { + FIRCLSSDKLog("Error: Found an invalid start address\n"); + return false; + } + break; + case 'S': + record->signalFrame = true; + break; + default: + FIRCLSSDKLog("Error: Unhandled augmentation string entry %c\n", record->augmentation[i]); + return false; + } + + // small sanity check + if (*ptr > ending) { + return false; + } + } + + return true; +} + +bool FIRCLSDwarfParseFDERecord(DWARFFDERecord* fdeRecord, + bool parseCIE, + DWARFCIERecord* cieRecord, + const void* ptr) { + if (!fdeRecord || !cieRecord || !ptr) { + return false; + } + + fdeRecord->length = FIRCLSParseRecordLengthAndAdvance(&ptr); + if (fdeRecord->length == 0) { + FIRCLSSDKLog("Error: FDE has zero length\n"); + return false; + } + + // length does not include length field + const void* endAddress = ptr + fdeRecord->length; + + // According to the spec, this is 32/64 bit value, but libunwind always + // parses this as a 32bit value. + fdeRecord->cieOffset = FIRCLSParseUint32AndAdvance(&ptr); + if (fdeRecord->cieOffset == 0) { + FIRCLSSDKLog("Error: CIE offset invalid\n"); + return false; + } + + if (parseCIE) { + // The CIE offset is really weird. It appears to be an offset from the + // beginning of its field. This isn't what the documentation says, but it is + // a little ambigious. This is what DwarfParser.hpp does. + // Note that we have to back up one sizeof(uint32_t), because we've advanced + // by parsing the offset + const void* ciePointer = ptr - fdeRecord->cieOffset - sizeof(uint32_t); + if (!FIRCLSDwarfParseCIERecord(cieRecord, ciePointer)) { + FIRCLSSDKLog("Error: Unable to parse CIE record\n"); + return false; + } + } + + if (!FIRCLSDwarfCIEIsValid(cieRecord)) { + FIRCLSSDKLog("Error: CIE invalid\n"); + return false; + } + + // the next field depends on the pointer encoding style used + fdeRecord->startAddress = + FIRCLSParseAddressWithEncodingAndAdvance(&ptr, cieRecord->pointerEncoding); + if (fdeRecord->startAddress == CLS_INVALID_ADDRESS) { + FIRCLSSDKLog("Error: Found an invalid start address\n"); + return false; + } + + // Here's something weird too. The range is encoded as a "special" address, where only the value + // is used, regardless of other pointer-encoding schemes. + fdeRecord->rangeSize = FIRCLSParseAddressWithEncodingAndAdvance( + &ptr, cieRecord->pointerEncoding & DW_EH_PE_VALUE_MASK); + if (fdeRecord->rangeSize == CLS_INVALID_ADDRESS) { + FIRCLSSDKLog("Error: Found an invalid address range\n"); + return false; + } + + // Just skip over the section for now. The data here is only needed for personality functions, + // which we don't need + if (FIRCLSDwarfCIEHasAugmentationData(cieRecord)) { + uintptr_t augmentationLength = (uintptr_t)FIRCLSParseULEB128AndAdvance(&ptr); + + ptr += augmentationLength; + } + + fdeRecord->instructions.data = ptr; + fdeRecord->instructions.length = (uint32_t)(endAddress - ptr); + + return true; +} + +bool FIRCLSDwarfParseCFIFromFDERecord(FIRCLSDwarfCFIRecord* record, const void* ptr) { + if (!record || !ptr) { + return false; + } + + return FIRCLSDwarfParseFDERecord(&record->fde, true, &record->cie, ptr); +} + +bool FIRCLSDwarfParseCFIFromFDERecordOffset(FIRCLSDwarfCFIRecord* record, + const void* ehFrame, + uintptr_t fdeOffset) { + if (!record || !ehFrame || (fdeOffset == 0)) { + return false; + } + + const void* ptr = ehFrame + fdeOffset; + + return FIRCLSDwarfParseCFIFromFDERecord(record, ptr); +} + +#pragma mark - Properties +bool FIRCLSDwarfCIEIsValid(DWARFCIERecord* cie) { + if (!cie) { + return false; + } + + if (cie->length == 0) { + return false; + } + + if (cie->version != 1 && cie->version != 3) { + return false; + } + + return true; +} + +bool FIRCLSDwarfCIEHasAugmentationData(DWARFCIERecord* cie) { + if (!cie) { + return false; + } + + if (!cie->augmentation) { + return false; + } + + return cie->augmentation[0] == 'z'; +} + +#pragma mark - Instructions + +static bool FIRCLSDwarfParseAndExecute_set_loc(const void** cursor, + DWARFCIERecord* cieRecord, + intptr_t* codeOffset) { + uintptr_t operand = FIRCLSParseAddressWithEncodingAndAdvance(cursor, cieRecord->pointerEncoding); + + *codeOffset = operand; + + FIRCLSDwarfLog("DW_CFA_set_loc %lu\n", operand); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_advance_loc1(const void** cursor, + DWARFCIERecord* cieRecord, + intptr_t* codeOffset) { + int64_t offset = FIRCLSParseUint8AndAdvance(cursor) * cieRecord->codeAlignFactor; + + *codeOffset += offset; + + FIRCLSDwarfLog("DW_CFA_advance_loc1 %lld\n", offset); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_advance_loc2(const void** cursor, + DWARFCIERecord* cieRecord, + intptr_t* codeOffset) { + int64_t offset = FIRCLSParseUint16AndAdvance(cursor) * cieRecord->codeAlignFactor; + + *codeOffset += offset; + + FIRCLSDwarfLog("DW_CFA_advance_loc2 %lld\n", offset); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_advance_loc4(const void** cursor, + DWARFCIERecord* cieRecord, + intptr_t* codeOffset) { + int64_t offset = FIRCLSParseUint32AndAdvance(cursor) * cieRecord->codeAlignFactor; + + *codeOffset += offset; + + FIRCLSDwarfLog("DW_CFA_advance_loc4 %lld\n", offset); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_def_cfa(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state) { + uint64_t regNum = FIRCLSParseULEB128AndAdvance(cursor); + + if (regNum > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: Found an invalid DW_CFA_def_cfa register number\n"); + return false; + } + + int64_t offset = FIRCLSParseULEB128AndAdvance(cursor); + + state->cfaRegister = regNum; + state->cfaRegisterOffset = offset; + + FIRCLSDwarfLog("DW_CFA_def_cfa %llu, %lld\n", regNum, offset); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_def_cfa_register(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state) { + uint64_t regNum = FIRCLSParseULEB128AndAdvance(cursor); + + if (regNum > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: Found an invalid DW_CFA_def_cfa_register register number\n"); + return false; + } + + state->cfaRegister = regNum; + + FIRCLSDwarfLog("DW_CFA_def_cfa_register %llu\n", regNum); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_def_cfa_offset(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state) { + uint64_t offset = FIRCLSParseULEB128AndAdvance(cursor); + + state->cfaRegisterOffset = offset; + + FIRCLSDwarfLog("DW_CFA_def_cfa_offset %lld\n", offset); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_same_value(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state) { + uint64_t regNum = FIRCLSParseULEB128AndAdvance(cursor); + + if (regNum > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: Found an invalid DW_CFA_same_value register number\n"); + return false; + } + + state->registers[regNum].location = FIRCLSDwarfRegisterUnused; + + FIRCLSDwarfLog("DW_CFA_same_value %llu\n", regNum); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_register(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state) { + uint64_t regNum = FIRCLSParseULEB128AndAdvance(cursor); + + if (regNum > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: Found an invalid DW_CFA_register number\n"); + return false; + } + + uint64_t regValue = FIRCLSParseULEB128AndAdvance(cursor); + + if (regValue > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: Found an invalid DW_CFA_register value\n"); + return false; + } + + state->registers[regNum].location = FIRCLSDwarfRegisterInRegister; + state->registers[regNum].value = regValue; + + FIRCLSDwarfLog("DW_CFA_register %llu %llu\n", regNum, regValue); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_expression(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state) { + uint64_t regNum = FIRCLSParseULEB128AndAdvance(cursor); + + if (regNum > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: Found an invalid DW_CFA_expression register number\n"); + return false; + } + + state->registers[regNum].location = FIRCLSDwarfRegisterAtExpression; + state->registers[regNum].value = (uintptr_t)*cursor; + + // read the length of the expression, and advance past it + uint64_t length = FIRCLSParseULEB128AndAdvance(cursor); + *cursor += length; + + FIRCLSDwarfLog("DW_CFA_expression %llu %llu\n", regNum, length); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_val_expression(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state) { + uint64_t regNum = FIRCLSParseULEB128AndAdvance(cursor); + + if (regNum > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: Found an invalid DW_CFA_val_expression register number\n"); + return false; + } + + state->registers[regNum].location = FIRCLSDwarfRegisterIsExpression; + state->registers[regNum].value = (uintptr_t)*cursor; + + // read the length of the expression, and advance past it + uint64_t length = FIRCLSParseULEB128AndAdvance(cursor); + *cursor += length; + + FIRCLSDwarfLog("DW_CFA_val_expression %llu %llu\n", regNum, length); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_def_cfa_expression(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state) { + state->cfaRegister = CLS_DWARF_INVALID_REGISTER_NUM; + state->cfaExpression = *cursor; + + // read the length of the expression, and advance past it + uint64_t length = FIRCLSParseULEB128AndAdvance(cursor); + *cursor += length; + + FIRCLSDwarfLog("DW_CFA_def_cfa_expression %llu\n", length); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_offset(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state, + uint8_t regNum) { + if (regNum > CLS_DWARF_MAX_REGISTER_NUM) { + FIRCLSSDKLog("Error: Found an invalid DW_CFA_offset register number\n"); + return false; + } + + int64_t offset = FIRCLSParseULEB128AndAdvance(cursor) * cieRecord->dataAlignFactor; + + state->registers[regNum].location = FIRCLSDwarfRegisterInCFA; + state->registers[regNum].value = offset; + + FIRCLSDwarfLog("DW_CFA_offset %u, %lld\n", regNum, offset); + + return true; +} + +static bool FIRCLSDwarfParseAndExecute_advance_loc(const void** cursor, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state, + uint8_t delta, + intptr_t* codeOffset) { + if (!FIRCLSIsValidPointer(codeOffset) || !FIRCLSIsValidPointer(cieRecord)) { + FIRCLSSDKLog("Error: invalid inputs\n"); + return false; + } + + *codeOffset = delta * (intptr_t)cieRecord->codeAlignFactor; + + FIRCLSDwarfLog("DW_CFA_advance_loc %u\n", delta); + + return true; +} + +static bool FIRCLSDwarfParseAndExecuteInstructionWithOperand(const void** cursor, + uint8_t instruction, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state, + intptr_t* codeOffset) { + uint8_t operand = instruction & DW_CFA_OPERAND_MASK; + bool success = false; + + switch (instruction & DW_CFA_OPCODE_MASK) { + case DW_CFA_offset: + success = FIRCLSDwarfParseAndExecute_offset(cursor, cieRecord, state, operand); + break; + case DW_CFA_advance_loc: + success = + FIRCLSDwarfParseAndExecute_advance_loc(cursor, cieRecord, state, operand, codeOffset); + break; + case DW_CFA_restore: + FIRCLSSDKLog("Error: Unimplemented DWARF instruction with operand 0x%x\n", instruction); + break; + default: + FIRCLSSDKLog("Error: Unrecognized DWARF instruction 0x%x\n", instruction); + break; + } + + return success; +} + +#pragma mark - Expressions +static bool FIRCLSDwarfEvalulateExpression(const void* cursor, + const FIRCLSThreadContext* registers, + intptr_t stackValue, + intptr_t* result) { + FIRCLSDwarfLog("starting at %p with initial value %lx\n", cursor, stackValue); + + if (!FIRCLSIsValidPointer(cursor) || !FIRCLSIsValidPointer(result)) { + FIRCLSSDKLog("Error: inputs invalid\n"); + return false; + } + + FIRCLSDwarfExpressionMachine machine; + + if (!FIRCLSDwarfExpressionMachineInit(&machine, cursor, registers, stackValue)) { + FIRCLSSDKLog("Error: unable to init DWARF expression machine\n"); + return false; + } + + if (!FIRCLSDwarfExpressionMachinePrepareForExecution(&machine)) { + FIRCLSSDKLog("Error: unable to prepare for execution\n"); + return false; + } + + while (!FIRCLSDwarfExpressionMachineIsFinished(&machine)) { + if (!FIRCLSDwarfExpressionMachineExecuteNextOpcode(&machine)) { + FIRCLSSDKLog("Error: failed to execute DWARF machine opcode\n"); + return false; + } + } + + if (!FIRCLSDwarfExpressionMachineGetResult(&machine, result)) { + FIRCLSSDKLog("Error: failed to get DWARF expression result\n"); + return false; + } + + FIRCLSDwarfLog("successfully computed expression result\n"); + + return true; +} + +#pragma mark - Execution +bool FIRCLSDwarfInstructionsEnumerate(DWARFInstructions* instructions, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state, + intptr_t pcOffset) { + if (!instructions || !cieRecord || !state) { + FIRCLSSDKLog("Error: inputs invalid\n"); + return false; + } + + // This is a little bit of state that can't be put into the state structure, because + // it is possible for instructions to push/pop state that does not affect this value. + intptr_t codeOffset = 0; + + const void* cursor = instructions->data; + const void* endAddress = cursor + instructions->length; + + FIRCLSDwarfLog("Running instructions from %p to %p\n", cursor, endAddress); + + // parse the instructions, as long as: + // - our data pointer is still in range + // - the pc offset is within the range of instructions that apply + + while ((cursor < endAddress) && (codeOffset < pcOffset)) { + uint8_t instruction = FIRCLSParseUint8AndAdvance(&cursor); + bool success = false; + + switch (instruction) { + case DW_CFA_nop: + FIRCLSDwarfLog("DW_CFA_nop\n"); + continue; + case DW_CFA_set_loc: + success = FIRCLSDwarfParseAndExecute_set_loc(&cursor, cieRecord, &codeOffset); + break; + case DW_CFA_advance_loc1: + success = FIRCLSDwarfParseAndExecute_advance_loc1(&cursor, cieRecord, &codeOffset); + break; + case DW_CFA_advance_loc2: + success = FIRCLSDwarfParseAndExecute_advance_loc2(&cursor, cieRecord, &codeOffset); + break; + case DW_CFA_advance_loc4: + success = FIRCLSDwarfParseAndExecute_advance_loc4(&cursor, cieRecord, &codeOffset); + break; + case DW_CFA_def_cfa: + success = FIRCLSDwarfParseAndExecute_def_cfa(&cursor, cieRecord, state); + break; + case DW_CFA_def_cfa_register: + success = FIRCLSDwarfParseAndExecute_def_cfa_register(&cursor, cieRecord, state); + break; + case DW_CFA_def_cfa_offset: + success = FIRCLSDwarfParseAndExecute_def_cfa_offset(&cursor, cieRecord, state); + break; + case DW_CFA_same_value: + success = FIRCLSDwarfParseAndExecute_same_value(&cursor, cieRecord, state); + break; + case DW_CFA_register: + success = FIRCLSDwarfParseAndExecute_register(&cursor, cieRecord, state); + break; + case DW_CFA_def_cfa_expression: + success = FIRCLSDwarfParseAndExecute_def_cfa_expression(&cursor, cieRecord, state); + break; + case DW_CFA_expression: + success = FIRCLSDwarfParseAndExecute_expression(&cursor, cieRecord, state); + break; + case DW_CFA_val_expression: + success = FIRCLSDwarfParseAndExecute_val_expression(&cursor, cieRecord, state); + break; + case DW_CFA_offset_extended: + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + case DW_CFA_offset_extended_sf: + case DW_CFA_def_cfa_sf: + case DW_CFA_def_cfa_offset_sf: + case DW_CFA_val_offset: + case DW_CFA_val_offset_sf: + case DW_CFA_GNU_window_save: + case DW_CFA_GNU_args_size: + case DW_CFA_GNU_negative_offset_extended: + FIRCLSSDKLog("Error: Unimplemented DWARF instruction 0x%x\n", instruction); + return false; + default: + success = FIRCLSDwarfParseAndExecuteInstructionWithOperand(&cursor, instruction, cieRecord, + state, &codeOffset); + break; + } + + if (!success) { + FIRCLSSDKLog("Error: Failed to execute dwarf instruction 0x%x\n", instruction); + return false; + } + } + + return true; +} + +bool FIRCLSDwarfUnwindComputeRegisters(FIRCLSDwarfCFIRecord* record, + FIRCLSThreadContext* registers) { + if (!record || !registers) { + return false; + } + + // We need to run the dwarf instructions to compute our register values. + // - initialize state + // - run the CIE instructions + // - run the FDE instructions + // - grab the values + + FIRCLSDwarfState state; + + memset(&state, 0, sizeof(FIRCLSDwarfState)); + + // We need to run all the instructions in the CIE record. So, pass in a large value for the pc + // offset so we don't stop early. + if (!FIRCLSDwarfInstructionsEnumerate(&record->cie.instructions, &record->cie, &state, + INTPTR_MAX)) { + FIRCLSSDKLog("Error: Unable to run CIE instructions\n"); + return false; + } + + intptr_t pcOffset = FIRCLSThreadContextGetPC(registers) - record->fde.startAddress; + if (pcOffset < 0) { + FIRCLSSDKLog("Error: The FDE pcOffset value cannot be negative\n"); + return false; + } + + if (!FIRCLSDwarfInstructionsEnumerate(&record->fde.instructions, &record->cie, &state, + pcOffset)) { + FIRCLSSDKLog("Error: Unable to run FDE instructions\n"); + return false; + } + + uintptr_t cfaRegister = 0; + + if (!FIRCLSDwarfGetCFA(&state, registers, &cfaRegister)) { + FIRCLSSDKLog("Error: failed to get CFA\n"); + return false; + } + + if (!FIRCLSDwarfUnwindAssignRegisters(&state, registers, cfaRegister, registers)) { + FIRCLSSDKLogError("Error: Unable to assign DWARF registers\n"); + return false; + } + + return true; +} + +bool FIRCLSDwarfUnwindAssignRegisters(const FIRCLSDwarfState* state, + const FIRCLSThreadContext* registers, + uintptr_t cfaRegister, + FIRCLSThreadContext* outputRegisters) { + if (!FIRCLSIsValidPointer(state) || !FIRCLSIsValidPointer(registers)) { + FIRCLSSDKLogError("Error: input invalid\n"); + return false; + } + + // make a copy, which we'll be changing + FIRCLSThreadContext newThreadState = *registers; + + // loop through all the registers, so we can set their values + for (size_t i = 0; i <= CLS_DWARF_MAX_REGISTER_NUM; ++i) { + if (state->registers[i].location == FIRCLSDwarfRegisterUnused) { + continue; + } + + const uintptr_t value = + FIRCLSDwarfGetSavedRegister(registers, cfaRegister, state->registers[i]); + + if (!FIRCLSDwarfUnwindSetRegisterValue(&newThreadState, i, value)) { + FIRCLSSDKLog("Error: Unable to restore register value\n"); + return false; + } + } + + if (!FIRCLSDwarfUnwindSetRegisterValue(&newThreadState, CLS_DWARF_REG_SP, cfaRegister)) { + FIRCLSSDKLog("Error: Unable to restore SP value\n"); + return false; + } + + // sanity-check that things have changed + if (FIRCLSDwarfCompareRegisters(registers, &newThreadState, CLS_DWARF_REG_SP)) { + FIRCLSSDKLog("Error: Stack pointer hasn't changed\n"); + return false; + } + + if (FIRCLSDwarfCompareRegisters(registers, &newThreadState, CLS_DWARF_REG_RETURN)) { + FIRCLSSDKLog("Error: PC hasn't changed\n"); + return false; + } + + // set our new value + *outputRegisters = newThreadState; + + return true; +} + +#pragma mark - Register Operations +bool FIRCLSDwarfCompareRegisters(const FIRCLSThreadContext* a, + const FIRCLSThreadContext* b, + uint64_t registerNum) { + return FIRCLSDwarfUnwindGetRegisterValue(a, registerNum) == + FIRCLSDwarfUnwindGetRegisterValue(b, registerNum); +} + +bool FIRCLSDwarfGetCFA(FIRCLSDwarfState* state, + const FIRCLSThreadContext* registers, + uintptr_t* cfa) { + if (!FIRCLSIsValidPointer(state) || !FIRCLSIsValidPointer(registers) || + !FIRCLSIsValidPointer(cfa)) { + FIRCLSSDKLog("Error: invalid input\n"); + return false; + } + + if (state->cfaExpression) { + if (!FIRCLSDwarfEvalulateExpression(state->cfaExpression, registers, 0, (intptr_t*)cfa)) { + FIRCLSSDKLog("Error: failed to compute CFA expression\n"); + return false; + } + + return true; + } + + // libunwind checks that cfaRegister is not zero. This seems like a potential bug - why couldn't + // it be zero? + + *cfa = FIRCLSDwarfUnwindGetRegisterValue(registers, state->cfaRegister) + + (uintptr_t)state->cfaRegisterOffset; + + return true; +} + +uintptr_t FIRCLSDwarfGetSavedRegister(const FIRCLSThreadContext* registers, + uintptr_t cfaRegister, + FIRCLSDwarfRegister dRegister) { + intptr_t result = 0; + + FIRCLSDwarfLog("Getting register %x\n", dRegister.location); + + switch (dRegister.location) { + case FIRCLSDwarfRegisterInCFA: { + const uintptr_t address = cfaRegister + (uintptr_t)dRegister.value; + + if (!FIRCLSReadMemory(address, &result, sizeof(result))) { + FIRCLSSDKLog("Error: Unable to read CFA value\n"); + return 0; + } + } + return result; + case FIRCLSDwarfRegisterInRegister: + return FIRCLSDwarfUnwindGetRegisterValue(registers, dRegister.value); + case FIRCLSDwarfRegisterOffsetFromCFA: + FIRCLSSDKLog("Error: OffsetFromCFA unhandled\n"); + break; + case FIRCLSDwarfRegisterAtExpression: + if (!FIRCLSDwarfEvalulateExpression((void*)dRegister.value, registers, cfaRegister, + &result)) { + FIRCLSSDKLog("Error: unable to evaluate expression\n"); + return 0; + } + + if (!FIRCLSReadMemory(result, &result, sizeof(result))) { + FIRCLSSDKLog("Error: Unable to read memory computed from expression\n"); + return 0; + } + + return result; + case FIRCLSDwarfRegisterIsExpression: + if (!FIRCLSDwarfEvalulateExpression((void*)dRegister.value, registers, cfaRegister, + &result)) { + FIRCLSSDKLog("Error: unable to evaluate expression\n"); + return 0; + } + + return result; + default: + FIRCLSSDKLog("Error: Unrecognized register save location 0x%x\n", dRegister.location); + break; + } + + return 0; +} + +#if DEBUG +#pragma mark - Debugging +void FIRCLSCFIRecordShow(FIRCLSDwarfCFIRecord* record) { + if (!record) { + FIRCLSSDKLog("Error: CFI record: null\n"); + return; + } + + FIRCLSCIERecordShow(&record->cie); + FIRCLSFDERecordShow(&record->fde, &record->cie); +} + +void FIRCLSCIERecordShow(DWARFCIERecord* record) { + if (!record) { + FIRCLSSDKLog("Error: CIE: null\n"); + return; + } + + FIRCLSSDKLog("CIE:\n"); + FIRCLSSDKLog(" length: %llu\n", record->length); + FIRCLSSDKLog(" version: %u\n", record->version); + FIRCLSSDKLog(" augmentation: %s\n", record->augmentation); + FIRCLSSDKLog(" EH Data: 0x%04lx\n", record->ehData); + FIRCLSSDKLog("LSDA encoding: 0x%02x\n", record->lsdaEncoding); + FIRCLSSDKLog(" personality: 0x%lx\n", record->personalityFunction); + + FIRCLSDwarfPointerEncodingShow(" encoding", record->pointerEncoding); + FIRCLSDwarfPointerEncodingShow(" P encoding", record->personalityEncoding); + + FIRCLSSDKLog(" code align: %llu\n", record->codeAlignFactor); + FIRCLSSDKLog(" data align: %lld\n", record->dataAlignFactor); + FIRCLSSDKLog(" RA register: %llu\n", record->returnAddressRegister); + + FIRCLSDwarfInstructionsShow(&record->instructions, record); +} + +void FIRCLSFDERecordShow(DWARFFDERecord* record, DWARFCIERecord* cie) { + if (!record) { + FIRCLSSDKLog("FDE: null\n"); + return; + } + + FIRCLSSDKLog("FDE:\n"); + FIRCLSSDKLog(" length: %llu\n", record->length); + FIRCLSSDKLog(" CIE offset: %llu\n", record->cieOffset); + FIRCLSSDKLog(" start addr: 0x%lx\n", record->startAddress); + FIRCLSSDKLog(" range: %lu\n", record->rangeSize); + + FIRCLSDwarfInstructionsShow(&record->instructions, cie); +} + +void FIRCLSDwarfPointerEncodingShow(const char* leadString, uint8_t encoding) { + if (encoding == DW_EH_PE_omit) { + FIRCLSSDKLog("%s: 0x%02x (omit)\n", leadString, encoding); + } else { + const char* peValue = ""; + const char* peOffset = ""; + + switch (encoding & DW_EH_PE_VALUE_MASK) { + case DW_EH_PE_absptr: + peValue = "DW_EH_PE_absptr"; + break; + case DW_EH_PE_uleb128: + peValue = "DW_EH_PE_uleb128"; + break; + case DW_EH_PE_udata2: + peValue = "DW_EH_PE_udata2"; + break; + case DW_EH_PE_udata4: + peValue = "DW_EH_PE_udata4"; + break; + case DW_EH_PE_udata8: + peValue = "DW_EH_PE_udata8"; + break; + case DW_EH_PE_signed: + peValue = "DW_EH_PE_signed"; + break; + case DW_EH_PE_sleb128: + peValue = "DW_EH_PE_sleb128"; + break; + case DW_EH_PE_sdata2: + peValue = "DW_EH_PE_sdata2"; + break; + case DW_EH_PE_sdata4: + peValue = "DW_EH_PE_sdata4"; + break; + case DW_EH_PE_sdata8: + peValue = "DW_EH_PE_sdata8"; + break; + default: + peValue = "unknown"; + break; + } + + switch (encoding & DW_EH_PE_RELATIVE_OFFSET_MASK) { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + peOffset = " + DW_EH_PE_pcrel"; + break; + case DW_EH_PE_textrel: + peOffset = " + DW_EH_PE_textrel"; + break; + case DW_EH_PE_datarel: + peOffset = " + DW_EH_PE_datarel"; + break; + case DW_EH_PE_funcrel: + peOffset = " + DW_EH_PE_funcrel"; + break; + case DW_EH_PE_aligned: + peOffset = " + DW_EH_PE_aligned"; + break; + case DW_EH_PE_indirect: + peOffset = " + DW_EH_PE_indirect"; + break; + default: + break; + } + + FIRCLSSDKLog("%s: 0x%02x (%s%s)\n", leadString, encoding, peValue, peOffset); + } +} + +void FIRCLSDwarfInstructionsShow(DWARFInstructions* instructions, DWARFCIERecord* cie) { + if (!instructions) { + FIRCLSSDKLog("Error: Instructions null\n"); + } + + FIRCLSDwarfState state; + + memset(&state, 0, sizeof(FIRCLSDwarfState)); + + FIRCLSDwarfInstructionsEnumerate(instructions, cie, &state, -1); +} + +#endif + +#else +INJECT_STRIP_SYMBOL(dwarf_unwind) +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwind.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwind.h new file mode 100644 index 000000000..bbb28805b --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwind.h @@ -0,0 +1,138 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +#include "FIRCLSDwarfUnwindRegisters.h" +#include "FIRCLSThreadState.h" + +#if CLS_DWARF_UNWINDING_SUPPORTED + +#pragma mark Structures +typedef struct { + uint32_t length; + const void* data; +} DWARFInstructions; + +typedef struct { + uint64_t length; + uint8_t version; + uintptr_t ehData; // 8 bytes for 64-bit architectures, 4 bytes for 32 + const char* augmentation; + uint8_t pointerEncoding; + uint8_t lsdaEncoding; + uint8_t personalityEncoding; + uintptr_t personalityFunction; + uint64_t codeAlignFactor; + int64_t dataAlignFactor; + uint64_t returnAddressRegister; // is 64 bits enough for this value? + bool signalFrame; + + DWARFInstructions instructions; +} DWARFCIERecord; + +typedef struct { + uint64_t length; + uint64_t cieOffset; // also an arch-specific size + uintptr_t startAddress; + uintptr_t rangeSize; + + DWARFInstructions instructions; +} DWARFFDERecord; + +typedef struct { + DWARFCIERecord cie; + DWARFFDERecord fde; +} FIRCLSDwarfCFIRecord; + +typedef enum { + FIRCLSDwarfRegisterUnused = 0, + FIRCLSDwarfRegisterInCFA, + FIRCLSDwarfRegisterOffsetFromCFA, + FIRCLSDwarfRegisterInRegister, + FIRCLSDwarfRegisterAtExpression, + FIRCLSDwarfRegisterIsExpression +} FIRCLSDwarfRegisterLocation; + +typedef struct { + FIRCLSDwarfRegisterLocation location; + uint64_t value; +} FIRCLSDwarfRegister; + +typedef struct { + uint64_t cfaRegister; + int64_t cfaRegisterOffset; + const void* cfaExpression; + uint32_t spArgSize; + + FIRCLSDwarfRegister registers[CLS_DWARF_MAX_REGISTER_NUM + 1]; +} FIRCLSDwarfState; + +__BEGIN_DECLS + +#pragma mark - Parsing +bool FIRCLSDwarfParseCIERecord(DWARFCIERecord* cie, const void* ptr); +bool FIRCLSDwarfParseFDERecord(DWARFFDERecord* fdeRecord, + bool parseCIE, + DWARFCIERecord* cieRecord, + const void* ptr); +bool FIRCLSDwarfParseCFIFromFDERecord(FIRCLSDwarfCFIRecord* record, const void* ptr); +bool FIRCLSDwarfParseCFIFromFDERecordOffset(FIRCLSDwarfCFIRecord* record, + const void* ehFrame, + uintptr_t fdeOffset); + +#pragma mark - Properties +bool FIRCLSDwarfCIEIsValid(DWARFCIERecord* cie); +bool FIRCLSDwarfCIEHasAugmentationData(DWARFCIERecord* cie); + +#pragma mark - Execution +bool FIRCLSDwarfInstructionsEnumerate(DWARFInstructions* instructions, + DWARFCIERecord* cieRecord, + FIRCLSDwarfState* state, + intptr_t pcOffset); +bool FIRCLSDwarfUnwindComputeRegisters(FIRCLSDwarfCFIRecord* record, + FIRCLSThreadContext* registers); +bool FIRCLSDwarfUnwindAssignRegisters(const FIRCLSDwarfState* state, + const FIRCLSThreadContext* registers, + uintptr_t cfaRegister, + FIRCLSThreadContext* outputRegisters); + +#pragma mark - Register Operations +bool FIRCLSDwarfCompareRegisters(const FIRCLSThreadContext* a, + const FIRCLSThreadContext* b, + uint64_t registerNum); + +bool FIRCLSDwarfGetCFA(FIRCLSDwarfState* state, + const FIRCLSThreadContext* registers, + uintptr_t* cfa); +uintptr_t FIRCLSDwarfGetSavedRegister(const FIRCLSThreadContext* registers, + uintptr_t cfaRegister, + FIRCLSDwarfRegister dRegister); + +#if DEBUG +#pragma mark - Debugging +void FIRCLSCFIRecordShow(FIRCLSDwarfCFIRecord* record); +void FIRCLSCIERecordShow(DWARFCIERecord* record); +void FIRCLSFDERecordShow(DWARFFDERecord* record, DWARFCIERecord* cie); +void FIRCLSDwarfPointerEncodingShow(const char* leadString, uint8_t encoding); +void FIRCLSDwarfInstructionsShow(DWARFInstructions* instructions, DWARFCIERecord* cie); +#endif + +__END_DECLS + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwindRegisters.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwindRegisters.h new file mode 100644 index 000000000..1e3739692 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/Dwarf/FIRCLSDwarfUnwindRegisters.h @@ -0,0 +1,152 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include "FIRCLSFeatures.h" + +#if CLS_CPU_X86_64 +enum { + CLS_DWARF_X86_64_RAX = 0, + CLS_DWARF_X86_64_RDX = 1, + CLS_DWARF_X86_64_RCX = 2, + CLS_DWARF_X86_64_RBX = 3, + CLS_DWARF_X86_64_RSI = 4, + CLS_DWARF_X86_64_RDI = 5, + CLS_DWARF_X86_64_RBP = 6, + CLS_DWARF_X86_64_RSP = 7, + CLS_DWARF_X86_64_R8 = 8, + CLS_DWARF_X86_64_R9 = 9, + CLS_DWARF_X86_64_R10 = 10, + CLS_DWARF_X86_64_R11 = 11, + CLS_DWARF_X86_64_R12 = 12, + CLS_DWARF_X86_64_R13 = 13, + CLS_DWARF_X86_64_R14 = 14, + CLS_DWARF_X86_64_R15 = 15, + + CLS_DWARF_X86_64_RET_ADDR = 16 +}; + +#define CLS_DWARF_REG_RETURN CLS_DWARF_X86_64_RET_ADDR +#define CLS_DWARF_REG_SP CLS_DWARF_X86_64_RSP +#define CLS_DWARF_REG_FP CLS_DWARF_X86_64_RBP + +#define CLS_DWARF_MAX_REGISTER_NUM (CLS_DWARF_X86_64_RET_ADDR) + +#elif CLS_CPU_I386 + +enum { + CLS_DWARF_X86_EAX = 0, + CLS_DWARF_X86_ECX = 1, + CLS_DWARF_X86_EDX = 2, + CLS_DWARF_X86_EBX = 3, + CLS_DWARF_X86_EBP = 4, + CLS_DWARF_X86_ESP = 5, + CLS_DWARF_X86_ESI = 6, + CLS_DWARF_X86_EDI = 7, + + CLS_DWARF_X86_RET_ADDR = 8 +}; + +#define CLS_DWARF_REG_RETURN CLS_DWARF_X86_RET_ADDR +#define CLS_DWARF_REG_SP CLS_DWARF_X86_ESP +#define CLS_DWARF_REG_FP CLS_DWARF_X86_EBP + +#define CLS_DWARF_MAX_REGISTER_NUM (CLS_DWARF_X86_RET_ADDR) + +#elif CLS_CPU_ARM64 + +// 64-bit ARM64 registers +enum { + CLS_DWARF_ARM64_X0 = 0, + CLS_DWARF_ARM64_X1 = 1, + CLS_DWARF_ARM64_X2 = 2, + CLS_DWARF_ARM64_X3 = 3, + CLS_DWARF_ARM64_X4 = 4, + CLS_DWARF_ARM64_X5 = 5, + CLS_DWARF_ARM64_X6 = 6, + CLS_DWARF_ARM64_X7 = 7, + CLS_DWARF_ARM64_X8 = 8, + CLS_DWARF_ARM64_X9 = 9, + CLS_DWARF_ARM64_X10 = 10, + CLS_DWARF_ARM64_X11 = 11, + CLS_DWARF_ARM64_X12 = 12, + CLS_DWARF_ARM64_X13 = 13, + CLS_DWARF_ARM64_X14 = 14, + CLS_DWARF_ARM64_X15 = 15, + CLS_DWARF_ARM64_X16 = 16, + CLS_DWARF_ARM64_X17 = 17, + CLS_DWARF_ARM64_X18 = 18, + CLS_DWARF_ARM64_X19 = 19, + CLS_DWARF_ARM64_X20 = 20, + CLS_DWARF_ARM64_X21 = 21, + CLS_DWARF_ARM64_X22 = 22, + CLS_DWARF_ARM64_X23 = 23, + CLS_DWARF_ARM64_X24 = 24, + CLS_DWARF_ARM64_X25 = 25, + CLS_DWARF_ARM64_X26 = 26, + CLS_DWARF_ARM64_X27 = 27, + CLS_DWARF_ARM64_X28 = 28, + CLS_DWARF_ARM64_X29 = 29, + CLS_DWARF_ARM64_FP = 29, + CLS_DWARF_ARM64_X30 = 30, + CLS_DWARF_ARM64_LR = 30, + CLS_DWARF_ARM64_X31 = 31, + CLS_DWARF_ARM64_SP = 31, + // reserved block + CLS_DWARF_ARM64_D0 = 64, + CLS_DWARF_ARM64_D1 = 65, + CLS_DWARF_ARM64_D2 = 66, + CLS_DWARF_ARM64_D3 = 67, + CLS_DWARF_ARM64_D4 = 68, + CLS_DWARF_ARM64_D5 = 69, + CLS_DWARF_ARM64_D6 = 70, + CLS_DWARF_ARM64_D7 = 71, + CLS_DWARF_ARM64_D8 = 72, + CLS_DWARF_ARM64_D9 = 73, + CLS_DWARF_ARM64_D10 = 74, + CLS_DWARF_ARM64_D11 = 75, + CLS_DWARF_ARM64_D12 = 76, + CLS_DWARF_ARM64_D13 = 77, + CLS_DWARF_ARM64_D14 = 78, + CLS_DWARF_ARM64_D15 = 79, + CLS_DWARF_ARM64_D16 = 80, + CLS_DWARF_ARM64_D17 = 81, + CLS_DWARF_ARM64_D18 = 82, + CLS_DWARF_ARM64_D19 = 83, + CLS_DWARF_ARM64_D20 = 84, + CLS_DWARF_ARM64_D21 = 85, + CLS_DWARF_ARM64_D22 = 86, + CLS_DWARF_ARM64_D23 = 87, + CLS_DWARF_ARM64_D24 = 88, + CLS_DWARF_ARM64_D25 = 89, + CLS_DWARF_ARM64_D26 = 90, + CLS_DWARF_ARM64_D27 = 91, + CLS_DWARF_ARM64_D28 = 92, + CLS_DWARF_ARM64_D29 = 93, + CLS_DWARF_ARM64_D30 = 94, + CLS_DWARF_ARM64_D31 = 95 +}; + +#define CLS_DWARF_MAX_REGISTER_NUM (CLS_DWARF_ARM64_SP) + +#define CLS_DWARF_REG_RETURN CLS_DWARF_ARM64_LR +#define CLS_DWARF_REG_SP CLS_DWARF_ARM64_SP +#define CLS_DWARF_REG_FP CLS_DWARF_ARM64_FP + +#endif + +#define CLS_DWARF_INVALID_REGISTER_NUM (CLS_DWARF_MAX_REGISTER_NUM + 1) diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.c new file mode 100644 index 000000000..7961071d2 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.c @@ -0,0 +1,319 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSUnwind.h" +#include "FIRCLSBinaryImage.h" +#include "FIRCLSCompactUnwind.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSGlobals.h" +#include "FIRCLSUtility.h" + +#include +#include +#include + +// Without a limit on the number of frames we unwind, there's a real possibility +// we'll get stuck in an infinite loop. But, we still need pretty big limits, +// because stacks can get quite big. Also, the stacks are different on the platforms. +// These values were empirically determined (~525000 on OS X, ~65000 on iOS). +#if TARGET_OS_EMBEDDED +const uint32_t FIRCLSUnwindMaxFrames = 100000; +#else +const uint32_t FIRCLSUnwindMaxFrames = 600000; +#endif + +const uint32_t FIRCLSUnwindInfiniteRecursionCountThreshold = 10; + +#pragma mark Prototypes +static bool FIRCLSUnwindNextFrameUsingAllStrategies(FIRCLSUnwindContext* context); +#if CLS_COMPACT_UNWINDING_SUPPORTED +static bool FIRCLSUnwindWithCompactUnwindInfo(FIRCLSUnwindContext* context); +#endif +bool FIRCLSUnwindContextHasValidPCAndSP(FIRCLSUnwindContext* context); + +#pragma mark - API +bool FIRCLSUnwindInit(FIRCLSUnwindContext* context, FIRCLSThreadContext threadContext) { + if (!context) { + return false; + } + + memset(context, 0, sizeof(FIRCLSUnwindContext)); + + context->registers = threadContext; + + return true; +} + +bool FIRCLSUnwindNextFrame(FIRCLSUnwindContext* context) { + if (!FIRCLSIsValidPointer(context)) { + FIRCLSSDKLog("Error: invalid inputs\n"); + return false; + } + + if (!FIRCLSUnwindContextHasValidPCAndSP(context)) { + // This is a special-case. It is possible to try to unwind a thread that has no stack (ie, is + // executing zero functions. I believe this happens when a thread has exited, but before the + // kernel has actually cleaned it up. This situation can only apply to the first frame. So, in + // that case, we don't count it as an error. But, if it happens mid-unwind, it's a problem. + + if (context->frameCount == 0) { + FIRCLSSDKLog("Cancelling unwind for thread with invalid PC/SP\n"); + } else { + FIRCLSSDKLog("Error: thread PC/SP invalid before unwind\n"); + } + + return false; + } + + if (!FIRCLSUnwindNextFrameUsingAllStrategies(context)) { + FIRCLSSDKLogError("Failed to advance to the next frame\n"); + return false; + } + + uintptr_t pc = FIRCLSUnwindGetPC(context); + uintptr_t sp = FIRCLSUnwindGetStackPointer(context); + + // Unwinding will complete when this is no longer a valid value + if (!FIRCLSIsValidPointer(pc)) { + return false; + } + + // after unwinding, validate that we have a sane register value + if (!FIRCLSIsValidPointer(sp)) { + FIRCLSSDKLog("Error: SP (%p) isn't a valid pointer\n", (void*)sp); + return false; + } + + // track repeating frames + if (context->lastFramePC == pc) { + context->repeatCount += 1; + } else { + context->repeatCount = 0; + } + + context->frameCount += 1; + context->lastFramePC = pc; + + return true; +} + +#pragma mark - Register Accessors +uintptr_t FIRCLSUnwindGetPC(FIRCLSUnwindContext* context) { + if (!FIRCLSIsValidPointer(context)) { + return 0; + } + + return FIRCLSThreadContextGetPC(&context->registers); +} + +uintptr_t FIRCLSUnwindGetStackPointer(FIRCLSUnwindContext* context) { + if (!FIRCLSIsValidPointer(context)) { + return 0; + } + + return FIRCLSThreadContextGetStackPointer(&context->registers); +} + +static uintptr_t FIRCLSUnwindGetFramePointer(FIRCLSUnwindContext* context) { + if (!FIRCLSIsValidPointer(context)) { + return 0; + } + + return FIRCLSThreadContextGetFramePointer(&context->registers); +} + +uint32_t FIRCLSUnwindGetFrameRepeatCount(FIRCLSUnwindContext* context) { + if (!FIRCLSIsValidPointer(context)) { + return 0; + } + + return context->repeatCount; +} + +#pragma mark - Unwind Strategies +static bool FIRCLSUnwindNextFrameUsingAllStrategies(FIRCLSUnwindContext* context) { + if (!FIRCLSIsValidPointer(context)) { + FIRCLSSDKLogError("Arguments invalid\n"); + return false; + } + + if (context->frameCount >= FIRCLSUnwindMaxFrames) { + FIRCLSSDKLogWarn("Exceeded maximum number of frames\n"); + return false; + } + + uintptr_t pc = FIRCLSUnwindGetPC(context); + + // Ok, what's going on here? libunwind's UnwindCursor::setInfoBasedOnIPRegister has a + // parameter that, if true, does this subtraction. Despite the comments in the code + // (of 35.1), I found that the parameter was almost always set to true. + // + // I then ran into a problem when unwinding from _pthread_start -> thread_start. This + // is a common transition, which happens in pretty much every report. An extra frame + // was being generated, because the PC we get for _pthread_start was mapping to exactly + // one greater than the function's last byte, according to the compact unwind info. This + // resulted in using the wrong compact encoding, and picking the next function, which + // turned out to be dwarf instead of a frame pointer. + + // So, the moral is - do the subtraction for all frames except the first. I haven't found + // a case where it produces an incorrect result. Also note that at first, I thought this would + // subtract one from the final addresses too. But, the end of this function will *compute* PC, + // so this value is used only to look up unwinding data. + + if (context->frameCount > 0) { + --pc; + if (!FIRCLSThreadContextSetPC(&context->registers, pc)) { + FIRCLSSDKLogError("Unable to set PC\n"); + return false; + } + } + + if (!FIRCLSIsValidPointer(pc)) { + FIRCLSSDKLogError("PC is invalid\n"); + return false; + } + + // the first frame is special - as the registers we need + // are already loaded by definition + if (context->frameCount == 0) { + return true; + } + +#if CLS_COMPACT_UNWINDING_SUPPORTED + // attempt to advance to the next frame using compact unwinding, and + // only fall back to the frame pointer if that fails + if (FIRCLSUnwindWithCompactUnwindInfo(context)) { + return true; + } +#endif + + // If the frame pointer is zero, we cannot use an FP-based unwind and we can reasonably + // assume that we've just gotten to the end of the stack. + if (FIRCLSUnwindGetFramePointer(context) == 0) { + FIRCLSSDKLogWarn("FP is zero, aborting unwind\n"); + // make sure to set the PC to zero, to indicate the unwind is complete + return FIRCLSThreadContextSetPC(&context->registers, 0); + } + + // Only allow stack scanning (as a last resort) if we're on the first frame. All others + // are too likely to screw up. + if (FIRCLSUnwindWithFramePointer(&context->registers, context->frameCount == 1)) { + return true; + } + + FIRCLSSDKLogError("Unable to use frame pointer\n"); + + return false; +} + +#if CLS_COMPACT_UNWINDING_SUPPORTED +static bool FIRCLSUnwindWithCompactUnwindInfo(FIRCLSUnwindContext* context) { + if (!context) { + return false; + } + + // step one - find the image the current pc is within + FIRCLSBinaryImageRuntimeNode image; + + uintptr_t pc = FIRCLSUnwindGetPC(context); + + if (!FIRCLSBinaryImageSafeFindImageForAddress(pc, &image)) { + FIRCLSSDKLogWarn("Unable to find binary for %p\n", (void*)pc); + return false; + } + +#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME + FIRCLSSDKLogDebug("Binary image for %p at %p => %s\n", (void*)pc, image.baseAddress, image.name); +#else + FIRCLSSDKLogDebug("Binary image for %p at %p\n", (void*)pc, image.baseAddress); +#endif + + if (!FIRCLSBinaryImageSafeHasUnwindInfo(&image)) { + FIRCLSSDKLogInfo("Binary image at %p has no unwind info\n", image.baseAddress); + return false; + } + + if (!FIRCLSCompactUnwindInit(&context->compactUnwindState, image.unwindInfo, image.ehFrame, + (uintptr_t)image.baseAddress)) { + FIRCLSSDKLogError("Unable to read unwind info\n"); + return false; + } + + // this function will actually attempt to find compact unwind info for the current PC, + // and use it to mutate the context register state + return FIRCLSCompactUnwindLookupAndCompute(&context->compactUnwindState, &context->registers); +} +#endif + +#pragma mark - Utility Functions +bool FIRCLSUnwindContextHasValidPCAndSP(FIRCLSUnwindContext* context) { + return FIRCLSIsValidPointer(FIRCLSUnwindGetPC(context)) && + FIRCLSIsValidPointer(FIRCLSUnwindGetStackPointer(context)); +} + +#if CLS_CPU_64BIT +#define BASIC_INFO_TYPE vm_region_basic_info_64_t +#define BASIC_INFO VM_REGION_BASIC_INFO_64 +#define BASIC_INFO_COUNT VM_REGION_BASIC_INFO_COUNT_64 +#define vm_region_query_fn vm_region_64 +#else +#define BASIC_INFO_TYPE vm_region_basic_info_t +#define BASIC_INFO VM_REGION_BASIC_INFO +#define BASIC_INFO_COUNT VM_REGION_BASIC_INFO_COUNT +#define vm_region_query_fn vm_region +#endif +bool FIRCLSUnwindIsAddressExecutable(vm_address_t address) { +#if CLS_COMPACT_UNWINDING_SUPPORTED + FIRCLSBinaryImageRuntimeNode unusedNode; + + return FIRCLSBinaryImageSafeFindImageForAddress(address, &unusedNode); +#else + return true; +#endif +} + +bool FIRCLSUnwindFirstExecutableAddress(vm_address_t start, + vm_address_t end, + vm_address_t* foundAddress) { + // This function walks up the data on the stack, looking for the first value that is an address on + // an exectuable page. This is a heurestic, and can hit false positives. + + *foundAddress = 0; // write in a 0 + + do { + vm_address_t address; + + FIRCLSSDKLogDebug("Checking address %p => %p\n", (void*)start, (void*)*(uintptr_t*)start); + + // if start isn't a valid pointer, don't even bother trying + if (FIRCLSIsValidPointer(start)) { + if (!FIRCLSReadMemory(start, &address, sizeof(void*))) { + // if we fail to read from the stack, we're done + return false; + } + + FIRCLSSDKLogDebug("Checking for executable %p\n", (void*)address); + // when we find an exectuable address, we're finished + if (FIRCLSUnwindIsAddressExecutable(address)) { + *foundAddress = address; + return true; + } + } + + start += sizeof(void*); // move back up the stack + + } while (start < end); + + return false; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.h new file mode 100644 index 000000000..a92a2871d --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.h @@ -0,0 +1,53 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "FIRCLSThreadState.h" +#include "FIRCLSUtility.h" +#if CLS_COMPACT_UNWINDING_SUPPORTED +#include "FIRCLSCompactUnwind.h" +#endif +#include +#include + +#include "FIRCLSUnwind_arch.h" + +extern const uint32_t FIRCLSUnwindMaxFrames; + +extern const uint32_t FIRCLSUnwindInfiniteRecursionCountThreshold; + +typedef struct { + FIRCLSThreadContext registers; + uint32_t frameCount; +#if CLS_COMPACT_UNWINDING_SUPPORTED + FIRCLSCompactUnwindContext compactUnwindState; +#endif + uintptr_t lastFramePC; + uint32_t repeatCount; +} FIRCLSUnwindContext; + +// API +bool FIRCLSUnwindInit(FIRCLSUnwindContext* context, FIRCLSThreadContext threadContext); + +bool FIRCLSUnwindNextFrame(FIRCLSUnwindContext* context); +uintptr_t FIRCLSUnwindGetPC(FIRCLSUnwindContext* context); +uintptr_t FIRCLSUnwindGetStackPointer(FIRCLSUnwindContext* context); +uint32_t FIRCLSUnwindGetFrameRepeatCount(FIRCLSUnwindContext* context); + +// utility functions +bool FIRCLSUnwindIsAddressExecutable(vm_address_t address); +bool FIRCLSUnwindFirstExecutableAddress(vm_address_t start, + vm_address_t end, + vm_address_t* foundAddress); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_arch.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_arch.h new file mode 100644 index 000000000..3accd40ea --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_arch.h @@ -0,0 +1,32 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "FIRCLSFeatures.h" +#include "FIRCLSThreadState.h" +#if CLS_COMPACT_UNWINDING_SUPPORTED +#include "FIRCLSCompactUnwind.h" +#endif + +bool FIRCLSUnwindWithFramePointer(FIRCLSThreadContext *registers, bool allowScanning); +uintptr_t FIRCLSUnwindStackPointerFromFramePointer(uintptr_t framePtr); + +#if CLS_DWARF_UNWINDING_SUPPORTED +uintptr_t FIRCLSCompactUnwindDwarfOffset(compact_unwind_encoding_t encoding); +bool FIRCLSDwarfUnwindSetRegisterValue(FIRCLSThreadContext *registers, + uint64_t num, + uintptr_t value); +uintptr_t FIRCLSDwarfUnwindGetRegisterValue(const FIRCLSThreadContext *registers, uint64_t num); +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_arm.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_arm.c new file mode 100644 index 000000000..ead1a29f4 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_arm.c @@ -0,0 +1,313 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSCompactUnwind.h" +#include "FIRCLSCompactUnwind_Private.h" +#include "FIRCLSDefines.h" +#include "FIRCLSDwarfUnwind.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSUnwind.h" +#include "FIRCLSUnwind_arch.h" +#include "FIRCLSUtility.h" + +#if CLS_CPU_ARM || CLS_CPU_ARM64 + +static bool FIRCLSUnwindWithLRRegister(FIRCLSThreadContext* registers) { + if (!FIRCLSIsValidPointer(registers)) { + return false; + } + + // Return address is in LR, SP is pointing to the next frame. + uintptr_t value = FIRCLSThreadContextGetLinkRegister(registers); + + if (!FIRCLSIsValidPointer(value)) { + FIRCLSSDKLog("Error: LR value is invalid\n"); + return false; + } + + return FIRCLSThreadContextSetPC(registers, value); +} + +bool FIRCLSUnwindWithFramePointer(FIRCLSThreadContext* registers, bool allowScanning) { + if (allowScanning) { + // The LR register does have the return address here, but there are situations where + // this can produce false matches. Better backend rules can fix this up in many cases. + if (FIRCLSUnwindWithLRRegister(registers)) { + return true; + } else { + // In this case, we're unable to use the LR. We don't want to just stop unwinding, so + // proceed with the normal, non-scanning path + FIRCLSSDKLog("Unable to use LR, skipping\n"); + } + } + + // read the values from the stack + const uintptr_t framePointer = FIRCLSThreadContextGetFramePointer(registers); + uintptr_t stack[2]; + + if (!FIRCLSReadMemory((vm_address_t)framePointer, stack, sizeof(stack))) { + // unable to read the first stack frame + FIRCLSSDKLog("Error: failed to read memory at address %p\n", (void*)framePointer); + return false; + } + + if (!FIRCLSThreadContextSetPC(registers, stack[1])) { + return false; + } + + if (!FIRCLSThreadContextSetFramePointer(registers, stack[0])) { + return false; + } + + if (!FIRCLSThreadContextSetStackPointer(registers, + FIRCLSUnwindStackPointerFromFramePointer(framePointer))) { + return false; + } + + return true; +} + +uintptr_t FIRCLSUnwindStackPointerFromFramePointer(uintptr_t framePtr) { + // the stack pointer is the frame pointer plus the two saved pointers for the frame + return framePtr + 2 * sizeof(void*); +} + +#if CLS_COMPACT_UNWINDING_SUPPORTED +bool FIRCLSCompactUnwindComputeRegisters(FIRCLSCompactUnwindContext* context, + FIRCLSCompactUnwindResult* result, + FIRCLSThreadContext* registers) { + if (!context || !result || !registers) { + return false; + } + + // Note that compact_uwnind_encoding.h has a few bugs in it prior to iOS 8.0. + // Only refer to the >= 8.0 header. + switch (result->encoding & UNWIND_ARM64_MODE_MASK) { + case UNWIND_ARM64_MODE_FRAMELESS: + // Interestingly, we also know the size of the stack frame, by + // using UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK. Is that useful? + return FIRCLSUnwindWithLRRegister(registers); + break; + case UNWIND_ARM64_MODE_DWARF: + return FIRCLSCompactUnwindDwarfFrame( + context, result->encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET, registers); + break; + case UNWIND_ARM64_MODE_FRAME: + return FIRCLSUnwindWithFramePointer(registers, false); + default: + FIRCLSSDKLog("Invalid encoding 0x%x\n", result->encoding); + break; + } + + return false; +} +#endif + +#if CLS_DWARF_UNWINDING_SUPPORTED +uintptr_t FIRCLSDwarfUnwindGetRegisterValue(const FIRCLSThreadContext* registers, uint64_t num) { + switch (num) { + case CLS_DWARF_ARM64_X0: + return registers->__ss.__x[0]; + case CLS_DWARF_ARM64_X1: + return registers->__ss.__x[1]; + case CLS_DWARF_ARM64_X2: + return registers->__ss.__x[2]; + case CLS_DWARF_ARM64_X3: + return registers->__ss.__x[3]; + case CLS_DWARF_ARM64_X4: + return registers->__ss.__x[4]; + case CLS_DWARF_ARM64_X5: + return registers->__ss.__x[5]; + case CLS_DWARF_ARM64_X6: + return registers->__ss.__x[6]; + case CLS_DWARF_ARM64_X7: + return registers->__ss.__x[7]; + case CLS_DWARF_ARM64_X8: + return registers->__ss.__x[8]; + case CLS_DWARF_ARM64_X9: + return registers->__ss.__x[9]; + case CLS_DWARF_ARM64_X10: + return registers->__ss.__x[10]; + case CLS_DWARF_ARM64_X11: + return registers->__ss.__x[11]; + case CLS_DWARF_ARM64_X12: + return registers->__ss.__x[12]; + case CLS_DWARF_ARM64_X13: + return registers->__ss.__x[13]; + case CLS_DWARF_ARM64_X14: + return registers->__ss.__x[14]; + case CLS_DWARF_ARM64_X15: + return registers->__ss.__x[15]; + case CLS_DWARF_ARM64_X16: + return registers->__ss.__x[16]; + case CLS_DWARF_ARM64_X17: + return registers->__ss.__x[17]; + case CLS_DWARF_ARM64_X18: + return registers->__ss.__x[18]; + case CLS_DWARF_ARM64_X19: + return registers->__ss.__x[19]; + case CLS_DWARF_ARM64_X20: + return registers->__ss.__x[20]; + case CLS_DWARF_ARM64_X21: + return registers->__ss.__x[21]; + case CLS_DWARF_ARM64_X22: + return registers->__ss.__x[22]; + case CLS_DWARF_ARM64_X23: + return registers->__ss.__x[23]; + case CLS_DWARF_ARM64_X24: + return registers->__ss.__x[24]; + case CLS_DWARF_ARM64_X25: + return registers->__ss.__x[25]; + case CLS_DWARF_ARM64_X26: + return registers->__ss.__x[26]; + case CLS_DWARF_ARM64_X27: + return registers->__ss.__x[27]; + case CLS_DWARF_ARM64_X28: + return registers->__ss.__x[28]; + case CLS_DWARF_ARM64_FP: + return FIRCLSThreadContextGetFramePointer(registers); + case CLS_DWARF_ARM64_LR: + return FIRCLSThreadContextGetLinkRegister(registers); + case CLS_DWARF_ARM64_SP: + return FIRCLSThreadContextGetStackPointer(registers); + default: + break; + } + + FIRCLSSDKLog("Error: Unrecognized get register number %llu\n", num); + + return 0; +} + +bool FIRCLSDwarfUnwindSetRegisterValue(FIRCLSThreadContext* registers, + uint64_t num, + uintptr_t value) { + switch (num) { + case CLS_DWARF_ARM64_X0: + registers->__ss.__x[0] = value; + return true; + case CLS_DWARF_ARM64_X1: + registers->__ss.__x[1] = value; + return true; + case CLS_DWARF_ARM64_X2: + registers->__ss.__x[2] = value; + return true; + case CLS_DWARF_ARM64_X3: + registers->__ss.__x[3] = value; + return true; + case CLS_DWARF_ARM64_X4: + registers->__ss.__x[4] = value; + return true; + case CLS_DWARF_ARM64_X5: + registers->__ss.__x[5] = value; + return true; + case CLS_DWARF_ARM64_X6: + registers->__ss.__x[6] = value; + return true; + case CLS_DWARF_ARM64_X7: + registers->__ss.__x[7] = value; + return true; + case CLS_DWARF_ARM64_X8: + registers->__ss.__x[8] = value; + return true; + case CLS_DWARF_ARM64_X9: + registers->__ss.__x[9] = value; + return true; + case CLS_DWARF_ARM64_X10: + registers->__ss.__x[10] = value; + return true; + case CLS_DWARF_ARM64_X11: + registers->__ss.__x[11] = value; + return true; + case CLS_DWARF_ARM64_X12: + registers->__ss.__x[12] = value; + return true; + case CLS_DWARF_ARM64_X13: + registers->__ss.__x[13] = value; + return true; + case CLS_DWARF_ARM64_X14: + registers->__ss.__x[14] = value; + return true; + case CLS_DWARF_ARM64_X15: + registers->__ss.__x[15] = value; + return true; + case CLS_DWARF_ARM64_X16: + registers->__ss.__x[16] = value; + return true; + case CLS_DWARF_ARM64_X17: + registers->__ss.__x[17] = value; + return true; + case CLS_DWARF_ARM64_X18: + registers->__ss.__x[18] = value; + return true; + case CLS_DWARF_ARM64_X19: + registers->__ss.__x[19] = value; + return true; + case CLS_DWARF_ARM64_X20: + registers->__ss.__x[20] = value; + return true; + case CLS_DWARF_ARM64_X21: + registers->__ss.__x[21] = value; + return true; + case CLS_DWARF_ARM64_X22: + registers->__ss.__x[22] = value; + return true; + case CLS_DWARF_ARM64_X23: + registers->__ss.__x[23] = value; + return true; + case CLS_DWARF_ARM64_X24: + registers->__ss.__x[24] = value; + return true; + case CLS_DWARF_ARM64_X25: + registers->__ss.__x[25] = value; + return true; + case CLS_DWARF_ARM64_X26: + registers->__ss.__x[26] = value; + return true; + case CLS_DWARF_ARM64_X27: + registers->__ss.__x[27] = value; + return true; + case CLS_DWARF_ARM64_X28: + registers->__ss.__x[28] = value; + return true; + case CLS_DWARF_ARM64_FP: + FIRCLSThreadContextSetFramePointer(registers, value); + return true; + case CLS_DWARF_ARM64_SP: + FIRCLSThreadContextSetStackPointer(registers, value); + return true; + case CLS_DWARF_ARM64_LR: + // Here's what's going on. For x86, the "return register" is virtual. The architecture + // doesn't actually have one, but DWARF does have the concept. So, when the system + // tries to set the return register, we set the PC. You can see this behavior + // in the FIRCLSDwarfUnwindSetRegisterValue implemenation for that architecture. In the + // case of ARM64, the register is real. So, we have to be extra careful to make sure + // we update the PC here. Otherwise, when a DWARF unwind completes, it won't have + // changed the PC to the right value. + FIRCLSThreadContextSetLinkRegister(registers, value); + FIRCLSThreadContextSetPC(registers, value); + return true; + default: + break; + } + + FIRCLSSDKLog("Unrecognized set register number %llu\n", num); + + return false; +} +#endif + +#else +INJECT_STRIP_SYMBOL(unwind_arm) +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_x86.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_x86.c new file mode 100644 index 000000000..cc0655a92 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_x86.c @@ -0,0 +1,537 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSUnwind_x86.h" +#include "FIRCLSCompactUnwind_Private.h" +#include "FIRCLSDefines.h" +#include "FIRCLSDwarfUnwind.h" +#include "FIRCLSFeatures.h" +#include "FIRCLSUnwind.h" +#include "FIRCLSUnwind_arch.h" +#include "FIRCLSUtility.h" + +#if CLS_CPU_X86 + +static bool FIRCLSCompactUnwindBPFrame(compact_unwind_encoding_t encoding, + FIRCLSThreadContext* registers); +static bool FIRCLSCompactUnwindFrameless(compact_unwind_encoding_t encoding, + FIRCLSThreadContext* registers, + uintptr_t functionStart, + bool indirect); + +#if CLS_COMPACT_UNWINDING_SUPPORTED +bool FIRCLSCompactUnwindComputeRegisters(FIRCLSCompactUnwindContext* context, + FIRCLSCompactUnwindResult* result, + FIRCLSThreadContext* registers) { + if (!FIRCLSIsValidPointer(context) || !FIRCLSIsValidPointer(result) || + !FIRCLSIsValidPointer(registers)) { + FIRCLSSDKLogError("invalid inputs\n"); + return false; + } + + FIRCLSSDKLogDebug("Computing registers for encoding %x\n", result->encoding); + + switch (result->encoding & CLS_X86_MODE_MASK) { + case CLS_X86_MODE_BP_FRAME: + return FIRCLSCompactUnwindBPFrame(result->encoding, registers); + case CLS_X86_MODE_STACK_IMMD: + return FIRCLSCompactUnwindFrameless(result->encoding, registers, result->functionStart, + false); + case CLS_X86_MODE_STACK_IND: + return FIRCLSCompactUnwindFrameless(result->encoding, registers, result->functionStart, true); + case CLS_X86_MODE_DWARF: + return FIRCLSCompactUnwindDwarfFrame(context, result->encoding & CLS_X86_DWARF_SECTION_OFFSET, + registers); + default: + FIRCLSSDKLogError("Invalid encoding %x\n", result->encoding); + break; + } + + return false; +} +#endif + +static bool FIRCLSCompactUnwindBPFrame(compact_unwind_encoding_t encoding, + FIRCLSThreadContext* registers) { + // this is the plain-vanilla frame pointer process + + // uint32_t offset = GET_BITS_WITH_MASK(encoding, UNWIND_X86_EBP_FRAME_OFFSET); + // uint32_t locations = GET_BITS_WITH_MASK(encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + // TODO: pretty sure we do need to restore registers here, so that if a subsequent frame needs + // these results, they will be correct + + // Checkout CompactUnwinder.hpp in libunwind for how to do this. Since we don't make use of any of + // those registers for a stacktrace only, there's nothing we need do with them. + + // read the values from the stack + const uintptr_t framePointer = FIRCLSThreadContextGetFramePointer(registers); + uintptr_t stack[2]; + + if (!FIRCLSReadMemory((vm_address_t)framePointer, stack, sizeof(stack))) { + // unable to read the first stack frame + FIRCLSSDKLog("Error: failed to read memory at address %p\n", (void*)framePointer); + return false; + } + + if (!FIRCLSThreadContextSetPC(registers, stack[1])) { + return false; + } + + if (!FIRCLSThreadContextSetFramePointer(registers, stack[0])) { + return false; + } + + if (!FIRCLSThreadContextSetStackPointer(registers, + FIRCLSUnwindStackPointerFromFramePointer(framePointer))) { + return false; + } + + return true; +} + +bool FIRCLSUnwindWithStackScanning(FIRCLSThreadContext* registers) { + vm_address_t start = (vm_address_t)FIRCLSThreadContextGetStackPointer(registers); + vm_address_t end = (vm_address_t)FIRCLSThreadContextGetFramePointer(registers); + + uintptr_t newPC = 0; + + if (!FIRCLSUnwindFirstExecutableAddress(start, end, (vm_address_t*)&newPC)) { + return false; + } + + return FIRCLSThreadContextSetPC(registers, newPC); +} + +bool FIRCLSUnwindWithFramePointer(FIRCLSThreadContext* registers, bool allowScanning) { + // Here's an interesting case. We've just processed the first frame, and it did + // not have any unwind info. If that first function did not allocate + // a stack frame, we'll "skip" the caller. This might sound unlikely, but it actually + // happens a lot in practice. + + // Sooo, one thing we can do is try to stack the stack for things that look like return + // addresses. Normally, this technique will hit many false positives. But, if we do it + // only for the second frame, and only when we don't have other unwind info available. + + if (allowScanning) { + FIRCLSSDKLogInfo("Attempting stack scan\n"); + if (FIRCLSUnwindWithStackScanning(registers)) { + FIRCLSSDKLogInfo("Stack scan successful\n"); + return true; + } + } + + // If we ever do anything else with the encoding, we need to be sure + // to set it up right. + return FIRCLSCompactUnwindBPFrame(CLS_X86_MODE_BP_FRAME, registers); +} + +uintptr_t FIRCLSUnwindStackPointerFromFramePointer(uintptr_t framePtr) { + // the stack pointer is the frame pointer plus the two saved pointers for the frame + return framePtr + 2 * sizeof(void*); +} + +#if CLS_COMPACT_UNWINDING_SUPPORTED || CLS_DWARF_UNWINDING_SUPPORTED +uintptr_t FIRCLSDwarfUnwindGetRegisterValue(const FIRCLSThreadContext* registers, uint64_t num) { + switch (num) { +#if CLS_CPU_X86_64 + case CLS_DWARF_X86_64_RAX: + return registers->__ss.__rax; + case CLS_DWARF_X86_64_RDX: + return registers->__ss.__rdx; + case CLS_DWARF_X86_64_RCX: + return registers->__ss.__rcx; + case CLS_DWARF_X86_64_RBX: + return registers->__ss.__rbx; + case CLS_DWARF_X86_64_RSI: + return registers->__ss.__rsi; + case CLS_DWARF_X86_64_RDI: + return registers->__ss.__rdi; + case CLS_DWARF_X86_64_RBP: + return registers->__ss.__rbp; + case CLS_DWARF_X86_64_RSP: + return registers->__ss.__rsp; + case CLS_DWARF_X86_64_R8: + return registers->__ss.__r8; + case CLS_DWARF_X86_64_R9: + return registers->__ss.__r9; + case CLS_DWARF_X86_64_R10: + return registers->__ss.__r10; + case CLS_DWARF_X86_64_R11: + return registers->__ss.__r11; + case CLS_DWARF_X86_64_R12: + return registers->__ss.__r12; + case CLS_DWARF_X86_64_R13: + return registers->__ss.__r13; + case CLS_DWARF_X86_64_R14: + return registers->__ss.__r14; + case CLS_DWARF_X86_64_R15: + return registers->__ss.__r15; + case CLS_DWARF_X86_64_RET_ADDR: + return registers->__ss.__rip; +#elif CLS_CPU_I386 + case CLS_DWARF_X86_EAX: + return registers->__ss.__eax; + case CLS_DWARF_X86_ECX: + return registers->__ss.__ecx; + case CLS_DWARF_X86_EDX: + return registers->__ss.__edx; + case CLS_DWARF_X86_EBX: + return registers->__ss.__ebx; + case CLS_DWARF_X86_EBP: + return registers->__ss.__ebp; + case CLS_DWARF_X86_ESP: + return registers->__ss.__esp; + case CLS_DWARF_X86_ESI: + return registers->__ss.__esi; + case CLS_DWARF_X86_EDI: + return registers->__ss.__edi; + case CLS_DWARF_X86_RET_ADDR: + return registers->__ss.__eip; +#endif + default: + break; + } + + FIRCLSSDKLog("Error: Unrecognized get register number %llu\n", num); + + return 0; +} + +bool FIRCLSDwarfUnwindSetRegisterValue(FIRCLSThreadContext* registers, + uint64_t num, + uintptr_t value) { + switch (num) { +#if CLS_CPU_X86_64 + case CLS_DWARF_X86_64_RAX: + registers->__ss.__rax = value; + return true; + case CLS_DWARF_X86_64_RDX: + registers->__ss.__rdx = value; + return true; + case CLS_DWARF_X86_64_RCX: + registers->__ss.__rcx = value; + return true; + case CLS_DWARF_X86_64_RBX: + registers->__ss.__rbx = value; + return true; + case CLS_DWARF_X86_64_RSI: + registers->__ss.__rsi = value; + return true; + case CLS_DWARF_X86_64_RDI: + registers->__ss.__rdi = value; + return true; + case CLS_DWARF_X86_64_RBP: + registers->__ss.__rbp = value; + return true; + case CLS_DWARF_X86_64_RSP: + registers->__ss.__rsp = value; + return true; + case CLS_DWARF_X86_64_R8: + registers->__ss.__r8 = value; + return true; + case CLS_DWARF_X86_64_R9: + registers->__ss.__r9 = value; + return true; + case CLS_DWARF_X86_64_R10: + registers->__ss.__r10 = value; + return true; + case CLS_DWARF_X86_64_R11: + registers->__ss.__r11 = value; + return true; + case CLS_DWARF_X86_64_R12: + registers->__ss.__r12 = value; + return true; + case CLS_DWARF_X86_64_R13: + registers->__ss.__r13 = value; + return true; + case CLS_DWARF_X86_64_R14: + registers->__ss.__r14 = value; + return true; + case CLS_DWARF_X86_64_R15: + registers->__ss.__r15 = value; + return true; + case CLS_DWARF_X86_64_RET_ADDR: + registers->__ss.__rip = value; + return true; +#elif CLS_CPU_I386 + case CLS_DWARF_X86_EAX: + registers->__ss.__eax = value; + return true; + case CLS_DWARF_X86_ECX: + registers->__ss.__ecx = value; + return true; + case CLS_DWARF_X86_EDX: + registers->__ss.__edx = value; + return true; + case CLS_DWARF_X86_EBX: + registers->__ss.__ebx = value; + return true; + case CLS_DWARF_X86_EBP: + registers->__ss.__ebp = value; + return true; + case CLS_DWARF_X86_ESP: + registers->__ss.__esp = value; + return true; + case CLS_DWARF_X86_ESI: + registers->__ss.__esi = value; + return true; + case CLS_DWARF_X86_EDI: + registers->__ss.__edi = value; + return true; + case CLS_DWARF_X86_RET_ADDR: + registers->__ss.__eip = value; + return true; +#endif + default: + break; + } + + FIRCLSSDKLog("Unrecognized set register number %llu\n", num); + + return false; +} +#endif + +#if CLS_COMPACT_UNWINDING_SUPPORTED +bool FIRCLSCompactUnwindComputeStackSize(const compact_unwind_encoding_t encoding, + const uintptr_t functionStart, + const bool indirect, + uint32_t* const stackSize) { + if (!FIRCLSIsValidPointer(stackSize)) { + FIRCLSSDKLog("Error: invalid inputs\n"); + return false; + } + + const uint32_t stackSizeEncoded = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_SIZE); + + if (!indirect) { + *stackSize = stackSizeEncoded * sizeof(void*); + return true; + } + + const vm_address_t sublAddress = functionStart + stackSizeEncoded; + uint32_t sublValue = 0; + + if (!FIRCLSReadMemory(sublAddress, &sublValue, sizeof(uint32_t))) { + FIRCLSSDKLog("Error: unable to read subl value\n"); + return false; + } + + const uint32_t stackAdjust = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_ADJUST); + + *stackSize = sublValue + stackAdjust * sizeof(void*); + + return true; +} + +bool FIRCLSCompactUnwindDecompressPermutation(const compact_unwind_encoding_t encoding, + uintptr_t permutatedRegisters[const static 6]) { + const uint32_t regCount = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_REG_PERMUTATION); + + switch (regCount) { + case 6: + permutatedRegisters[0] = permutation / 120; + permutation -= (permutatedRegisters[0] * 120); + permutatedRegisters[1] = permutation / 24; + permutation -= (permutatedRegisters[1] * 24); + permutatedRegisters[2] = permutation / 6; + permutation -= (permutatedRegisters[2] * 6); + permutatedRegisters[3] = permutation / 2; + permutation -= (permutatedRegisters[3] * 2); + permutatedRegisters[4] = permutation; + permutatedRegisters[5] = 0; + break; + case 5: + permutatedRegisters[0] = permutation / 120; + permutation -= (permutatedRegisters[0] * 120); + permutatedRegisters[1] = permutation / 24; + permutation -= (permutatedRegisters[1] * 24); + permutatedRegisters[2] = permutation / 6; + permutation -= (permutatedRegisters[2] * 6); + permutatedRegisters[3] = permutation / 2; + permutation -= (permutatedRegisters[3] * 2); + permutatedRegisters[4] = permutation; + break; + case 4: + permutatedRegisters[0] = permutation / 60; + permutation -= (permutatedRegisters[0] * 60); + permutatedRegisters[1] = permutation / 12; + permutation -= (permutatedRegisters[1] * 12); + permutatedRegisters[2] = permutation / 3; + permutation -= (permutatedRegisters[2] * 3); + permutatedRegisters[3] = permutation; + break; + case 3: + permutatedRegisters[0] = permutation / 20; + permutation -= (permutatedRegisters[0] * 20); + permutatedRegisters[1] = permutation / 4; + permutation -= (permutatedRegisters[1] * 4); + permutatedRegisters[2] = permutation; + break; + case 2: + permutatedRegisters[0] = permutation / 5; + permutation -= (permutatedRegisters[0] * 5); + permutatedRegisters[1] = permutation; + break; + case 1: + permutatedRegisters[0] = permutation; + break; + case 0: + break; + default: + FIRCLSSDKLog("Error: unhandled number of register permutations for encoding %x\n", encoding); + return false; + } + + return true; +} + +bool FIRCLSCompactUnwindRemapRegisters(const compact_unwind_encoding_t encoding, + uintptr_t permutatedRegisters[const static 6], + uintptr_t savedRegisters[const static 6]) { + const uint32_t regCount = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_REG_COUNT); + + if (regCount > 6) { + FIRCLSSDKLog("Error: invalid register number count %d\n", regCount); + return false; + } + + // Re-number the registers + + // You are probably wondering, what the hell is this algorithm even doing? It is + // taken from libunwind's implemenation that does the same thing. + bool used[7] = {false, false, false, false, false, false, false}; + for (uint32_t i = 0; i < regCount; ++i) { + int renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permutatedRegisters[i]) { + savedRegisters[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + + return true; +} + +bool FIRCLSCompactUnwindRestoreRegisters(compact_unwind_encoding_t encoding, + FIRCLSThreadContext* registers, + uint32_t stackSize, + const uintptr_t savedRegisters[const static 6], + uintptr_t* address) { + if (!FIRCLSIsValidPointer(registers) || !FIRCLSIsValidPointer(address)) { + FIRCLSSDKLog("Error: invalid inputs\n"); + return false; + } + + const uint32_t regCount = GET_BITS_WITH_MASK(encoding, CLS_X86_FRAMELESS_STACK_REG_COUNT); + + // compute initial address of saved registers + *address = FIRCLSThreadContextGetStackPointer(registers) + stackSize - sizeof(void*) - + sizeof(void*) * regCount; + uintptr_t value = 0; + + for (uint32_t i = 0; i < regCount; ++i) { + value = 0; + + switch (savedRegisters[i]) { + case CLS_X86_REG_RBP: + if (!FIRCLSReadMemory((vm_address_t)*address, (void*)&value, sizeof(uintptr_t))) { + FIRCLSSDKLog("Error: unable to read memory to set register\n"); + return false; + } + + if (!FIRCLSThreadContextSetFramePointer(registers, value)) { + FIRCLSSDKLog("Error: unable to set FP\n"); + return false; + } + break; + default: + // here, we are restoring a register we don't need for unwinding + FIRCLSSDKLog("Error: skipping a restore of register %d at %p\n", (int)savedRegisters[i], + (void*)*address); + break; + } + + *address += sizeof(void*); + } + + return true; +} + +static bool FIRCLSCompactUnwindFrameless(compact_unwind_encoding_t encoding, + FIRCLSThreadContext* registers, + uintptr_t functionStart, + bool indirect) { + FIRCLSSDKLog("Frameless unwind encountered with encoding %x\n", encoding); + + uint32_t stackSize = 0; + if (!FIRCLSCompactUnwindComputeStackSize(encoding, functionStart, indirect, &stackSize)) { + FIRCLSSDKLog("Error: unable to compute stack size for encoding %x\n", encoding); + return false; + } + + uintptr_t permutatedRegisters[6]; + + memset(permutatedRegisters, 0, sizeof(permutatedRegisters)); + if (!FIRCLSCompactUnwindDecompressPermutation(encoding, permutatedRegisters)) { + FIRCLSSDKLog("Error: unable to decompress registers %x\n", encoding); + return false; + } + + uintptr_t savedRegisters[6]; + + memset(savedRegisters, 0, sizeof(savedRegisters)); + if (!FIRCLSCompactUnwindRemapRegisters(encoding, permutatedRegisters, savedRegisters)) { + FIRCLSSDKLog("Error: unable to remap registers %x\n", encoding); + return false; + } + + uintptr_t address = 0; + + if (!FIRCLSCompactUnwindRestoreRegisters(encoding, registers, stackSize, savedRegisters, + &address)) { + FIRCLSSDKLog("Error: unable to restore registers\n"); + return false; + } + + FIRCLSSDKLog("SP is %p and we are reading %p\n", + (void*)FIRCLSThreadContextGetStackPointer(registers), (void*)address); + // read the value from the stack, now that we know the address to read + uintptr_t value = 0; + if (!FIRCLSReadMemory((vm_address_t)address, (void*)&value, sizeof(uintptr_t))) { + FIRCLSSDKLog("Error: unable to read memory to set register\n"); + return false; + } + + FIRCLSSDKLog("Read PC to be %p\n", (void*)value); + if (!FIRCLSIsValidPointer(value)) { + FIRCLSSDKLog("Error: computed PC is invalid\n"); + return false; + } + + return FIRCLSThreadContextSetPC(registers, value) && + FIRCLSThreadContextSetStackPointer(registers, address + sizeof(void*)); +} +#endif + +#else +INJECT_STRIP_SYMBOL(unwind_x86) +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_x86.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_x86.h new file mode 100644 index 000000000..7c8010ec0 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Unwind/FIRCLSUnwind_x86.h @@ -0,0 +1,76 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "FIRCLSCompactUnwind.h" +#include "FIRCLSFeatures.h" + +// Add some abstraction to compact unwinding, because compact +// unwinding is nearly identical between 32 and 64 bit +#if CLS_CPU_X86_64 + +#define CLS_X86_MODE_MASK UNWIND_X86_64_MODE_MASK +#define CLS_X86_MODE_BP_FRAME UNWIND_X86_64_MODE_RBP_FRAME +#define CLS_X86_MODE_STACK_IMMD UNWIND_X86_64_MODE_STACK_IMMD +#define CLS_X86_MODE_STACK_IND UNWIND_X86_64_MODE_STACK_IND +#define CLS_X86_MODE_DWARF UNWIND_X86_64_MODE_DWARF + +#define CLS_X86_BP_FRAME_REGISTERS UNWIND_X86_64_RBP_FRAME_REGISTERS +#define CLS_X86_BP_FRAME_OFFSET UNWIND_X86_64_RBP_FRAME_OFFSET + +#define CLS_X86_FRAMELESS_STACK_SIZE UNWIND_X86_64_FRAMELESS_STACK_SIZE +#define CLS_X86_FRAMELESS_STACK_ADJUST UNWIND_X86_64_FRAMELESS_STACK_ADJUST +#define CLS_X86_FRAMELESS_STACK_REG_COUNT UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT +#define CLS_X86_FRAMELESS_STACK_REG_PERMUTATION UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION + +#define CLS_X86_DWARF_SECTION_OFFSET UNWIND_X86_64_DWARF_SECTION_OFFSET + +#define CLS_X86_REG_RBP UNWIND_X86_64_REG_RBP + +#else + +#define CLS_X86_MODE_MASK UNWIND_X86_MODE_MASK +#define CLS_X86_MODE_BP_FRAME UNWIND_X86_MODE_EBP_FRAME +#define CLS_X86_MODE_STACK_IMMD UNWIND_X86_MODE_STACK_IMMD +#define CLS_X86_MODE_STACK_IND UNWIND_X86_MODE_STACK_IND +#define CLS_X86_MODE_DWARF UNWIND_X86_MODE_DWARF + +#define CLS_X86_BP_FRAME_REGISTERS UNWIND_X86_RBP_FRAME_REGISTERS +#define CLS_X86_BP_FRAME_OFFSET UNWIND_X86_RBP_FRAME_OFFSET + +#define CLS_X86_FRAMELESS_STACK_SIZE UNWIND_X86_FRAMELESS_STACK_SIZE +#define CLS_X86_FRAMELESS_STACK_ADJUST UNWIND_X86_FRAMELESS_STACK_ADJUST +#define CLS_X86_FRAMELESS_STACK_REG_COUNT UNWIND_X86_FRAMELESS_STACK_REG_COUNT +#define CLS_X86_FRAMELESS_STACK_REG_PERMUTATION UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION + +#define CLS_X86_DWARF_SECTION_OFFSET UNWIND_X86_DWARF_SECTION_OFFSET + +#define CLS_X86_REG_RBP UNWIND_X86_REG_EBP + +#endif + +#if CLS_COMPACT_UNWINDING_SUPPORTED +bool FIRCLSCompactUnwindComputeStackSize(const compact_unwind_encoding_t encoding, + const uintptr_t functionStart, + const bool indirect, + uint32_t* const stackSize); +bool FIRCLSCompactUnwindDecompressPermutation(const compact_unwind_encoding_t encoding, + uintptr_t permutatedRegisters[const static 6]); +bool FIRCLSCompactUnwindRestoreRegisters(compact_unwind_encoding_t encoding, + FIRCLSThreadContext* registers, + uint32_t stackSize, + const uintptr_t savedRegisters[const static 6], + uintptr_t* address); +#endif diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/LICENSE b/ios/Pods/FirebaseCrashlytics/Crashlytics/LICENSE similarity index 88% rename from ios/Pods/FirebaseCoreDiagnosticsInterop/LICENSE rename to ios/Pods/FirebaseCrashlytics/Crashlytics/LICENSE index d64569567..925bc57fc 100644 --- a/ios/Pods/FirebaseCoreDiagnosticsInterop/LICENSE +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/LICENSE @@ -200,3 +200,31 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +================================================================================ + +The following copyright from Hewlett-Packard Development Company, L.P. +applies to the dwarf.h file in third_party/libunwind + + libunwind - a platform-independent unwind library + Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Protogen/nanopb/crashlytics.nanopb.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Protogen/nanopb/crashlytics.nanopb.c new file mode 100644 index 000000000..d2e9d0455 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Protogen/nanopb/crashlytics.nanopb.c @@ -0,0 +1,75 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.9.5 */ + +#include "crashlytics.nanopb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_crashlytics_Report_fields[8] = { + PB_FIELD( 1, BYTES , SINGULAR, POINTER , FIRST, google_crashlytics_Report, sdk_version, sdk_version, 0), + PB_FIELD( 3, BYTES , SINGULAR, POINTER , OTHER, google_crashlytics_Report, gmp_app_id, sdk_version, 0), + PB_FIELD( 4, UENUM , SINGULAR, STATIC , OTHER, google_crashlytics_Report, platform, gmp_app_id, 0), + PB_FIELD( 5, BYTES , SINGULAR, POINTER , OTHER, google_crashlytics_Report, installation_uuid, platform, 0), + PB_FIELD( 6, BYTES , SINGULAR, POINTER , OTHER, google_crashlytics_Report, build_version, installation_uuid, 0), + PB_FIELD( 7, BYTES , SINGULAR, POINTER , OTHER, google_crashlytics_Report, display_version, build_version, 0), + PB_FIELD( 10, MESSAGE , SINGULAR, STATIC , OTHER, google_crashlytics_Report, apple_payload, display_version, &google_crashlytics_FilesPayload_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_crashlytics_FilesPayload_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, POINTER , FIRST, google_crashlytics_FilesPayload, files, files, &google_crashlytics_FilesPayload_File_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_crashlytics_FilesPayload_File_fields[3] = { + PB_FIELD( 1, BYTES , SINGULAR, POINTER , FIRST, google_crashlytics_FilesPayload_File, filename, filename, 0), + PB_FIELD( 2, BYTES , SINGULAR, POINTER , OTHER, google_crashlytics_FilesPayload_File, contents, filename, 0), + PB_LAST_FIELD +}; + + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_crashlytics_Report, apple_payload) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_crashlytics_Report_google_crashlytics_FilesPayload_google_crashlytics_FilesPayload_File) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_crashlytics_Report, apple_payload) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_crashlytics_Report_google_crashlytics_FilesPayload_google_crashlytics_FilesPayload_File) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Protogen/nanopb/crashlytics.nanopb.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Protogen/nanopb/crashlytics.nanopb.h new file mode 100644 index 000000000..c873fbcd8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Protogen/nanopb/crashlytics.nanopb.h @@ -0,0 +1,107 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.9.5 */ + +#ifndef PB_GOOGLE_CRASHLYTICS_CRASHLYTICS_NANOPB_H_INCLUDED +#define PB_GOOGLE_CRASHLYTICS_CRASHLYTICS_NANOPB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + +/* Enum definitions */ +typedef enum _google_crashlytics_Platforms { + google_crashlytics_Platforms_UNKNOWN_PLATFORM = 0, + google_crashlytics_Platforms_IOS = 1, + google_crashlytics_Platforms_TVOS = 2, + google_crashlytics_Platforms_MAC_OS_X = 5 +} google_crashlytics_Platforms; +#define _google_crashlytics_Platforms_MIN google_crashlytics_Platforms_UNKNOWN_PLATFORM +#define _google_crashlytics_Platforms_MAX google_crashlytics_Platforms_MAC_OS_X +#define _google_crashlytics_Platforms_ARRAYSIZE ((google_crashlytics_Platforms)(google_crashlytics_Platforms_MAC_OS_X+1)) + +/* Struct definitions */ +typedef struct _google_crashlytics_FilesPayload { + pb_size_t files_count; + struct _google_crashlytics_FilesPayload_File *files; +/* @@protoc_insertion_point(struct:google_crashlytics_FilesPayload) */ +} google_crashlytics_FilesPayload; + +typedef struct _google_crashlytics_FilesPayload_File { + pb_bytes_array_t *filename; + pb_bytes_array_t *contents; +/* @@protoc_insertion_point(struct:google_crashlytics_FilesPayload_File) */ +} google_crashlytics_FilesPayload_File; + +typedef struct _google_crashlytics_Report { + pb_bytes_array_t *sdk_version; + pb_bytes_array_t *gmp_app_id; + google_crashlytics_Platforms platform; + pb_bytes_array_t *installation_uuid; + pb_bytes_array_t *build_version; + pb_bytes_array_t *display_version; + google_crashlytics_FilesPayload apple_payload; +/* @@protoc_insertion_point(struct:google_crashlytics_Report) */ +} google_crashlytics_Report; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_crashlytics_Report_init_default {NULL, NULL, _google_crashlytics_Platforms_MIN, NULL, NULL, NULL, google_crashlytics_FilesPayload_init_default} +#define google_crashlytics_FilesPayload_init_default {0, NULL} +#define google_crashlytics_FilesPayload_File_init_default {NULL, NULL} +#define google_crashlytics_Report_init_zero {NULL, NULL, _google_crashlytics_Platforms_MIN, NULL, NULL, NULL, google_crashlytics_FilesPayload_init_zero} +#define google_crashlytics_FilesPayload_init_zero {0, NULL} +#define google_crashlytics_FilesPayload_File_init_zero {NULL, NULL} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_crashlytics_FilesPayload_files_tag 1 +#define google_crashlytics_FilesPayload_File_filename_tag 1 +#define google_crashlytics_FilesPayload_File_contents_tag 2 +#define google_crashlytics_Report_sdk_version_tag 1 +#define google_crashlytics_Report_gmp_app_id_tag 3 +#define google_crashlytics_Report_platform_tag 4 +#define google_crashlytics_Report_installation_uuid_tag 5 +#define google_crashlytics_Report_build_version_tag 6 +#define google_crashlytics_Report_display_version_tag 7 +#define google_crashlytics_Report_apple_payload_tag 10 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_crashlytics_Report_fields[8]; +extern const pb_field_t google_crashlytics_FilesPayload_fields[2]; +extern const pb_field_t google_crashlytics_FilesPayload_File_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* google_crashlytics_Report_size depends on runtime parameters */ +/* google_crashlytics_FilesPayload_size depends on runtime parameters */ +/* google_crashlytics_FilesPayload_File_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define CRASHLYTICS_MESSAGES \ + + +#endif + +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/README.md b/ios/Pods/FirebaseCrashlytics/Crashlytics/README.md new file mode 100644 index 000000000..55d36e659 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/README.md @@ -0,0 +1,39 @@ +# Firebase Crashlytics SDK + +## Development + +Follow the subsequent instructions to develop, debug, unit test, and +integration test FirebaseCrashlytics: + +### Prereqs + +- At least CocoaPods 1.6.0 +- Install [cocoapods-generate](https://github.com/square/cocoapods-generate) +- For nanopb and GDT: + - `brew install protobuf nanopb-generator` + - `easy_install protobuf python` + +### To Develop + +- Run `Crashlytics/generate_project.sh` +- `open gen/FirebaseCrashlytics/FirebaseCrashlytics.xcworkspace` + +You're now in an Xcode workspace generate for building, debugging and +testing the FirebaseCrashlytics CocoaPod. + +### Running Unit Tests + +Open the generated workspace, choose the FirebaseCrashlytics-Unit-unit scheme and press Command-u. + +### Changing crash report uploads (using GDT) + +#### Update report proto + +If the crash report proto needs to be updated, follow these instructions: + +- Update `ProtoSupport/Protos/crashlytics.proto` with the new changes +- Depending on the type of fields added/removed, also update `ProtoSupport/Protos/crashlytics.options`. + `CALLBACK` type fields in crashlytics.nanopb.c needs to be changed to `POINTER` + (through the options file). Known field types that require an entry in crashlytics.options are + `strings`, `repeated` and `bytes`. +- Run `generate_project.sh` to update the nanopb .c/.h files. \ No newline at end of file diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSByteUtility.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSByteUtility.h new file mode 100644 index 000000000..ca2abb81d --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSByteUtility.h @@ -0,0 +1,41 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + * Returns a SHA1 Hash of the input NSData + */ +NSString *FIRCLSHashNSData(NSData *data); +/** + * Returns a SHA256 Hash of the input NSData + */ +NSString *FIRCLS256HashNSData(NSData *data); +/** + * Returns a SHA1 Hash of the input bytes + */ +NSString *FIRCLSHashBytes(const void *bytes, size_t length); +/** + * Populates a Hex value conversion of value into outputBuffer. + * If value is nil, then outputBuffer is not modified. + */ +void FIRCLSSafeHexToString(const uint8_t *value, size_t length, char *outputBuffer); + +/** + * Iterates through the raw bytes of NSData in a way that is similar to + * -[NSData enumerateByteRangesUsingBlock:], but is safe to call from older + * OSes that do not support it. + */ +void FIRCLSEnumerateByteRangesOfNSDataUsingBlock( + NSData *data, void (^block)(const void *bytes, NSRange byteRange, BOOL *stop)); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSByteUtility.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSByteUtility.m new file mode 100644 index 000000000..79f46f3f7 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSByteUtility.m @@ -0,0 +1,120 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSByteUtility.h" + +#import +#import + +#pragma mark Private functions + +static const char FIRCLSHexMap[] = "0123456789abcdef"; + +void FIRCLSHexFromByte(uint8_t c, char output[]) { + if (!output) { + return; + } + + output[0] = FIRCLSHexMap[c >> 4]; + output[1] = FIRCLSHexMap[c & 0x0f]; +} + +void FIRCLSSafeHexToString(const uint8_t *value, size_t length, char *outputBuffer) { + if (!outputBuffer) { + return; + } + + memset(outputBuffer, 0, (length * 2) + 1); + + if (!value) { + return; + } + + for (size_t i = 0; i < length; ++i) { + uint8_t c = value[i]; + + FIRCLSHexFromByte(c, &outputBuffer[i * 2]); + } +} + +NSString *FIRCLSNSDataPrettyDescription(NSData *data) { + NSString *string; + char *buffer; + size_t size; + NSUInteger length; + + // we need 2 hex char for every byte of data, plus one more spot for a + // null terminator + length = data.length; + size = (length * 2) + 1; + buffer = malloc(sizeof(char) * size); + + if (!buffer) { + return nil; + } + + FIRCLSSafeHexToString(data.bytes, length, buffer); + + string = [NSString stringWithUTF8String:buffer]; + + free(buffer); + + return string; +} + +#pragma mark Public functions + +NSString *FIRCLSHashBytes(const void *bytes, size_t length) { + uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0}; + CC_SHA1(bytes, (CC_LONG)length, digest); + + NSData *result = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]; + + return FIRCLSNSDataPrettyDescription(result); +} + +NSString *FIRCLSHashNSData(NSData *data) { + return FIRCLSHashBytes(data.bytes, data.length); +} + +NSString *FIRCLS256HashBytes(const void *bytes, size_t length) { + uint8_t digest[CC_SHA256_DIGEST_LENGTH] = {0}; + CC_SHA256(bytes, (CC_LONG)length, digest); + + NSData *result = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; + + return FIRCLSNSDataPrettyDescription(result); +} + +NSString *FIRCLS256HashNSData(NSData *data) { + return FIRCLS256HashBytes(data.bytes, data.length); +} + +void FIRCLSEnumerateByteRangesOfNSDataUsingBlock( + NSData *data, void (^block)(const void *bytes, NSRange byteRange, BOOL *stop)) { + if ([data respondsToSelector:@selector(enumerateByteRangesUsingBlock:)]) { + [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) { + block(bytes, byteRange, stop); + }]; + + return; + } + + // Fall back to the less-efficient mechanism for older OSes. Safe + // to ignore the return value of stop, since we'll only ever + // call this once anyways + BOOL stop = NO; + + block(data.bytes, NSMakeRange(0, data.length), &stop); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSConstants.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSConstants.h new file mode 100644 index 000000000..c17ee02c5 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSConstants.h @@ -0,0 +1,45 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +FOUNDATION_EXPORT NSString *const FIRCLSDeveloperToken; + +FOUNDATION_EXPORT NSString *const FIRCLSVersion; + +// User Messages +FOUNDATION_EXPORT NSString *const FIRCLSMissingConsumerKeyMsg; +FOUNDATION_EXPORT NSString *const FIRCLSMissingConsumerSecretMsg; + +// Exceptions +FOUNDATION_EXPORT NSString *const FIRCLSException; + +// Endpoints +FOUNDATION_EXPORT NSString *const FIRCLSSettingsEndpoint; +FOUNDATION_EXPORT NSString *const FIRCLSConfigureEndpoint; +FOUNDATION_EXPORT NSString *const FIRCLSReportsEndpoint; + +// Network requests +FOUNDATION_EXPORT NSString *const FIRCLSNetworkAccept; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkAcceptCharset; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkApplicationJson; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkAcceptLanguage; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkContentLanguage; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkCrashlyticsAPIClientDisplayVersion; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkCrashlyticsAPIClientId; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkCrashlyticsDeveloperToken; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkCrashlyticsGoogleAppId; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkCrashlyticsOrgId; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkUserAgent; +FOUNDATION_EXPORT NSString *const FIRCLSNetworkUTF8; diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSConstants.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSConstants.m new file mode 100644 index 000000000..f733f0de7 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSConstants.m @@ -0,0 +1,49 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSConstants.h" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +NSString* const FIRCLSDeveloperToken = @"77f0789d8e230eccdb4b99b82dccd78d47f9b604"; + +NSString* const FIRCLSVersion = @STR(DISPLAY_VERSION); + +// User Messages +NSString* const FIRCLSMissingConsumerKeyMsg = @"consumer key is nil or zero length"; +NSString* const FIRCLSMissingConsumerSecretMsg = @"consumer secret is nil or zero length"; + +// Exceptions +NSString* const FIRCLSException = @"FIRCLSException"; + +// Endpoints +NSString* const FIRCLSSettingsEndpoint = @"https://firebase-settings.crashlytics.com"; +NSString* const FIRCLSConfigureEndpoint = @"https://update.crashlytics.com"; +NSString* const FIRCLSReportsEndpoint = @"https://reports.crashlytics.com"; + +// Network requests +NSString* const FIRCLSNetworkAccept = @"Accept"; +NSString* const FIRCLSNetworkAcceptCharset = @"Accept-Charset"; +NSString* const FIRCLSNetworkApplicationJson = @"application/json"; +NSString* const FIRCLSNetworkAcceptLanguage = @"Accept-Language"; +NSString* const FIRCLSNetworkContentLanguage = @"Content-Language"; +NSString* const FIRCLSNetworkCrashlyticsAPIClientDisplayVersion = + @"X-Crashlytics-API-Client-Display-Version"; +NSString* const FIRCLSNetworkCrashlyticsAPIClientId = @"X-Crashlytics-API-Client-Id"; +NSString* const FIRCLSNetworkCrashlyticsDeveloperToken = @"X-Crashlytics-Developer-Token"; +NSString* const FIRCLSNetworkCrashlyticsGoogleAppId = @"X-Crashlytics-Google-App-Id"; +NSString* const FIRCLSNetworkCrashlyticsOrgId = @"X-Crashlytics-Org-Id"; +NSString* const FIRCLSNetworkUserAgent = @"User-Agent"; +NSString* const FIRCLSNetworkUTF8 = @"utf-8"; diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSFABHost.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSFABHost.h new file mode 100644 index 000000000..82c8fccfd --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSFABHost.h @@ -0,0 +1,35 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + * Returns the OS version of the host device + */ +NSOperatingSystemVersion FIRCLSHostGetOSVersion(void); + +/** + * Returns model info for the device on which app is running + */ +NSString *FIRCLSHostModelInfo(void); + +/** + * Returns a string representing the OS build + */ +NSString *FIRCLSHostOSBuildVersion(void); + +/** + * Returns a concatenated string of the OS version(majorVersion.minorVersion.patchVersion) + */ +NSString *FIRCLSHostOSDisplayVersion(void); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSFABHost.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSFABHost.m new file mode 100644 index 000000000..4c3206c1e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSFABHost.m @@ -0,0 +1,119 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSFABHost.h" + +#if TARGET_OS_WATCH +#import +#elif TARGET_OS_IPHONE +#import +#endif + +#include + +#define FIRCLS_HOST_SYSCTL_BUFFER_SIZE (128) + +#pragma mark - OS Versions + +#pragma mark Private + +static NSString *FIRCLSHostSysctlEntry(const char *sysctlKey) { + char buffer[FIRCLS_HOST_SYSCTL_BUFFER_SIZE]; + size_t bufferSize = FIRCLS_HOST_SYSCTL_BUFFER_SIZE; + if (sysctlbyname(sysctlKey, buffer, &bufferSize, NULL, 0) != 0) { + return nil; + } + return [NSString stringWithUTF8String:buffer]; +} + +#pragma mark Public + +NSOperatingSystemVersion FIRCLSHostGetOSVersion(void) { + // works on macos(10.10), ios(8.0), watchos(2.0), tvos(9.0) + if ([NSProcessInfo.processInfo respondsToSelector:@selector(operatingSystemVersion)]) { + return [NSProcessInfo.processInfo operatingSystemVersion]; + } + + NSOperatingSystemVersion version = {0, 0, 0}; + +#if TARGET_OS_IPHONE + +#if TARGET_OS_WATCH + NSString *versionString = [[WKInterfaceDevice currentDevice] systemVersion]; +#else + NSString *versionString = [[UIDevice currentDevice] systemVersion]; +#endif + + NSArray *parts = [versionString componentsSeparatedByString:@"."]; + + if (parts.count > 0) { + version.majorVersion = [[parts objectAtIndex:0] integerValue]; + } + + if ([parts count] > 1) { + version.minorVersion = [[parts objectAtIndex:1] integerValue]; + } + + if ([parts count] > 2) { + version.patchVersion = [[parts objectAtIndex:2] integerValue]; + } + +#endif + + return version; +} + +NSString *FIRCLSHostOSBuildVersion(void) { + return FIRCLSHostSysctlEntry("kern.osversion"); +} + +NSString *FIRCLSHostOSDisplayVersion(void) { + NSOperatingSystemVersion version = FIRCLSHostGetOSVersion(); + return [NSString stringWithFormat:@"%ld.%ld.%ld", (long)version.majorVersion, + (long)version.minorVersion, (long)version.patchVersion]; +} + +#pragma mark - Host Models + +#pragma mark Public + +NSString *FIRCLSHostModelInfo(void) { + NSString *model = nil; + +#if TARGET_OS_SIMULATOR +#if TARGET_OS_WATCH + model = @"watchOS Simulator"; +#elif TARGET_OS_TV + model = @"tvOS Simulator"; +#elif TARGET_OS_IPHONE + switch (UI_USER_INTERFACE_IDIOM()) { + case UIUserInterfaceIdiomPhone: + model = @"iOS Simulator (iPhone)"; + break; + case UIUserInterfaceIdiomPad: + model = @"iOS Simulator (iPad)"; + break; + default: + model = @"iOS Simulator (Unknown)"; + break; + } +#endif +#elif TARGET_OS_EMBEDDED + model = FIRCLSHostSysctlEntry("hw.machine"); +#else + model = FIRCLSHostSysctlEntry("hw.model"); +#endif + + return model; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSCodeMapping.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSCodeMapping.h new file mode 100644 index 000000000..ae80c46ad --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSCodeMapping.h @@ -0,0 +1,34 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +typedef enum { + FIRCLSCodeMappingSourceUnknown, + FIRCLSCodeMappingSourceBuild, + FIRCLSCodeSourceCache, + FIRCLSCodeSourceSpotlight +} FIRCLSCodeMappingSource; + +@interface FIRCLSCodeMapping : NSObject + ++ (instancetype)mappingWithURL:(NSURL*)URL; + +- (instancetype)initWithURL:(NSURL*)URL; + +@property(nonatomic, copy, readonly) NSURL* URL; +@property(nonatomic, assign) FIRCLSCodeMappingSource source; +@property(nonatomic, copy, readonly) NSString* sourceName; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSCodeMapping.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSCodeMapping.m new file mode 100644 index 000000000..c212ce7d1 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSCodeMapping.m @@ -0,0 +1,40 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSCodeMapping.h" + +@interface FIRCLSCodeMapping () { + FIRCLSCodeMappingSource _source; +} + +@end + +@implementation FIRCLSCodeMapping + ++ (instancetype)mappingWithURL:(NSURL*)URL { + return [[self alloc] initWithURL:URL]; +} + +- (instancetype)initWithURL:(NSURL*)URL { + self = [super init]; + if (!self) { + return nil; + } + + _URL = [URL copy]; + + return self; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachO.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachO.h new file mode 100644 index 000000000..59b460556 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachO.h @@ -0,0 +1,110 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include +#include + +#include + +struct FIRCLSMachOFile { + int fd; + size_t mappedSize; + void* mappedFile; +}; +typedef struct FIRCLSMachOFile* FIRCLSMachOFileRef; + +struct FIRCLSMachOSlice { + const void* startAddress; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; +}; +typedef struct FIRCLSMachOSlice* FIRCLSMachOSliceRef; + +typedef struct { + uint32_t major; + uint32_t minor; + uint32_t bugfix; +} FIRCLSMachOVersion; + +typedef struct { + uint64_t addr; + uint64_t size; + uint32_t offset; +} FIRCLSMachOSection; + +typedef struct { + char segname[16]; + uint64_t vmaddr; + uint64_t vmsize; +} FIRCLSMachOSegmentCommand; + +typedef void (^FIRCLSMachOSliceIterator)(FIRCLSMachOSliceRef slice); +typedef void (^FIRCLSMachOLoadCommandIterator)(uint32_t type, + uint32_t size, + const struct load_command* cmd); + +__BEGIN_DECLS + +bool FIRCLSMachOFileInitWithPath(FIRCLSMachOFileRef file, const char* path); +bool FIRCLSMachOFileInitWithCurrent(FIRCLSMachOFileRef file); +void FIRCLSMachOFileDestroy(FIRCLSMachOFileRef file); +void FIRCLSMachOFileEnumerateSlices(FIRCLSMachOFileRef file, FIRCLSMachOSliceIterator block); +struct FIRCLSMachOSlice FIRCLSMachOFileSliceWithArchitectureName(FIRCLSMachOFileRef file, + const char* name); + +void FIRCLSMachOEnumerateSlicesAtAddress(void* executableData, FIRCLSMachOSliceIterator block); +void FIRCLSMachOSliceEnumerateLoadCommands(FIRCLSMachOSliceRef slice, + FIRCLSMachOLoadCommandIterator block); +struct FIRCLSMachOSlice FIRCLSMachOSliceGetCurrent(void); +struct FIRCLSMachOSlice FIRCLSMachOSliceWithHeader(void* machHeader); + +const char* FIRCLSMachOSliceGetExecutablePath(FIRCLSMachOSliceRef slice); +const char* FIRCLSMachOSliceGetArchitectureName(FIRCLSMachOSliceRef slice); +bool FIRCLSMachOSliceIs64Bit(FIRCLSMachOSliceRef slice); +bool FIRCLSMachOSliceGetSectionByName(FIRCLSMachOSliceRef slice, + const char* segName, + const char* sectionName, + const void** ptr); +bool FIRCLSMachOSliceInitSectionByName(FIRCLSMachOSliceRef slice, + const char* segName, + const char* sectionName, + FIRCLSMachOSection* section); +void FIRCLSMachOSliceGetUnwindInformation(FIRCLSMachOSliceRef slice, + const void** ehFrame, + const void** unwindInfo); + +// load-command-specific calls for convenience + +// returns a pointer to the 16-byte UUID +uint8_t const* FIRCLSMachOGetUUID(const struct load_command* cmd); +const char* FIRCLSMachOGetDylibPath(const struct load_command* cmd); + +// return true if the header indicates the binary is encrypted +bool FIRCLSMachOGetEncrypted(const struct load_command* cmd); + +// SDK minimums +FIRCLSMachOVersion FIRCLSMachOGetMinimumOSVersion(const struct load_command* cmd); +FIRCLSMachOVersion FIRCLSMachOGetLinkedSDKVersion(const struct load_command* cmd); + +// Helpers +FIRCLSMachOSegmentCommand FIRCLSMachOGetSegmentCommand(const struct load_command* cmd); + +#ifdef __OBJC__ +NSString* FIRCLSMachONormalizeUUID(CFUUIDBytes* uuidBytes); +NSString* FIRCLSMachOFormatVersion(FIRCLSMachOVersion* version); +#endif +__END_DECLS diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachO.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachO.m new file mode 100644 index 000000000..386438c46 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachO.m @@ -0,0 +1,509 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FIRCLSMachO.h" + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include + +// This is defined in newer versions of iOS/macOS in usr/include/mach/machine.h +#define CLS_CPU_SUBTYPE_ARM64E ((cpu_subtype_t)2) + +static void FIRCLSMachOHeaderValues(FIRCLSMachOSliceRef slice, + const struct load_command** cmds, + uint32_t* cmdCount); +static bool FIRCLSMachOSliceIsValid(FIRCLSMachOSliceRef slice); + +bool FIRCLSMachOFileInitWithPath(FIRCLSMachOFileRef file, const char* path) { + struct stat statBuffer; + + if (!file || !path) { + return false; + } + + file->fd = 0; + file->mappedFile = NULL; + file->mappedSize = 0; + + file->fd = open(path, O_RDONLY); + if (file->fd < 0) { + // unable to open mach-o file + return false; + } + + if (fstat(file->fd, &statBuffer) == -1) { + close(file->fd); + return false; + } + + // We need some minimum size for this to even be a possible mach-o file. I believe + // its probably quite a bit bigger than this, but this at least covers something. + // We also need it to be a regular file. + file->mappedSize = (size_t)statBuffer.st_size; + if (statBuffer.st_size < 16 || !(statBuffer.st_mode & S_IFREG)) { + close(file->fd); + return false; + } + + // Map the file to memory. MAP_SHARED can potentially reduce the amount of actual private + // memory needed to do this mapping. Also, be sure to check for the correct failure result. + file->mappedFile = mmap(0, file->mappedSize, PROT_READ, MAP_FILE | MAP_SHARED, file->fd, 0); + if (!file->mappedFile || (file->mappedFile == MAP_FAILED)) { + close(file->fd); + return false; + } + + return true; +} + +bool FIRCLSMachOFileInitWithCurrent(FIRCLSMachOFileRef file) { + struct FIRCLSMachOSlice slice = FIRCLSMachOSliceGetCurrent(); + + const char* imagePath = FIRCLSMachOSliceGetExecutablePath(&slice); + + return FIRCLSMachOFileInitWithPath(file, imagePath); +} + +void FIRCLSMachOFileDestroy(FIRCLSMachOFileRef file) { + if (!file) { + return; + } + + if (file->mappedFile && file->mappedSize > 0) { + munmap(file->mappedFile, file->mappedSize); + } + + close(file->fd); +} + +void FIRCLSMachOFileEnumerateSlices(FIRCLSMachOFileRef file, FIRCLSMachOSliceIterator block) { + FIRCLSMachOEnumerateSlicesAtAddress(file->mappedFile, block); +} + +void FIRCLSMachOEnumerateSlicesAtAddress(void* executableData, FIRCLSMachOSliceIterator block) { + // check the magic value, to determine if we have a fat header or not + uint32_t magicValue; + uint32_t archCount; + const struct fat_arch* fatArch; + struct FIRCLSMachOSlice slice; + + memset(&slice, 0, sizeof(struct FIRCLSMachOSlice)); + + magicValue = ((struct fat_header*)executableData)->magic; + if ((magicValue != FAT_MAGIC) && (magicValue != FAT_CIGAM)) { + slice.startAddress = executableData; + + // use this to fill in the values + FIRCLSMachOHeaderValues(&slice, NULL, NULL); + + block(&slice); + + return; + } + + archCount = OSSwapBigToHostInt32(((struct fat_header*)executableData)->nfat_arch); + fatArch = executableData + sizeof(struct fat_header); + + for (uint32_t i = 0; i < archCount; ++i) { + slice.cputype = OSSwapBigToHostInt32(fatArch->cputype); + slice.cpusubtype = OSSwapBigToHostInt32(fatArch->cpusubtype); + slice.startAddress = executableData + OSSwapBigToHostInt32(fatArch->offset); + + block(&slice); + + // advance to the next fat_arch structure + fatArch = (struct fat_arch*)((uintptr_t)fatArch + sizeof(struct fat_arch)); + } +} + +struct FIRCLSMachOSlice FIRCLSMachOFileSliceWithArchitectureName(FIRCLSMachOFileRef file, + const char* name) { + __block struct FIRCLSMachOSlice value; + + memset(&value, 0, sizeof(struct FIRCLSMachOSlice)); + + FIRCLSMachOFileEnumerateSlices(file, ^(FIRCLSMachOSliceRef slice) { + if (strcmp(FIRCLSMachOSliceGetArchitectureName(slice), name) == 0) { + value = *slice; + } + }); + + return value; +} + +static void FIRCLSMachOHeaderValues(FIRCLSMachOSliceRef slice, + const struct load_command** cmds, + uint32_t* cmdCount) { + const struct mach_header* header32 = (const struct mach_header*)slice->startAddress; + const struct mach_header_64* header64 = (const struct mach_header_64*)slice->startAddress; + uint32_t commandCount; + const void* commandsAddress; + + if (cmds) { + *cmds = NULL; + } + + if (cmdCount) { + *cmdCount = 0; + } + + if (!slice->startAddress) { + return; + } + + // the 32 and 64 bit versions have an identical structures, so this will work + switch (header32->magic) { + case MH_MAGIC: // 32-bit + case MH_CIGAM: + slice->cputype = header32->cputype; + slice->cpusubtype = header32->cpusubtype; + commandCount = header32->ncmds; + commandsAddress = slice->startAddress + sizeof(struct mach_header); + break; + case MH_MAGIC_64: // 64-bit + case MH_CIGAM_64: + slice->cputype = header64->cputype; + slice->cpusubtype = header64->cpusubtype; + commandCount = header64->ncmds; + commandsAddress = slice->startAddress + sizeof(struct mach_header_64); + break; + default: + // not a valid header + return; + } + + // assign everything back by reference + if (cmds) { + *cmds = commandsAddress; + } + + if (cmdCount) { + *cmdCount = commandCount; + } +} + +static bool FIRCLSMachOSliceIsValid(FIRCLSMachOSliceRef slice) { + if (!slice) { + return false; + } + + if (!slice->startAddress) { + return false; + } + + return true; +} + +void FIRCLSMachOSliceEnumerateLoadCommands(FIRCLSMachOSliceRef slice, + FIRCLSMachOLoadCommandIterator block) { + const struct load_command* cmd; + uint32_t cmdCount; + + if (!block) { + return; + } + + if (!FIRCLSMachOSliceIsValid(slice)) { + return; + } + + FIRCLSMachOHeaderValues(slice, &cmd, &cmdCount); + + for (uint32_t i = 0; cmd != NULL && i < cmdCount; ++i) { + block(cmd->cmd, cmd->cmdsize, cmd); + + cmd = (struct load_command*)((uintptr_t)cmd + cmd->cmdsize); + } +} + +struct FIRCLSMachOSlice FIRCLSMachOSliceGetCurrent(void) { + const NXArchInfo* archInfo; + struct FIRCLSMachOSlice slice; + void* executableSymbol; + Dl_info dlinfo; + + archInfo = NXGetLocalArchInfo(); + if (archInfo) { + slice.cputype = archInfo->cputype; + slice.cpusubtype = archInfo->cpusubtype; + } + + slice.startAddress = NULL; + + // This call can fail when Exported Symbols File in Build Settings is missing the symbol value + // defined as _MH_EXECUTE_SYM (if you look in the header the underscored MH_EXECUTE_SYM define is + // there) + executableSymbol = dlsym(RTLD_MAIN_ONLY, MH_EXECUTE_SYM); + + // get the address of the main function + if (dladdr(executableSymbol, &dlinfo) != 0) { + slice.startAddress = dlinfo.dli_fbase; + } + + return slice; +} + +struct FIRCLSMachOSlice FIRCLSMachOSliceWithHeader(void* machHeader) { + struct FIRCLSMachOSlice slice; + + slice.startAddress = machHeader; + + return slice; +} + +const char* FIRCLSMachOSliceGetExecutablePath(FIRCLSMachOSliceRef slice) { + Dl_info info; + + if (!FIRCLSMachOSliceIsValid(slice)) { + return NULL; + } + + // use dladdr here to look up the information we need for a binary image + if (dladdr(slice->startAddress, &info) == 0) { + return NULL; + } + + return info.dli_fname; +} + +const char* FIRCLSMachOSliceGetArchitectureName(FIRCLSMachOSliceRef slice) { + const NXArchInfo* archInfo; + + // there are some special cases here for types not handled by earlier OSes + if (slice->cputype == CPU_TYPE_ARM && slice->cpusubtype == CPU_SUBTYPE_ARM_V7S) { + return "armv7s"; + } + + if (slice->cputype == (CPU_TYPE_ARM | CPU_ARCH_ABI64)) { + if (slice->cpusubtype == CLS_CPU_SUBTYPE_ARM64E) { + return "arm64e"; + } else if (slice->cpusubtype == CPU_SUBTYPE_ARM64_ALL) { + return "arm64"; + } + } + + if (slice->cputype == (CPU_TYPE_ARM) && slice->cpusubtype == CPU_SUBTYPE_ARM_V7K) { + return "armv7k"; + } + + archInfo = NXGetArchInfoFromCpuType(slice->cputype, slice->cpusubtype); + if (!archInfo) { + return "unknown"; + } + + return archInfo->name; +} + +bool FIRCLSMachOSliceIs64Bit(FIRCLSMachOSliceRef slice) { + // I'm pretty sure this is sufficient... + return (slice->cputype & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; +} + +bool FIRCLSMachOSliceGetSectionByName(FIRCLSMachOSliceRef slice, + const char* segName, + const char* sectionName, + const void** ptr) { + if (!ptr) { + return false; + } + + *ptr = NULL; // make sure this is set before returning + + FIRCLSMachOSection section; + + if (!FIRCLSMachOSliceInitSectionByName(slice, segName, sectionName, §ion)) { + return false; + } + + // WARNING: this calculation isn't correct, but is here to maintain backwards + // compatibility for now with callers of FIRCLSMachOSliceGetSectionByName. All new + // users should be calling FIRCLSMachOSliceInitSectionByName + *ptr = (const void*)((uintptr_t)slice->startAddress + section.offset); + + return true; +} + +bool FIRCLSMachOSliceInitSectionByName(FIRCLSMachOSliceRef slice, + const char* segName, + const char* sectionName, + FIRCLSMachOSection* section) { + if (!FIRCLSMachOSliceIsValid(slice)) { + return false; + } + + if (!section) { + return false; + } + + memset(section, 0, sizeof(FIRCLSMachOSection)); + + if (FIRCLSMachOSliceIs64Bit(slice)) { + const struct section_64* sect = + getsectbynamefromheader_64(slice->startAddress, segName, sectionName); + if (!sect) { + return false; + } + + section->addr = sect->addr; + section->size = sect->size; + section->offset = sect->offset; + } else { + const struct section* sect = getsectbynamefromheader(slice->startAddress, segName, sectionName); + if (!sect) { + return false; + } + + section->addr = sect->addr; + section->size = sect->size; + section->offset = sect->offset; + } + + return true; +} + +// TODO: this is left in-place just to ensure that old crashltyics + new fabric are still compatible +// with each other. As a happy bonus, if that situation does come up, this will also fix the bug +// that was preventing compact unwind on arm64 + iOS 9 from working correctly. +void FIRCLSMachOSliceGetUnwindInformation(FIRCLSMachOSliceRef slice, + const void** ehFrame, + const void** unwindInfo) { + if (!unwindInfo && !ehFrame) { + return; + } + + bool found = false; + intptr_t slide = 0; + + // This is inefficient, but we have no other safe way to do this correctly. Modifying the + // FIRCLSMachOSlice structure is tempting, but could introduce weird binary-compatibility issues + // with version mis-matches. + for (uint32_t i = 0; i < _dyld_image_count(); ++i) { + const struct mach_header* header = _dyld_get_image_header(i); + + if (header == slice->startAddress) { + found = true; + slide = _dyld_get_image_vmaddr_slide(i); + break; + } + } + + // make sure we were able to find a matching value + if (!found) { + return; + } + + FIRCLSMachOSection section; + + if (unwindInfo) { + if (FIRCLSMachOSliceInitSectionByName(slice, SEG_TEXT, "__unwind_info", §ion)) { + *unwindInfo = (void*)(section.addr + slide); + } + } + + if (ehFrame) { + if (FIRCLSMachOSliceInitSectionByName(slice, SEG_TEXT, "__eh_frame", §ion)) { + *ehFrame = (void*)(section.addr + slide); + } + } +} + +uint8_t const* FIRCLSMachOGetUUID(const struct load_command* cmd) { + return ((const struct uuid_command*)cmd)->uuid; +} + +const char* FIRCLSMachOGetDylibPath(const struct load_command* cmd) { + const struct dylib_command* dylibcmd = (const struct dylib_command*)cmd; + + return (const char*)((uintptr_t)cmd + dylibcmd->dylib.name.offset); +} + +bool FIRCLSMachOGetEncrypted(const struct load_command* cmd) { + return ((struct encryption_info_command*)cmd)->cryptid > 0; +} + +static FIRCLSMachOVersion FIRCLSMachOVersionFromEncoded(uint32_t encoded) { + FIRCLSMachOVersion version; + + version.major = (encoded & 0xffff0000) >> 16; + version.minor = (encoded & 0x0000ff00) >> 8; + version.bugfix = encoded & 0x000000ff; + + return version; +} + +FIRCLSMachOVersion FIRCLSMachOGetMinimumOSVersion(const struct load_command* cmd) { + return FIRCLSMachOVersionFromEncoded(((const struct version_min_command*)cmd)->version); +} + +FIRCLSMachOVersion FIRCLSMachOGetLinkedSDKVersion(const struct load_command* cmd) { + return FIRCLSMachOVersionFromEncoded(((const struct version_min_command*)cmd)->sdk); +} + +FIRCLSMachOSegmentCommand FIRCLSMachOGetSegmentCommand(const struct load_command* cmd) { + FIRCLSMachOSegmentCommand segmentCommand; + + memset(&segmentCommand, 0, sizeof(FIRCLSMachOSegmentCommand)); + + if (!cmd) { + return segmentCommand; + } + + if (cmd->cmd == LC_SEGMENT) { + struct segment_command* segCmd = (struct segment_command*)cmd; + + memcpy(segmentCommand.segname, segCmd->segname, 16); + segmentCommand.vmaddr = segCmd->vmaddr; + segmentCommand.vmsize = segCmd->vmsize; + } else if (cmd->cmd == LC_SEGMENT_64) { + struct segment_command_64* segCmd = (struct segment_command_64*)cmd; + + memcpy(segmentCommand.segname, segCmd->segname, 16); + segmentCommand.vmaddr = segCmd->vmaddr; + segmentCommand.vmsize = segCmd->vmsize; + } + + return segmentCommand; +} + +NSString* FIRCLSMachONormalizeUUID(CFUUIDBytes* uuidBytes) { + CFUUIDRef uuid = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *uuidBytes); + + NSString* string = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid)); + + CFRelease(uuid); + + return [[string stringByReplacingOccurrencesOfString:@"-" withString:@""] lowercaseString]; +} + +NSString* FIRCLSMachOFormatVersion(FIRCLSMachOVersion* version) { + if (!version) { + return nil; + } + + return [NSString stringWithFormat:@"%d.%d.%d", version->major, version->minor, version->bugfix]; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOBinary.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOBinary.h new file mode 100644 index 000000000..57d54988a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOBinary.h @@ -0,0 +1,41 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "FIRCLSMachO.h" + +@class FIRCLSMachOSlice; + +@interface FIRCLSMachOBinary : NSObject { + NSURL* _url; + + struct FIRCLSMachOFile _file; + NSMutableArray* _slices; + NSString* _instanceIdentifier; +} + ++ (id)MachOBinaryWithPath:(NSString*)path; + +- (id)initWithURL:(NSURL*)url; + +@property(nonatomic, copy, readonly) NSURL* URL; +@property(nonatomic, copy, readonly) NSString* path; +@property(nonatomic, strong, readonly) NSArray* slices; +@property(nonatomic, copy, readonly) NSString* instanceIdentifier; + +- (void)enumerateUUIDs:(void (^)(NSString* uuid, NSString* architecture))block; + +- (FIRCLSMachOSlice*)sliceForArchitecture:(NSString*)architecture; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOBinary.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOBinary.m new file mode 100644 index 000000000..12598e331 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOBinary.m @@ -0,0 +1,175 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSMachOBinary.h" + +#import "FIRCLSMachOSlice.h" + +#import + +static void FIRCLSSafeHexToString(const uint8_t* value, size_t length, char* outputBuffer); +static NSString* FIRCLSNSDataToNSString(NSData* data); +static NSString* FIRCLSHashBytes(const void* bytes, size_t length); +static NSString* FIRCLSHashNSString(NSString* value); + +@interface FIRCLSMachOBinary () + ++ (NSString*)hashNSString:(NSString*)value; + +@end + +@implementation FIRCLSMachOBinary + ++ (id)MachOBinaryWithPath:(NSString*)path { + return [[self alloc] initWithURL:[NSURL fileURLWithPath:path]]; +} + +@synthesize slices = _slices; + +- (id)initWithURL:(NSURL*)url { + self = [super init]; + if (self) { + _url = [url copy]; + + if (!FIRCLSMachOFileInitWithPath(&_file, [[_url path] fileSystemRepresentation])) { + return nil; + } + + _slices = [NSMutableArray new]; + FIRCLSMachOFileEnumerateSlices(&_file, ^(FIRCLSMachOSliceRef slice) { + FIRCLSMachOSlice* sliceObject; + + sliceObject = [[FIRCLSMachOSlice alloc] initWithSlice:slice]; + + [self->_slices addObject:sliceObject]; + }); + } + + return self; +} + +- (void)dealloc { + FIRCLSMachOFileDestroy(&_file); +} + +- (NSURL*)URL { + return _url; +} + +- (NSString*)path { + return [_url path]; +} + +- (NSString*)instanceIdentifier { + if (_instanceIdentifier) { + return _instanceIdentifier; + } + + NSMutableString* prehashedString = [NSMutableString new]; + + // sort the slices by architecture + NSArray* sortedSlices = + [_slices sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { + return [[obj1 architectureName] compare:[obj2 architectureName]]; + }]; + + // append them all into a big string + for (FIRCLSMachOSlice* slice in sortedSlices) { + [prehashedString appendString:[slice uuid]]; + } + + _instanceIdentifier = [FIRCLSHashNSString(prehashedString) copy]; + + return _instanceIdentifier; +} + +- (void)enumerateUUIDs:(void (^)(NSString* uuid, NSString* architecture))block { + for (FIRCLSMachOSlice* slice in _slices) { + block([slice uuid], [slice architectureName]); + } +} + +- (FIRCLSMachOSlice*)sliceForArchitecture:(NSString*)architecture { + for (FIRCLSMachOSlice* slice in [self slices]) { + if ([[slice architectureName] isEqualToString:architecture]) { + return slice; + } + } + + return nil; +} + ++ (NSString*)hashNSString:(NSString*)value { + return FIRCLSHashNSString(value); +} + +@end + +// TODO: Functions copied from the SDK. We should figure out a way to share this. +static void FIRCLSSafeHexToString(const uint8_t* value, size_t length, char* outputBuffer) { + const char hex[] = "0123456789abcdef"; + + if (!value) { + outputBuffer[0] = '\0'; + return; + } + + for (size_t i = 0; i < length; ++i) { + unsigned char c = value[i]; + outputBuffer[i * 2] = hex[c >> 4]; + outputBuffer[i * 2 + 1] = hex[c & 0x0F]; + } + + outputBuffer[length * 2] = '\0'; // null terminate +} + +static NSString* FIRCLSNSDataToNSString(NSData* data) { + NSString* string; + char* buffer; + size_t size; + NSUInteger length; + + // we need 2 hex char for every byte of data, plus one more spot for a + // null terminator + length = [data length]; + size = (length * 2) + 1; + buffer = malloc(sizeof(char) * size); + + if (!buffer) { + return nil; + } + + FIRCLSSafeHexToString([data bytes], length, buffer); + + string = [NSString stringWithUTF8String:buffer]; + + free(buffer); + + return string; +} + +static NSString* FIRCLSHashBytes(const void* bytes, size_t length) { + uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0}; + CC_SHA1(bytes, (CC_LONG)length, digest); + + NSData* result = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]; + + return FIRCLSNSDataToNSString(result); +} + +static NSString* FIRCLSHashNSString(NSString* value) { + const char* s = [value cStringUsingEncoding:NSUTF8StringEncoding]; + + return FIRCLSHashBytes(s, strlen(s)); +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOSlice.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOSlice.h new file mode 100644 index 000000000..9f7bcb43f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOSlice.h @@ -0,0 +1,37 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "FIRCLSMachO.h" + +@interface FIRCLSMachOSlice : NSObject { + struct FIRCLSMachOSlice _slice; + + NSString* _uuidString; + NSArray* _linkedDylibs; + FIRCLSMachOVersion _minimumOSVersion; + FIRCLSMachOVersion _linkedSDKVersion; +} + ++ (id)runningSlice; + +- (id)initWithSlice:(FIRCLSMachOSliceRef)sliceRef; + +@property(nonatomic, copy, readonly) NSString* uuid; +@property(nonatomic, copy, readonly) NSString* architectureName; +@property(nonatomic, strong, readonly) NSArray* linkedDylibs; +@property(nonatomic, assign, readonly) FIRCLSMachOVersion minimumOSVersion; +@property(nonatomic, assign, readonly) FIRCLSMachOVersion linkedSDKVersion; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOSlice.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOSlice.m new file mode 100644 index 000000000..961e144a8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSMachOSlice.m @@ -0,0 +1,93 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSMachOSlice.h" + +#include + +// this is defined only if __OPEN_SOURCE__ is *not* defined in the TVOS SDK's mach-o/loader.h +// also, it has not yet made it back to the OSX SDKs, for example +#ifndef LC_VERSION_MIN_TVOS +#define LC_VERSION_MIN_TVOS 0x2F +#endif + +@implementation FIRCLSMachOSlice + ++ (id)runningSlice { + struct FIRCLSMachOSlice slice; + + slice = FIRCLSMachOSliceGetCurrent(); + + return [[self alloc] initWithSlice:&slice]; +} + +@synthesize minimumOSVersion = _minimumOSVersion; +@synthesize linkedSDKVersion = _linkedSDKVersion; + +- (id)initWithSlice:(FIRCLSMachOSliceRef)sliceRef { + self = [super init]; + if (self) { + NSMutableArray* dylibs; + + _slice = *sliceRef; + + _minimumOSVersion.major = 0; + _minimumOSVersion.minor = 0; + _minimumOSVersion.bugfix = 0; + + _linkedSDKVersion.major = 0; + _linkedSDKVersion.minor = 0; + _linkedSDKVersion.bugfix = 0; + + dylibs = [NSMutableArray array]; + + FIRCLSMachOSliceEnumerateLoadCommands( + &_slice, ^(uint32_t type, uint32_t size, const struct load_command* cmd) { + switch (type) { + case LC_UUID: + self->_uuidString = + [FIRCLSMachONormalizeUUID((CFUUIDBytes*)FIRCLSMachOGetUUID(cmd)) copy]; + break; + case LC_LOAD_DYLIB: + [dylibs addObject:[NSString stringWithUTF8String:FIRCLSMachOGetDylibPath(cmd)]]; + break; + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_WATCHOS: + case LC_VERSION_MIN_TVOS: + self->_minimumOSVersion = FIRCLSMachOGetMinimumOSVersion(cmd); + self->_linkedSDKVersion = FIRCLSMachOGetLinkedSDKVersion(cmd); + break; + } + }); + + _linkedDylibs = [dylibs copy]; + } + + return self; +} + +- (NSString*)architectureName { + return [NSString stringWithUTF8String:FIRCLSMachOSliceGetArchitectureName(&_slice)]; +} + +- (NSString*)uuid { + return _uuidString; +} + +- (NSArray*)linkedDylibs { + return _linkedDylibs; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSdSYM.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSdSYM.h new file mode 100644 index 000000000..c80ac745f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSdSYM.h @@ -0,0 +1,38 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +@class FIRCLSMachOBinary; + +@interface FIRCLSdSYM : NSObject + +NS_ASSUME_NONNULL_BEGIN + ++ (id)dSYMWithURL:(NSURL*)url; + +- (id)initWithURL:(NSURL*)url; + +@property(nonatomic, readonly) FIRCLSMachOBinary* binary; +@property(nonatomic, copy, readonly, nullable) NSString* bundleIdentifier; +@property(nonatomic, copy, readonly) NSURL* executableURL; +@property(nonatomic, copy, readonly) NSString* executablePath; +@property(nonatomic, copy, readonly) NSString* bundleVersion; +@property(nonatomic, copy, readonly) NSString* shortBundleVersion; + +- (void)enumerateUUIDs:(void (^)(NSString* uuid, NSString* architecture))block; + +NS_ASSUME_NONNULL_END + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSdSYM.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSdSYM.m new file mode 100644 index 000000000..cda787914 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSMachO/FIRCLSdSYM.m @@ -0,0 +1,109 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSdSYM.h" + +#import "FIRCLSMachOBinary.h" + +#define CLS_XCODE_DSYM_PREFIX (@"com.apple.xcode.dsym.") + +@interface FIRCLSdSYM () + +@property(nonatomic, readonly) NSBundle* bundle; + +@end + +@implementation FIRCLSdSYM + ++ (id)dSYMWithURL:(NSURL*)url { + return [[self alloc] initWithURL:url]; +} + +- (id)initWithURL:(NSURL*)url { + self = [super init]; + if (self) { + NSDirectoryEnumerator* enumerator; + NSString* path; + NSFileManager* fileManager; + BOOL isDirectory; + BOOL fileExistsAtPath; + NSArray* itemsInDWARFDir; + + fileManager = [NSFileManager defaultManager]; + + // Is there a file at this path? + if (![fileManager fileExistsAtPath:[url path]]) { + return nil; + } + + _bundle = [NSBundle bundleWithURL:url]; + if (!_bundle) { + return nil; + } + + path = [[url path] stringByAppendingPathComponent:@"Contents/Resources/DWARF"]; + + // Does this path exist and is it a directory? + fileExistsAtPath = [fileManager fileExistsAtPath:path isDirectory:&isDirectory]; + if (!fileExistsAtPath || !isDirectory) { + return nil; + } + + enumerator = [fileManager enumeratorAtPath:path]; + itemsInDWARFDir = [enumerator allObjects]; + // Do we have a Contents/Resources/DWARF dir but no contents? + if ([itemsInDWARFDir count] == 0) { + return nil; + } + + path = [path stringByAppendingPathComponent:[itemsInDWARFDir objectAtIndex:0]]; + + _binary = [[FIRCLSMachOBinary alloc] initWithURL:[NSURL fileURLWithPath:path]]; + } + + return self; +} + +- (NSString*)bundleIdentifier { + NSString* identifier; + + identifier = [_bundle bundleIdentifier]; + if ([identifier hasPrefix:CLS_XCODE_DSYM_PREFIX]) { + return [identifier substringFromIndex:[CLS_XCODE_DSYM_PREFIX length]]; + } + + return identifier; +} + +- (NSURL*)executableURL { + return [_binary URL]; +} + +- (NSString*)executablePath { + return [_binary path]; +} + +- (NSString*)bundleVersion { + return [[_bundle infoDictionary] objectForKey:@"CFBundleVersion"]; +} + +- (NSString*)shortBundleVersion { + return [[_bundle infoDictionary] objectForKey:@"CFBundleShortVersionString"]; +} + +- (void)enumerateUUIDs:(void (^)(NSString* uuid, NSString* architecture))block { + [_binary enumerateUUIDs:block]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSFABNetworkClient.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSFABNetworkClient.h new file mode 100644 index 000000000..ebbd26c91 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSFABNetworkClient.h @@ -0,0 +1,56 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +OBJC_EXTERN const NSUInteger FIRCLSNetworkMaximumRetryCount; + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^FIRCLSNetworkDataTaskCompletionHandlerBlock)(NSData *__nullable data, + NSURLResponse *__nullable response, + NSError *__nullable error); +typedef void (^FIRCLSNetworkDownloadTaskCompletionHandlerBlock)(NSURL *__nullable location, + NSURLResponse *__nullable response, + NSError *__nullable error); + +@interface FIRCLSFABNetworkClient : NSObject + +- (instancetype)init; +- (instancetype)initWithQueue:(nullable NSOperationQueue *)operationQueue; +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config + queue:(nullable NSOperationQueue *)operationQueue + NS_DESIGNATED_INITIALIZER; + +- (void)startDataTaskWithRequest:(NSURLRequest *)request + retryLimit:(NSUInteger)retryLimit + completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler; +- (void)startDownloadTaskWithRequest:(NSURLRequest *)request + retryLimit:(NSUInteger)retryLimit + completionHandler: + (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler; + +- (void)invalidateAndCancel; + +// Backwards compatibility (we cannot change an interface in Fabric Base that other kits rely on, +// since we have no control of versioning dependencies) +- (void)startDataTaskWithRequest:(NSURLRequest *)request + completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler; +- (void)startDownloadTaskWithRequest:(NSURLRequest *)request + completionHandler: + (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSFABNetworkClient.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSFABNetworkClient.m new file mode 100644 index 000000000..d11b3b611 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSFABNetworkClient.m @@ -0,0 +1,280 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSFABNetworkClient.h" + +#if FIRCLSURLSESSION_REQUIRED +#import "FIRCLSURLSession.h" +#endif + +#import "FIRCLSNetworkResponseHandler.h" + +static const float FIRCLSNetworkMinimumRetryJitter = 0.90f; +static const float FIRCLSNetworkMaximumRetryJitter = 1.10f; +const NSUInteger FIRCLSNetworkMaximumRetryCount = 10; + +@interface FIRCLSFABNetworkClient () + +@property(nonatomic, strong, readonly) NSURLSession *session; + +@end + +@implementation FIRCLSFABNetworkClient + +- (instancetype)init { + return [self initWithQueue:nil]; +} + +- (instancetype)initWithQueue:(nullable NSOperationQueue *)operationQueue { +#if !FIRCLSURLSESSION_REQUIRED + NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; +#else + NSURLSessionConfiguration *config = [FIRCLSURLSessionConfiguration defaultSessionConfiguration]; +#endif + return [self initWithSessionConfiguration:config queue:operationQueue]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config + queue:(nullable NSOperationQueue *)operationQueue { + self = [super init]; + if (!self) { + return nil; + } + +#if !FIRCLSURLSESSION_REQUIRED + _session = [NSURLSession sessionWithConfiguration:config + delegate:self + delegateQueue:operationQueue]; +#else + _session = [FIRCLSURLSession sessionWithConfiguration:config + delegate:self + delegateQueue:operationQueue]; +#endif + if (!_session) { + return nil; + } + + return self; +} + +- (void)dealloc { + [_session finishTasksAndInvalidate]; +} + +#pragma mark - Delay Handling +- (double)randomDoubleWithMin:(double)min max:(double)max { + return min + ((max - min) * drand48()); +} + +- (double)generateRandomJitter { + return [self randomDoubleWithMin:FIRCLSNetworkMinimumRetryJitter + max:FIRCLSNetworkMaximumRetryJitter]; +} + +- (NSTimeInterval)computeDelayForResponse:(NSURLResponse *)response + withRetryCount:(NSUInteger)count { + NSTimeInterval initialValue = [FIRCLSNetworkResponseHandler retryValueForResponse:response]; + + // make sure count is > 0 + count = MAX(count, 1); + // make sure initialValue is >2 for exponential backoff to work reasonably with low count numbers + initialValue = MAX(initialValue, 2.0); + + const double jitter = [self generateRandomJitter]; + + return pow(initialValue, count) * jitter; // exponential backoff +} + +- (void)runAfterRetryValueFromResponse:(NSURLResponse *)response + attempts:(NSUInteger)count + onQueue:(dispatch_queue_t)queue + block:(void (^)(void))block { + const NSTimeInterval delay = [self computeDelayForResponse:response withRetryCount:count]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t)(delay * NSEC_PER_SEC)), queue, block); +} + +- (void)runAfterRetryValueFromResponse:(NSURLResponse *)response + attempts:(NSUInteger)count + block:(void (^)(void))block { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + [self runAfterRetryValueFromResponse:response attempts:count onQueue:queue block:block]; +} + +#pragma mark - Tasks + +- (void)startDataTaskWithRequest:(NSURLRequest *)request + retryLimit:(NSUInteger)retryLimit + tries:(NSUInteger)tries + completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler { + NSURLSessionTask *task = [self.session + dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, NSError *taskError) { + [FIRCLSNetworkResponseHandler + handleCompletedResponse:response + forOriginalRequest:request + error:taskError + block:^(BOOL retry, NSError *error) { + if (!retry) { + completionHandler(data, response, error); + return; + } + + if (tries >= retryLimit) { + NSDictionary *userInfo = @{ + @"retryLimit" : @(retryLimit), + NSURLErrorFailingURLStringErrorKey : request.URL + }; + completionHandler( + nil, nil, + [NSError + errorWithDomain:FIRCLSNetworkErrorDomain + code:FIRCLSNetworkErrorMaximumAttemptsReached + userInfo:userInfo]); + return; + } + + [self + runAfterRetryValueFromResponse:response + attempts:tries + block:^{ + [self + startDataTaskWithRequest: + request + retryLimit: + retryLimit + tries: + (tries + + 1) + completionHandler: + completionHandler]; + }]; + }]; + }]; + + [task resume]; + + if (!task) { + completionHandler(nil, nil, + [NSError errorWithDomain:FIRCLSNetworkErrorDomain + code:FIRCLSNetworkErrorFailedToStartOperation + userInfo:nil]); + } +} + +- (void)startDataTaskWithRequest:(NSURLRequest *)request + retryLimit:(NSUInteger)retryLimit + completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler { + [self startDataTaskWithRequest:request + retryLimit:retryLimit + tries:0 + completionHandler:completionHandler]; +} + +- (void)startDataTaskWithRequest:(NSURLRequest *)request + completionHandler:(FIRCLSNetworkDataTaskCompletionHandlerBlock)completionHandler { + [self startDataTaskWithRequest:request + retryLimit:FIRCLSNetworkMaximumRetryCount + completionHandler:completionHandler]; +} + +- (void)startDownloadTaskWithRequest:(NSURLRequest *)request + retryLimit:(NSUInteger)retryLimit + tries:(NSUInteger)tries + completionHandler: + (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler { + NSURLSessionTask *task = [self.session + downloadTaskWithRequest:request + completionHandler:^(NSURL *location, NSURLResponse *response, NSError *taskError) { + [FIRCLSNetworkResponseHandler + handleCompletedResponse:response + forOriginalRequest:request + error:taskError + block:^(BOOL retry, NSError *error) { + if (!retry) { + completionHandler(location, response, error); + return; + } + + if (tries >= retryLimit) { + NSDictionary *userInfo = @{ + @"retryLimit" : @(retryLimit), + NSURLErrorFailingURLStringErrorKey : request.URL + }; + completionHandler( + nil, nil, + [NSError + errorWithDomain:FIRCLSNetworkErrorDomain + code: + FIRCLSNetworkErrorMaximumAttemptsReached + userInfo:userInfo]); + return; + } + + [self + runAfterRetryValueFromResponse:response + attempts:tries + block:^{ + [self + startDownloadTaskWithRequest: + request + retryLimit: + retryLimit + tries: + (tries + + 1) + completionHandler: + completionHandler]; + }]; + }]; + }]; + + [task resume]; + + if (!task) { + completionHandler(nil, nil, + [NSError errorWithDomain:FIRCLSNetworkErrorDomain + code:FIRCLSNetworkErrorFailedToStartOperation + userInfo:nil]); + } +} + +- (void)startDownloadTaskWithRequest:(NSURLRequest *)request + retryLimit:(NSUInteger)retryLimit + completionHandler: + (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler { + [self startDownloadTaskWithRequest:request + retryLimit:retryLimit + tries:0 + completionHandler:completionHandler]; +} + +- (void)startDownloadTaskWithRequest:(NSURLRequest *)request + completionHandler: + (FIRCLSNetworkDownloadTaskCompletionHandlerBlock)completionHandler { + [self startDownloadTaskWithRequest:request + retryLimit:FIRCLSNetworkMaximumRetryCount + completionHandler:completionHandler]; +} + +- (void)invalidateAndCancel { + [self.session invalidateAndCancel]; +} + +#pragma mark - NSURLSession Delegate +- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSMultipartMimeStreamEncoder.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSMultipartMimeStreamEncoder.h new file mode 100644 index 000000000..c3630a5b4 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSMultipartMimeStreamEncoder.h @@ -0,0 +1,88 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + * This class is a helper class for generating Multipart requests, as described in + * http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html. In the case of multiple part messages, in + * which one or more different sets of data are combined in a single body, a "multipart" + * Content-Type field must appear in the entity's header. The body must then contain one or more + * "body parts," each preceded by an encapsulation boundary, and the last one followed by a closing + * boundary. Each part starts with an encapsulation boundary, and then contains a body part + * consisting of header area, a blank line, and a body area. + */ +@interface FIRCLSMultipartMimeStreamEncoder : NSObject + +/** + * Convenience class method to populate a NSMutableURLRequest with data from a block that takes an + * instance of this class as input. + */ ++ (void)populateRequest:(NSMutableURLRequest *)request + withDataFromEncoder:(void (^)(FIRCLSMultipartMimeStreamEncoder *encoder))block; + +/** + * Returns a NSString instance with multipart/form-data appended to the boundary. + */ ++ (NSString *)contentTypeHTTPHeaderValueWithBoundary:(NSString *)boundary; +/** + * Convenience class method that returns an instance of this class + */ ++ (instancetype)encoderWithStream:(NSOutputStream *)stream andBoundary:(NSString *)boundary; +/** + * Returns a unique boundary string. + */ ++ (NSString *)generateBoundary; +/** + * Designated initializer + * @param stream NSOutputStream associated with the Multipart request + * @param boundary the unique Boundary string to be used + */ +- (instancetype)initWithStream:(NSOutputStream *)stream + andBoundary:(NSString *)boundary NS_DESIGNATED_INITIALIZER; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; +/** + * Encodes this block within the boundary on the output stream + */ +- (void)encode:(void (^)(void))block; +/** + * Adds the contents of the file data with given Mime type anf fileName within the boundary in + * stream + */ +- (void)addFileData:(NSData *)data + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + fieldName:(NSString *)name; +/** + * Convenience method for the method above. Converts fileURL to data and calls the above method. + */ +- (void)addFile:(NSURL *)fileURL + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + fieldName:(NSString *)name; +/** + * Adds this field and value in the stream + */ +- (void)addValue:(id)value fieldName:(NSString *)name; +/** + * String referring to the multipart MIME type with boundary + */ +@property(nonatomic, copy, readonly) NSString *contentTypeHTTPHeaderValue; +/** + * Length of the data written to stream + */ +@property(nonatomic, copy, readonly) NSString *contentLengthHTTPHeaderValue; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSMultipartMimeStreamEncoder.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSMultipartMimeStreamEncoder.m new file mode 100644 index 000000000..134b1cefb --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSMultipartMimeStreamEncoder.m @@ -0,0 +1,208 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSMultipartMimeStreamEncoder.h" + +#import "FIRCLSByteUtility.h" +#import "FIRCLSLogger.h" +#import "FIRCLSUUID.h" + +@interface FIRCLSMultipartMimeStreamEncoder () + +@property(nonatomic) NSUInteger length; +@property(nonatomic, copy) NSString *boundary; +@property(nonatomic, copy, readonly) NSData *headerData; +@property(nonatomic, copy, readonly) NSData *footerData; +@property(nonatomic, strong) NSOutputStream *outputStream; + +@end + +@implementation FIRCLSMultipartMimeStreamEncoder + ++ (void)populateRequest:(NSMutableURLRequest *)request + withDataFromEncoder:(void (^)(FIRCLSMultipartMimeStreamEncoder *encoder))block { + NSString *boundary = [self generateBoundary]; + + NSOutputStream *stream = [NSOutputStream outputStreamToMemory]; + + FIRCLSMultipartMimeStreamEncoder *encoder = + [[FIRCLSMultipartMimeStreamEncoder alloc] initWithStream:stream andBoundary:boundary]; + + [encoder encode:^{ + block(encoder); + }]; + + [request setValue:encoder.contentTypeHTTPHeaderValue forHTTPHeaderField:@"Content-Type"]; + [request setValue:encoder.contentLengthHTTPHeaderValue forHTTPHeaderField:@"Content-Length"]; + + NSData *data = [stream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; + request.HTTPBody = data; +} + ++ (NSString *)contentTypeHTTPHeaderValueWithBoundary:(NSString *)boundary { + return [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; +} + ++ (instancetype)encoderWithStream:(NSOutputStream *)stream andBoundary:(NSString *)boundary { + return [[self alloc] initWithStream:stream andBoundary:boundary]; +} + ++ (NSString *)generateBoundary { + return FIRCLSGenerateUUID(); +} + +- (instancetype)initWithStream:(NSOutputStream *)stream andBoundary:(NSString *)boundary { + self = [super init]; + if (!self) { + return nil; + } + + self.outputStream = stream; + + if (!boundary) { + boundary = [FIRCLSMultipartMimeStreamEncoder generateBoundary]; + } + + _boundary = boundary; + + return self; +} + +- (void)encode:(void (^)(void))block { + [self beginEncoding]; + + block(); + + [self endEncoding]; +} + +- (NSString *)contentTypeHTTPHeaderValue { + return [[self class] contentTypeHTTPHeaderValueWithBoundary:self.boundary]; +} + +- (NSString *)contentLengthHTTPHeaderValue { + return [NSString stringWithFormat:@"%lu", (unsigned long)_length]; +} + +#pragma - mark MIME part API +- (void)beginEncoding { + _length = 0; + + [self.outputStream open]; + + [self writeData:self.headerData]; +} + +- (void)endEncoding { + [self writeData:self.footerData]; + + [self.outputStream close]; +} + +- (NSData *)headerData { + return [@"MIME-Version: 1.0\r\n" dataUsingEncoding:NSUTF8StringEncoding]; +} + +- (NSData *)footerData { + return [[NSString stringWithFormat:@"--%@--\r\n", self.boundary] + dataUsingEncoding:NSUTF8StringEncoding]; +} + +- (void)addFileData:(NSData *)data + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + fieldName:(NSString *)name { + if ([data length] == 0) { + FIRCLSErrorLog(@"Unable to MIME encode data with zero length (%@)", name); + return; + } + + if ([name length] == 0 || [fileName length] == 0) { + FIRCLSErrorLog(@"name (%@) or fieldname (%@) is invalid", name, fileName); + return; + } + + NSMutableString *string; + + string = [NSMutableString + stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", + self.boundary, name, fileName]; + + if (mimeType) { + [string appendFormat:@"Content-Type: %@\r\n", mimeType]; + [string appendString:@"Content-Transfer-Encoding: binary\r\n\r\n"]; + } else { + [string appendString:@"Content-Type: application/octet-stream\r\n\r\n"]; + } + + [self writeData:[string dataUsingEncoding:NSUTF8StringEncoding]]; + + [self writeData:data]; + + [self writeData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; +} + +- (void)addValue:(id)value fieldName:(NSString *)name { + if ([name length] == 0 || !value || value == NSNull.null) { + FIRCLSErrorLog(@"name (%@) or value (%@) is invalid", name, value); + return; + } + + NSMutableString *string; + + string = + [NSMutableString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n", + self.boundary, name]; + [string appendString:@"Content-Type: text/plain\r\n\r\n"]; + [string appendFormat:@"%@\r\n", value]; + + [self writeData:[string dataUsingEncoding:NSUTF8StringEncoding]]; +} + +- (void)addFile:(NSURL *)fileURL + fileName:(NSString *)fileName + mimeType:(NSString *)mimeType + fieldName:(NSString *)name { + NSData *data = [NSData dataWithContentsOfURL:fileURL]; + + [self addFileData:data fileName:fileName mimeType:mimeType fieldName:name]; +} + +- (BOOL)writeBytes:(const void *)bytes ofLength:(NSUInteger)length { + if ([self.outputStream write:bytes maxLength:length] != length) { + FIRCLSErrorLog(@"Failed to write bytes to stream"); + return NO; + } + + _length += length; + + return YES; +} + +- (void)writeData:(NSData *)data { + FIRCLSEnumerateByteRangesOfNSDataUsingBlock( + data, ^(const void *bytes, NSRange byteRange, BOOL *stop) { + NSUInteger length = byteRange.length; + + if ([self.outputStream write:bytes maxLength:length] != length) { + FIRCLSErrorLog(@"Failed to write data to stream"); + *stop = YES; + return; + } + + self->_length += length; + }); +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSNetworkResponseHandler.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSNetworkResponseHandler.h new file mode 100644 index 000000000..42f0bb493 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSNetworkResponseHandler.h @@ -0,0 +1,87 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + * Type to indicate response status + */ +typedef NS_ENUM(NSInteger, FIRCLSNetworkClientResponseType) { + FIRCLSNetworkClientResponseSuccess, + FIRCLSNetworkClientResponseInvalid, + FIRCLSNetworkClientResponseFailure, + FIRCLSNetworkClientResponseRetry, + FIRCLSNetworkClientResponseBackOff +}; + +typedef NS_ENUM(NSInteger, FIRCLSNetworkErrorType) { + FIRCLSNetworkErrorUnknown = -1, + FIRCLSNetworkErrorFailedToStartOperation = -3, + FIRCLSNetworkErrorResponseInvalid = -4, + FIRCLSNetworkErrorRequestFailed = -5, + FIRCLSNetworkErrorMaximumAttemptsReached = -6, +}; + +extern NSInteger const FIRCLSNetworkErrorUnknownURLCancelReason; + +/** + * This block is an input parameter to handleCompletedResponse: and handleCompletedTask: methods of + * this class. + * @param retryMightSucceed is YES if the request should be retried. + * @param error is the error received back in response. + */ +typedef void (^FIRCLSNetworkResponseCompletionHandlerBlock)(BOOL retryMightSucceed, NSError *error); + +/** + * Error domain for Crashlytics network errors + */ +extern NSString *const FIRCLSNetworkErrorDomain; +/** + * This class handles network responses. + */ +@interface FIRCLSNetworkResponseHandler : NSObject +/** + * Returns the header in the given NSURLResponse with name as key + */ ++ (NSString *)headerForResponse:(NSURLResponse *)response withKey:(NSString *)key; +/** + * Returns Retry-After header value in response, and if absent returns a default retry value + */ ++ (NSTimeInterval)retryValueForResponse:(NSURLResponse *)response; +/** + * Checks if the content type for response matches the request + */ ++ (BOOL)contentTypeForResponse:(NSURLResponse *)response matchesRequest:(NSURLRequest *)request; + ++ (NSInteger)cancelReasonFromURLError:(NSError *)error; + ++ (BOOL)retryableURLError:(NSError *)error; + +/** + * Convenience method that calls back the input block with FIRCLSNetworkClientResponseType after + * checking the response code in response + */ ++ (void)clientResponseType:(NSURLResponse *)response + handler:(void (^)(FIRCLSNetworkClientResponseType type, + NSInteger statusCode))responseTypeAndStatusCodeHandlerBlock; +/** + * Handles a completed response for request and calls back input block. Populates error even if + * error was nil, but response code indicated an error. + */ ++ (void)handleCompletedResponse:(NSURLResponse *)response + forOriginalRequest:(NSURLRequest *)originalRequest + error:(NSError *)error + block:(FIRCLSNetworkResponseCompletionHandlerBlock)completionHandlerBlock; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSNetworkResponseHandler.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSNetworkResponseHandler.m new file mode 100644 index 000000000..d82cdf69a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSNetworkResponseHandler.m @@ -0,0 +1,290 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSNetworkResponseHandler.h" + +@implementation FIRCLSNetworkResponseHandler + +static const NSTimeInterval kFIRCLSNetworkResponseHandlerDefaultRetryInterval = 2.0; +static NSString *const kFIRCLSNetworkResponseHandlerContentType = @"Content-Type"; +NSString *const FIRCLSNetworkErrorDomain = @"FIRCLSNetworkError"; + +NSInteger const FIRCLSNetworkErrorUnknownURLCancelReason = -1; + +#pragma mark - Header Handling ++ (NSString *)headerForResponse:(NSURLResponse *)response withKey:(NSString *)key { + if (![response respondsToSelector:@selector(allHeaderFields)]) { + return nil; + } + + return [((NSHTTPURLResponse *)response).allHeaderFields objectForKey:key]; +} + ++ (NSTimeInterval)retryValueForResponse:(NSURLResponse *)response { + NSString *retryValueString = [self headerForResponse:response withKey:@"Retry-After"]; + if (!retryValueString) { + return kFIRCLSNetworkResponseHandlerDefaultRetryInterval; + } + + NSTimeInterval value = retryValueString.doubleValue; + if (value < 0.0) { + return kFIRCLSNetworkResponseHandlerDefaultRetryInterval; + } + + return value; +} + ++ (NSString *)requestIdForResponse:(NSURLResponse *)response { + return [self headerForResponse:response withKey:@"X-Request-Id"]; +} + ++ (BOOL)contentTypeForResponse:(NSURLResponse *)response matchesRequest:(NSURLRequest *)request { + NSString *accept = [request.allHTTPHeaderFields objectForKey:@"Accept"]; + if (!accept) { + // An omitted accept header is defined to match everything + return YES; + } + + NSString *contentHeader = [self.class headerForResponse:response + withKey:kFIRCLSNetworkResponseHandlerContentType]; + if (!contentHeader) { + // FIRCLSDeveloperLog("Network", @"Content-Type not present in response"); + return NO; + } + + NSString *acceptCharset = request.allHTTPHeaderFields[@"Accept-Charset"]; + + NSArray *parts = [contentHeader componentsSeparatedByString:@"; charset="]; + if (!parts) { + parts = @[ contentHeader ]; + } + + if ([[parts objectAtIndex:0] caseInsensitiveCompare:accept] != NSOrderedSame) { + // FIRCLSDeveloperLog("Network", @"Content-Type does not match Accept"); + return NO; + } + + if (!acceptCharset) { + return YES; + } + + if (parts.count < 2) { + return YES; + } + + return [[parts objectAtIndex:1] caseInsensitiveCompare:acceptCharset] == NSOrderedSame; +} + ++ (NSInteger)cancelReasonFromURLError:(NSError *)error { + if (![[error domain] isEqualToString:NSURLErrorDomain]) { + return FIRCLSNetworkErrorUnknownURLCancelReason; + } + + if ([error code] != NSURLErrorCancelled) { + return FIRCLSNetworkErrorUnknownURLCancelReason; + } + + NSNumber *reason = [[error userInfo] objectForKey:NSURLErrorBackgroundTaskCancelledReasonKey]; + if (reason == nil) { + return FIRCLSNetworkErrorUnknownURLCancelReason; + } + + return [reason integerValue]; +} + ++ (BOOL)retryableURLError:(NSError *)error { + // So far, the only task errors seen are NSURLErrorDomain. For others, we're not + // sure what to do. + if (![[error domain] isEqualToString:NSURLErrorDomain]) { + return NO; + } + + // cases that we know are definitely not retryable + switch ([error code]) { + case NSURLErrorBadURL: + case NSURLErrorUnsupportedURL: + case NSURLErrorHTTPTooManyRedirects: + case NSURLErrorRedirectToNonExistentLocation: + case NSURLErrorUserCancelledAuthentication: + case NSURLErrorUserAuthenticationRequired: + case NSURLErrorAppTransportSecurityRequiresSecureConnection: + case NSURLErrorFileDoesNotExist: + case NSURLErrorFileIsDirectory: + case NSURLErrorDataLengthExceedsMaximum: + case NSURLErrorSecureConnectionFailed: + case NSURLErrorServerCertificateHasBadDate: + case NSURLErrorServerCertificateUntrusted: + case NSURLErrorServerCertificateHasUnknownRoot: + case NSURLErrorServerCertificateNotYetValid: + case NSURLErrorClientCertificateRejected: + case NSURLErrorClientCertificateRequired: + case NSURLErrorBackgroundSessionRequiresSharedContainer: + return NO; + } + + // All other errors, as far as I can tell, are things that could clear up + // without action on the part of the client. + + // NSURLErrorCancelled is a potential special-case. I believe there are + // situations where a cancelled request cannot be successfully restarted. But, + // until I can prove it, we'll retry. There are defnitely many cases where + // a cancelled request definitely can be restarted and will work. + + return YES; +} + +#pragma mark - Error Creation ++ (NSError *)errorForCode:(NSInteger)code userInfo:(NSDictionary *)userInfo { + return [NSError errorWithDomain:FIRCLSNetworkErrorDomain code:code userInfo:userInfo]; +} + ++ (NSError *)errorForResponse:(NSURLResponse *)response + ofType:(FIRCLSNetworkClientResponseType)type + status:(NSInteger)status { + if (type == FIRCLSNetworkClientResponseSuccess) { + return nil; + } + + NSString *requestId = [self requestIdForResponse:response]; + NSString *contentType = [self headerForResponse:response + withKey:kFIRCLSNetworkResponseHandlerContentType]; + + // this could be nil, so be careful + requestId = requestId ? requestId : @""; + contentType = contentType ? contentType : @""; + + NSDictionary *userInfo = @{ + @"type" : @(type), + @"status_code" : @(status), + @"request_id" : requestId, + @"content_type" : contentType + }; + + // compute a reasonable error code type + NSInteger errorCode = FIRCLSNetworkErrorUnknown; + switch (type) { + case FIRCLSNetworkClientResponseFailure: + errorCode = FIRCLSNetworkErrorRequestFailed; + break; + case FIRCLSNetworkClientResponseInvalid: + errorCode = FIRCLSNetworkErrorResponseInvalid; + break; + default: + break; + } + + return [self errorForCode:errorCode userInfo:userInfo]; +} + ++ (void)clientResponseType:(NSURLResponse *)response + handler:(void (^)(FIRCLSNetworkClientResponseType type, + NSInteger statusCode))responseTypeAndStatusCodeHandlerBlock { + if (![response respondsToSelector:@selector(statusCode)]) { + responseTypeAndStatusCodeHandlerBlock(FIRCLSNetworkClientResponseInvalid, 0); + return; + } + + NSInteger code = ((NSHTTPURLResponse *)response).statusCode; + + switch (code) { + case 200: + case 201: + case 202: + case 204: + case 304: + responseTypeAndStatusCodeHandlerBlock(FIRCLSNetworkClientResponseSuccess, code); + return; + case 420: + case 429: + responseTypeAndStatusCodeHandlerBlock(FIRCLSNetworkClientResponseBackOff, code); + return; + case 408: + responseTypeAndStatusCodeHandlerBlock(FIRCLSNetworkClientResponseRetry, code); + return; + case 400: + case 401: + case 403: + case 404: + case 406: + case 410: + case 411: + case 413: + case 419: + case 422: + case 431: + responseTypeAndStatusCodeHandlerBlock(FIRCLSNetworkClientResponseFailure, code); + return; + } + + // check for a 5xx + if (code >= 500 && code <= 599) { + responseTypeAndStatusCodeHandlerBlock(FIRCLSNetworkClientResponseRetry, code); + return; + } + + responseTypeAndStatusCodeHandlerBlock(FIRCLSNetworkClientResponseInvalid, code); +} + ++ (void)handleCompletedResponse:(NSURLResponse *)response + forOriginalRequest:(NSURLRequest *)originalRequest + error:(NSError *)originalError + block: + (FIRCLSNetworkResponseCompletionHandlerBlock)completionHandlerBlock { + // if we have an error, we can just continue + if (originalError) { + BOOL retryable = [self retryableURLError:originalError]; + + completionHandlerBlock(retryable, originalError); + return; + } + + [self.class clientResponseType:response + handler:^(FIRCLSNetworkClientResponseType type, NSInteger statusCode) { + NSError *error = nil; + + switch (type) { + case FIRCLSNetworkClientResponseInvalid: + error = [self errorForResponse:response + ofType:type + status:statusCode]; + break; + case FIRCLSNetworkClientResponseBackOff: + case FIRCLSNetworkClientResponseRetry: + error = [self errorForResponse:response + ofType:type + status:statusCode]; + completionHandlerBlock(YES, error); + return; + case FIRCLSNetworkClientResponseFailure: + error = [self errorForResponse:response + ofType:type + status:statusCode]; + break; + case FIRCLSNetworkClientResponseSuccess: + if (![self contentTypeForResponse:response + matchesRequest:originalRequest]) { + error = [self errorForResponse:response + ofType:FIRCLSNetworkClientResponseInvalid + status:statusCode]; + break; + } + + break; + } + + completionHandlerBlock(NO, error); + }]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSURLBuilder.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSURLBuilder.h new file mode 100644 index 000000000..c8fbaa94a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSURLBuilder.h @@ -0,0 +1,44 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + * This is a convenience class to ease constructing NSURLs. + */ +@interface FIRCLSURLBuilder : NSObject + +/** + * Convenience method that returns a FIRCLSURLBuilder instance with the input base URL appended to + * it. + */ ++ (instancetype)URLWithBase:(NSString *)base; +/** + * Appends the component to the URL being built by FIRCLSURLBuilder instance + */ +- (void)appendComponent:(NSString *)component; +/** + * Escapes and appends the component to the URL being built by FIRCLSURLBuilder instance + */ +- (void)escapeAndAppendComponent:(NSString *)component; +/** + * Adds a query and value to the URL being built + */ +- (void)appendValue:(id)value forQueryParam:(NSString *)param; +/** + * Returns the built URL + */ +- (NSURL *)URL; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSURLBuilder.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSURLBuilder.m new file mode 100644 index 000000000..e832c8987 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSNetworking/FIRCLSURLBuilder.m @@ -0,0 +1,103 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSURLBuilder.h" + +#import "FIRCLSLogger.h" + +@interface FIRCLSURLBuilder () + +@property(nonatomic) NSMutableString *URLString; +@property(nonatomic) NSUInteger queryParams; + +- (NSString *)escapeString:(NSString *)string; + +@end + +@implementation FIRCLSURLBuilder + ++ (instancetype)URLWithBase:(NSString *)base { + FIRCLSURLBuilder *url = [[FIRCLSURLBuilder alloc] init]; + + [url appendComponent:base]; + + return url; +} + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _URLString = [[NSMutableString alloc] init]; + _queryParams = 0; + + return self; +} + +- (NSString *)escapeString:(NSString *)string { +#if TARGET_OS_WATCH + // TODO: Question - Why does watchOS use a different encoding from the other platforms and the + // Android SDK? + return + [string stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet + URLPathAllowedCharacterSet]]; +#else + return + [string stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet + .URLQueryAllowedCharacterSet]; +#endif +} + +- (void)appendComponent:(NSString *)component { + if (component.length == 0) { + FIRCLSErrorLog(@"URLBuilder parameter component must not be empty"); + return; + } + + [self.URLString appendString:component]; +} + +- (void)escapeAndAppendComponent:(NSString *)component { + [self appendComponent:[self escapeString:component]]; +} + +- (void)appendValue:(id)value forQueryParam:(NSString *)param { + if (!value) { + return; + } + + if (self.queryParams == 0) { + [self appendComponent:@"?"]; + } else { + [self appendComponent:@"&"]; + } + + self.queryParams += 1; + + [self appendComponent:param]; + [self appendComponent:@"="]; + if ([value isKindOfClass:NSString.class]) { + [self escapeAndAppendComponent:value]; + } else { + [self escapeAndAppendComponent:[value description]]; + } +} + +- (NSURL *)URL { + return [NSURL URLWithString:self.URLString]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSCompoundOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSCompoundOperation.h new file mode 100644 index 000000000..63b9362a9 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSCompoundOperation.h @@ -0,0 +1,58 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSFABAsyncOperation.h" + +/** + * If the compound operation is sent a @c -[cancel] message while executing, it will attempt to + * cancel all operations on its internal queue, and will return an error in its @c asyncCompletion + * block with this value as its code. + */ +FOUNDATION_EXPORT const NSUInteger FIRCLSCompoundOperationErrorCodeCancelled; + +/** + * If one or more of the operations on the @c compoundQueue fail, this operation returns an error + * in its @c asyncCompletion block with this code, and an array of @c NSErrors keyed on @c + * FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors in the @c userInfo dictionary. + */ +FOUNDATION_EXPORT const NSUInteger FIRCLSCompoundOperationErrorCodeSuboperationFailed; + +/** + * When all the operations complete, this @c FIRCLSCompoundOperation instance's @c asyncCompletion + * block is called. If any errors were passed by the suboperations' @c asyncCompletion blocks, they + * are put in an array which can be accessed in the @c userInfo dictionary in the error parameter + * for this instance's @c asyncCompletion block. + */ +FOUNDATION_EXPORT NSString *const FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors; + +/** + * An operation that executes a collection of suboperations on an internal private queue. Any + * instance of @c FIRCLSFABAsyncOperation passed into this instance's @c operations property has the + * potential to return an @c NSError in its @c asyncCompletion block. This instance's @c + * asyncCompletion block will put all such errors in an @c NSArray and return an @c NSError whose @c + * userInfo contains that array keyed by @c FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors. + */ +@interface FIRCLSCompoundOperation : FIRCLSFABAsyncOperation + +/** + * An array of @c NSOperations to execute, which can include instances of @c FIRCLSFABAsyncOperation + * or + * @c FIRCLSCompoundOperation. This operation will not be marked as finished until all suboperations + * are marked as finished. + */ +@property(copy, nonatomic) NSArray *operations; + +@property(strong, nonatomic, readonly) NSOperationQueue *compoundQueue; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSCompoundOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSCompoundOperation.m new file mode 100644 index 000000000..5dcf42885 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSCompoundOperation.m @@ -0,0 +1,165 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSCompoundOperation.h" + +#import "FIRCLSFABAsyncOperation_Private.h" + +#define FIRCLS_DISPATCH_QUEUES_AS_OBJECTS OS_OBJECT_USE_OBJC_RETAIN_RELEASE + +const NSUInteger FIRCLSCompoundOperationErrorCodeCancelled = UINT_MAX - 1; +const NSUInteger FIRCLSCompoundOperationErrorCodeSuboperationFailed = UINT_MAX - 2; + +NSString *const FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors = + @"com.google.firebase.crashlytics.FIRCLSCompoundOperation.error.user-info-key.underlying-" + @"errors"; + +static NSString *const FIRCLSCompoundOperationErrorDomain = + @"com.google.firebase.crashlytics.FIRCLSCompoundOperation.error"; +static char *const FIRCLSCompoundOperationCountingQueueLabel = + "com.google.firebase.crashlytics.FIRCLSCompoundOperation.dispatch-queue.counting-queue"; + +@interface FIRCLSCompoundOperation () + +@property(strong, nonatomic, readwrite) NSOperationQueue *compoundQueue; +@property(assign, nonatomic) NSUInteger completedOperations; +@property(strong, nonatomic) NSMutableArray *errors; +#if FIRCLS_DISPATCH_QUEUES_AS_OBJECTS +@property(strong, nonatomic) dispatch_queue_t countingQueue; +#else +@property(assign, nonatomic) dispatch_queue_t countingQueue; +#endif + +@end + +@implementation FIRCLSCompoundOperation + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _compoundQueue = [[NSOperationQueue alloc] init]; + _completedOperations = 0; + _errors = [NSMutableArray array]; + _countingQueue = + dispatch_queue_create(FIRCLSCompoundOperationCountingQueueLabel, DISPATCH_QUEUE_SERIAL); + + return self; +} + +#if !FIRCLS_DISPATCH_QUEUES_AS_OBJECTS +- (void)dealloc { + if (_countingQueue) { + dispatch_release(_countingQueue); + } +} +#endif + +- (void)main { + for (FIRCLSFABAsyncOperation *operation in self.operations) { + [self injectCompoundAsyncCompletionInOperation:operation]; + [self injectCompoundSyncCompletionInOperation:operation]; + + [self.compoundQueue addOperation:operation]; + } +} + +- (void)cancel { + if (self.compoundQueue.operations.count > 0) { + [self.compoundQueue cancelAllOperations]; + dispatch_sync(self.countingQueue, ^{ + [self attemptCompoundCompletion]; + }); + } else { + for (NSOperation *operation in self.operations) { + [operation cancel]; + } + + // we have to add the operations to the queue in order for their isFinished property to be set + // to true. + [self.compoundQueue addOperations:self.operations waitUntilFinished:NO]; + } + [super cancel]; +} + +- (void)injectCompoundAsyncCompletionInOperation:(FIRCLSFABAsyncOperation *)operation { + __weak FIRCLSCompoundOperation *weakSelf = self; + FIRCLSFABAsyncOperationCompletionBlock originalAsyncCompletion = [operation.asyncCompletion copy]; + FIRCLSFABAsyncOperationCompletionBlock completion = ^(NSError *error) { + __strong FIRCLSCompoundOperation *strongSelf = weakSelf; + + if (originalAsyncCompletion) { + dispatch_sync(strongSelf.countingQueue, ^{ + originalAsyncCompletion(error); + }); + } + + [strongSelf updateCompletionCountsWithError:error]; + }; + operation.asyncCompletion = completion; +} + +- (void)injectCompoundSyncCompletionInOperation:(FIRCLSFABAsyncOperation *)operation { + __weak FIRCLSCompoundOperation *weakSelf = self; + void (^originalSyncCompletion)(void) = [operation.completionBlock copy]; + void (^completion)(void) = ^{ + __strong FIRCLSCompoundOperation *strongSelf = weakSelf; + + if (originalSyncCompletion) { + dispatch_sync(strongSelf.countingQueue, ^{ + originalSyncCompletion(); + }); + } + + dispatch_sync(strongSelf.countingQueue, ^{ + [strongSelf attemptCompoundCompletion]; + }); + }; + operation.completionBlock = completion; +} + +- (void)updateCompletionCountsWithError:(NSError *)error { + dispatch_sync(self.countingQueue, ^{ + if (!error) { + self.completedOperations += 1; + } else { + [self.errors addObject:error]; + } + }); +} + +- (void)attemptCompoundCompletion { + if (self.isCancelled) { + [self finishWithError:[NSError errorWithDomain:FIRCLSCompoundOperationErrorDomain + code:FIRCLSCompoundOperationErrorCodeCancelled + userInfo:@{ + NSLocalizedDescriptionKey : [NSString + stringWithFormat:@"%@ cancelled", self.name] + }]]; + self.asyncCompletion = nil; + } else if (self.completedOperations + self.errors.count == self.operations.count) { + NSError *error = nil; + if (self.errors.count > 0) { + error = [NSError + errorWithDomain:FIRCLSCompoundOperationErrorDomain + code:FIRCLSCompoundOperationErrorCodeSuboperationFailed + userInfo:@{FIRCLSCompoundOperationErrorUserInfoKeyUnderlyingErrors : self.errors}]; + } + [self finishWithError:error]; + } +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation.h new file mode 100644 index 000000000..e5d2c7ef4 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation.h @@ -0,0 +1,39 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +/** + * Completion block that can be called in your subclass implementation. It is up to you when you + * want to call it. + */ +typedef void (^FIRCLSFABAsyncOperationCompletionBlock)(NSError *__nullable error); + +/** + * FIRCLSFABAsyncOperation is a subclass of NSOperation that allows for asynchronous work to be + * performed, for things like networking, IPC or UI-driven logic. Create your own subclasses to + * encapsulate custom logic. + * @warning When subclassing to create your own operations, be sure to call -[finishWithError:] at + * some point, or program execution will hang. + * @see -[finishWithError:] in FIRCLSFABAsyncOperation_Private.h + */ +@interface FIRCLSFABAsyncOperation : NSOperation + +/** + * Add a callback method for consumers of your subclasses to set when the asynchronous work is + * marked as complete with -[finishWithError:]. + */ +@property(copy, nonatomic, nullable) FIRCLSFABAsyncOperationCompletionBlock asyncCompletion; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation.m new file mode 100644 index 000000000..dcad16adf --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation.m @@ -0,0 +1,146 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSFABAsyncOperation.h" + +#import "FIRCLSFABAsyncOperation_Private.h" + +@interface FIRCLSFABAsyncOperation () { + BOOL _internalExecuting; + BOOL _internalFinished; +} + +@property(nonatomic, strong) NSRecursiveLock *lock; + +@end + +@implementation FIRCLSFABAsyncOperation + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + _internalExecuting = NO; + _internalFinished = NO; + + _lock = [[NSRecursiveLock alloc] init]; + _lock.name = [NSString stringWithFormat:@"com.google.firebase.crashlytics.%@-lock", [self class]]; + ; + + return self; +} + +#pragma mark - NSOperation Overrides +- (BOOL)isConcurrent { + return YES; +} + +- (BOOL)isAsynchronous { + return YES; +} + +- (BOOL)isExecuting { + [self.lock lock]; + BOOL result = _internalExecuting; + [self.lock unlock]; + + return result; +} + +- (BOOL)isFinished { + [self.lock lock]; + BOOL result = _internalFinished; + [self.lock unlock]; + + return result; +} + +- (void)start { + if ([self checkForCancellation]) { + return; + } + + [self markStarted]; + + [self main]; +} + +#pragma mark - Utilities +- (void)changeValueForKey:(NSString *)key inBlock:(void (^)(void))block { + [self willChangeValueForKey:key]; + block(); + [self didChangeValueForKey:key]; +} + +- (void)lock:(void (^)(void))block { + [self.lock lock]; + block(); + [self.lock unlock]; +} + +- (BOOL)checkForCancellation { + if ([self isCancelled]) { + [self markDone]; + return YES; + } + + return NO; +} + +#pragma mark - State Management +- (void)unlockedMarkFinished { + [self changeValueForKey:@"isFinished" + inBlock:^{ + self->_internalFinished = YES; + }]; +} + +- (void)unlockedMarkStarted { + [self changeValueForKey:@"isExecuting" + inBlock:^{ + self->_internalExecuting = YES; + }]; +} + +- (void)unlockedMarkComplete { + [self changeValueForKey:@"isExecuting" + inBlock:^{ + self->_internalExecuting = NO; + }]; +} + +- (void)markStarted { + [self lock:^{ + [self unlockedMarkStarted]; + }]; +} + +- (void)markDone { + [self lock:^{ + [self unlockedMarkComplete]; + [self unlockedMarkFinished]; + }]; +} + +#pragma mark - Protected +- (void)finishWithError:(NSError *)error { + if (self.asyncCompletion) { + self.asyncCompletion(error); + } + [self markDone]; +} + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation_Private.h new file mode 100644 index 000000000..d1e5797b3 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSFABAsyncOperation_Private.h @@ -0,0 +1,32 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSFABAsyncOperation.h" + +@interface FIRCLSFABAsyncOperation (Private) + +/** + * Subclasses must call this method when they are done performing work. When it is called is up to + * you; it can be directly after kicking of a network request, say, or in the callback for its + * response. Once this method is called, the operation queue it is on will begin executing the next + * waiting operation. If you directly invoked -[start] on the instance, execution will proceed to + * the next code statement. + * @note as soon as this method is called, @c NSOperation's standard @c completionBlock will be + * executed if one exists, as a result of setting the operation's isFinished property to YES, and + * the asyncCompletion block is called. + * @param error Any error to pass to asyncCompletion, or nil if there is none. + */ +- (void)finishWithError:(NSError *__nullable)error; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSOperation.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSOperation.h new file mode 100644 index 000000000..83fc69473 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSOperation/FIRCLSOperation.h @@ -0,0 +1,19 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#import "FIRCLSCompoundOperation.h" +#import "FIRCLSFABAsyncOperation.h" +#import "FIRCLSFABAsyncOperation_Private.h" diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSUUID.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSUUID.h new file mode 100644 index 000000000..dc3aedae2 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSUUID.h @@ -0,0 +1,27 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "FIRCLSConstants.h" + +/** + * Generates and returns a UUID + * This is also used by used by Answers to generate UUIDs. + */ +NSString *FIRCLSGenerateUUID(void); + +/** + * Converts the input uint8_t UUID to NSString + */ +NSString *FIRCLSUUIDToNSString(const uint8_t *uuid); diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSUUID.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSUUID.m new file mode 100644 index 000000000..6534d417f --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Shared/FIRCLSUUID.m @@ -0,0 +1,39 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "FIRCLSUUID.h" + +#import "FIRCLSByteUtility.h" + +static NSInteger const FIRCLSUUIDStringLength = 33; + +#pragma mark Public methods + +NSString *FIRCLSGenerateUUID(void) { + NSString *string; + + CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); + string = CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid)); + CFRelease(uuid); + + return string; +} + +NSString *FIRCLSUUIDToNSString(const uint8_t *uuid) { + char uuidString[FIRCLSUUIDStringLength]; + + FIRCLSSafeHexToString(uuid, 16, uuidString); + + return [NSString stringWithUTF8String:uuidString]; +} diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/third_party/libunwind/dwarf.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/third_party/libunwind/dwarf.h new file mode 100644 index 000000000..9c81868a0 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/third_party/libunwind/dwarf.h @@ -0,0 +1,256 @@ +/* libunwind - a platform-independent unwind library + Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P. + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#pragma once + +// +#define DWARF_EXTENDED_LENGTH_FLAG (0xffffffff) +#define DWARF_CIE_ID_CIE_FLAG (0) + +// Exception Handling Pointer Encoding constants +#define DW_EH_PE_VALUE_MASK (0x0F) +#define DW_EH_PE_RELATIVE_OFFSET_MASK (0x70) + +// Register Definitions +#define DW_EN_MAX_REGISTER_NUMBER (120) + +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + + DW_EH_PE_absptr = 0x00, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + +// Unwind Instructions + +#define DW_CFA_OPCODE_MASK (0xC0) +#define DW_CFA_OPERAND_MASK (0x3F) + +enum { + DW_CFA_nop = 0x0, + DW_CFA_set_loc = 0x1, + DW_CFA_advance_loc1 = 0x2, + DW_CFA_advance_loc2 = 0x3, + DW_CFA_advance_loc4 = 0x4, + DW_CFA_offset_extended = 0x5, + DW_CFA_restore_extended = 0x6, + DW_CFA_undefined = 0x7, + DW_CFA_same_value = 0x8, + DW_CFA_register = 0x9, + DW_CFA_remember_state = 0xA, + DW_CFA_restore_state = 0xB, + DW_CFA_def_cfa = 0xC, + DW_CFA_def_cfa_register = 0xD, + DW_CFA_def_cfa_offset = 0xE, + DW_CFA_def_cfa_expression = 0xF, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + // opcode is in high 2 bits, operand in is lower 6 bits + DW_CFA_advance_loc = 0x40, // operand is delta + DW_CFA_offset = 0x80, // operand is register + DW_CFA_restore = 0xC0, // operand is register + + // GNU extensions + DW_CFA_GNU_window_save = 0x2D, + DW_CFA_GNU_args_size = 0x2E, + DW_CFA_GNU_negative_offset_extended = 0x2F +}; + +// Expression Instructions +enum { + DW_OP_addr = 0x03, + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0A, + DW_OP_const2s = 0x0B, + DW_OP_const4u = 0x0C, + DW_OP_const4s = 0x0D, + DW_OP_const8u = 0x0E, + DW_OP_const8s = 0x0F, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1A, + DW_OP_div = 0x1B, + DW_OP_minus = 0x1C, + DW_OP_mod = 0x1D, + DW_OP_mul = 0x1E, + DW_OP_neg = 0x1F, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2F, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2A, + DW_OP_gt = 0x2B, + DW_OP_le = 0x2C, + DW_OP_lt = 0x2D, + DW_OP_ne = 0x2E, + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3A, + DW_OP_lit11 = 0x3B, + DW_OP_lit12 = 0x3C, + DW_OP_lit13 = 0x3D, + DW_OP_lit14 = 0x3E, + DW_OP_lit15 = 0x3F, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4A, + DW_OP_lit27 = 0x4B, + DW_OP_lit28 = 0x4C, + DW_OP_lit29 = 0x4D, + DW_OP_lit30 = 0x4E, + DW_OP_lit31 = 0x4F, + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5A, + DW_OP_reg11 = 0x5B, + DW_OP_reg12 = 0x5C, + DW_OP_reg13 = 0x5D, + DW_OP_reg14 = 0x5E, + DW_OP_reg15 = 0x5F, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6A, + DW_OP_reg27 = 0x6B, + DW_OP_reg28 = 0x6C, + DW_OP_reg29 = 0x6D, + DW_OP_reg30 = 0x6E, + DW_OP_reg31 = 0x6F, + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7A, + DW_OP_breg11 = 0x7B, + DW_OP_breg12 = 0x7C, + DW_OP_breg13 = 0x7D, + DW_OP_breg14 = 0x7E, + DW_OP_breg15 = 0x7F, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8A, + DW_OP_breg27 = 0x8B, + DW_OP_breg28 = 0x8C, + DW_OP_breg29 = 0x8D, + DW_OP_breg30 = 0x8E, + DW_OP_breg31 = 0x8F, + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + DW_OP_push_object_addres = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9A, + DW_OP_lo_user = 0xE0, + DW_OP_APPLE_uninit = 0xF0, + DW_OP_hi_user = 0xFF +}; diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRAppInternal.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRAppInternal.h new file mode 100644 index 000000000..9a0c943db --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRAppInternal.h @@ -0,0 +1,173 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIRApp.h" +#else +#import +#endif + +// The has_include is a workaround so the old IID needed for the FIS tests can find FIRErrors.h +#if __has_include("FirebaseCore/Sources/Private/FIRErrors.h") +#import "FirebaseCore/Sources/Private/FIRErrors.h" +#else +#import +#endif + +@class FIRComponentContainer; +@protocol FIRLibrary; + +/** + * The internal interface to FIRApp. This is meant for first-party integrators, who need to receive + * FIRApp notifications, log info about the success or failure of their configuration, and access + * other internal functionality of FIRApp. + * + * TODO(b/28296561): Restructure this header. + */ +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, FIRConfigType) { + FIRConfigTypeCore = 1, + FIRConfigTypeSDK = 2, +}; + +extern NSString *const kFIRDefaultAppName; +extern NSString *const kFIRAppReadyToConfigureSDKNotification; +extern NSString *const kFIRAppDeleteNotification; +extern NSString *const kFIRAppIsDefaultAppKey; +extern NSString *const kFIRAppNameKey; +extern NSString *const kFIRGoogleAppIDKey; + +/** + * The format string for the User Defaults key used for storing the data collection enabled flag. + * This includes formatting to append the Firebase App's name. + */ +extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat; + +/** + * The plist key used for storing the data collection enabled flag. + */ +extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey; + +/** + * A notification fired containing diagnostic information when SDK errors occur. + */ +extern NSString *const kFIRAppDiagnosticsNotification; + +/** @var FIRAuthStateDidChangeInternalNotification + @brief The name of the @c NSNotificationCenter notification which is posted when the auth state + changes (e.g. a new token has been produced, a user logs in or out). The object parameter of + the notification is a dictionary possibly containing the key: + @c FIRAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not + contain this key it indicates a sign-out event took place. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotification; + +/** @var FIRAuthStateDidChangeInternalNotificationTokenKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the new access token. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey; + +/** @var FIRAuthStateDidChangeInternalNotificationAppKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the FIRApp associated with the auth instance. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey; + +/** @var FIRAuthStateDidChangeInternalNotificationUIDKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the new user's UID (or nil if there is no longer a user signed in). + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey; + +@interface FIRApp () + +/** + * A flag indicating if this is the default app (has the default app name). + */ +@property(nonatomic, readonly) BOOL isDefaultApp; + +/* + * The container of interop SDKs for this app. + */ +@property(nonatomic) FIRComponentContainer *container; + +/** + * Creates an error for failing to configure a subspec service. This method is called by each + * FIRApp notification listener. + */ ++ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain + errorCode:(FIRErrorCode)code + service:(NSString *)service + reason:(NSString *)reason; +/** + * Checks if the default app is configured without trying to configure it. + */ ++ (BOOL)isDefaultAppConfigured; + +/** + * Registers a given third-party library with the given version number to be reported for + * analytics. + * + * @param name Name of the library. + * @param version Version of the library. + */ ++ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version; + +/** + * Registers a given internal library with the given version number to be reported for + * analytics. + * + * @param library Optional parameter for component registration. + * @param name Name of the library. + * @param version Version of the library. + */ ++ (void)registerInternalLibrary:(nonnull Class)library + withName:(nonnull NSString *)name + withVersion:(nonnull NSString *)version; + +/** + * A concatenated string representing all the third-party libraries and version numbers. + */ ++ (NSString *)firebaseUserAgent; + +/** + * Used by each SDK to send logs about SDK configuration status to Clearcut. + * + * @note This API is a no-op, please remove calls to it. + */ +- (void)sendLogsWithServiceName:(NSString *)serviceName + version:(NSString *)version + error:(NSError *)error; + +/** + * Can be used by the unit tests in eack SDK to reset FIRApp. This method is thread unsafe. + */ ++ (void)resetApps; + +/** + * Can be used by the unit tests in each SDK to set customized options. + */ +- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponent.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponent.h new file mode 100644 index 000000000..cb51ee70e --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponent.h @@ -0,0 +1,91 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class FIRApp; +@class FIRComponentContainer; + +NS_ASSUME_NONNULL_BEGIN + +/// Provides a system to clean up cached instances returned from the component system. +NS_SWIFT_NAME(ComponentLifecycleMaintainer) +@protocol FIRComponentLifecycleMaintainer +/// The associated app will be deleted, clean up any resources as they are about to be deallocated. +- (void)appWillBeDeleted:(FIRApp *)app; +@end + +typedef _Nullable id (^FIRComponentCreationBlock)(FIRComponentContainer *container, + BOOL *isCacheable) + NS_SWIFT_NAME(ComponentCreationBlock); + +@class FIRDependency; + +/// Describes the timing of instantiation. Note: new components should default to lazy unless there +/// is a strong reason to be eager. +typedef NS_ENUM(NSInteger, FIRInstantiationTiming) { + FIRInstantiationTimingLazy, + FIRInstantiationTimingAlwaysEager, + FIRInstantiationTimingEagerInDefaultApp +} NS_SWIFT_NAME(InstantiationTiming); + +/// A component that can be used from other Firebase SDKs. +NS_SWIFT_NAME(Component) +@interface FIRComponent : NSObject + +/// The protocol describing functionality provided from the Component. +@property(nonatomic, strong, readonly) Protocol *protocol; + +/// The timing of instantiation. +@property(nonatomic, readonly) FIRInstantiationTiming instantiationTiming; + +/// An array of dependencies for the component. +@property(nonatomic, copy, readonly) NSArray *dependencies; + +/// A block to instantiate an instance of the component with the appropriate dependencies. +@property(nonatomic, copy, readonly) FIRComponentCreationBlock creationBlock; + +// There's an issue with long NS_SWIFT_NAMES that causes compilation to fail, disable clang-format +// for the next two methods. +// clang-format off + +/// Creates a component with no dependencies that will be lazily initialized. ++ (instancetype)componentWithProtocol:(Protocol *)protocol + creationBlock:(FIRComponentCreationBlock)creationBlock +NS_SWIFT_NAME(init(_:creationBlock:)); + +/// Creates a component to be registered with the component container. +/// +/// @param protocol - The protocol describing functionality provided by the component. +/// @param instantiationTiming - When the component should be initialized. Use .lazy unless there's +/// a good reason to be instantiated earlier. +/// @param dependencies - Any dependencies the `implementingClass` has, optional or required. +/// @param creationBlock - A block to instantiate the component with a container, and if +/// @return A component that can be registered with the component container. ++ (instancetype)componentWithProtocol:(Protocol *)protocol + instantiationTiming:(FIRInstantiationTiming)instantiationTiming + dependencies:(NSArray *)dependencies + creationBlock:(FIRComponentCreationBlock)creationBlock +NS_SWIFT_NAME(init(_:instantiationTiming:dependencies:creationBlock:)); + +// clang-format on + +/// Unavailable. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponentContainer.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponentContainer.h new file mode 100644 index 000000000..db2bafef8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponentContainer.h @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#import + +// The has_include is a workaround so the old IID needed for the FIS tests can find the headers. +#if __has_include("FirebaseCore/Sources/Private/FIRComponentType.h") +#import "FirebaseCore/Sources/Private/FIRComponentType.h" +#import "FirebaseCore/Sources/Private/FIRLibrary.h" +#else +#import +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/// A type-safe macro to retrieve a component from a container. This should be used to retrieve +/// components instead of using the container directly. +#define FIR_COMPONENT(type, container) \ + [FIRComponentType> instanceForProtocol:@protocol(type) inContainer:container] + +@class FIRApp; + +/// A container that holds different components that are registered via the +/// `registerAsComponentRegistrant:` call. These classes should conform to `FIRComponentRegistrant` +/// in order to properly register components for Core. +NS_SWIFT_NAME(FirebaseComponentContainer) +@interface FIRComponentContainer : NSObject + +/// A weak reference to the app that an instance of the container belongs to. +@property(nonatomic, weak, readonly) FIRApp *app; + +/// Unavailable. Use the `container` property on `FIRApp`. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponentType.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponentType.h new file mode 100644 index 000000000..6f2aca7b8 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRComponentType.h @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class FIRComponentContainer; + +NS_ASSUME_NONNULL_BEGIN + +/// Do not use directly. A placeholder type in order to provide a macro that will warn users of +/// mis-matched protocols. +NS_SWIFT_NAME(ComponentType) +@interface FIRComponentType<__covariant T> : NSObject + +/// Do not use directly. A factory method to retrieve an instance that provides a specific +/// functionality. ++ (T)instanceForProtocol:(Protocol *)protocol inContainer:(FIRComponentContainer *)container; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h new file mode 100644 index 000000000..76c0c05f0 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h @@ -0,0 +1,35 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class FIRDiagnosticsData; +@class FIROptions; + +NS_ASSUME_NONNULL_BEGIN + +/** Connects FIRCore with the CoreDiagnostics library. */ +@interface FIRCoreDiagnosticsConnector : NSObject + +/** Logs FirebaseCore related data. + * + * @param options The options object containing data to log. + */ ++ (void)logCoreTelemetryWithOptions:(FIROptions *)options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRDependency.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRDependency.h new file mode 100644 index 000000000..46e9b7ea6 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRDependency.h @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// A dependency on a specific protocol's functionality. +NS_SWIFT_NAME(Dependency) +@interface FIRDependency : NSObject + +/// The protocol describing functionality being depended on. +@property(nonatomic, strong, readonly) Protocol *protocol; + +/// A flag to specify if the dependency is required or not. +@property(nonatomic, readonly) BOOL isRequired; + +/// Initializes a dependency that is required. Calls `initWithProtocol:isRequired` with `YES` for +/// the required parameter. +/// Creates a required dependency on the specified protocol's functionality. ++ (instancetype)dependencyWithProtocol:(Protocol *)protocol; + +/// Creates a dependency on the specified protocol's functionality and specify if it's required for +/// the class's functionality. ++ (instancetype)dependencyWithProtocol:(Protocol *)protocol isRequired:(BOOL)required; + +/// Use `dependencyWithProtocol:isRequired:` instead. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRErrorCode.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRErrorCode.h new file mode 100644 index 000000000..c90d9eecf --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRErrorCode.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Error codes in Firebase error domain. */ +typedef NS_ENUM(NSInteger, FIRErrorCode) { + /** + * Unknown error. + */ + FIRErrorCodeUnknown = 0, + /** + * Loading data from the GoogleService-Info.plist file failed. This is a fatal error and should + * not be ignored. Further calls to the API will fail and/or possibly cause crashes. + */ + FIRErrorCodeInvalidPlistFile = -100, + + /** + * Validating the Google App ID format failed. + */ + FIRErrorCodeInvalidAppID = -101, + + /** + * Error code for failing to configure a specific service. It's deprecated, but + * still used after copybara. + */ + FIRErrorCodeConfigFailed = -114, +}; diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRErrors.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRErrors.h new file mode 100644 index 000000000..19e47328a --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRErrors.h @@ -0,0 +1,24 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#include "FIRErrorCode.h" + +extern NSString *const kFirebaseErrorDomain; +extern NSString *const kFirebaseConfigErrorDomain; +extern NSString *const kFirebaseCoreErrorDomain; +extern NSString *const kFirebasePerfErrorDomain; diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h new file mode 100644 index 000000000..bfff73e5d --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h @@ -0,0 +1,39 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRHeartbeatInfo : NSObject + +// Enum representing the different heartbeat codes. +typedef NS_ENUM(NSInteger, FIRHeartbeatInfoCode) { + FIRHeartbeatInfoCodeNone = 0, + FIRHeartbeatInfoCodeSDK = 1, + FIRHeartbeatInfoCodeGlobal = 2, + FIRHeartbeatInfoCodeCombined = 3, +}; + +/** + * Get heartbeat code requred for the sdk. + * @param heartbeatTag String representing the sdk heartbeat tag. + * @return Heartbeat code indicating whether or not an sdk/global heartbeat + * needs to be sent + */ ++ (FIRHeartbeatInfoCode)heartbeatCodeForTag:(NSString *)heartbeatTag; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRLibrary.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRLibrary.h new file mode 100644 index 000000000..e7a9e077c --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRLibrary.h @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRLibrary_h +#define FIRLibrary_h + +#import + +// The has_include is a workaround so the old IID needed for the FIS tests can find the headers. +#if __has_include("FirebaseCore/Sources/Private/FIRComponent.h") +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#else +#import +#endif + +@class FIRApp; + +NS_ASSUME_NONNULL_BEGIN + +/// Provide an interface to register a library for userAgent logging and availability to others. +NS_SWIFT_NAME(Library) +@protocol FIRLibrary + +/// Returns one or more FIRComponents that will be registered in +/// FIRApp and participate in dependency resolution and injection. ++ (NSArray *)componentsToRegister; + +@optional +/// Implement this method if the library needs notifications for lifecycle events. This method is +/// called when the developer calls `FirebaseApp.configure()`. ++ (void)configureWithApp:(FIRApp *)app; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* FIRLibrary_h */ diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRLogger.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRLogger.h new file mode 100644 index 000000000..6fd778441 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIRLogger.h @@ -0,0 +1,156 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIRLoggerLevel.h" +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + * The Firebase services used in Firebase logger. + */ +typedef NSString *const FIRLoggerService; + +extern FIRLoggerService kFIRLoggerABTesting; +extern FIRLoggerService kFIRLoggerAdMob; +extern FIRLoggerService kFIRLoggerAnalytics; +extern FIRLoggerService kFIRLoggerAuth; +extern FIRLoggerService kFIRLoggerCrash; +extern FIRLoggerService kFIRLoggerCore; +extern FIRLoggerService kFIRLoggerMLKit; +extern FIRLoggerService kFIRLoggerPerf; +extern FIRLoggerService kFIRLoggerRemoteConfig; + +/** + * The key used to store the logger's error count. + */ +extern NSString *const kFIRLoggerErrorCountKey; + +/** + * The key used to store the logger's warning count. + */ +extern NSString *const kFIRLoggerWarningCountKey; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * Enables or disables Analytics debug mode. + * If set to YES, the logging level for Analytics will be set to FIRLoggerLevelDebug. + * Enabling the debug mode has no effect if the app is running from App Store. + * (required) analytics debug mode flag. + */ +void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode); + +/** + * Changes the default logging level of FIRLoggerLevelNotice to a user-specified level. + * The default level cannot be set above FIRLoggerLevelNotice if the app is running from App Store. + * (required) log level (one of the FIRLoggerLevel enum values). + */ +void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel); + +/** + * Checks if the specified logger level is loggable given the current settings. + * (required) log level (one of the FIRLoggerLevel enum values). + * (required) whether or not this function is called from the Analytics component. + */ +BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent); + +/** + * Logs a message to the Xcode console and the device log. If running from AppStore, will + * not log any messages with a level higher than FIRLoggerLevelNotice to avoid log spamming. + * (required) log level (one of the FIRLoggerLevel enum values). + * (required) service name of type FIRLoggerService. + * (required) message code starting with "I-" which means iOS, followed by a capitalized + * three-character service identifier and a six digit integer message ID that is unique + * within the service. + * An example of the message code is @"I-COR000001". + * (required) message string which can be a format string. + * (optional) variable arguments list obtained from calling va_start, used when message is a format + * string. + */ +extern void FIRLogBasic(FIRLoggerLevel level, + FIRLoggerService service, + NSString *messageCode, + NSString *message, +// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable +// See: http://stackoverflow.com/q/29095469 +#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX + va_list args_ptr +#else + va_list _Nullable args_ptr +#endif +); + +/** + * The following functions accept the following parameters in order: + * (required) service name of type FIRLoggerService. + * (required) message code starting from "I-" which means iOS, followed by a capitalized + * three-character service identifier and a six digit integer message ID that is unique + * within the service. + * An example of the message code is @"I-COR000001". + * See go/firebase-log-proposal for details. + * (required) message string which can be a format string. + * (optional) the list of arguments to substitute into the format string. + * Example usage: + * FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name); + */ +extern void FIRLogError(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogWarning(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogNotice(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogInfo(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogDebug(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +@interface FIRLoggerWrapper : NSObject + +/** + * Objective-C wrapper for FIRLogBasic to allow weak linking to FIRLogger + * (required) log level (one of the FIRLoggerLevel enum values). + * (required) service name of type FIRLoggerService. + * (required) message code starting with "I-" which means iOS, followed by a capitalized + * three-character service identifier and a six digit integer message ID that is unique + * within the service. + * An example of the message code is @"I-COR000001". + * (required) message string which can be a format string. + * (optional) variable arguments list obtained from calling va_start, used when message is a format + * string. + */ + ++ (void)logWithLevel:(FIRLoggerLevel)level + withService:(FIRLoggerService)service + withCode:(NSString *)messageCode + withMessage:(NSString *)message + withArgs:(va_list)args; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIROptionsInternal.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIROptionsInternal.h new file mode 100644 index 000000000..acaf45868 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FIROptionsInternal.h @@ -0,0 +1,119 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIROptions.h" +#else +#import +#endif + +/** + * Keys for the strings in the plist file. + */ +extern NSString *const kFIRAPIKey; +extern NSString *const kFIRTrackingID; +extern NSString *const kFIRGoogleAppID; +extern NSString *const kFIRClientID; +extern NSString *const kFIRGCMSenderID; +extern NSString *const kFIRAndroidClientID; +extern NSString *const kFIRDatabaseURL; +extern NSString *const kFIRStorageBucket; +extern NSString *const kFIRBundleID; +extern NSString *const kFIRProjectID; + +/** + * Keys for the plist file name + */ +extern NSString *const kServiceInfoFileName; + +extern NSString *const kServiceInfoFileType; + +/** + * This header file exposes the initialization of FIROptions to internal use. + */ +@interface FIROptions () + +/** + * resetDefaultOptions and initInternalWithOptionsDictionary: are exposed only for unit tests. + */ ++ (void)resetDefaultOptions; + +/** + * Initializes the options with dictionary. The above strings are the keys of the dictionary. + * This is the designated initializer. + */ +- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary; + +/** + * defaultOptions and defaultOptionsDictionary are exposed in order to be used in FIRApp and + * other first party services. + */ ++ (FIROptions *)defaultOptions; + ++ (NSDictionary *)defaultOptionsDictionary; + +/** + * Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at + * runtime. + */ +@property(nonatomic, readonly) BOOL isAnalyticsCollectionExplicitlySet; + +/** + * Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless + * explicitly disabled in GoogleService-Info.plist. + */ +@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled; + +/** + * Whether or not Analytics Collection was completely disabled. If YES, then + * isAnalyticsCollectionEnabled will be NO. + */ +@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated; + +/** + * The version ID of the client library, e.g. @"1100000". + */ +@property(nonatomic, readonly, copy) NSString *libraryVersionID; + +/** + * The flag indicating whether this object was constructed with the values in the default plist + * file. + */ +@property(nonatomic) BOOL usingOptionsFromDefaultPlist; + +/** + * Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in + * GoogleService-Info.plist. + */ +@property(nonatomic, readonly) BOOL isMeasurementEnabled; + +/** + * Whether or not Analytics was enabled in the developer console. + */ +@property(nonatomic, readonly) BOOL isAnalyticsEnabled; + +/** + * Whether or not SignIn was enabled in the developer console. + */ +@property(nonatomic, readonly) BOOL isSignInEnabled; + +/** + * Whether or not editing is locked. This should occur after FIROptions has been set on a FIRApp. + */ +@property(nonatomic, getter=isEditingLocked) BOOL editingLocked; + +@end diff --git a/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FirebaseCoreInternal.h b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FirebaseCoreInternal.h new file mode 100644 index 000000000..93af6cb8d --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/FirebaseCore/Sources/Private/FirebaseCoreInternal.h @@ -0,0 +1,31 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// An umbrella header, for any other libraries in this repo to access Firebase Public and Private +// headers. Any package manager complexity should be handled here. + +#if SWIFT_PACKAGE +@import FirebaseCore; +#else +#import +#endif + +#import "FirebaseCore/Sources/Private/FIRAppInternal.h" +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#import "FirebaseCore/Sources/Private/FIRComponentContainer.h" +#import "FirebaseCore/Sources/Private/FIRDependency.h" +#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h" +#import "FirebaseCore/Sources/Private/FIRLibrary.h" +#import "FirebaseCore/Sources/Private/FIRLogger.h" +#import "FirebaseCore/Sources/Private/FIROptionsInternal.h" diff --git a/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRAnalyticsInterop.h b/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRAnalyticsInterop.h new file mode 100644 index 000000000..6581b5368 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRAnalyticsInterop.h @@ -0,0 +1,66 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@protocol FIRAnalyticsInteropListener; + +NS_ASSUME_NONNULL_BEGIN + +/// Block typedef callback parameter to getUserPropertiesWithCallback:. +typedef void (^FIRAInteropUserPropertiesCallback)(NSDictionary *userProperties); + +/// Connector for bridging communication between Firebase SDKs and FirebaseAnalytics API. +@protocol FIRAnalyticsInterop + +/// Sets user property when trigger event is logged. This API is only available in the SDK. +- (void)setConditionalUserProperty:(NSDictionary *)conditionalUserProperty; + +/// Clears user property if set. +- (void)clearConditionalUserProperty:(NSString *)userPropertyName + forOrigin:(NSString *)origin + clearEventName:(NSString *)clearEventName + clearEventParameters:(NSDictionary *)clearEventParameters; + +/// Returns currently set user properties. +- (NSArray *> *)conditionalUserProperties:(NSString *)origin + propertyNamePrefix: + (NSString *)propertyNamePrefix; + +/// Returns the maximum number of user properties. +- (NSInteger)maxUserProperties:(NSString *)origin; + +/// Returns the user properties to a callback function. +- (void)getUserPropertiesWithCallback:(FIRAInteropUserPropertiesCallback)callback; + +/// Logs events. +- (void)logEventWithOrigin:(NSString *)origin + name:(NSString *)name + parameters:(nullable NSDictionary *)parameters; + +/// Sets user property. +- (void)setUserPropertyWithOrigin:(NSString *)origin name:(NSString *)name value:(id)value; + +/// Registers an Analytics listener for the given origin. +- (void)registerAnalyticsListener:(id)listener + withOrigin:(NSString *)origin; + +/// Unregisters an Analytics listener for the given origin. +- (void)unregisterAnalyticsListenerWithOrigin:(NSString *)origin; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRAnalyticsInteropListener.h b/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRAnalyticsInteropListener.h new file mode 100644 index 000000000..45cde5506 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRAnalyticsInteropListener.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// Handles events and messages from Analytics. +@protocol FIRAnalyticsInteropListener + +/// Triggers when an Analytics event happens for the registered origin with +/// `FIRAnalyticsInterop`s `registerAnalyticsListener:withOrigin:`. +- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters; + +@end \ No newline at end of file diff --git a/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRInteropEventNames.h b/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRInteropEventNames.h new file mode 100644 index 000000000..efc54ab22 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRInteropEventNames.h @@ -0,0 +1,28 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// @file FIRInteropEventNames.h + +#import + +/// Notification open event name. +static NSString *const kFIRIEventNotificationOpen = @"_no"; + +/// Notification foreground event name. +static NSString *const kFIRIEventNotificationForeground = @"_nf"; + +/// Campaign event name. +static NSString *const kFIRIEventFirebaseCampaign = @"_cmp"; diff --git a/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRInteropParameterNames.h b/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRInteropParameterNames.h new file mode 100644 index 000000000..ae440becf --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/Interop/Analytics/Public/FIRInteropParameterNames.h @@ -0,0 +1,73 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +/// @file FIRInteropParameterNames.h +/// +/// Predefined event parameter names used by Firebase. This file is a subset of the +/// FirebaseAnalytics FIRParameterNames.h public header. +/// +/// The origin of your traffic, such as an Ad network (for example, google) or partner (urban +/// airship). Identify the advertiser, site, publication, etc. that is sending traffic to your +/// property. Highly recommended (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterSource : @"InMobi",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRIParameterSource NS_SWIFT_NAME(AnalyticsParameterSource) = @"source"; + +/// The advertising or marketing medium, for example: cpc, banner, email, push. Highly recommended +/// (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterMedium : @"email",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRIParameterMedium NS_SWIFT_NAME(AnalyticsParameterMedium) = @"medium"; + +/// The individual campaign name, slogan, promo code, etc. Some networks have pre-defined macro to +/// capture campaign information, otherwise can be populated by developer. Highly Recommended +/// (NSString). +///
+///     NSDictionary *params = @{
+///       kFIRParameterCampaign : @"winter_promotion",
+///       // ...
+///     };
+/// 
+static NSString *const kFIRIParameterCampaign NS_SWIFT_NAME(AnalyticsParameterCampaign) = + @"campaign"; + +/// Message identifier. +static NSString *const kFIRIParameterMessageIdentifier = @"_nmid"; + +/// Message name. +static NSString *const kFIRIParameterMessageName = @"_nmn"; + +/// Message send time. +static NSString *const kFIRIParameterMessageTime = @"_nmt"; + +/// Message device time. +static NSString *const kFIRIParameterMessageDeviceTime = @"_ndt"; + +/// Topic message. +static NSString *const kFIRIParameterTopic = @"_nt"; + +/// Stores the message_id of the last notification opened by the app. +static NSString *const kFIRIUserPropertyLastNotification = @"_ln"; diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/README.md b/ios/Pods/FirebaseCrashlytics/README.md similarity index 61% rename from ios/Pods/FirebaseCoreDiagnosticsInterop/README.md rename to ios/Pods/FirebaseCrashlytics/README.md index 3ddc8fbd2..d778205ff 100644 --- a/ios/Pods/FirebaseCoreDiagnosticsInterop/README.md +++ b/ios/Pods/FirebaseCrashlytics/README.md @@ -1,10 +1,30 @@ -# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +[![Version](https://img.shields.io/cocoapods/v/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) -This repository contains a subset of the Firebase iOS SDK source. It currently -includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase, -FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, -FirebaseInAppMessagingDisplay, FirebaseMessaging, FirebaseRemoteConfig, and -FirebaseStorage. +[![Actions Status][gh-abtesting-badge]][gh-actions] +[![Actions Status][gh-auth-badge]][gh-actions] +[![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-crashlytics-badge]][gh-actions] +[![Actions Status][gh-database-badge]][gh-actions] +[![Actions Status][gh-datatransport-badge]][gh-actions] +[![Actions Status][gh-dynamiclinks-badge]][gh-actions] +[![Actions Status][gh-firebasepod-badge]][gh-actions] +[![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-functions-badge]][gh-actions] +[![Actions Status][gh-inappmessaging-badge]][gh-actions] +[![Actions Status][gh-interop-badge]][gh-actions] +[![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-remoteconfig-badge]][gh-actions] +[![Actions Status][gh-storage-badge]][gh-actions] +[![Actions Status][gh-symbolcollision-badge]][gh-actions] +[![Actions Status][gh-zip-badge]][gh-actions] +[![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) + +# Firebase Apple Open Source Development + +This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics, +FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The [GoogleUtilities](GoogleUtilities/README.md) pod is @@ -69,12 +89,16 @@ Instructions for the experimental Carthage distribution are at Instructions for installing binary frameworks via [Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md). +### Using Firebase from a Framework or a library + +[Using Firebase from a Framework or a library](docs/firebase_in_libraries.md) + ## Development To develop Firebase software in this repository, ensure that you have at least the following software: - * Xcode 10.1 (or later) + * Xcode 10.3 (or later) * CocoaPods 1.7.2 (or later) * [CocoaPods generate](https://github.com/square/cocoapods-generate) @@ -105,6 +129,10 @@ Firestore has a self contained Xcode project. See See [AddNewPod.md](AddNewPod.md). +### Managing Headers and Imports + +See [HeadersImports.md](HeadersImports.md). + ### Code Formatting To ensure that the code is formatted consistently, run the script @@ -116,8 +144,8 @@ Travis will verify that any code changes are done in a style compliant way. Inst These commands will get the right versions: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/e3496d9/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/7963c3d/Formula/swiftformat.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb +brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb ``` Note: if you already have a newer version of these installed you may need to @@ -131,7 +159,7 @@ match the versions in the CI failure logs Select a scheme and press Command-u to build a component and run its unit tests. -#### Viewing Code Coverage +#### Viewing Code Coverage (Deprecated) First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`. @@ -162,18 +190,26 @@ See the sections below for any special instructions for those components. ### Firebase Auth If you're doing specific Firebase Auth development, see -[the Auth Sample README](Example/Auth/README.md) for instructions about +[the Auth Sample README](FirebaseAuth/Tests/Sample/README.md) for instructions about building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database -To run the Database Integration tests, make your database authentication rules -[public](https://firebase.google.com/docs/database/security/quickstart). +The Firebase Database Integration tests can be run against a locally running Database Emulator +or against a production instance. + +To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before +running the integration test. + +To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to +`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to +[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are +running. ### Firebase Storage To run the Storage Integration tests, follow the instructions in -[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m). +[FIRStorageIntegrationTests.m](FirebaseStorage/Tests/Integration/FIRStorageIntegrationTests.m). #### Push Notifications @@ -199,29 +235,34 @@ We've seen an amazing amount of interest and contributions to improve the Fireba very grateful! We'd like to empower as many developers as we can to be able to use Firebase and participate in the Firebase community. -### tvOS, macOS, and Catalyst -Thanks to contributions from the community, FirebaseABTesting, FirebaseAuth, FirebaseCore, -FirebaseDatabase, FirebaseMessaging, FirebaseFirestore, -FirebaseFunctions, FirebaseRemoteConfig, and FirebaseStorage now compile, run unit tests, and work on -tvOS, macOS, and Catalyst. +### tvOS, macOS, watchOS and Catalyst +Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on +tvOS, macOS, watchOS and Catalyst. For tvOS, checkout the [Sample](Example/tvOSSample). +For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the +[Independent Watch App Sample](Example/watchOSSample). -Keep in mind that macOS, Catalyst and tvOS are not officially supported by Firebase, and this +Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and this repository is actively developed primarily for iOS. While we can catch basic unit test issues with -Travis, there may be some changes where the SDK no longer works as expected on macOS or tvOS. If you +Travis, there may be some changes where the SDK no longer works as expected on macOS, tvOS or watchOS. If you encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues). +During app setup in the console, you may get to a step that mentions something like "Checking if the app +has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst. +**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected. + To install, add a subset of the following to the Podfile: ``` -pod 'Firebase/ABTesting' -pod 'Firebase/Auth' -pod 'Firebase/Database' -pod 'Firebase/Firestore' -pod 'Firebase/Functions' +pod 'Firebase/ABTesting' # No watchOS support yet +pod 'Firebase/Auth' # No watchOS support yet +pod 'Firebase/Crashlytics' # No watchOS support yet +pod 'Firebase/Database' # No watchOS support yet +pod 'Firebase/Firestore' # No watchOS support yet +pod 'Firebase/Functions' # No watchOS support yet pod 'Firebase/Messaging' -pod 'Firebase/RemoteConfig' +pod 'Firebase/RemoteConfig' # No watchOS support yet pod 'Firebase/Storage' ``` @@ -249,3 +290,22 @@ The contents of this repository is licensed under the Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). + +[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg +[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg +[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg +[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg +[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg +[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg +[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg +[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg +[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg +[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg +[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg +[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg +[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg +[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/FirebaseCrashlytics/run b/ios/Pods/FirebaseCrashlytics/run new file mode 100755 index 000000000..9316eeaf7 --- /dev/null +++ b/ios/Pods/FirebaseCrashlytics/run @@ -0,0 +1,76 @@ +#!/bin/sh + +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# run +# +# This script is meant to be run as a Run Script in the "Build Phases" section +# of your Xcode project. It sends debug symbols to symbolicate stacktraces, +# sends build events to track versions, and onboards apps for Crashlytics. +# +# This script calls upload-symbols twice: +# +# 1) First it calls upload-symbols synchronously in "validation" mode. If the +# script finds issues with the build environment, it will report errors to Xcode. +# In validation mode it exits before doing any time consuming work. +# +# 2) Then it calls upload-symbols in the background to actually send the build +# event and upload symbols. It does this in the background so that it doesn't +# slow down your builds. If an error happens here, you won't see it in Xcode. +# +# You can find the output for the background execution in Console.app, by +# searching for "upload-symbols". +# +# If you want verbose output, you can pass the --debug flag to this script +# + +# Figure out where we're being called from +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +# If the first argument is specified without a dash, treat it as the Fabric API +# Key and add it as an argument. +if [ -z "$1" ] || [[ $1 == -* ]]; then + API_KEY_ARG="" +else + API_KEY_ARG="-a $1"; shift +fi + +# Build up the arguments list, passing through any flags added after the +# API Key +ARGUMENTS="$API_KEY_ARG $@" +VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate" +UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase" + +# Quote the path to handle folders with special characters +COMMAND_PATH="\"$DIR/upload-symbols\" " + +# Ensure params are as expected, run in sync mode to validate, +# and cause a build error if validation fails +eval $COMMAND_PATH$VALIDATE_ARGUMENTS +return_code=$? + +if [[ $return_code != 0 ]]; then + exit $return_code +fi + +# Verification passed, convert and upload cSYMs in the background to prevent +# build delays +# +# Note: Validation is performed again at this step before upload +# +# Note: Output can still be found in Console.app, by searching for +# "upload-symbols" +# +eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 & diff --git a/ios/Pods/FirebaseCrashlytics/upload-symbols b/ios/Pods/FirebaseCrashlytics/upload-symbols new file mode 100755 index 000000000..76738d0c4 Binary files /dev/null and b/ios/Pods/FirebaseCrashlytics/upload-symbols differ diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRAppInternal.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRAppInternal.h new file mode 100644 index 000000000..9a0c943db --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRAppInternal.h @@ -0,0 +1,173 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIRApp.h" +#else +#import +#endif + +// The has_include is a workaround so the old IID needed for the FIS tests can find FIRErrors.h +#if __has_include("FirebaseCore/Sources/Private/FIRErrors.h") +#import "FirebaseCore/Sources/Private/FIRErrors.h" +#else +#import +#endif + +@class FIRComponentContainer; +@protocol FIRLibrary; + +/** + * The internal interface to FIRApp. This is meant for first-party integrators, who need to receive + * FIRApp notifications, log info about the success or failure of their configuration, and access + * other internal functionality of FIRApp. + * + * TODO(b/28296561): Restructure this header. + */ +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, FIRConfigType) { + FIRConfigTypeCore = 1, + FIRConfigTypeSDK = 2, +}; + +extern NSString *const kFIRDefaultAppName; +extern NSString *const kFIRAppReadyToConfigureSDKNotification; +extern NSString *const kFIRAppDeleteNotification; +extern NSString *const kFIRAppIsDefaultAppKey; +extern NSString *const kFIRAppNameKey; +extern NSString *const kFIRGoogleAppIDKey; + +/** + * The format string for the User Defaults key used for storing the data collection enabled flag. + * This includes formatting to append the Firebase App's name. + */ +extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat; + +/** + * The plist key used for storing the data collection enabled flag. + */ +extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey; + +/** + * A notification fired containing diagnostic information when SDK errors occur. + */ +extern NSString *const kFIRAppDiagnosticsNotification; + +/** @var FIRAuthStateDidChangeInternalNotification + @brief The name of the @c NSNotificationCenter notification which is posted when the auth state + changes (e.g. a new token has been produced, a user logs in or out). The object parameter of + the notification is a dictionary possibly containing the key: + @c FIRAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not + contain this key it indicates a sign-out event took place. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotification; + +/** @var FIRAuthStateDidChangeInternalNotificationTokenKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the new access token. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey; + +/** @var FIRAuthStateDidChangeInternalNotificationAppKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the FIRApp associated with the auth instance. + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey; + +/** @var FIRAuthStateDidChangeInternalNotificationUIDKey + @brief A key present in the dictionary object parameter of the + @c FIRAuthStateDidChangeInternalNotification notification. The value associated with this + key will contain the new user's UID (or nil if there is no longer a user signed in). + */ +extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey; + +@interface FIRApp () + +/** + * A flag indicating if this is the default app (has the default app name). + */ +@property(nonatomic, readonly) BOOL isDefaultApp; + +/* + * The container of interop SDKs for this app. + */ +@property(nonatomic) FIRComponentContainer *container; + +/** + * Creates an error for failing to configure a subspec service. This method is called by each + * FIRApp notification listener. + */ ++ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain + errorCode:(FIRErrorCode)code + service:(NSString *)service + reason:(NSString *)reason; +/** + * Checks if the default app is configured without trying to configure it. + */ ++ (BOOL)isDefaultAppConfigured; + +/** + * Registers a given third-party library with the given version number to be reported for + * analytics. + * + * @param name Name of the library. + * @param version Version of the library. + */ ++ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version; + +/** + * Registers a given internal library with the given version number to be reported for + * analytics. + * + * @param library Optional parameter for component registration. + * @param name Name of the library. + * @param version Version of the library. + */ ++ (void)registerInternalLibrary:(nonnull Class)library + withName:(nonnull NSString *)name + withVersion:(nonnull NSString *)version; + +/** + * A concatenated string representing all the third-party libraries and version numbers. + */ ++ (NSString *)firebaseUserAgent; + +/** + * Used by each SDK to send logs about SDK configuration status to Clearcut. + * + * @note This API is a no-op, please remove calls to it. + */ +- (void)sendLogsWithServiceName:(NSString *)serviceName + version:(NSString *)version + error:(NSError *)error; + +/** + * Can be used by the unit tests in eack SDK to reset FIRApp. This method is thread unsafe. + */ ++ (void)resetApps; + +/** + * Can be used by the unit tests in each SDK to set customized options. + */ +- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponent.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponent.h new file mode 100644 index 000000000..cb51ee70e --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponent.h @@ -0,0 +1,91 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class FIRApp; +@class FIRComponentContainer; + +NS_ASSUME_NONNULL_BEGIN + +/// Provides a system to clean up cached instances returned from the component system. +NS_SWIFT_NAME(ComponentLifecycleMaintainer) +@protocol FIRComponentLifecycleMaintainer +/// The associated app will be deleted, clean up any resources as they are about to be deallocated. +- (void)appWillBeDeleted:(FIRApp *)app; +@end + +typedef _Nullable id (^FIRComponentCreationBlock)(FIRComponentContainer *container, + BOOL *isCacheable) + NS_SWIFT_NAME(ComponentCreationBlock); + +@class FIRDependency; + +/// Describes the timing of instantiation. Note: new components should default to lazy unless there +/// is a strong reason to be eager. +typedef NS_ENUM(NSInteger, FIRInstantiationTiming) { + FIRInstantiationTimingLazy, + FIRInstantiationTimingAlwaysEager, + FIRInstantiationTimingEagerInDefaultApp +} NS_SWIFT_NAME(InstantiationTiming); + +/// A component that can be used from other Firebase SDKs. +NS_SWIFT_NAME(Component) +@interface FIRComponent : NSObject + +/// The protocol describing functionality provided from the Component. +@property(nonatomic, strong, readonly) Protocol *protocol; + +/// The timing of instantiation. +@property(nonatomic, readonly) FIRInstantiationTiming instantiationTiming; + +/// An array of dependencies for the component. +@property(nonatomic, copy, readonly) NSArray *dependencies; + +/// A block to instantiate an instance of the component with the appropriate dependencies. +@property(nonatomic, copy, readonly) FIRComponentCreationBlock creationBlock; + +// There's an issue with long NS_SWIFT_NAMES that causes compilation to fail, disable clang-format +// for the next two methods. +// clang-format off + +/// Creates a component with no dependencies that will be lazily initialized. ++ (instancetype)componentWithProtocol:(Protocol *)protocol + creationBlock:(FIRComponentCreationBlock)creationBlock +NS_SWIFT_NAME(init(_:creationBlock:)); + +/// Creates a component to be registered with the component container. +/// +/// @param protocol - The protocol describing functionality provided by the component. +/// @param instantiationTiming - When the component should be initialized. Use .lazy unless there's +/// a good reason to be instantiated earlier. +/// @param dependencies - Any dependencies the `implementingClass` has, optional or required. +/// @param creationBlock - A block to instantiate the component with a container, and if +/// @return A component that can be registered with the component container. ++ (instancetype)componentWithProtocol:(Protocol *)protocol + instantiationTiming:(FIRInstantiationTiming)instantiationTiming + dependencies:(NSArray *)dependencies + creationBlock:(FIRComponentCreationBlock)creationBlock +NS_SWIFT_NAME(init(_:instantiationTiming:dependencies:creationBlock:)); + +// clang-format on + +/// Unavailable. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponentContainer.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponentContainer.h new file mode 100644 index 000000000..db2bafef8 --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponentContainer.h @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#import + +// The has_include is a workaround so the old IID needed for the FIS tests can find the headers. +#if __has_include("FirebaseCore/Sources/Private/FIRComponentType.h") +#import "FirebaseCore/Sources/Private/FIRComponentType.h" +#import "FirebaseCore/Sources/Private/FIRLibrary.h" +#else +#import +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/// A type-safe macro to retrieve a component from a container. This should be used to retrieve +/// components instead of using the container directly. +#define FIR_COMPONENT(type, container) \ + [FIRComponentType> instanceForProtocol:@protocol(type) inContainer:container] + +@class FIRApp; + +/// A container that holds different components that are registered via the +/// `registerAsComponentRegistrant:` call. These classes should conform to `FIRComponentRegistrant` +/// in order to properly register components for Core. +NS_SWIFT_NAME(FirebaseComponentContainer) +@interface FIRComponentContainer : NSObject + +/// A weak reference to the app that an instance of the container belongs to. +@property(nonatomic, weak, readonly) FIRApp *app; + +/// Unavailable. Use the `container` property on `FIRApp`. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponentType.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponentType.h new file mode 100644 index 000000000..6f2aca7b8 --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRComponentType.h @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class FIRComponentContainer; + +NS_ASSUME_NONNULL_BEGIN + +/// Do not use directly. A placeholder type in order to provide a macro that will warn users of +/// mis-matched protocols. +NS_SWIFT_NAME(ComponentType) +@interface FIRComponentType<__covariant T> : NSObject + +/// Do not use directly. A factory method to retrieve an instance that provides a specific +/// functionality. ++ (T)instanceForProtocol:(Protocol *)protocol inContainer:(FIRComponentContainer *)container; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h new file mode 100644 index 000000000..76c0c05f0 --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h @@ -0,0 +1,35 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +@class FIRDiagnosticsData; +@class FIROptions; + +NS_ASSUME_NONNULL_BEGIN + +/** Connects FIRCore with the CoreDiagnostics library. */ +@interface FIRCoreDiagnosticsConnector : NSObject + +/** Logs FirebaseCore related data. + * + * @param options The options object containing data to log. + */ ++ (void)logCoreTelemetryWithOptions:(FIROptions *)options; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRDependency.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRDependency.h new file mode 100644 index 000000000..46e9b7ea6 --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRDependency.h @@ -0,0 +1,45 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// A dependency on a specific protocol's functionality. +NS_SWIFT_NAME(Dependency) +@interface FIRDependency : NSObject + +/// The protocol describing functionality being depended on. +@property(nonatomic, strong, readonly) Protocol *protocol; + +/// A flag to specify if the dependency is required or not. +@property(nonatomic, readonly) BOOL isRequired; + +/// Initializes a dependency that is required. Calls `initWithProtocol:isRequired` with `YES` for +/// the required parameter. +/// Creates a required dependency on the specified protocol's functionality. ++ (instancetype)dependencyWithProtocol:(Protocol *)protocol; + +/// Creates a dependency on the specified protocol's functionality and specify if it's required for +/// the class's functionality. ++ (instancetype)dependencyWithProtocol:(Protocol *)protocol isRequired:(BOOL)required; + +/// Use `dependencyWithProtocol:isRequired:` instead. +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRErrorCode.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRErrorCode.h new file mode 100644 index 000000000..c90d9eecf --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRErrorCode.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** Error codes in Firebase error domain. */ +typedef NS_ENUM(NSInteger, FIRErrorCode) { + /** + * Unknown error. + */ + FIRErrorCodeUnknown = 0, + /** + * Loading data from the GoogleService-Info.plist file failed. This is a fatal error and should + * not be ignored. Further calls to the API will fail and/or possibly cause crashes. + */ + FIRErrorCodeInvalidPlistFile = -100, + + /** + * Validating the Google App ID format failed. + */ + FIRErrorCodeInvalidAppID = -101, + + /** + * Error code for failing to configure a specific service. It's deprecated, but + * still used after copybara. + */ + FIRErrorCodeConfigFailed = -114, +}; diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRErrors.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRErrors.h new file mode 100644 index 000000000..19e47328a --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRErrors.h @@ -0,0 +1,24 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#include "FIRErrorCode.h" + +extern NSString *const kFirebaseErrorDomain; +extern NSString *const kFirebaseConfigErrorDomain; +extern NSString *const kFirebaseCoreErrorDomain; +extern NSString *const kFirebasePerfErrorDomain; diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h new file mode 100644 index 000000000..bfff73e5d --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRHeartbeatInfo.h @@ -0,0 +1,39 @@ +// Copyright 2019 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRHeartbeatInfo : NSObject + +// Enum representing the different heartbeat codes. +typedef NS_ENUM(NSInteger, FIRHeartbeatInfoCode) { + FIRHeartbeatInfoCodeNone = 0, + FIRHeartbeatInfoCodeSDK = 1, + FIRHeartbeatInfoCodeGlobal = 2, + FIRHeartbeatInfoCodeCombined = 3, +}; + +/** + * Get heartbeat code requred for the sdk. + * @param heartbeatTag String representing the sdk heartbeat tag. + * @return Heartbeat code indicating whether or not an sdk/global heartbeat + * needs to be sent + */ ++ (FIRHeartbeatInfoCode)heartbeatCodeForTag:(NSString *)heartbeatTag; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRLibrary.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRLibrary.h new file mode 100644 index 000000000..e7a9e077c --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRLibrary.h @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIRLibrary_h +#define FIRLibrary_h + +#import + +// The has_include is a workaround so the old IID needed for the FIS tests can find the headers. +#if __has_include("FirebaseCore/Sources/Private/FIRComponent.h") +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#else +#import +#endif + +@class FIRApp; + +NS_ASSUME_NONNULL_BEGIN + +/// Provide an interface to register a library for userAgent logging and availability to others. +NS_SWIFT_NAME(Library) +@protocol FIRLibrary + +/// Returns one or more FIRComponents that will be registered in +/// FIRApp and participate in dependency resolution and injection. ++ (NSArray *)componentsToRegister; + +@optional +/// Implement this method if the library needs notifications for lifecycle events. This method is +/// called when the developer calls `FirebaseApp.configure()`. ++ (void)configureWithApp:(FIRApp *)app; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* FIRLibrary_h */ diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRLogger.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRLogger.h new file mode 100644 index 000000000..6fd778441 --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIRLogger.h @@ -0,0 +1,156 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIRLoggerLevel.h" +#else +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +/** + * The Firebase services used in Firebase logger. + */ +typedef NSString *const FIRLoggerService; + +extern FIRLoggerService kFIRLoggerABTesting; +extern FIRLoggerService kFIRLoggerAdMob; +extern FIRLoggerService kFIRLoggerAnalytics; +extern FIRLoggerService kFIRLoggerAuth; +extern FIRLoggerService kFIRLoggerCrash; +extern FIRLoggerService kFIRLoggerCore; +extern FIRLoggerService kFIRLoggerMLKit; +extern FIRLoggerService kFIRLoggerPerf; +extern FIRLoggerService kFIRLoggerRemoteConfig; + +/** + * The key used to store the logger's error count. + */ +extern NSString *const kFIRLoggerErrorCountKey; + +/** + * The key used to store the logger's warning count. + */ +extern NSString *const kFIRLoggerWarningCountKey; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * Enables or disables Analytics debug mode. + * If set to YES, the logging level for Analytics will be set to FIRLoggerLevelDebug. + * Enabling the debug mode has no effect if the app is running from App Store. + * (required) analytics debug mode flag. + */ +void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode); + +/** + * Changes the default logging level of FIRLoggerLevelNotice to a user-specified level. + * The default level cannot be set above FIRLoggerLevelNotice if the app is running from App Store. + * (required) log level (one of the FIRLoggerLevel enum values). + */ +void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel); + +/** + * Checks if the specified logger level is loggable given the current settings. + * (required) log level (one of the FIRLoggerLevel enum values). + * (required) whether or not this function is called from the Analytics component. + */ +BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent); + +/** + * Logs a message to the Xcode console and the device log. If running from AppStore, will + * not log any messages with a level higher than FIRLoggerLevelNotice to avoid log spamming. + * (required) log level (one of the FIRLoggerLevel enum values). + * (required) service name of type FIRLoggerService. + * (required) message code starting with "I-" which means iOS, followed by a capitalized + * three-character service identifier and a six digit integer message ID that is unique + * within the service. + * An example of the message code is @"I-COR000001". + * (required) message string which can be a format string. + * (optional) variable arguments list obtained from calling va_start, used when message is a format + * string. + */ +extern void FIRLogBasic(FIRLoggerLevel level, + FIRLoggerService service, + NSString *messageCode, + NSString *message, +// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable +// See: http://stackoverflow.com/q/29095469 +#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX + va_list args_ptr +#else + va_list _Nullable args_ptr +#endif +); + +/** + * The following functions accept the following parameters in order: + * (required) service name of type FIRLoggerService. + * (required) message code starting from "I-" which means iOS, followed by a capitalized + * three-character service identifier and a six digit integer message ID that is unique + * within the service. + * An example of the message code is @"I-COR000001". + * See go/firebase-log-proposal for details. + * (required) message string which can be a format string. + * (optional) the list of arguments to substitute into the format string. + * Example usage: + * FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name); + */ +extern void FIRLogError(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogWarning(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogNotice(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogInfo(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); +extern void FIRLogDebug(FIRLoggerService service, NSString *messageCode, NSString *message, ...) + NS_FORMAT_FUNCTION(3, 4); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +@interface FIRLoggerWrapper : NSObject + +/** + * Objective-C wrapper for FIRLogBasic to allow weak linking to FIRLogger + * (required) log level (one of the FIRLoggerLevel enum values). + * (required) service name of type FIRLoggerService. + * (required) message code starting with "I-" which means iOS, followed by a capitalized + * three-character service identifier and a six digit integer message ID that is unique + * within the service. + * An example of the message code is @"I-COR000001". + * (required) message string which can be a format string. + * (optional) variable arguments list obtained from calling va_start, used when message is a format + * string. + */ + ++ (void)logWithLevel:(FIRLoggerLevel)level + withService:(FIRLoggerService)service + withCode:(NSString *)messageCode + withMessage:(NSString *)message + withArgs:(va_list)args; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIROptionsInternal.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIROptionsInternal.h new file mode 100644 index 000000000..acaf45868 --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FIROptionsInternal.h @@ -0,0 +1,119 @@ +/* + * Copyright 2017 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if SWIFT_PACKAGE +// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM. +#import "FIROptions.h" +#else +#import +#endif + +/** + * Keys for the strings in the plist file. + */ +extern NSString *const kFIRAPIKey; +extern NSString *const kFIRTrackingID; +extern NSString *const kFIRGoogleAppID; +extern NSString *const kFIRClientID; +extern NSString *const kFIRGCMSenderID; +extern NSString *const kFIRAndroidClientID; +extern NSString *const kFIRDatabaseURL; +extern NSString *const kFIRStorageBucket; +extern NSString *const kFIRBundleID; +extern NSString *const kFIRProjectID; + +/** + * Keys for the plist file name + */ +extern NSString *const kServiceInfoFileName; + +extern NSString *const kServiceInfoFileType; + +/** + * This header file exposes the initialization of FIROptions to internal use. + */ +@interface FIROptions () + +/** + * resetDefaultOptions and initInternalWithOptionsDictionary: are exposed only for unit tests. + */ ++ (void)resetDefaultOptions; + +/** + * Initializes the options with dictionary. The above strings are the keys of the dictionary. + * This is the designated initializer. + */ +- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary; + +/** + * defaultOptions and defaultOptionsDictionary are exposed in order to be used in FIRApp and + * other first party services. + */ ++ (FIROptions *)defaultOptions; + ++ (NSDictionary *)defaultOptionsDictionary; + +/** + * Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at + * runtime. + */ +@property(nonatomic, readonly) BOOL isAnalyticsCollectionExplicitlySet; + +/** + * Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless + * explicitly disabled in GoogleService-Info.plist. + */ +@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled; + +/** + * Whether or not Analytics Collection was completely disabled. If YES, then + * isAnalyticsCollectionEnabled will be NO. + */ +@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated; + +/** + * The version ID of the client library, e.g. @"1100000". + */ +@property(nonatomic, readonly, copy) NSString *libraryVersionID; + +/** + * The flag indicating whether this object was constructed with the values in the default plist + * file. + */ +@property(nonatomic) BOOL usingOptionsFromDefaultPlist; + +/** + * Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in + * GoogleService-Info.plist. + */ +@property(nonatomic, readonly) BOOL isMeasurementEnabled; + +/** + * Whether or not Analytics was enabled in the developer console. + */ +@property(nonatomic, readonly) BOOL isAnalyticsEnabled; + +/** + * Whether or not SignIn was enabled in the developer console. + */ +@property(nonatomic, readonly) BOOL isSignInEnabled; + +/** + * Whether or not editing is locked. This should occur after FIROptions has been set on a FIRApp. + */ +@property(nonatomic, getter=isEditingLocked) BOOL editingLocked; + +@end diff --git a/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FirebaseCoreInternal.h b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FirebaseCoreInternal.h new file mode 100644 index 000000000..93af6cb8d --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseCore/Sources/Private/FirebaseCoreInternal.h @@ -0,0 +1,31 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// An umbrella header, for any other libraries in this repo to access Firebase Public and Private +// headers. Any package manager complexity should be handled here. + +#if SWIFT_PACKAGE +@import FirebaseCore; +#else +#import +#endif + +#import "FirebaseCore/Sources/Private/FIRAppInternal.h" +#import "FirebaseCore/Sources/Private/FIRComponent.h" +#import "FirebaseCore/Sources/Private/FIRComponentContainer.h" +#import "FirebaseCore/Sources/Private/FIRDependency.h" +#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h" +#import "FirebaseCore/Sources/Private/FIRLibrary.h" +#import "FirebaseCore/Sources/Private/FIRLogger.h" +#import "FirebaseCore/Sources/Private/FIROptionsInternal.h" diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h index 5bc21a11c..941aa3dd1 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h @@ -16,7 +16,7 @@ #import -#import +#import "FirebaseInstallations/Source/Library/Public/FIRInstallationsErrors.h" @class FIRInstallationsHTTPError; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.m index f85923ace..1b3357005 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.m @@ -14,9 +14,9 @@ * limitations under the License. */ -#import "FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" -#import "FIRInstallationsHTTPError.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.h" NSString *const kFirebaseInstallationsErrorDomain = @"com.firebase.installations"; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.m index 5b3eae22b..4236f452e 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.m @@ -14,8 +14,8 @@ * limitations under the License. */ -#import "FIRInstallationsHTTPError.h" -#import "FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" @implementation FIRInstallationsHTTPError @@ -41,9 +41,10 @@ + (NSDictionary *)userInfoWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse data:(nullable NSData *)data { NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - NSString *failureReason = [NSString - stringWithFormat:@"The server responded with an error. HTTP response: %@\nResponse body: %@", - HTTPResponse, responseString]; + NSString *failureReason = + [NSString stringWithFormat:@"The server responded with an error: \n - URL: %@ \n - HTTP " + @"status code: %ld \n - Response body: %@", + HTTPResponse.URL, (long)HTTPResponse.statusCode, responseString]; return @{NSLocalizedFailureReasonErrorKey : failureReason}; } diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallations.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallations.m index 71e7dd434..cbdd8b299 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallations.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallations.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallations.h" +#import "FirebaseInstallations/Source/Library/Public/FIRInstallations.h" #if __has_include() #import @@ -22,21 +22,16 @@ #import "FBLPromises.h" #endif -#import -#import -#import -#import -#import -#import +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" -#import "FIRInstallationsAuthTokenResultInternal.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResultInternal.h" -#import "FIRInstallationsErrorUtil.h" -#import "FIRInstallationsIDController.h" -#import "FIRInstallationsItem.h" -#import "FIRInstallationsLogger.h" -#import "FIRInstallationsStoredAuthToken.h" -#import "FIRInstallationsVersion.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsItem.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsLogger.h" +#import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h" +#import "FirebaseInstallations/Source/Library/Public/FIRInstallationsVersion.h" NS_ASSUME_NONNULL_BEGIN @@ -89,10 +84,12 @@ NS_ASSUME_NONNULL_BEGIN projectID:appOptions.projectID GCMSenderID:appOptions.GCMSenderID accessGroup:appOptions.appGroupID]; + + // `prefetchAuthToken` is disabled due to b/156746574. return [self initWithAppOptions:appOptions appName:appName installationsIDController:IDController - prefetchAuthToken:YES]; + prefetchAuthToken:NO]; } /// The initializer is supposed to be used by tests to inject `installationsStore`. diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResult.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResult.m index 92e5fab19..47a71e845 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResult.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResult.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallationsAuthTokenResultInternal.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResultInternal.h" @implementation FIRInstallationsAuthTokenResult diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResultInternal.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResultInternal.h index 0c959dba5..2233dd346 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResultInternal.h +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsAuthTokenResultInternal.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#import +#import "FirebaseInstallations/Source/Library/Public/FIRInstallationsAuthTokenResult.h" NS_ASSUME_NONNULL_BEGIN diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsItem.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsItem.h index 95fdf835f..4b64b7fc1 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsItem.h +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsItem.h @@ -16,7 +16,7 @@ #import -#import "FIRInstallationsStatus.h" +#import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsStatus.h" @class FIRInstallationsStoredItem; @class FIRInstallationsStoredAuthToken; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsItem.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsItem.m index bc819bf8a..200d8ae41 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsItem.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsItem.m @@ -14,10 +14,10 @@ * limitations under the License. */ -#import "FIRInstallationsItem.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsItem.h" -#import "FIRInstallationsStoredAuthToken.h" -#import "FIRInstallationsStoredItem.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h" @implementation FIRInstallationsItem diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsLogger.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsLogger.h index baeadb2e3..e4192bf66 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsLogger.h +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsLogger.h @@ -16,7 +16,7 @@ #import -#import +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" extern FIRLoggerService kFIRLoggerInstallations; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsLogger.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsLogger.m index c2bdf37f1..edfcde970 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsLogger.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsLogger.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallationsLogger.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsLogger.h" FIRLoggerService kFIRLoggerInstallations = @"[Firebase/Installations]"; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsVersion.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsVersion.m index a75e3f5be..8509680f9 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsVersion.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/FIRInstallationsVersion.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallationsVersion.h" +#import "FirebaseInstallations/Source/Library/Public/FIRInstallationsVersion.h" // Convert the macro to a string #define STR(x) STR_EXPAND(x) diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDStore.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDStore.m index 1f3a82afa..1a77831df 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDStore.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDStore.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallationsIIDStore.h" +#import "FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDStore.h" #if __has_include() #import @@ -23,7 +23,7 @@ #endif #import -#import "FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" static NSString *const kFIRInstallationsIIDKeyPairPublicTagPrefix = @"com.google.iid.keypair.public-"; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.m index b2b69313e..7d6226353 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallationsIIDTokenStore.h" +#import "FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.h" #if __has_include() #import @@ -22,9 +22,9 @@ #import "FBLPromises.h" #endif -#import +#import "GoogleUtilities/Environment/Private/GULKeychainUtils.h" -#import "FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" static NSString *const kFIRInstallationsIIDTokenKeychainId = @"com.google.iid-tokens"; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m index 6e7462203..8f38e0c6c 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.m @@ -14,9 +14,9 @@ * limitations under the License. */ -#import "FIRInstallationsAPIService.h" +#import "FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.h" -#import +#import "FirebaseInstallations/Source/Library/Public/FIRInstallationsVersion.h" #if __has_include() #import @@ -24,11 +24,10 @@ #import "FBLPromises.h" #endif -#import -#import -#import "FIRInstallationsErrorUtil.h" -#import "FIRInstallationsItem+RegisterInstallationAPI.h" -#import "FIRInstallationsLogger.h" +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsLogger.h" +#import "FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.h" NSString *const kFIRInstallationsAPIBaseURL = @"https://firebaseinstallations.googleapis.com"; NSString *const kFIRInstallationsAPIKey = @"X-Goog-Api-Key"; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.h index cc6b54320..0e4af55e4 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.h +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallationsItem.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsItem.h" @class FIRInstallationsStoredAuthToken; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.m index 569e35b99..e5c736010 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.m @@ -14,10 +14,10 @@ * limitations under the License. */ -#import "FIRInstallationsItem+RegisterInstallationAPI.h" +#import "FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsItem+RegisterInstallationAPI.h" -#import "FIRInstallationsErrorUtil.h" -#import "FIRInstallationsStoredAuthToken.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h" @implementation FIRInstallationsItem (RegisterInstallationAPI) diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m index 1b8ddcbfa..b6ea4b24f 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallationsIDController.h" +#import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsIDController.h" #if __has_include() #import @@ -22,20 +22,20 @@ #import "FBLPromises.h" #endif -#import -#import +#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h" +#import "GoogleUtilities/Environment/Private/GULKeychainStorage.h" -#import "FIRInstallationsAPIService.h" -#import "FIRInstallationsErrorUtil.h" -#import "FIRInstallationsIIDStore.h" -#import "FIRInstallationsIIDTokenStore.h" -#import "FIRInstallationsItem.h" -#import "FIRInstallationsLogger.h" -#import "FIRInstallationsSingleOperationPromiseCache.h" -#import "FIRInstallationsStore.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsItem.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsLogger.h" +#import "FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDStore.h" +#import "FirebaseInstallations/Source/Library/IIDMigration/FIRInstallationsIIDTokenStore.h" +#import "FirebaseInstallations/Source/Library/InstallationsAPI/FIRInstallationsAPIService.h" +#import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsSingleOperationPromiseCache.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.h" -#import "FIRInstallationsHTTPError.h" -#import "FIRInstallationsStoredAuthToken.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsHTTPError.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h" const NSNotificationName FIRInstallationIDDidChangeNotification = @"FIRInstallationIDDidChangeNotification"; @@ -44,6 +44,8 @@ NSString *const kFIRInstallationIDDidChangeNotificationAppNameKey = NSTimeInterval const kFIRInstallationsTokenExpirationThreshold = 60 * 60; // 1 hour. +static NSString *const kKeychainService = @"com.firebase.FIRInstallations.installations"; + @interface FIRInstallationsIDController () @property(nonatomic, readonly) NSString *appID; @property(nonatomic, readonly) NSString *appName; @@ -71,9 +73,9 @@ NSTimeInterval const kFIRInstallationsTokenExpirationThreshold = 60 * 60; // 1 APIKey:(NSString *)APIKey projectID:(NSString *)projectID GCMSenderID:(NSString *)GCMSenderID - accessGroup:(NSString *)accessGroup { - GULKeychainStorage *secureStorage = - [[GULKeychainStorage alloc] initWithService:@"com.firebase.FIRInstallations.installations"]; + accessGroup:(nullable NSString *)accessGroup { + NSString *serviceName = [FIRInstallationsIDController keychainServiceWithAppID:appID]; + GULKeychainStorage *secureStorage = [[GULKeychainStorage alloc] initWithService:serviceName]; FIRInstallationsStore *installationsStore = [[FIRInstallationsStore alloc] initWithSecureStorage:secureStorage accessGroup:accessGroup]; @@ -256,9 +258,9 @@ NSTimeInterval const kFIRInstallationsTokenExpirationThreshold = 60 * 60; // 1 if ([self doesRegistrationErrorRequireConfigChange:error]) { FIRLogError(kFIRLoggerInstallations, kFIRInstallationsMessageCodeInvalidFirebaseConfiguration, - @"Firebase Installation registration failed for app with name: %@, error: " + @"Firebase Installation registration failed for app with name: %@, error:\n" @"%@\nPlease make sure you use valid GoogleService-Info.plist", - self.appName, error); + self.appName, error.userInfo[NSLocalizedFailureReasonErrorKey]); } }) .then(^id(FIRInstallationsItem *registeredInstallation) { @@ -456,4 +458,23 @@ NSTimeInterval const kFIRInstallationsTokenExpirationThreshold = 60 * 60; // 1 return [self.appName isEqualToString:kFIRDefaultAppName]; } +#pragma mark - Keychain + ++ (NSString *)keychainServiceWithAppID:(NSString *)appID { +#if TARGET_OS_MACCATALYST || TARGET_OS_OSX + // We need to keep service name unique per application on macOS. + // Applications on macOS may request access to Keychain items stored by other applications. It + // means that when the app looks up for a relevant Keychain item in the service scope it will + // request user password to grant access to the Keychain if there are other Keychain items from + // other applications stored under the same Keychain Service. + return [kKeychainService stringByAppendingFormat:@".%@", appID]; +#else + // Use a constant Keychain service for non-macOS because: + // 1. Keychain items cannot be shared between apps until configured specifically so the service + // name collisions are not a concern + // 2. We don't want to change the service name to avoid doing a migration. + return kKeychainService; +#endif +} + @end diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsSingleOperationPromiseCache.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsSingleOperationPromiseCache.m index dfccfe36d..7ae8781f8 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsSingleOperationPromiseCache.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsSingleOperationPromiseCache.m @@ -14,7 +14,7 @@ * limitations under the License. */ -#import "FIRInstallationsSingleOperationPromiseCache.h" +#import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsSingleOperationPromiseCache.h" #if __has_include() #import diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.m index 46f58dba7..72041000d 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.m @@ -14,9 +14,9 @@ * limitations under the License. */ -#import "FIRInstallationsStore.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStore.h" -#import +#import "GoogleUtilities/UserDefaults/Private/GULUserDefaults.h" #if __has_include() #import @@ -24,11 +24,11 @@ #import "FBLPromises.h" #endif -#import +#import "GoogleUtilities/Environment/Private/GULKeychainStorage.h" -#import "FIRInstallationsErrorUtil.h" -#import "FIRInstallationsItem.h" -#import "FIRInstallationsStoredItem.h" +#import "FirebaseInstallations/Source/Library/Errors/FIRInstallationsErrorUtil.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsItem.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h" NSString *const kFIRInstallationsStoreUserDefaultsID = @"com.firebase.FIRInstallations"; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.m index b21f6dd23..8236f2a63 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.m @@ -14,9 +14,9 @@ * limitations under the License. */ -#import "FIRInstallationsStoredAuthToken.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h" -#import "FIRInstallationsLogger.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsLogger.h" NSString *const kFIRInstallationsStoredAuthTokenStatusKey = @"status"; NSString *const kFIRInstallationsStoredAuthTokenTokenKey = @"token"; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h index 4926588c2..83902deb0 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h @@ -16,7 +16,7 @@ #import -#import "FIRInstallationsStatus.h" +#import "FirebaseInstallations/Source/Library/InstallationsIDController/FIRInstallationsStatus.h" @class FIRInstallationsStoredAuthToken; @class FIRInstallationsStoredIIDCheckin; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.m b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.m index 0c7655c39..4e199559a 100644 --- a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.m +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.m @@ -14,10 +14,10 @@ * limitations under the License. */ -#import "FIRInstallationsStoredItem.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredItem.h" -#import "FIRInstallationsLogger.h" -#import "FIRInstallationsStoredAuthToken.h" +#import "FirebaseInstallations/Source/Library/FIRInstallationsLogger.h" +#import "FirebaseInstallations/Source/Library/InstallationsStore/FIRInstallationsStoredAuthToken.h" NSString *const kFIRInstallationsStoredItemFirebaseInstallationIDKey = @"firebaseInstallationID"; NSString *const kFIRInstallationsStoredItemRefreshTokenKey = @"refreshToken"; diff --git a/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h new file mode 100644 index 000000000..cd40f1723 --- /dev/null +++ b/ios/Pods/FirebaseInstallations/FirebaseInstallations/Source/Library/Private/FirebaseInstallationsInternal.h @@ -0,0 +1,23 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// An umbrella header, for any other libraries in this repo to access Firebase +// Installations Public headers. Any package manager complexity should be +// handled here. + +#if SWIFT_PACKAGE +@import FirebaseInstallations; +#else +#import +#endif diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h b/ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h similarity index 91% rename from ios/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h rename to ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h index d5502647c..2fb162261 100644 --- a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/third_party/GULAppEnvironmentUtil.h +++ b/ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULAppEnvironmentUtil.h @@ -41,6 +41,7 @@ + (BOOL)isAppExtension; /// @return Returns @YES when is run on iOS version greater or equal to 7.0 -+ (BOOL)isIOS7OrHigher; ++ (BOOL)isIOS7OrHigher DEPRECATED_MSG_ATTRIBUTE( + "Always `YES` because only iOS 8 and higher supported. The method will be removed."); @end diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULHeartbeatDateStorage.h b/ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULHeartbeatDateStorage.h similarity index 100% rename from ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULHeartbeatDateStorage.h rename to ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULHeartbeatDateStorage.h diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainStorage.h b/ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULKeychainStorage.h similarity index 100% rename from ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainStorage.h rename to ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULKeychainStorage.h diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainUtils.h b/ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULKeychainUtils.h similarity index 100% rename from ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULKeychainUtils.h rename to ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULKeychainUtils.h diff --git a/ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULSecureCoding.h b/ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULSecureCoding.h similarity index 100% rename from ios/Pods/GoogleUtilities/GoogleUtilities/Environment/Public/GULSecureCoding.h rename to ios/Pods/FirebaseInstallations/GoogleUtilities/Environment/Private/GULSecureCoding.h diff --git a/ios/Pods/FirebaseInstallations/GoogleUtilities/UserDefaults/Private/GULUserDefaults.h b/ios/Pods/FirebaseInstallations/GoogleUtilities/UserDefaults/Private/GULUserDefaults.h new file mode 100644 index 000000000..0d0478184 --- /dev/null +++ b/ios/Pods/FirebaseInstallations/GoogleUtilities/UserDefaults/Private/GULUserDefaults.h @@ -0,0 +1,110 @@ +// Copyright 2018 Google +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// A thread-safe user defaults that uses C functions from CFPreferences.h instead of +/// `NSUserDefaults`. This is to avoid sending an `NSNotification` when it's changed from a +/// background thread to avoid crashing. // TODO: Insert radar number here. +@interface GULUserDefaults : NSObject + +/// A shared user defaults similar to +[NSUserDefaults standardUserDefaults] and accesses the same +/// data of the standardUserDefaults. ++ (GULUserDefaults *)standardUserDefaults; + +/// Initializes preferences with a suite name that is the same with the NSUserDefaults' suite name. +/// Both of CFPreferences and NSUserDefaults share the same plist file so their data will exactly +/// the same. +/// +/// @param suiteName The name of the suite of the user defaults. +- (instancetype)initWithSuiteName:(nullable NSString *)suiteName; + +#pragma mark - Getters + +/// Searches the receiver's search list for a default with the key 'defaultName' and return it. If +/// another process has changed defaults in the search list, NSUserDefaults will automatically +/// update to the latest values. If the key in question has been marked as ubiquitous via a Defaults +/// Configuration File, the latest value may not be immediately available, and the registered value +/// will be returned instead. +- (nullable id)objectForKey:(NSString *)defaultName; + +/// Equivalent to -objectForKey:, except that it will return nil if the value is not an NSArray. +- (nullable NSArray *)arrayForKey:(NSString *)defaultName; + +/// Equivalent to -objectForKey:, except that it will return nil if the value +/// is not an NSDictionary. +- (nullable NSDictionary *)dictionaryForKey:(NSString *)defaultName; + +/// Equivalent to -objectForKey:, except that it will convert NSNumber values to their NSString +/// representation. If a non-string non-number value is found, nil will be returned. +- (nullable NSString *)stringForKey:(NSString *)defaultName; + +/// Equivalent to -objectForKey:, except that it converts the returned value to an NSInteger. If the +/// value is an NSNumber, the result of -integerValue will be returned. If the value is an NSString, +/// it will be converted to NSInteger if possible. If the value is a boolean, it will be converted +/// to either 1 for YES or 0 for NO. If the value is absent or can't be converted to an integer, 0 +/// will be returned. +- (NSInteger)integerForKey:(NSString *)defaultName; + +/// Similar to -integerForKey:, except that it returns a float, and boolean values will not be +/// converted. +- (float)floatForKey:(NSString *)defaultName; + +/// Similar to -integerForKey:, except that it returns a double, and boolean values will not be +/// converted. +- (double)doubleForKey:(NSString *)defaultName; + +/// Equivalent to -objectForKey:, except that it converts the returned value to a BOOL. If the value +/// is an NSNumber, NO will be returned if the value is 0, YES otherwise. If the value is an +/// NSString, values of "YES" or "1" will return YES, and values of "NO", "0", or any other string +/// will return NO. If the value is absent or can't be converted to a BOOL, NO will be returned. +- (BOOL)boolForKey:(NSString *)defaultName; + +#pragma mark - Setters + +/// Immediately stores a value (or removes the value if `nil` is passed as the value) for the +/// provided key in the search list entry for the receiver's suite name in the current user and any +/// host, then asynchronously stores the value persistently, where it is made available to other +/// processes. +- (void)setObject:(nullable id)value forKey:(NSString *)defaultName; + +/// Equivalent to -setObject:forKey: except that the value is converted from a float to an NSNumber. +- (void)setFloat:(float)value forKey:(NSString *)defaultName; + +/// Equivalent to -setObject:forKey: except that the value is converted from a double to an +/// NSNumber. +- (void)setDouble:(double)value forKey:(NSString *)defaultName; + +/// Equivalent to -setObject:forKey: except that the value is converted from an NSInteger to an +/// NSNumber. +- (void)setInteger:(NSInteger)value forKey:(NSString *)defaultName; + +/// Equivalent to -setObject:forKey: except that the value is converted from a BOOL to an NSNumber. +- (void)setBool:(BOOL)value forKey:(NSString *)defaultName; + +#pragma mark - Removing Defaults + +/// Equivalent to -[... setObject:nil forKey:defaultName] +- (void)removeObjectForKey:(NSString *)defaultName; + +#pragma mark - Save data + +/// Blocks the calling thread until all in-progress set operations have completed. +- (void)synchronize; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/FirebaseInstallations/README.md b/ios/Pods/FirebaseInstallations/README.md index b04a27099..1d9f0f678 100644 --- a/ios/Pods/FirebaseInstallations/README.md +++ b/ios/Pods/FirebaseInstallations/README.md @@ -1,18 +1,29 @@ -# Firebase iOS Open Source Development - [![Actions Status][gh-auth-badge]][gh-actions] - [![Actions Status][gh-core-badge]][gh-actions] - [![Actions Status][gh-datatransport-badge]][gh-actions] - [![Actions Status][gh-dynamiclinks-badge]][gh-actions] - [![Actions Status][gh-firebasepod-badge]][gh-actions] - [![Actions Status][gh-firestore-badge]][gh-actions] - [![Actions Status][gh-interop-badge]][gh-actions] - [![Actions Status][gh-messaging-badge]][gh-actions] - [![Actions Status][gh-storage-badge]][gh-actions] - [![Actions Status][gh-symbolcollision-badge]][gh-actions] - [![Actions Status][gh-zip-badge]][gh-actions] - [![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) +[![Version](https://img.shields.io/cocoapods/v/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![License](https://img.shields.io/cocoapods/l/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) +[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase) -This repository contains all Firebase iOS SDK source except FirebaseAnalytics, +[![Actions Status][gh-abtesting-badge]][gh-actions] +[![Actions Status][gh-auth-badge]][gh-actions] +[![Actions Status][gh-core-badge]][gh-actions] +[![Actions Status][gh-crashlytics-badge]][gh-actions] +[![Actions Status][gh-database-badge]][gh-actions] +[![Actions Status][gh-datatransport-badge]][gh-actions] +[![Actions Status][gh-dynamiclinks-badge]][gh-actions] +[![Actions Status][gh-firebasepod-badge]][gh-actions] +[![Actions Status][gh-firestore-badge]][gh-actions] +[![Actions Status][gh-functions-badge]][gh-actions] +[![Actions Status][gh-inappmessaging-badge]][gh-actions] +[![Actions Status][gh-interop-badge]][gh-actions] +[![Actions Status][gh-messaging-badge]][gh-actions] +[![Actions Status][gh-remoteconfig-badge]][gh-actions] +[![Actions Status][gh-storage-badge]][gh-actions] +[![Actions Status][gh-symbolcollision-badge]][gh-actions] +[![Actions Status][gh-zip-badge]][gh-actions] +[![Travis](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) + +# Firebase Apple Open Source Development + +This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics, FirebasePerformance, and FirebaseML. The repository also includes GoogleUtilities source. The @@ -87,7 +98,7 @@ Instructions for installing binary frameworks via To develop Firebase software in this repository, ensure that you have at least the following software: - * Xcode 10.1 (or later) + * Xcode 10.3 (or later) * CocoaPods 1.7.2 (or later) * [CocoaPods generate](https://github.com/square/cocoapods-generate) @@ -118,6 +129,10 @@ Firestore has a self contained Xcode project. See See [AddNewPod.md](AddNewPod.md). +### Managing Headers and Imports + +See [HeadersImports.md](HeadersImports.md). + ### Code Formatting To ensure that the code is formatted consistently, run the script @@ -125,21 +140,13 @@ To ensure that the code is formatted consistently, run the script before creating a PR. Travis will verify that any code changes are done in a style compliant way. Install -`clang-format` and `swiftformat`. -These commands will get the right versions: +`clang-format` and `swiftformat`: ``` -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb -brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb +brew install clang-format +brew install swiftformat ``` -Note: if you already have a newer version of these installed you may need to -`brew switch` to this version. - -To update this section, find the versions of clang-format and swiftformat.rb to -match the versions in the CI failure logs -[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula). - ### Running Unit Tests Select a scheme and press Command-u to build a component and run its unit tests. @@ -162,12 +169,7 @@ files without real values, but can be replaced with real plist files. To get you 2. Create a new Firebase project, if you don't already have one 3. For each sample app you want to test, create a new Firebase app with the sample app's bundle identifier (e.g. `com.google.Database-Example`) -4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file -(e.g. in [Example/Database/App/](Example/Database/App/)); - -Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require -special Apple capabilities, and you will have to change the sample app to use a unique bundle -identifier that you can control in your own Apple Developer account. +4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project. ## Specific Component Instructions See the sections below for any special instructions for those components. @@ -180,8 +182,16 @@ building and running the FirebaseAuth pod along with various samples and tests. ### Firebase Database -To run the Database Integration tests, make your database authentication rules -[public](https://firebase.google.com/docs/database/security/quickstart). +The Firebase Database Integration tests can be run against a locally running Database Emulator +or against a production instance. + +To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before +running the integration test. + +To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to +`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to +[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are +running. ### Firebase Storage @@ -269,14 +279,20 @@ Your use of Firebase is governed by the [Terms of Service for Firebase Services](https://firebase.google.com/terms/). [gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions +[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg [gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg [gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg +[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg +[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg [gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg [gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg [gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg [gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg +[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg +[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg [gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg [gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg +[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg [gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg [gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg [gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg diff --git a/ios/Pods/Flipper/README.md b/ios/Pods/Flipper/README.md index 47df05320..b028c7ec3 100644 --- a/ios/Pods/Flipper/README.md +++ b/ios/Pods/Flipper/README.md @@ -38,19 +38,19 @@ Both Flipper's desktop app and native mobile SDKs are open-source and MIT licens This repository includes all parts of Flipper. This includes: -* Flipper's desktop app built using [Electron](https://electronjs.org) (`/src`) +* Flipper's desktop app built using [Electron](https://electronjs.org) (`/desktop`) * native Flipper SDKs for iOS (`/iOS`) * native Flipper SDKs for Android (`/android`) * Plugins: - * Logs (`/src/device-plugins/logs`) - * Layout inspector (`/src/plugins/layout`) - * Network inspector (`/src/plugins/network`) - * Shared Preferences/NSUserDefaults inspector (`/src/plugins/shared_preferences`) + * Logs (`/desktop/src/device-plugins/logs`) + * Layout inspector (`/desktop/plugins/layout`) + * Network inspector (`/desktop/plugins/network`) + * Shared Preferences/NSUserDefaults inspector (`/desktop/plugins/shared_preferences`) * website and documentation (`/website` / `/docs`) # Getting started -Please refer to our [Getting Started guide](https://fbflipper.com/docs/getting-started.html) to set up Flipper. +Please refer to our [Getting Started guide](https://fbflipper.com/docs/getting-started/index) to set up Flipper. ## Requirements @@ -66,7 +66,7 @@ Please refer to our [Getting Started guide](https://fbflipper.com/docs/getting-s ``` git clone https://github.com/facebook/flipper.git -cd flipper +cd flipper/desktop yarn yarn start ``` @@ -119,9 +119,9 @@ Alternatively, the app can be started on `iOS` by running `yarn ios`. Older yarn versions might show an error / hang with the message 'Waiting for the other yarn instance to finish'. If that happens, run the command `yarn` first separately in the directory `react-native/react-native-flipper`. -## Documentation +# Documentation -Find the full documentation for this project at [fbflipper.com](https://fbflipper.com/docs). +Find the full documentation for this project at [fbflipper.com](https://fbflipper.com/). Our documentation is built with [Docusaurus](https://docusaurus.io/). You can build it locally by running this: diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperClient.cpp b/ios/Pods/Flipper/xplat/Flipper/FlipperClient.cpp index dc3325e54..6b897d581 100644 --- a/ios/Pods/Flipper/xplat/Flipper/FlipperClient.cpp +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperClient.cpp @@ -69,12 +69,6 @@ void FlipperClient::addPlugin(std::shared_ptr plugin) { step->complete(); if (connected_) { refreshPlugins(); - if (plugin->runInBackground()) { - auto& conn = connections_[plugin->identifier()]; - conn = std::make_shared( - socket_.get(), plugin->identifier()); - plugin->didConnect(conn); - } } }); } @@ -95,27 +89,6 @@ void FlipperClient::removePlugin(std::shared_ptr plugin) { }); } -void FlipperClient::startBackgroundPlugins() { - std::cout << "Activating Background Plugins..." << std::endl; - for (std::map>::iterator it = - plugins_.begin(); - it != plugins_.end(); - ++it) { - std::cout << it->first << std::endl; - if (it->second.get()->runInBackground()) { - try { - auto& conn = connections_[it->first]; - conn = - std::make_shared(socket_.get(), it->first); - it->second.get()->didConnect(conn); - } catch (std::exception& e) { - log("Exception starting background plugin: " + it->first + ". " + - e.what()); - } - } - } -} - std::shared_ptr FlipperClient::getPlugin( const std::string& identifier) { std::lock_guard lock(mutex_); @@ -130,6 +103,15 @@ bool FlipperClient::hasPlugin(const std::string& identifier) { return plugins_.find(identifier) != plugins_.end(); } +void FlipperClient::connect(std::shared_ptr plugin) { + if (connections_.find(plugin->identifier()) == connections_.end()) { + auto& conn = connections_[plugin->identifier()]; + conn = std::make_shared( + socket_.get(), plugin->identifier()); + plugin->didConnect(conn); + } +} + void FlipperClient::disconnect(std::shared_ptr plugin) { const auto& conn = connections_.find(plugin->identifier()); if (conn != connections_.end()) { @@ -151,7 +133,6 @@ void FlipperClient::onConnected() { std::lock_guard lock(mutex_); connected_ = true; - startBackgroundPlugins(); }); } @@ -189,6 +170,18 @@ void FlipperClient::onMessageReceived( return; } + if (method == "getBackgroundPlugins") { + dynamic identifiers = dynamic::array(); + for (const auto& elem : plugins_) { + if (elem.second->runInBackground()) { + identifiers.push_back(elem.first); + } + } + dynamic response = dynamic::object("plugins", identifiers); + responder->success(response); + return; + } + if (method == "init") { const auto identifier = params["plugin"].getString(); if (plugins_.find(identifier) == plugins_.end()) { @@ -200,12 +193,7 @@ void FlipperClient::onMessageReceived( return; } const auto plugin = plugins_.at(identifier); - if (!plugin.get()->runInBackground()) { - auto& conn = connections_[plugin->identifier()]; - conn = std::make_shared( - socket_.get(), plugin->identifier()); - plugin->didConnect(conn); - } + connect(plugin); return; } @@ -220,9 +208,7 @@ void FlipperClient::onMessageReceived( return; } const auto plugin = plugins_.at(identifier); - if (!plugin.get()->runInBackground()) { - disconnect(plugin); - } + disconnect(plugin); return; } diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperClient.h b/ios/Pods/Flipper/xplat/Flipper/FlipperClient.h index 1c107d6a6..28071e818 100644 --- a/ios/Pods/Flipper/xplat/Flipper/FlipperClient.h +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperClient.h @@ -108,8 +108,8 @@ class FlipperClient : public FlipperConnectionManager::Callbacks { std::mutex mutex_; std::shared_ptr flipperState_; + void connect(std::shared_ptr plugin); void disconnect(std::shared_ptr plugin); - void startBackgroundPlugins(); std::string callstack(); void handleError(std::exception& e); }; diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.cpp b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.cpp index 9cbcfb5e9..c10fab364 100644 --- a/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.cpp +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.cpp @@ -35,7 +35,7 @@ static constexpr int maxPayloadSize = 0xFFFFFF; // Not a public-facing version number. // Used for compatibility checking with desktop flipper. // To be bumped for every core platform interface change. -static constexpr int sdkVersion = 3; +static constexpr int sdkVersion = 4; namespace facebook { namespace flipper { @@ -127,34 +127,36 @@ void FlipperConnectionManagerImpl::startSync() { : "Establish main connection"); try { if (isClientSetupStep) { - doCertificateExchange(); + bool success = doCertificateExchange(); + if (!success) { + reconnect(); + return; + } } else { - connectSecurely(); + if (!connectSecurely()) { + // The expected code path when flipper desktop is not running. + // Don't count as a failed attempt, or it would invalidate the + // connection files for no reason. On iOS devices, we can always connect + // to the local port forwarding server even when it can't connect to + // flipper. In that case we get a Network error instead of a Port not + // open error, so we treat them the same. + step->fail( + "No route to flipper found. Is flipper desktop running? Retrying..."); + reconnect(); + } } step->complete(); } catch (const folly::AsyncSocketException& e) { - if (e.getType() == folly::AsyncSocketException::NOT_OPEN || - e.getType() == folly::AsyncSocketException::NETWORK_ERROR) { - // The expected code path when flipper desktop is not running. - // Don't count as a failed attempt, or it would invalidate the connection - // files for no reason. On iOS devices, we can always connect to the local - // port forwarding server even when it can't connect to flipper. In that - // case we get a Network error instead of a Port not open error, so we - // treat them the same. - step->fail( - "No route to flipper found. Is flipper desktop running? Retrying..."); + if (e.getType() == folly::AsyncSocketException::SSL_ERROR) { + auto message = std::string(e.what()) + + "\nMake sure the date and time of your device is up to date."; + log(message); + step->fail(message); } else { - if (e.getType() == folly::AsyncSocketException::SSL_ERROR) { - auto message = std::string(e.what()) + - "\nMake sure the date and time of your device is up to date."; - log(message); - step->fail(message); - } else { - log(e.what()); - step->fail(e.what()); - } - failedConnectionAttempts_++; + log(e.what()); + step->fail(e.what()); } + failedConnectionAttempts_++; reconnect(); } catch (const std::exception& e) { log(e.what()); @@ -164,7 +166,7 @@ void FlipperConnectionManagerImpl::startSync() { } } -void FlipperConnectionManagerImpl::doCertificateExchange() { +bool FlipperConnectionManagerImpl::doCertificateExchange() { rsocket::SetupParameters parameters; folly::SocketAddress address; @@ -175,7 +177,7 @@ void FlipperConnectionManagerImpl::doCertificateExchange() { auto connectingInsecurely = flipperState_->start("Connect insecurely"); connectionIsTrusted_ = false; - client_ = + auto newClient = rsocket::RSocket::createConnectedClient( std::make_unique( *connectionEventBase_->getEventBase(), std::move(address)), @@ -184,7 +186,22 @@ void FlipperConnectionManagerImpl::doCertificateExchange() { std::chrono::seconds(connectionKeepaliveSeconds), // keepaliveInterval nullptr, // stats std::make_shared(this)) + .thenError([](const auto& e) { + if (e.getType() == folly::AsyncSocketException::NOT_OPEN || + e.getType() == folly::AsyncSocketException::NETWORK_ERROR) { + // This is the state where no Flipper desktop client is connected. + // We don't want an exception thrown here. + return std::unique_ptr(nullptr); + } + throw e; + }) .get(); + + if (newClient.get() == nullptr) { + return false; + } + + client_ = std::move(newClient); connectingInsecurely->complete(); auto resettingState = flipperState_->start("Reset state"); @@ -192,9 +209,10 @@ void FlipperConnectionManagerImpl::doCertificateExchange() { resettingState->complete(); requestSignedCertFromFlipper(); + return true; } -void FlipperConnectionManagerImpl::connectSecurely() { +bool FlipperConnectionManagerImpl::connectSecurely() { rsocket::SetupParameters parameters; folly::SocketAddress address; @@ -216,7 +234,8 @@ void FlipperConnectionManagerImpl::connectSecurely() { contextStore_->getSSLContext(); auto connectingSecurely = flipperState_->start("Connect securely"); connectionIsTrusted_ = true; - client_ = + + auto newClient = rsocket::RSocket::createConnectedClient( std::make_unique( *connectionEventBase_->getEventBase(), @@ -227,9 +246,24 @@ void FlipperConnectionManagerImpl::connectSecurely() { std::chrono::seconds(connectionKeepaliveSeconds), // keepaliveInterval nullptr, // stats std::make_shared(this)) + .thenError([](const auto& e) { + if (e.getType() == folly::AsyncSocketException::NOT_OPEN || + e.getType() == folly::AsyncSocketException::NETWORK_ERROR) { + // This is the state where no Flipper desktop client is connected. + // We don't want an exception thrown here. + return std::unique_ptr(nullptr); + } + throw e; + }) .get(); + if (newClient.get() == nullptr) { + return false; + } + + client_ = std::move(newClient); connectingSecurely->complete(); failedConnectionAttempts_ = 0; + return true; } void FlipperConnectionManagerImpl::reconnect() { diff --git a/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h index 9763748ce..004c5be31 100644 --- a/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h +++ b/ios/Pods/Flipper/xplat/Flipper/FlipperConnectionManagerImpl.h @@ -68,8 +68,8 @@ class FlipperConnectionManagerImpl : public FlipperConnectionManager { std::shared_ptr contextStore_; void startSync(); - void doCertificateExchange(); - void connectSecurely(); + bool doCertificateExchange(); + bool connectSecurely(); bool isCertificateExchangeNeeded(); void requestSignedCertFromFlipper(); bool isRunningInOwnThread(); diff --git a/ios/Pods/FlipperKit/README.md b/ios/Pods/FlipperKit/README.md index 47df05320..b028c7ec3 100644 --- a/ios/Pods/FlipperKit/README.md +++ b/ios/Pods/FlipperKit/README.md @@ -38,19 +38,19 @@ Both Flipper's desktop app and native mobile SDKs are open-source and MIT licens This repository includes all parts of Flipper. This includes: -* Flipper's desktop app built using [Electron](https://electronjs.org) (`/src`) +* Flipper's desktop app built using [Electron](https://electronjs.org) (`/desktop`) * native Flipper SDKs for iOS (`/iOS`) * native Flipper SDKs for Android (`/android`) * Plugins: - * Logs (`/src/device-plugins/logs`) - * Layout inspector (`/src/plugins/layout`) - * Network inspector (`/src/plugins/network`) - * Shared Preferences/NSUserDefaults inspector (`/src/plugins/shared_preferences`) + * Logs (`/desktop/src/device-plugins/logs`) + * Layout inspector (`/desktop/plugins/layout`) + * Network inspector (`/desktop/plugins/network`) + * Shared Preferences/NSUserDefaults inspector (`/desktop/plugins/shared_preferences`) * website and documentation (`/website` / `/docs`) # Getting started -Please refer to our [Getting Started guide](https://fbflipper.com/docs/getting-started.html) to set up Flipper. +Please refer to our [Getting Started guide](https://fbflipper.com/docs/getting-started/index) to set up Flipper. ## Requirements @@ -66,7 +66,7 @@ Please refer to our [Getting Started guide](https://fbflipper.com/docs/getting-s ``` git clone https://github.com/facebook/flipper.git -cd flipper +cd flipper/desktop yarn yarn start ``` @@ -119,9 +119,9 @@ Alternatively, the app can be started on `iOS` by running `yarn ios`. Older yarn versions might show an error / hang with the message 'Waiting for the other yarn instance to finish'. If that happens, run the command `yarn` first separately in the directory `react-native/react-native-flipper`. -## Documentation +# Documentation -Find the full documentation for this project at [fbflipper.com](https://fbflipper.com/docs). +Find the full documentation for this project at [fbflipper.com](https://fbflipper.com/). Our documentation is built with [Docusaurus](https://docusaurus.io/). You can build it locally by running this: diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h index 6cb5f6251..c27aaf46b 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h @@ -10,6 +10,7 @@ #import #import +#import #import "SKDescriptorMapper.h" #import "SKInvalidation.h" @@ -29,4 +30,7 @@ @end +/** Exposed for tests only. */ +SK_EXTERN_C dispatch_queue_t SKLayoutPluginSerialBackgroundQueue(void); + #endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm index d64db0b0d..2ef733b97 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.mm @@ -24,9 +24,8 @@ NSMapTable* _trackedObjects; NSString* _lastHighlightedNode; NSMutableSet* _invalidObjects; - Boolean _invalidateMessageQueued; - NSDate* _lastInvalidateMessage; - std::mutex invalidObjectsMutex; + BOOL _invalidateMessageQueued; + std::mutex _invalidObjectsMutex; id _rootNode; id _tapListener; @@ -49,10 +48,7 @@ if (self = [super init]) { _descriptorMapper = mapper; _trackedObjects = [NSMapTable strongToWeakObjectsMapTable]; - _lastHighlightedNode = nil; _invalidObjects = [NSMutableSet new]; - _invalidateMessageQueued = false; - _lastInvalidateMessage = [NSDate date]; _rootNode = rootNode; _tapListener = tapListener; @@ -95,15 +91,6 @@ responder); }]; - [connection receive:@"getAllNodes" - withBlock:^(NSDictionary* params, id responder) { - FlipperPerformBlockOnMainThread( - ^{ - [weakSelf onCallGetAllNodesWithResponder:responder]; - }, - responder); - }]; - [connection receive:@"getNodes" withBlock:^(NSDictionary* params, id responder) { FlipperPerformBlockOnMainThread( @@ -189,19 +176,6 @@ [responder success:rootNode]; } -- (void)populateAllNodesFromNode:(nonnull NSString*)identifier - inDictionary: - (nonnull NSMutableDictionary*) - mutableDict { - NSDictionary* nodeDict = [self getNode:identifier]; - mutableDict[identifier] = nodeDict; - NSArray* arr = nodeDict[@"children"]; - for (NSString* childIdentifier in arr) { - [self populateAllNodesFromNode:childIdentifier inDictionary:mutableDict]; - } - return; -} - - (void)populateAllNodesFromNode:(nonnull NSString*)identifier inArray:(nonnull NSMutableArray*) mutableArray { @@ -216,26 +190,6 @@ } } -- (void)onCallGetAllNodesWithResponder:(nonnull id)responder { - NSMutableArray* allNodes = @[].mutableCopy; - NSString* identifier = [self trackObject:_rootNode]; - NSDictionary* rootNode = [self getNode:identifier]; - if (!rootNode) { - return [responder error:@{ - @"error" : [NSString - stringWithFormat: - @"getNode returned nil for the rootNode %@, while getting all the nodes", - identifier] - }]; - } - [allNodes addObject:rootNode]; - NSMutableDictionary* allNodesDict = @{}.mutableCopy; - [self populateAllNodesFromNode:identifier inDictionary:allNodesDict]; - [responder success:@{ - @"allNodes" : @{@"rootElement" : identifier, @"elements" : allNodesDict} - }]; -} - - (NSMutableArray*)getChildrenForNode:(id)node withDescriptor:(SKNodeDescriptor*)descriptor { NSMutableArray* children = [NSMutableArray new]; @@ -262,7 +216,10 @@ [elements addObject:node]; } - [responder success:@{@"elements" : elements}]; + // Converting to folly::dynamic is expensive, do it on a bg queue: + dispatch_async(SKLayoutPluginSerialBackgroundQueue(), ^{ + [responder success:@{@"elements" : elements}]; + }); } - (void)onCallSetData:(NSString*)objectId @@ -399,36 +356,37 @@ [descriptor invalidateNode:node]; // Collect invalidate messages before sending in a batch - std::lock_guard lock(invalidObjectsMutex); + std::lock_guard lock(_invalidObjectsMutex); [_invalidObjects addObject:nodeId]; if (_invalidateMessageQueued) { return; } - _invalidateMessageQueued = true; + _invalidateMessageQueued = YES; - if (_lastInvalidateMessage.timeIntervalSinceNow < -1) { - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC), - dispatch_get_main_queue(), - ^{ - [self reportInvalidatedObjects]; - }); - } + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, 500 * NSEC_PER_MSEC), + dispatch_get_main_queue(), + ^{ + [self _reportInvalidatedObjects]; + }); } -- (void)reportInvalidatedObjects { - std::lock_guard lock(invalidObjectsMutex); +- (void)invalidateRootNode { + [self invalidateNode:_rootNode]; +} + +- (void)_reportInvalidatedObjects { NSMutableArray* nodes = [NSMutableArray new]; - for (NSString* nodeId in self->_invalidObjects) { - [nodes addObject:[NSDictionary dictionaryWithObject:nodeId forKey:@"id"]]; - } - [self->_connection send:@"invalidate" - withParams:[NSDictionary dictionaryWithObject:nodes - forKey:@"nodes"]]; - self->_lastInvalidateMessage = [NSDate date]; - self->_invalidObjects = [NSMutableSet new]; - self->_invalidateMessageQueued = false; - return; + { // scope mutex acquisition + std::lock_guard lock(_invalidObjectsMutex); + for (NSString* nodeId in _invalidObjects) { + [nodes addObject:@{@"id" : nodeId}]; + } + _invalidObjects = [NSMutableSet new]; + _invalidateMessageQueued = NO; + } // release mutex before calling out to other code + + [_connection send:@"invalidate" withParams:@{@"nodes" : nodes}]; } - (void)updateNodeReference:(id)node { @@ -567,4 +525,22 @@ @end +/** + Operations like converting NSDictionary to folly::dynamic can be expensive. + Do them on this serial background queue to avoid blocking the main thread. + (Of course, ideally we wouldn't bother with building NSDictionary objects + in the first place, in favor of just using folly::dynamic directly...) + */ +dispatch_queue_t SKLayoutPluginSerialBackgroundQueue(void) { + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("flipper.layout.bg", DISPATCH_QUEUE_SERIAL); + // This should be relatively high priority, to prevent Flipper lag. + dispatch_set_target_queue( + queue, dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)); + }); + return queue; +} + #endif diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h index 9b26d599e..f2eee5819 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKInvalidation.h @@ -10,7 +10,7 @@ @protocol SKInvalidationDelegate - (void)invalidateNode:(id)node; - +- (void)invalidateRootNode; - (void)updateNodeReference:(id)node; @end diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.h b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.h index c805b8c64..b00289c5e 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.h +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/SKNodeDescriptor.h @@ -45,9 +45,13 @@ typedef void (^SKNodeUpdateData)(id value); - (NSString*)identifierForNode:(T)node; /** - An ID which is equal between reflowing components is needed to get the - identifier of root node of a tree which need to be invalidated on - FlipperKitLayoutPlugin side. + When the setData command is received from Flipper to change a node's data, + an "invalidateWithData" command is sent back to signal that the node has + changed. However sometimes you may want to invalidate some other node, + not the node that had its data actually modified; usually some ancestor. + This method allows you to substitute another node's identifier. + If you do not override it, the default behavior is to simply return + the node's identifier. */ - (NSString*)identifierForInvalidation:(T)node; diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.mm index 3da4f961b..10058261c 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.mm +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/UIView+SKInvalidation.mm @@ -9,7 +9,6 @@ #import #import - #import "SKInvalidation.h" #import "SKSwizzle.h" #import "UIView+SKInvalidation.h" @@ -31,6 +30,14 @@ FB_LINKABLE(UIView_SKInvalidation) }); } +/** +This function takes in a view and returns true if the view is a UIWindow and its +windowLevel is an alert one otherwise it returns false. +*/ +static auto shouldInvalidateRootNode(UIView* view) -> bool { + return [view isKindOfClass:[UIWindow class]]; +} + - (void)swizzle_setHidden:(BOOL)hidden { [self swizzle_setHidden:hidden]; @@ -47,7 +54,11 @@ FB_LINKABLE(UIView_SKInvalidation) id delegate = [SKInvalidation sharedInstance].delegate; if (delegate != nil) { - [delegate invalidateNode:view]; + if (shouldInvalidateRootNode(view.superview)) { + [delegate invalidateRootNode]; + return; + } + [delegate invalidateNode:view.superview]; } } @@ -55,6 +66,10 @@ FB_LINKABLE(UIView_SKInvalidation) id delegate = [SKInvalidation sharedInstance].delegate; if (delegate != nil && self.superview != nil) { + if (shouldInvalidateRootNode(self.superview)) { + [delegate invalidateRootNode]; + return; + } [delegate invalidateNode:self.superview]; } diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.mm index 0a6bc759e..44bef0406 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.mm +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitLayoutPlugin/FlipperKitLayoutPlugin/descriptors/SKViewDescriptor.mm @@ -453,6 +453,11 @@ static NSDictionary* YGUnitEnumMap = nil; @"Accessibility.accessibilityTraits.UIAccessibilityTraitCausesPageTurn": ^(NSNumber *value) { node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitCausesPageTurn, [value boolValue]); }, + @"Accessibility.accessibilityTraits.UIAccessibilityTraitTabBar": ^(NSNumber *value) { + if (@available(iOS 10.0, *)) { + node.accessibilityTraits = AccessibilityTraitsToggle(node.accessibilityTraits, UIAccessibilityTraitTabBar, [value boolValue]); + } + }, @"Accessibility.accessibilityViewIsModal": ^(NSNumber *value) { node.accessibilityViewIsModal = [value boolValue]; }, diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.mm b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.mm index ab17894db..f102aeeaf 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.mm +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.mm @@ -78,6 +78,11 @@ } - (void)didObserveResponse:(SKResponseInfo*)response { + // Only track HTTP(S) calls, data URLs cannot be casted to NSHTTPURLResponse + if (![response.response isKindOfClass:[NSHTTPURLResponse class]]) { + return; + } + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response.response; NSMutableArray*>* headers = [NSMutableArray new]; diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.m index 1213dd16d..668e03c13 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.m +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitNetworkPlugin/FlipperKitNetworkPlugin/SKResponseInfo.m @@ -29,6 +29,11 @@ } + (BOOL)shouldStripReponseBodyWithResponse:(NSURLResponse*)response { + // Only HTTP(S) responses have Content-Type headers + if (![response isKindOfClass:[NSHTTPURLResponse class]]) { + return YES; + } + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; NSString* contentType = httpResponse.allHeaderFields[@"content-type"]; if (!contentType) { diff --git a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m index ced17e17a..d995fb874 100644 --- a/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m +++ b/ios/Pods/FlipperKit/iOS/Plugins/FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.m @@ -82,6 +82,15 @@ static NSString* const kAppSuiteUserDefaultsName = @"App Suite UserDefaults"; forKey:preferenceName]; [responder success:[sharedPreferences dictionaryRepresentation]]; }]; + + [connection receive:@"deleteSharedPreference" + withBlock:^(NSDictionary* params, id responder) { + NSUserDefaults* sharedPreferences = + [self sharedPreferencesForParams:params]; + NSString* preferenceName = params[@"preferenceName"]; + [sharedPreferences removeObjectForKey:preferenceName]; + [responder success:[sharedPreferences dictionaryRepresentation]]; + }]; } - (void)didDisconnect { diff --git a/ios/Pods/Folly/LICENSE b/ios/Pods/Folly/LICENSE index f433b1a53..48bdb1282 100644 --- a/ios/Pods/Folly/LICENSE +++ b/ios/Pods/Folly/LICENSE @@ -175,3 +175,26 @@ of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + + +Files in folly/external/farmhash licensed as follows + + Copyright (c) 2014 Google, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. diff --git a/ios/Pods/Folly/README.md b/ios/Pods/Folly/README.md index cd093f41e..f50d687b9 100644 --- a/ios/Pods/Folly/README.md +++ b/ios/Pods/Folly/README.md @@ -66,13 +66,13 @@ is to look at the headers in [top level `folly/` directory](https://github.com/f check the [`docs` folder](folly/docs) for documentation, starting with the [overview](folly/docs/Overview.md). -Folly is published on Github at https://github.com/facebook/folly +Folly is published on GitHub at https://github.com/facebook/folly ### Build Notes #### Dependencies -folly requires gcc 4.9+ and a version of boost compiled with C++14 support. +folly requires gcc 5.1+ and a version of boost compiled with C++14 support. googletest is required to build and run folly's tests. You can download it from https://github.com/google/googletest/archive/release-1.8.0.tar.gz @@ -104,6 +104,11 @@ cmake \ -DCMAKE_LIBRARY_PATH=/alt/lib/path1:/alt/lib/path2 ... ``` +#### Building tests + +By default, building the tests is disabled as part of the CMake `all` target. +To build the tests, specify `-DBUILD_TESTS=ON` to CMake at configure time. + #### Ubuntu 16.04 LTS The following packages are required (feel free to cut and paste the apt-get @@ -127,7 +132,21 @@ sudo apt-get install \ binutils-dev \ libjemalloc-dev \ libssl-dev \ - pkg-config + pkg-config \ + libunwind-dev +``` + +Folly relies on [fmt](https://github.com/fmtlib/fmt) which needs to be installed from source. +The following commands will download, compile, and install fmt. + +``` +git clone https://github.com/fmtlib/fmt.git && cd fmt + +mkdir _build && cd _build +cmake .. + +make -j$(nproc) +sudo make install ``` If advanced debugging functionality is required, use: @@ -139,12 +158,12 @@ sudo apt-get install \ libdwarf-dev ``` -In the folly directory, run: +In the folly directory (e.g. the checkout root or the archive unpack root), run: ``` mkdir _build && cd _build cmake .. make -j $(nproc) - make install + make install # with either sudo or DESTDIR as necessary ``` #### OS X (Homebrew) @@ -154,19 +173,19 @@ folly is available as a Formula and releases may be built via `brew install foll You may also use `folly/build/bootstrap-osx-homebrew.sh` to build against `master`: ``` - cd folly - ./build/bootstrap-osx-homebrew.sh + ./folly/build/bootstrap-osx-homebrew.sh ``` +This will create a build directory `_build` in the top-level. + #### OS X (MacPorts) Install the required packages from MacPorts: ``` sudo port install \ - autoconf \ - automake \ boost \ + cmake \ gflags \ git \ google-glog \ @@ -174,8 +193,9 @@ Install the required packages from MacPorts: libtool \ lz4 \ lzma \ - scons \ + openssl \ snappy \ + xz \ zlib ``` @@ -193,9 +213,10 @@ Download and install folly with the parameters listed below: ``` git clone https://github.com/facebook/folly.git - cd folly/folly - autoreconf -ivf - ./configure CPPFLAGS="-I/opt/local/include" LDFLAGS="-L/opt/local/lib" + cd folly + mkdir _build + cd _build + cmake .. make sudo make install ``` diff --git a/ios/Pods/Folly/folly/AtomicHashArray-inl.h b/ios/Pods/Folly/folly/AtomicHashArray-inl.h index 91a66f61d..d05801160 100644 --- a/ios/Pods/Folly/folly/AtomicHashArray-inl.h +++ b/ios/Pods/Folly/folly/AtomicHashArray-inl.h @@ -1,11 +1,11 @@ /* - * Copyright 2012-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -21,7 +21,9 @@ #include #include +#include #include +#include namespace folly { @@ -58,7 +60,11 @@ AtomicHashArray< numEntries_(0, cacheSize), numPendingEntries_(0, cacheSize), isFull_(0), - numErases_(0) {} + numErases_(0) { + if (capacity == 0) { + throw_exception("capacity"); + } +} /* * findInternal -- @@ -480,10 +486,10 @@ struct AtomicHashArray< Allocator, ProbeFcn, KeyConvertFcn>::aha_iterator - : boost::iterator_facade< + : detail::IteratorFacade< aha_iterator, IterVal, - boost::forward_traversal_tag> { + std::forward_iterator_tag> { explicit aha_iterator() : aha_(nullptr) {} // Conversion ctor for interoperability between const_iterator and @@ -514,7 +520,8 @@ struct AtomicHashArray< private: friend class AtomicHashArray; - friend class boost::iterator_core_access; + friend class detail:: + IteratorFacade; void increment() { ++offset_; diff --git a/ios/Pods/Folly/folly/AtomicHashArray.h b/ios/Pods/Folly/folly/AtomicHashArray.h index 0bb4aa60b..cd62a2329 100644 --- a/ios/Pods/Folly/folly/AtomicHashArray.h +++ b/ios/Pods/Folly/folly/AtomicHashArray.h @@ -1,11 +1,11 @@ /* - * Copyright 2012-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -34,9 +34,6 @@ #include -#include -#include - #include #include #include @@ -102,7 +99,7 @@ template < class Allocator = std::allocator, class ProbeFcn = AtomicHashArrayLinearProbeFcn, class KeyConvertFcn = Identity> -class AtomicHashArray : boost::noncopyable { +class AtomicHashArray { static_assert( (std::is_convertible::value || std::is_convertible::value || @@ -422,6 +419,9 @@ class AtomicHashArray : boost::noncopyable { double maxLoadFactor, uint32_t cacheSize); + AtomicHashArray(const AtomicHashArray&) = delete; + AtomicHashArray& operator=(const AtomicHashArray&) = delete; + ~AtomicHashArray() = default; inline void unlockCell(value_type* const cell, KeyT newKey) { diff --git a/ios/Pods/Folly/folly/AtomicHashMap-inl.h b/ios/Pods/Folly/folly/AtomicHashMap-inl.h index 65498baa3..f15f07e2b 100644 --- a/ios/Pods/Folly/folly/AtomicHashMap-inl.h +++ b/ios/Pods/Folly/folly/AtomicHashMap-inl.h @@ -1,11 +1,11 @@ /* - * Copyright 2012-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -19,6 +19,9 @@ #endif #include +#include + +#include namespace folly { @@ -566,10 +569,10 @@ struct AtomicHashMap< Allocator, ProbeFcn, KeyConvertFcn>::ahm_iterator - : boost::iterator_facade< + : detail::IteratorFacade< ahm_iterator, IterVal, - boost::forward_traversal_tag> { + std::forward_iterator_tag> { explicit ahm_iterator() : ahm_(nullptr) {} // Conversion ctor for interoperability between const_iterator and @@ -596,7 +599,8 @@ struct AtomicHashMap< explicit ahm_iterator(ContT* ahm, uint32_t subMap, const SubIt& subIt) : ahm_(ahm), subMap_(subMap), subIt_(subIt) {} - friend class boost::iterator_core_access; + friend class detail:: + IteratorFacade; void increment() { CHECK(!isEnd()); diff --git a/ios/Pods/Folly/folly/AtomicHashMap.h b/ios/Pods/Folly/folly/AtomicHashMap.h index 75aeeceb4..a98d0866f 100644 --- a/ios/Pods/Folly/folly/AtomicHashMap.h +++ b/ios/Pods/Folly/folly/AtomicHashMap.h @@ -1,11 +1,11 @@ /* - * Copyright 2012-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * AtomicHashMap -- * @@ -81,10 +82,6 @@ #pragma once #define FOLLY_ATOMICHASHMAP_H_ -#include -#include -#include - #include #include #include @@ -162,7 +159,7 @@ template < class Allocator, class ProbeFcn, class KeyConvertFcn> -class AtomicHashMap : boost::noncopyable { +class AtomicHashMap { typedef AtomicHashArray< KeyT, ValueT, @@ -206,6 +203,9 @@ class AtomicHashMap : boost::noncopyable { // and a Config object to specify more advanced options. explicit AtomicHashMap(size_t finalSizeEst, const Config& c = Config()); + AtomicHashMap(const AtomicHashMap&) = delete; + AtomicHashMap& operator=(const AtomicHashMap&) = delete; + ~AtomicHashMap() { const unsigned int numMaps = numMapsAllocated_.load(std::memory_order_relaxed); diff --git a/ios/Pods/Folly/folly/AtomicIntrusiveLinkedList.h b/ios/Pods/Folly/folly/AtomicIntrusiveLinkedList.h index 95868bf97..aa2a866e0 100644 --- a/ios/Pods/Folly/folly/AtomicIntrusiveLinkedList.h +++ b/ios/Pods/Folly/folly/AtomicIntrusiveLinkedList.h @@ -1,11 +1,11 @@ /* - * Copyright 2014-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/ios/Pods/Folly/folly/AtomicLinkedList.h b/ios/Pods/Folly/folly/AtomicLinkedList.h index 254a48a08..ecff27ab3 100644 --- a/ios/Pods/Folly/folly/AtomicLinkedList.h +++ b/ios/Pods/Folly/folly/AtomicLinkedList.h @@ -1,11 +1,11 @@ /* - * Copyright 2014-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/ios/Pods/Folly/folly/AtomicUnorderedMap.h b/ios/Pods/Folly/folly/AtomicUnorderedMap.h index 28209ca3b..f7e84d7af 100644 --- a/ios/Pods/Folly/folly/AtomicUnorderedMap.h +++ b/ios/Pods/Folly/folly/AtomicUnorderedMap.h @@ -1,11 +1,11 @@ /* - * Copyright 2013-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -24,11 +24,10 @@ #include #include -#include - #include #include #include +#include #include #include #include @@ -135,8 +134,8 @@ template < typename Hash = std::hash, typename KeyEqual = std::equal_to, bool SkipKeyValueDeletion = - (boost::has_trivial_destructor::value && - boost::has_trivial_destructor::value), + (std::is_trivially_destructible::value && + std::is_trivially_destructible::value), template class Atom = std::atomic, typename IndexType = uint32_t, typename Allocator = folly::detail::MMapAlloc> @@ -362,8 +361,7 @@ struct AtomicUnorderedInsertMap { IndexType next_; /// Key and Value - typename std::aligned_storage::type - raw_; + aligned_storage_for_t raw_; ~Slot() { auto s = state(); @@ -463,7 +461,7 @@ struct AtomicUnorderedInsertMap { void zeroFillSlots() { using folly::detail::GivesZeroFilledMemory; if (!GivesZeroFilledMemory::value) { - memset(slots_, 0, mmapRequested_); + memset(static_cast(slots_), 0, mmapRequested_); } } }; @@ -478,8 +476,8 @@ template < typename Hash = std::hash, typename KeyEqual = std::equal_to, bool SkipKeyValueDeletion = - (boost::has_trivial_destructor::value && - boost::has_trivial_destructor::value), + (std::is_trivially_destructible::value && + std::is_trivially_destructible::value), template class Atom = std::atomic, typename Allocator = folly::detail::MMapAlloc> using AtomicUnorderedInsertMap64 = AtomicUnorderedInsertMap< diff --git a/ios/Pods/Folly/folly/Benchmark.h b/ios/Pods/Folly/folly/Benchmark.h index a2b286a24..f4d8fc7b0 100644 --- a/ios/Pods/Folly/folly/Benchmark.h +++ b/ios/Pods/Folly/folly/Benchmark.h @@ -1,11 +1,11 @@ /* - * Copyright 2012-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -52,22 +53,41 @@ inline bool runBenchmarksOnFlag() { return FLAGS_benchmark; } -namespace detail { +class UserMetric { + public: + enum class Type { CUSTOM, TIME, METRIC }; -using TimeIterPair = - std::pair; -using BenchmarkFun = std::function; + int64_t value{}; + Type type{Type::CUSTOM}; + + UserMetric() = default; + /* implicit */ UserMetric(int64_t val, Type typ = Type::CUSTOM) + : value(val), type(typ) {} +}; + +using UserCounters = std::unordered_map; + +namespace detail { +struct TimeIterData { + std::chrono::high_resolution_clock::duration duration; + unsigned int niter; + UserCounters userCounters; +}; + +using BenchmarkFun = std::function; struct BenchmarkRegistration { std::string file; std::string name; BenchmarkFun func; + bool useCounter = false; }; struct BenchmarkResult { std::string file; std::string name; double timeInNs; + UserCounters counters; }; /** @@ -77,7 +97,8 @@ struct BenchmarkResult { void addBenchmarkImpl( const char* file, const char* name, - std::function); + BenchmarkFun, + bool useCounter); } // namespace detail @@ -166,9 +187,7 @@ struct BenchmarkSuspender { * function). */ template -typename std::enable_if< - boost::function_types::function_arity< - decltype(&Lambda::operator())>::value == 2>::type +typename std::enable_if::value>::type addBenchmark(const char* file, const char* name, Lambda&& lambda) { auto execute = [=](unsigned int times) { BenchmarkSuspender::timeSpent = {}; @@ -179,13 +198,11 @@ addBenchmark(const char* file, const char* name, Lambda&& lambda) { niter = lambda(times); auto end = std::chrono::high_resolution_clock::now(); // CORE MEASUREMENT ENDS - - return detail::TimeIterPair( - (end - start) - BenchmarkSuspender::timeSpent, niter); + return detail::TimeIterData{ + (end - start) - BenchmarkSuspender::timeSpent, niter, UserCounters{}}; }; - detail::addBenchmarkImpl( - file, name, std::function(execute)); + detail::addBenchmarkImpl(file, name, detail::BenchmarkFun(execute), false); } /** @@ -195,9 +212,7 @@ addBenchmark(const char* file, const char* name, Lambda&& lambda) { * (iteration occurs outside the function). */ template -typename std::enable_if< - boost::function_types::function_arity< - decltype(&Lambda::operator())>::value == 1>::type +typename std::enable_if::value>::type addBenchmark(const char* file, const char* name, Lambda&& lambda) { addBenchmark(file, name, [=](unsigned int times) { unsigned int niter = 0; @@ -208,6 +223,47 @@ addBenchmark(const char* file, const char* name, Lambda&& lambda) { }); } +/** + * similar as previous two template specialization, but lambda will also take + * customized counters in the following two cases + */ +template +typename std::enable_if< + folly::is_invocable::value>::type +addBenchmark(const char* file, const char* name, Lambda&& lambda) { + auto execute = [=](unsigned int times) { + BenchmarkSuspender::timeSpent = {}; + unsigned int niter; + + // CORE MEASUREMENT STARTS + auto start = std::chrono::high_resolution_clock::now(); + UserCounters counters; + niter = lambda(counters, times); + auto end = std::chrono::high_resolution_clock::now(); + // CORE MEASUREMENT ENDS + return detail::TimeIterData{ + (end - start) - BenchmarkSuspender::timeSpent, niter, counters}; + }; + + detail::addBenchmarkImpl( + file, + name, + std::function(execute), + true); +} + +template +typename std::enable_if::value>::type +addBenchmark(const char* file, const char* name, Lambda&& lambda) { + addBenchmark(file, name, [=](UserCounters& counters, unsigned int times) { + unsigned int niter = 0; + while (times-- > 0) { + niter += lambda(counters); + } + return niter; + }); +} + /** * Call doNotOptimizeAway(var) to ensure that var will be computed even * post-optimization. Use it for variables that are computed during @@ -315,32 +371,50 @@ void printResultComparison( * Introduces a benchmark function. Used internally, see BENCHMARK and * friends below. */ -#define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName) \ - static void funName(paramType); \ - static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ - (::folly::addBenchmark( \ - __FILE__, \ - stringName, \ - [](paramType paramName) -> unsigned { \ - funName(paramName); \ - return rv; \ - }), \ - true); \ + +#define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName) \ + static void funName(paramType); \ + FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ + (::folly::addBenchmark( \ + __FILE__, \ + stringName, \ + [](paramType paramName) -> unsigned { \ + funName(paramName); \ + return rv; \ + }), \ + true); \ static void funName(paramType paramName) +#define BENCHMARK_IMPL_COUNTERS( \ + funName, stringName, counters, rv, paramType, paramName) \ + static void funName( \ + ::folly::UserCounters& FOLLY_PP_DETAIL_APPEND_VA_ARG(paramType)); \ + FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ + (::folly::addBenchmark( \ + __FILE__, \ + stringName, \ + [](::folly::UserCounters& counters FOLLY_PP_DETAIL_APPEND_VA_ARG( \ + paramType paramName)) -> unsigned { \ + funName(counters FOLLY_PP_DETAIL_APPEND_VA_ARG(paramName)); \ + return rv; \ + }), \ + true); \ + static void funName(::folly::UserCounters& counters \ + FOLLY_PP_DETAIL_APPEND_VA_ARG(paramType paramName)) + /** * Introduces a benchmark function with support for returning the actual * number of iterations. Used internally, see BENCHMARK_MULTI and friends * below. */ -#define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName) \ - static unsigned funName(paramType); \ - static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ - (::folly::addBenchmark( \ - __FILE__, \ - stringName, \ - [](paramType paramName) { return funName(paramName); }), \ - true); \ +#define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName) \ + static unsigned funName(paramType); \ + FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ + (::folly::addBenchmark( \ + __FILE__, \ + stringName, \ + [](paramType paramName) { return funName(paramName); }), \ + true); \ static unsigned funName(paramType paramName) /** @@ -355,9 +429,9 @@ void printResultComparison( * v.push_back(42); * } * - * BENCHMARK(insertVectorBegin, n) { + * BENCHMARK(insertVectorBegin, iters) { * vector v; - * FOR_EACH_RANGE (i, 0, n) { + * FOR_EACH_RANGE (i, 0, iters) { * v.insert(v.begin(), 42); * } * } @@ -365,11 +439,33 @@ void printResultComparison( #define BENCHMARK(name, ...) \ BENCHMARK_IMPL( \ name, \ - FB_STRINGIZE(name), \ + FOLLY_PP_STRINGIZE(name), \ FB_ARG_2_OR_1(1, ##__VA_ARGS__), \ FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ __VA_ARGS__) +/** + * Allow users to record customized counter during benchmarking, + * there will be one extra column showing in the output result for each counter + * + * BENCHMARK_COUNTERS(insertVectorBegin, couters, iters) { + * vector v; + * FOR_EACH_RANGE (i, 0, iters) { + * v.insert(v.begin(), 42); + * } + * BENCHMARK_SUSPEND { + * counters["foo"] = 10; + * } + * } + */ +#define BENCHMARK_COUNTERS(name, counters, ...) \ + BENCHMARK_IMPL_COUNTERS( \ + name, \ + FOLLY_PP_STRINGIZE(name), \ + counters, \ + FB_ARG_2_OR_1(1, ##__VA_ARGS__), \ + FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ + __VA_ARGS__) /** * Like BENCHMARK above, but allows the user to return the actual * number of iterations executed in the function body. This can be @@ -388,7 +484,7 @@ void printResultComparison( #define BENCHMARK_MULTI(name, ...) \ BENCHMARK_MULTI_IMPL( \ name, \ - FB_STRINGIZE(name), \ + FOLLY_PP_STRINGIZE(name), \ FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ __VA_ARGS__) @@ -446,27 +542,27 @@ void printResultComparison( * BENCHMARK_NAMED_PARAM(addValue, 0_to_1000, 10, 0, 1000) * BENCHMARK_NAMED_PARAM(addValue, 5k_to_20k, 250, 5000, 20000) */ -#define BENCHMARK_NAMED_PARAM(name, param_name, ...) \ - BENCHMARK_IMPL( \ - FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ - FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")", \ - iters, \ - unsigned, \ - iters) { \ - name(iters, ##__VA_ARGS__); \ +#define BENCHMARK_NAMED_PARAM(name, param_name, ...) \ + BENCHMARK_IMPL( \ + FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ + FOLLY_PP_STRINGIZE(name) "(" FOLLY_PP_STRINGIZE(param_name) ")", \ + iters, \ + unsigned, \ + iters) { \ + name(iters, ##__VA_ARGS__); \ } /** * Same as BENCHMARK_NAMED_PARAM, but allows one to return the actual number * of iterations that have been run. */ -#define BENCHMARK_NAMED_PARAM_MULTI(name, param_name, ...) \ - BENCHMARK_MULTI_IMPL( \ - FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ - FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")", \ - unsigned, \ - iters) { \ - return name(iters, ##__VA_ARGS__); \ +#define BENCHMARK_NAMED_PARAM_MULTI(name, param_name, ...) \ + BENCHMARK_MULTI_IMPL( \ + FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ + FOLLY_PP_STRINGIZE(name) "(" FOLLY_PP_STRINGIZE(param_name) ")", \ + unsigned, \ + iters) { \ + return name(iters, ##__VA_ARGS__); \ } /** @@ -496,11 +592,19 @@ void printResultComparison( #define BENCHMARK_RELATIVE(name, ...) \ BENCHMARK_IMPL( \ name, \ - "%" FB_STRINGIZE(name), \ + "%" FOLLY_PP_STRINGIZE(name), \ FB_ARG_2_OR_1(1, ##__VA_ARGS__), \ FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ __VA_ARGS__) +#define BENCHMARK_COUNTERS_RELATIVE(name, counters, ...) \ + BENCHMARK_IMPL_COUNTERS( \ + name, \ + "%" FOLLY_PP_STRINGIZE(name), \ + counters, \ + FB_ARG_2_OR_1(1, ##__VA_ARGS__), \ + FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ + __VA_ARGS__) /** * Same as BENCHMARK_RELATIVE, but allows one to return the actual number * of iterations that have been run. @@ -508,7 +612,7 @@ void printResultComparison( #define BENCHMARK_RELATIVE_MULTI(name, ...) \ BENCHMARK_MULTI_IMPL( \ name, \ - "%" FB_STRINGIZE(name), \ + "%" FOLLY_PP_STRINGIZE(name), \ FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \ __VA_ARGS__) @@ -528,35 +632,35 @@ void printResultComparison( /** * A combination of BENCHMARK_RELATIVE and BENCHMARK_NAMED_PARAM. */ -#define BENCHMARK_RELATIVE_NAMED_PARAM(name, param_name, ...) \ - BENCHMARK_IMPL( \ - FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ - "%" FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")", \ - iters, \ - unsigned, \ - iters) { \ - name(iters, ##__VA_ARGS__); \ +#define BENCHMARK_RELATIVE_NAMED_PARAM(name, param_name, ...) \ + BENCHMARK_IMPL( \ + FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ + "%" FOLLY_PP_STRINGIZE(name) "(" FOLLY_PP_STRINGIZE(param_name) ")", \ + iters, \ + unsigned, \ + iters) { \ + name(iters, ##__VA_ARGS__); \ } /** * Same as BENCHMARK_RELATIVE_NAMED_PARAM, but allows one to return the * actual number of iterations that have been run. */ -#define BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param_name, ...) \ - BENCHMARK_MULTI_IMPL( \ - FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ - "%" FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")", \ - unsigned, \ - iters) { \ - return name(iters, ##__VA_ARGS__); \ +#define BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param_name, ...) \ + BENCHMARK_MULTI_IMPL( \ + FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \ + "%" FOLLY_PP_STRINGIZE(name) "(" FOLLY_PP_STRINGIZE(param_name) ")", \ + unsigned, \ + iters) { \ + return name(iters, ##__VA_ARGS__); \ } /** * Draws a line of dashes. */ -#define BENCHMARK_DRAW_LINE() \ - static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ - (::folly::addBenchmark(__FILE__, "-", []() -> unsigned { return 0; }), \ +#define BENCHMARK_DRAW_LINE() \ + FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \ + (::folly::addBenchmark(__FILE__, "-", []() -> unsigned { return 0; }), \ true) /** diff --git a/ios/Pods/Folly/folly/Bits.h b/ios/Pods/Folly/folly/Bits.h index e5cf5be2f..1569d598c 100644 --- a/ios/Pods/Folly/folly/Bits.h +++ b/ios/Pods/Folly/folly/Bits.h @@ -1,11 +1,11 @@ /* - * Copyright 2011-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/ios/Pods/Folly/folly/CPortability.h b/ios/Pods/Folly/folly/CPortability.h index 82a1e8e07..976daf069 100644 --- a/ios/Pods/Folly/folly/CPortability.h +++ b/ios/Pods/Folly/folly/CPortability.h @@ -1,11 +1,11 @@ /* - * Copyright 2013-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -68,12 +68,14 @@ * itself was compiled without ASAN but a downstream project that uses folly is * compiling with ASAN enabled. * - * Use FOLLY_ASAN_ENABLED (defined in folly-config.h) to check if folly itself - * was compiled with ASAN enabled. + * Use FOLLY_LIBRARY_SANITIZE_ADDRESS (defined in folly-config.h) to check if + * folly itself was compiled with ASAN enabled. */ +#ifndef FOLLY_SANITIZE_ADDRESS #if FOLLY_HAS_FEATURE(address_sanitizer) || __SANITIZE_ADDRESS__ #define FOLLY_SANITIZE_ADDRESS 1 #endif +#endif /* Define attribute wrapper for function attribute used to disable * address sanitizer instrumentation. Unfortunately, this attribute @@ -101,17 +103,46 @@ /* Define a convenience macro to test when thread sanitizer is being used * across the different compilers (e.g. clang, gcc) */ +#ifndef FOLLY_SANITIZE_THREAD #if FOLLY_HAS_FEATURE(thread_sanitizer) || __SANITIZE_THREAD__ #define FOLLY_SANITIZE_THREAD 1 #endif +#endif + +#if FOLLY_SANITIZE_THREAD +#define FOLLY_DISABLE_THREAD_SANITIZER \ + __attribute__((no_sanitize_thread, noinline)) +#else +#define FOLLY_DISABLE_THREAD_SANITIZER +#endif /** - * Define a convenience macro to test when ASAN, UBSAN or TSAN sanitizer are - * being used + * Define a convenience macro to test when memory sanitizer is being used + * across the different compilers (e.g. clang, gcc) */ -#if defined(FOLLY_SANITIZE_ADDRESS) || defined(FOLLY_SANITIZE_THREAD) +#ifndef FOLLY_SANITIZE_MEMORY +#if FOLLY_HAS_FEATURE(memory_sanitizer) || __SANITIZE_MEMORY__ +#define FOLLY_SANITIZE_MEMORY 1 +#endif +#endif + +#if FOLLY_SANITIZE_MEMORY +#define FOLLY_DISABLE_MEMORY_SANITIZER \ + __attribute__((no_sanitize_memory, noinline)) +#else +#define FOLLY_DISABLE_MEMORY_SANITIZER +#endif + +/** + * Define a convenience macro to test when ASAN, UBSAN, TSAN or MSAN sanitizer + * are being used + */ +#ifndef FOLLY_SANITIZE +#if defined(FOLLY_SANITIZE_ADDRESS) || defined(FOLLY_SANITIZE_THREAD) || \ + defined(FOLLY_SANITIZE_MEMORY) #define FOLLY_SANITIZE 1 #endif +#endif #if FOLLY_SANITIZE #define FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(...) \ @@ -120,15 +151,15 @@ #define FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER(...) #endif // FOLLY_SANITIZE +#define FOLLY_DISABLE_SANITIZERS \ + FOLLY_DISABLE_ADDRESS_SANITIZER FOLLY_DISABLE_THREAD_SANITIZER \ + FOLLY_DISABLE_UNDEFINED_BEHAVIOR_SANITIZER("undefined") + /** * Macro for marking functions as having public visibility. */ #if defined(__GNUC__) -#if __GNUC_PREREQ(4, 9) -#define FOLLY_EXPORT [[gnu::visibility("default")]] -#else #define FOLLY_EXPORT __attribute__((__visibility__("default"))) -#endif #else #define FOLLY_EXPORT #endif @@ -136,7 +167,7 @@ // noinline #ifdef _MSC_VER #define FOLLY_NOINLINE __declspec(noinline) -#elif defined(__clang__) || defined(__GNUC__) +#elif defined(__GNUC__) #define FOLLY_NOINLINE __attribute__((__noinline__)) #else #define FOLLY_NOINLINE @@ -145,16 +176,16 @@ // always inline #ifdef _MSC_VER #define FOLLY_ALWAYS_INLINE __forceinline -#elif defined(__clang__) || defined(__GNUC__) +#elif defined(__GNUC__) #define FOLLY_ALWAYS_INLINE inline __attribute__((__always_inline__)) #else #define FOLLY_ALWAYS_INLINE inline #endif // attribute hidden -#if _MSC_VER +#if defined(_MSC_VER) #define FOLLY_ATTR_VISIBILITY_HIDDEN -#elif defined(__clang__) || defined(__GNUC__) +#elif defined(__GNUC__) #define FOLLY_ATTR_VISIBILITY_HIDDEN __attribute__((__visibility__("hidden"))) #else #define FOLLY_ATTR_VISIBILITY_HIDDEN @@ -174,21 +205,38 @@ #endif #endif -// These functions are defined by the TSAN runtime library and enable -// annotating mutexes for TSAN. -extern "C" FOLLY_ATTR_WEAK void -AnnotateRWLockCreate(const char* f, int l, const volatile void* addr); -extern "C" FOLLY_ATTR_WEAK void -AnnotateRWLockCreateStatic(const char* f, int l, const volatile void* addr); -extern "C" FOLLY_ATTR_WEAK void -AnnotateRWLockDestroy(const char* f, int l, const volatile void* addr); -extern "C" FOLLY_ATTR_WEAK void -AnnotateRWLockAcquired(const char* f, int l, const volatile void* addr, long w); -extern "C" FOLLY_ATTR_WEAK void -AnnotateRWLockReleased(const char* f, int l, const volatile void* addr, long w); -extern "C" FOLLY_ATTR_WEAK void AnnotateBenignRaceSized( - const char* f, - int l, - const volatile void* addr, - long size, - const char* desc); +// FOLLY_ERASE +// +// A conceptual attribute/syntax combo for erasing a function from the build +// artifacts and forcing all call-sites to inline the callee, at least as far +// as each compiler supports. +// +// Semantically includes the inline specifier. +#define FOLLY_ERASE FOLLY_ALWAYS_INLINE FOLLY_ATTR_VISIBILITY_HIDDEN + +// FOLLY_ERASE_HACK_GCC +// +// Equivalent to FOLLY_ERASE, but without hiding under gcc. Useful when applied +// to a function which may sometimes be hidden separately, for example by being +// declared in an anonymous namespace, since in such cases with -Wattributes +// enabled, gcc would emit: 'visibility' attribute ignored. +// +// Semantically includes the inline specifier. +#if defined(__GNUC__) && !defined(__clang__) +#define FOLLY_ERASE_HACK_GCC FOLLY_ALWAYS_INLINE +#else +#define FOLLY_ERASE_HACK_GCC FOLLY_ERASE +#endif + +// FOLLY_ERASE_TRYCATCH +// +// Equivalent to FOLLY_ERASE, but for code which might contain explicit +// exception handling. Has the effect of FOLLY_ERASE, except under MSVC which +// warns about __forceinline when functions contain exception handling. +// +// Semantically includes the inline specifier. +#ifdef _MSC_VER +#define FOLLY_ERASE_TRYCATCH inline +#else +#define FOLLY_ERASE_TRYCATCH FOLLY_ERASE +#endif diff --git a/ios/Pods/Folly/folly/CachelinePadded.h b/ios/Pods/Folly/folly/CachelinePadded.h index d706b18ad..635bc53da 100644 --- a/ios/Pods/Folly/folly/CachelinePadded.h +++ b/ios/Pods/Folly/folly/CachelinePadded.h @@ -1,11 +1,11 @@ /* - * Copyright 2016-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,9 +17,11 @@ #pragma once #include +#include #include #include +#include namespace folly { @@ -29,17 +31,20 @@ namespace folly { * * If `sizeof(T) <= alignof(T)` then the inner `T` will be entirely within one * false sharing range (AKA cache line). + * + * CachelinePadded may add padding both before and after the value. Consider + * whether alignas(folly::hardware_destructive_interference_size) suffices. */ template class CachelinePadded { - static_assert( - alignof(T) <= max_align_v, - "CachelinePadded does not support over-aligned types."); - public: template explicit CachelinePadded(Args&&... args) - : inner_(std::forward(args)...) {} + : inner_(std::forward(args)...) { + FOLLY_SAFE_DCHECK( + (reinterpret_cast(&inner_) % alignof(T)) == 0, + "CachelinePadded requires types aligned to their ABI requirement"); + } T* get() { return &inner_; diff --git a/ios/Pods/Folly/folly/CancellationToken-inl.h b/ios/Pods/Folly/folly/CancellationToken-inl.h new file mode 100644 index 000000000..8ce5c5c79 --- /dev/null +++ b/ios/Pods/Folly/folly/CancellationToken-inl.h @@ -0,0 +1,351 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +namespace folly { + +namespace detail { + +// Internal cancellation state object. +class CancellationState { + public: + FOLLY_NODISCARD static CancellationStateSourcePtr create(); + + private: + // Constructed initially with a CancellationSource reference count of 1. + CancellationState() noexcept; + + ~CancellationState(); + + friend struct CancellationStateTokenDeleter; + friend struct CancellationStateSourceDeleter; + + void removeTokenReference() noexcept; + void removeSourceReference() noexcept; + + public: + FOLLY_NODISCARD CancellationStateTokenPtr addTokenReference() noexcept; + + FOLLY_NODISCARD CancellationStateSourcePtr addSourceReference() noexcept; + + bool tryAddCallback( + CancellationCallback* callback, + bool incrementRefCountIfSuccessful) noexcept; + + void removeCallback(CancellationCallback* callback) noexcept; + + bool isCancellationRequested() const noexcept; + bool canBeCancelled() const noexcept; + + // Request cancellation. + // Return 'true' if cancellation had already been requested. + // Return 'false' if this was the first thread to request + // cancellation. + bool requestCancellation() noexcept; + + private: + void lock() noexcept; + void unlock() noexcept; + void unlockAndIncrementTokenCount() noexcept; + void unlockAndDecrementTokenCount() noexcept; + bool tryLockAndCancelUnlessCancelled() noexcept; + + template + bool tryLock(Predicate predicate) noexcept; + + static bool canBeCancelled(std::uint64_t state) noexcept; + static bool isCancellationRequested(std::uint64_t state) noexcept; + static bool isLocked(std::uint64_t state) noexcept; + + static constexpr std::uint64_t kCancellationRequestedFlag = 1; + static constexpr std::uint64_t kLockedFlag = 2; + static constexpr std::uint64_t kTokenReferenceCountIncrement = 4; + static constexpr std::uint64_t kSourceReferenceCountIncrement = + std::uint64_t(1) << 33u; + static constexpr std::uint64_t kTokenReferenceCountMask = + (kSourceReferenceCountIncrement - 1u) - + (kTokenReferenceCountIncrement - 1u); + static constexpr std::uint64_t kSourceReferenceCountMask = + std::numeric_limits::max() - + (kSourceReferenceCountIncrement - 1u); + + // Bit 0 - Cancellation Requested + // Bit 1 - Locked Flag + // Bits 2-32 - Token reference count (max ~2 billion) + // Bits 33-63 - Source reference count (max ~2 billion) + std::atomic state_; + CancellationCallback* head_; + std::thread::id signallingThreadId_; +}; + +inline void CancellationStateTokenDeleter::operator()( + CancellationState* state) noexcept { + state->removeTokenReference(); +} + +inline void CancellationStateSourceDeleter::operator()( + CancellationState* state) noexcept { + state->removeSourceReference(); +} + +} // namespace detail + +inline CancellationToken::CancellationToken( + const CancellationToken& other) noexcept + : state_() { + if (other.state_) { + state_ = other.state_->addTokenReference(); + } +} + +inline CancellationToken::CancellationToken(CancellationToken&& other) noexcept + : state_(std::move(other.state_)) {} + +inline CancellationToken& CancellationToken::operator=( + const CancellationToken& other) noexcept { + if (state_ != other.state_) { + CancellationToken temp{other}; + swap(temp); + } + return *this; +} + +inline CancellationToken& CancellationToken::operator=( + CancellationToken&& other) noexcept { + state_ = std::move(other.state_); + return *this; +} + +inline bool CancellationToken::isCancellationRequested() const noexcept { + return state_ != nullptr && state_->isCancellationRequested(); +} + +inline bool CancellationToken::canBeCancelled() const noexcept { + return state_ != nullptr && state_->canBeCancelled(); +} + +inline void CancellationToken::swap(CancellationToken& other) noexcept { + std::swap(state_, other.state_); +} + +inline CancellationToken::CancellationToken( + detail::CancellationStateTokenPtr state) noexcept + : state_(std::move(state)) {} + +inline bool operator==( + const CancellationToken& a, + const CancellationToken& b) noexcept { + return a.state_ == b.state_; +} + +inline bool operator!=( + const CancellationToken& a, + const CancellationToken& b) noexcept { + return !(a == b); +} + +inline CancellationSource::CancellationSource() + : state_(detail::CancellationState::create()) {} + +inline CancellationSource::CancellationSource( + const CancellationSource& other) noexcept + : state_() { + if (other.state_) { + state_ = other.state_->addSourceReference(); + } +} + +inline CancellationSource::CancellationSource( + CancellationSource&& other) noexcept + : state_(std::move(other.state_)) {} + +inline CancellationSource& CancellationSource::operator=( + const CancellationSource& other) noexcept { + if (state_ != other.state_) { + CancellationSource temp{other}; + swap(temp); + } + return *this; +} + +inline CancellationSource& CancellationSource::operator=( + CancellationSource&& other) noexcept { + state_ = std::move(other.state_); + return *this; +} + +inline CancellationSource CancellationSource::invalid() noexcept { + return CancellationSource{detail::CancellationStateSourcePtr{}}; +} + +inline bool CancellationSource::isCancellationRequested() const noexcept { + return state_ != nullptr && state_->isCancellationRequested(); +} + +inline bool CancellationSource::canBeCancelled() const noexcept { + return state_ != nullptr; +} + +inline CancellationToken CancellationSource::getToken() const noexcept { + if (state_ != nullptr) { + return CancellationToken{state_->addTokenReference()}; + } + return CancellationToken{}; +} + +inline bool CancellationSource::requestCancellation() const noexcept { + if (state_ != nullptr) { + return state_->requestCancellation(); + } + return false; +} + +inline void CancellationSource::swap(CancellationSource& other) noexcept { + std::swap(state_, other.state_); +} + +inline CancellationSource::CancellationSource( + detail::CancellationStateSourcePtr&& state) noexcept + : state_(std::move(state)) {} + +template < + typename Callable, + std::enable_if_t< + std::is_constructible:: + value, + int>> +inline CancellationCallback::CancellationCallback( + CancellationToken&& ct, + Callable&& callable) + : next_(nullptr), + prevNext_(nullptr), + state_(nullptr), + callback_(static_cast(callable)), + destructorHasRunInsideCallback_(nullptr), + callbackCompleted_(false) { + if (ct.state_ != nullptr && ct.state_->tryAddCallback(this, false)) { + state_ = ct.state_.release(); + } +} + +template < + typename Callable, + std::enable_if_t< + std::is_constructible:: + value, + int>> +inline CancellationCallback::CancellationCallback( + const CancellationToken& ct, + Callable&& callable) + : next_(nullptr), + prevNext_(nullptr), + state_(nullptr), + callback_(static_cast(callable)), + destructorHasRunInsideCallback_(nullptr), + callbackCompleted_(false) { + if (ct.state_ != nullptr && ct.state_->tryAddCallback(this, true)) { + state_ = ct.state_.get(); + } +} + +inline CancellationCallback::~CancellationCallback() { + if (state_ != nullptr) { + state_->removeCallback(this); + } +} + +inline void CancellationCallback::invokeCallback() noexcept { + // Invoke within a noexcept context so that we std::terminate() if it throws. + callback_(); +} + +namespace detail { + +inline CancellationStateSourcePtr CancellationState::create() { + return CancellationStateSourcePtr{new CancellationState()}; +} + +inline CancellationState::CancellationState() noexcept + : state_(kSourceReferenceCountIncrement), + head_(nullptr), + signallingThreadId_() {} + +inline CancellationStateTokenPtr +CancellationState::addTokenReference() noexcept { + state_.fetch_add(kTokenReferenceCountIncrement, std::memory_order_relaxed); + return CancellationStateTokenPtr{this}; +} + +inline void CancellationState::removeTokenReference() noexcept { + const auto oldState = state_.fetch_sub( + kTokenReferenceCountIncrement, std::memory_order_acq_rel); + DCHECK( + (oldState & kTokenReferenceCountMask) >= kTokenReferenceCountIncrement); + if (oldState < (2 * kTokenReferenceCountIncrement)) { + delete this; + } +} + +inline CancellationStateSourcePtr +CancellationState::addSourceReference() noexcept { + state_.fetch_add(kSourceReferenceCountIncrement, std::memory_order_relaxed); + return CancellationStateSourcePtr{this}; +} + +inline void CancellationState::removeSourceReference() noexcept { + const auto oldState = state_.fetch_sub( + kSourceReferenceCountIncrement, std::memory_order_acq_rel); + DCHECK( + (oldState & kSourceReferenceCountMask) >= kSourceReferenceCountIncrement); + if (oldState < + (kSourceReferenceCountIncrement + kTokenReferenceCountIncrement)) { + delete this; + } +} + +inline bool CancellationState::isCancellationRequested() const noexcept { + return isCancellationRequested(state_.load(std::memory_order_acquire)); +} + +inline bool CancellationState::canBeCancelled() const noexcept { + return canBeCancelled(state_.load(std::memory_order_acquire)); +} + +inline bool CancellationState::canBeCancelled(std::uint64_t state) noexcept { + // Can be cancelled if there is at least one CancellationSource ref-count + // or if cancellation has been requested. + return (state >= kSourceReferenceCountIncrement) || + isCancellationRequested(state); +} + +inline bool CancellationState::isCancellationRequested( + std::uint64_t state) noexcept { + return (state & kCancellationRequestedFlag) != 0; +} + +inline bool CancellationState::isLocked(std::uint64_t state) noexcept { + return (state & kLockedFlag) != 0; +} + +} // namespace detail + +} // namespace folly diff --git a/ios/Pods/Folly/folly/CancellationToken.h b/ios/Pods/Folly/folly/CancellationToken.h new file mode 100644 index 000000000..bdda121b0 --- /dev/null +++ b/ios/Pods/Folly/folly/CancellationToken.h @@ -0,0 +1,298 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace folly { + +class CancellationCallback; +class CancellationSource; +struct OperationCancelled : public std::exception { + const char* what() const noexcept override { + return "coroutine operation cancelled"; + } +}; + +namespace detail { +class CancellationState; +struct CancellationStateTokenDeleter { + void operator()(CancellationState*) noexcept; +}; +struct CancellationStateSourceDeleter { + void operator()(CancellationState*) noexcept; +}; +using CancellationStateTokenPtr = + std::unique_ptr; +using CancellationStateSourcePtr = + std::unique_ptr; +} // namespace detail + +// A CancellationToken is an object that can be passed into an function or +// operation that allows the caller to later request that the operation be +// cancelled. +// +// A CancellationToken object can be obtained by calling the .getToken() +// method on a CancellationSource or by copying another CancellationToken +// object. All CancellationToken objects obtained from the same original +// CancellationSource object all reference the same underlying cancellation +// state and will all be cancelled together. +// +// If your function needs to be cancellable but does not need to request +// cancellation then you should take a CancellationToken as a parameter. +// If your function needs to be able to request cancellation then you +// should instead take a CancellationSource as a parameter. +class CancellationToken { + public: + // Constructs to a token that can never be cancelled. + // + // Pass a default-constructed CancellationToken into an operation that + // you never intend to cancel. These objects are very cheap to create. + CancellationToken() noexcept = default; + + // Construct a copy of the token that shares the same underlying state. + CancellationToken(const CancellationToken& other) noexcept; + CancellationToken(CancellationToken&& other) noexcept; + + CancellationToken& operator=(const CancellationToken& other) noexcept; + CancellationToken& operator=(CancellationToken&& other) noexcept; + + // Query whether someone has called .requestCancellation() on an instance + // of CancellationSource object associated with this CancellationToken. + bool isCancellationRequested() const noexcept; + + // Query whether this CancellationToken can ever have cancellation requested + // on it. + // + // This will return false if the CancellationToken is not associated with a + // CancellationSource object. eg. because the CancellationToken was + // default-constructed, has been moved-from or because the last + // CancellationSource object associated with the underlying cancellation state + // has been destroyed and the operation has not yet been cancelled and so + // never will be. + // + // Implementations of operations may be able to take more efficient code-paths + // if they know they can never be cancelled. + bool canBeCancelled() const noexcept; + + void swap(CancellationToken& other) noexcept; + + friend bool operator==( + const CancellationToken& a, + const CancellationToken& b) noexcept; + + private: + friend class CancellationCallback; + friend class CancellationSource; + + explicit CancellationToken(detail::CancellationStateTokenPtr state) noexcept; + + detail::CancellationStateTokenPtr state_; +}; + +bool operator==( + const CancellationToken& a, + const CancellationToken& b) noexcept; +bool operator!=( + const CancellationToken& a, + const CancellationToken& b) noexcept; + +// A CancellationSource object provides the ability to request cancellation of +// operations that an associated CancellationToken was passed to. +// +// Example usage: +// CancellationSource cs; +// Future f = startSomeOperation(cs.getToken()); +// +// // Later... +// cs.requestCancellation(); +class CancellationSource { + public: + // Construct to a new, independent cancellation source. + CancellationSource(); + + // Construct a new reference to the same underlying cancellation state. + // + // Either the original or the new copy can be used to request cancellation + // of associated work. + CancellationSource(const CancellationSource& other) noexcept; + + // This leaves 'other' in an empty state where 'requestCancellation()' is a + // no-op and 'canBeCancelled()' returns false. + CancellationSource(CancellationSource&& other) noexcept; + + CancellationSource& operator=(const CancellationSource& other) noexcept; + CancellationSource& operator=(CancellationSource&& other) noexcept; + + // Construct a CancellationSource that cannot be cancelled. + // + // This factory function can be used to obtain a CancellationSource that + // is equivalent to a moved-from CancellationSource object without needing + // to allocate any shared-state. + static CancellationSource invalid() noexcept; + + // Query if cancellation has already been requested on this CancellationSource + // or any other CancellationSource object copied from the same original + // CancellationSource object. + bool isCancellationRequested() const noexcept; + + // Query if cancellation can be requested through this CancellationSource + // object. This will only return false if the CancellationSource object has + // been moved-from. + bool canBeCancelled() const noexcept; + + // Obtain a CancellationToken linked to this CancellationSource. + // + // This token can be passed into cancellable operations to allow the caller + // to later request cancellation of that operation. + CancellationToken getToken() const noexcept; + + // Request cancellation of work associated with this CancellationSource. + // + // This will ensure subsequent calls to isCancellationRequested() on any + // CancellationSource or CancellationToken object associated with the same + // underlying cancellation state to return true. + // + // If this is the first call to requestCancellation() on any + // CancellationSource object with the same underlying state then this call + // will also execute the callbacks associated with any CancellationCallback + // objects that were constructed with an associated CancellationToken. + // + // Note that it is possible that another thread may be concurrently + // registering a callback with CancellationCallback. This method guarantees + // that either this thread will see the callback registration and will + // ensure that the callback is called, or the CancellationCallback constructor + // will see the cancellation-requested signal and will execute the callback + // inline inside the constructor. + // + // Returns the previous state of 'isCancellationRequested()'. i.e. + // - 'true' if cancellation had previously been requested. + // - 'false' if this was the first call to request cancellation. + bool requestCancellation() const noexcept; + + void swap(CancellationSource& other) noexcept; + + friend bool operator==( + const CancellationSource& a, + const CancellationSource& b) noexcept; + + private: + explicit CancellationSource( + detail::CancellationStateSourcePtr&& state) noexcept; + + detail::CancellationStateSourcePtr state_; +}; + +bool operator==( + const CancellationSource& a, + const CancellationSource& b) noexcept; +bool operator!=( + const CancellationSource& a, + const CancellationSource& b) noexcept; + +class CancellationCallback { + using VoidFunction = folly::Function; + + public: + // Constructing a CancellationCallback object registers the callback + // with the specified CancellationToken such that the callback will be + // executed if the corresponding CancellationSource object has the + // requestCancellation() method called on it. + // + // If the CancellationToken object already had cancellation requested + // then the callback will be executed inline on the current thread before + // the constructor returns. Otherwise, the callback will be executed on + // in the execution context of the first thread to call requestCancellation() + // on a corresponding CancellationSource. + // + // The callback object must not throw any unhandled exceptions. Doing so + // will result in the program terminating via std::terminate(). + template < + typename Callable, + std::enable_if_t< + std::is_constructible::value, + int> = 0> + CancellationCallback(CancellationToken&& ct, Callable&& callable); + template < + typename Callable, + std::enable_if_t< + std::is_constructible::value, + int> = 0> + CancellationCallback(const CancellationToken& ct, Callable&& callable); + + // Deregisters the callback from the CancellationToken. + // + // If cancellation has been requested concurrently on another thread and the + // callback is currently executing then the destructor will block until after + // the callback has returned (otherwise it might be left with a dangling + // reference). + // + // You should generally try to implement your callback functions to be lock + // free to avoid deadlocks between the callback executing and the + // CancellationCallback destructor trying to deregister the callback. + // + // If the callback has not started executing yet then the callback will be + // deregistered from the CancellationToken before the destructor completes. + // + // Once the destructor returns you can be guaranteed that the callback will + // not be called by a subsequent call to 'requestCancellation()' on a + // CancellationSource associated with the CancellationToken passed to the + // constructor. + ~CancellationCallback(); + + // Not copyable/movable + CancellationCallback(const CancellationCallback&) = delete; + CancellationCallback(CancellationCallback&&) = delete; + CancellationCallback& operator=(const CancellationCallback&) = delete; + CancellationCallback& operator=(CancellationCallback&&) = delete; + + private: + friend class detail::CancellationState; + + void invokeCallback() noexcept; + + CancellationCallback* next_; + + // Pointer to the pointer that points to this node in the linked list. + // This could be the 'next_' of a previous CancellationCallback or could + // be the 'head_' pointer of the CancellationState. + // If this node is inserted in the list then this will be non-null. + CancellationCallback** prevNext_; + + detail::CancellationState* state_; + VoidFunction callback_; + + // Pointer to a flag stored on the stack of the caller to invokeCallback() + // that is used to indicate to the caller of invokeCallback() that the + // destructor has run and it is no longer valid to access the callback + // object. + bool* destructorHasRunInsideCallback_; + + // Flag used to signal that the callback has completed executing on another + // thread and it is now safe to exit the destructor. + std::atomic callbackCompleted_; +}; + +} // namespace folly + +#include diff --git a/ios/Pods/Folly/folly/Chrono.h b/ios/Pods/Folly/folly/Chrono.h index 06ca07fd3..3b3838b43 100644 --- a/ios/Pods/Folly/folly/Chrono.h +++ b/ios/Pods/Folly/folly/Chrono.h @@ -1,11 +1,11 @@ /* - * Copyright 2017-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -31,11 +31,12 @@ * * std::chrono::round */ -#if __cpp_lib_chrono >= 201510 || _MSC_VER +#if __cpp_lib_chrono >= 201510 || _LIBCPP_STD_VER > 14 || _MSC_VER namespace folly { namespace chrono { +/* using override */ using std::chrono::abs; /* using override */ using std::chrono::ceil; /* using override */ using std::chrono::floor; /* using override */ using std::chrono::round; @@ -81,6 +82,18 @@ constexpr To round_impl(Duration const& d, To const& t0) { } } // namespace detail +// mimic: std::chrono::abs, C++17 +template < + typename Rep, + typename Period, + typename = typename std::enable_if< + std::chrono::duration::min() < + std::chrono::duration::zero()>::type> +constexpr std::chrono::duration abs( + std::chrono::duration const& d) { + return d < std::chrono::duration::zero() ? -d : d; +} + // mimic: std::chrono::ceil, C++17 // from: http://en.cppreference.com/w/cpp/chrono/duration/ceil, CC-BY-SA template < @@ -168,17 +181,18 @@ struct coarse_steady_clock { using time_point = std::chrono::time_point; constexpr static bool is_steady = true; - static time_point now() { + static time_point now() noexcept { #ifndef CLOCK_MONOTONIC_COARSE return time_point(std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch())); #else timespec ts; auto ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts); - if (ret != 0) { + if (kIsDebug && (ret != 0)) { throw_exception( "Error using CLOCK_MONOTONIC_COARSE."); } + return time_point(std::chrono::duration_cast( std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); diff --git a/ios/Pods/Folly/folly/ClockGettimeWrappers.h b/ios/Pods/Folly/folly/ClockGettimeWrappers.h index dfb28f826..8c403193e 100644 --- a/ios/Pods/Folly/folly/ClockGettimeWrappers.h +++ b/ios/Pods/Folly/folly/ClockGettimeWrappers.h @@ -1,11 +1,11 @@ /* - * Copyright 2016-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/ios/Pods/Folly/folly/AtomicBitSet.h b/ios/Pods/Folly/folly/ConcurrentBitSet.h similarity index 75% rename from ios/Pods/Folly/folly/AtomicBitSet.h rename to ios/Pods/Folly/folly/ConcurrentBitSet.h index 967821d3c..2be6e2162 100644 --- a/ios/Pods/Folly/folly/AtomicBitSet.h +++ b/ios/Pods/Folly/folly/ConcurrentBitSet.h @@ -1,11 +1,11 @@ /* - * Copyright 2013-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -22,22 +22,26 @@ #include #include -#include - #include namespace folly { /** * An atomic bitset of fixed size (specified at compile time). + * + * Formerly known as AtomicBitSet. It was renamed while fixing a bug + * to avoid any silent breakages during run time. */ template -class AtomicBitSet : private boost::noncopyable { +class ConcurrentBitSet { public: /** - * Construct an AtomicBitSet; all bits are initially false. + * Construct a ConcurrentBitSet; all bits are initially false. */ - AtomicBitSet(); + ConcurrentBitSet(); + + ConcurrentBitSet(const ConcurrentBitSet&) = delete; + ConcurrentBitSet& operator=(const ConcurrentBitSet&) = delete; /** * Set bit idx to true, using the given memory order. Returns the @@ -115,43 +119,44 @@ class AtomicBitSet : private boost::noncopyable { // avoid casts static constexpr BlockType kOne = 1; - - std::array data_; + static constexpr size_t kNumBlocks = (N + kBitsPerBlock - 1) / kBitsPerBlock; + std::array data_; }; // value-initialize to zero template -inline AtomicBitSet::AtomicBitSet() : data_() {} +inline ConcurrentBitSet::ConcurrentBitSet() : data_() {} template -inline bool AtomicBitSet::set(size_t idx, std::memory_order order) { - assert(idx < N * kBitsPerBlock); +inline bool ConcurrentBitSet::set(size_t idx, std::memory_order order) { + assert(idx < N); BlockType mask = kOne << bitOffset(idx); return data_[blockIndex(idx)].fetch_or(mask, order) & mask; } template -inline bool AtomicBitSet::reset(size_t idx, std::memory_order order) { - assert(idx < N * kBitsPerBlock); +inline bool ConcurrentBitSet::reset(size_t idx, std::memory_order order) { + assert(idx < N); BlockType mask = kOne << bitOffset(idx); return data_[blockIndex(idx)].fetch_and(~mask, order) & mask; } template inline bool -AtomicBitSet::set(size_t idx, bool value, std::memory_order order) { +ConcurrentBitSet::set(size_t idx, bool value, std::memory_order order) { return value ? set(idx, order) : reset(idx, order); } template -inline bool AtomicBitSet::test(size_t idx, std::memory_order order) const { - assert(idx < N * kBitsPerBlock); +inline bool ConcurrentBitSet::test(size_t idx, std::memory_order order) + const { + assert(idx < N); BlockType mask = kOne << bitOffset(idx); return data_[blockIndex(idx)].load(order) & mask; } template -inline bool AtomicBitSet::operator[](size_t idx) const { +inline bool ConcurrentBitSet::operator[](size_t idx) const { return test(idx); } diff --git a/ios/Pods/Folly/folly/ConcurrentSkipList-inl.h b/ios/Pods/Folly/folly/ConcurrentSkipList-inl.h index a4e5ddbcf..a0eec3076 100644 --- a/ios/Pods/Folly/folly/ConcurrentSkipList-inl.h +++ b/ios/Pods/Folly/folly/ConcurrentSkipList-inl.h @@ -1,11 +1,11 @@ /* - * Copyright 2011-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -27,9 +27,7 @@ #include #include -#include #include -#include #include #include @@ -43,7 +41,7 @@ template class csl_iterator; template -class SkipListNode : private boost::noncopyable { +class SkipListNode { enum : uint16_t { IS_HEAD_NODE = 1, MARKED_FOR_REMOVAL = (1 << 1), @@ -53,6 +51,9 @@ class SkipListNode : private boost::noncopyable { public: typedef T value_type; + SkipListNode(const SkipListNode&) = delete; + SkipListNode& operator=(const SkipListNode&) = delete; + template < typename NodeAlloc, typename U, @@ -81,7 +82,7 @@ class SkipListNode : private boost::noncopyable { template struct DestroyIsNoOp : StrictConjunction< AllocatorHasTrivialDeallocate, - boost::has_trivial_destructor> {}; + std::is_trivially_destructible> {}; // copy the head node to a new head node assuming lock acquired SkipListNode* copyHead(SkipListNode* node) { diff --git a/ios/Pods/Folly/folly/ConcurrentSkipList.h b/ios/Pods/Folly/folly/ConcurrentSkipList.h index 1e7638c7f..ab75ce397 100644 --- a/ios/Pods/Folly/folly/ConcurrentSkipList.h +++ b/ios/Pods/Folly/folly/ConcurrentSkipList.h @@ -1,11 +1,11 @@ /* - * Copyright 2011-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -125,12 +125,12 @@ Sample usage: #include #include -#include #include #include #include #include +#include #include namespace folly { @@ -158,7 +158,7 @@ class ConcurrentSkipList { typedef T key_type; typedef detail::csl_iterator iterator; - typedef detail::csl_iterator const_iterator; + typedef detail::csl_iterator const_iterator; class Accessor; class Skipper; @@ -709,10 +709,10 @@ class ConcurrentSkipList::Accessor { // implements forward iterator concept. template -class detail::csl_iterator : public boost::iterator_facade< +class detail::csl_iterator : public detail::IteratorFacade< csl_iterator, ValT, - boost::forward_traversal_tag> { + std::forward_iterator_tag> { public: typedef ValT value_type; typedef value_type& reference; @@ -738,9 +738,10 @@ class detail::csl_iterator : public boost::iterator_facade< } private: - friend class boost::iterator_core_access; template friend class csl_iterator; + friend class detail:: + IteratorFacade; void increment() { node_ = node_->next(); diff --git a/ios/Pods/Folly/folly/ConstexprMath.h b/ios/Pods/Folly/folly/ConstexprMath.h index 3dae93a2c..4a70ff6cd 100644 --- a/ios/Pods/Folly/folly/ConstexprMath.h +++ b/ios/Pods/Folly/folly/ConstexprMath.h @@ -1,11 +1,11 @@ /* - * Copyright 2017-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,49 +17,11 @@ #pragma once #include +#include #include #include namespace folly { - -// TODO: Replace with std::equal_to, etc., after upgrading to C++14. -template -struct constexpr_equal_to { - constexpr bool operator()(T const& a, T const& b) const { - return a == b; - } -}; -template -struct constexpr_not_equal_to { - constexpr bool operator()(T const& a, T const& b) const { - return a != b; - } -}; -template -struct constexpr_less { - constexpr bool operator()(T const& a, T const& b) const { - return a < b; - } -}; -template -struct constexpr_less_equal { - constexpr bool operator()(T const& a, T const& b) const { - return a <= b; - } -}; -template -struct constexpr_greater { - constexpr bool operator()(T const& a, T const& b) const { - return a > b; - } -}; -template -struct constexpr_greater_equal { - constexpr bool operator()(T const& a, T const& b) const { - return a >= b; - } -}; - // TLDR: Prefer using operator< for ordering. And when // a and b are equivalent objects, we return b to make // sorting stable. @@ -91,7 +53,7 @@ constexpr_clamp(T const& v, T const& lo, T const& hi, Less less) { } template constexpr T const& constexpr_clamp(T const& v, T const& lo, T const& hi) { - return constexpr_clamp(v, lo, hi, constexpr_less{}); + return constexpr_clamp(v, lo, hi, std::less{}); } namespace detail { diff --git a/ios/Pods/Folly/folly/Conv.cpp b/ios/Pods/Folly/folly/Conv.cpp index 76e14c982..d74df582f 100644 --- a/ios/Pods/Folly/folly/Conv.cpp +++ b/ios/Pods/Folly/folly/Conv.cpp @@ -1,11 +1,11 @@ /* - * Copyright 2011-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include #include @@ -275,7 +276,7 @@ Expected str_to_bool(StringPiece* src) noexcept { } bool result; - size_t len = size_t(e - b); + auto len = size_t(e - b); switch (*b) { case '0': case '1': { @@ -399,7 +400,7 @@ Expected str_to_floating(StringPiece* src) noexcept { // There must be non-whitespace, otherwise we would have caught this above assert(b < e); - size_t size = size_t(e - b); + auto size = size_t(e - b); bool negative = false; if (*b == '-') { @@ -543,7 +544,7 @@ inline Expected digits_to( return makeUnexpected(err); } - size_t size = size_t(e - b); + auto size = size_t(e - b); /* Although the string is entirely made of digits, we still need to * check for overflow. @@ -777,7 +778,7 @@ ConversionError makeConversionError(ConversionCode code, StringPiece input) { if (err.quote) { tmp.append(1, '"'); } - if (input.size() > 0) { + if (!input.empty()) { tmp.append(input.data(), input.size()); } if (err.quote) { diff --git a/ios/Pods/Folly/folly/Conv.h b/ios/Pods/Folly/folly/Conv.h index b187d01a6..b6c0b87e1 100644 --- a/ios/Pods/Folly/folly/Conv.h +++ b/ios/Pods/Folly/folly/Conv.h @@ -1,11 +1,11 @@ /* - * Copyright 2011-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,15 +15,92 @@ */ /** - * Converts anything to anything, with an emphasis on performance and - * safety. * - * @author Andrei Alexandrescu (andrei.alexandrescu@fb.com) + * This file provides a generic interface for converting objects to and from + * string-like types (std::string, fbstring, StringPiece), as well as + * range-checked conversions between numeric and enum types. The mechanisms are + * extensible, so that user-specified types can add folly::to support. + * + ******************************************************************************* + * TYPE -> STRING CONVERSIONS + ******************************************************************************* + * You can call the to or to. These are variadic + * functions that convert their arguments to strings, and concatenate them to + * form a result. So, for example, + * + * auto str = to(123, "456", 789); + * + * Sets str to "123456789". + * + * In addition to just concatenating the arguments, related functions can + * delimit them with some string: toDelim(",", "123", 456, "789") + * will return the string "123,456,789". + * + * toAppend does not return a string; instead, it takes a pointer to a string as + * its last argument, and appends the result of the concatenation into it: + * std::string str = "123"; + * toAppend(456, "789", &str); // Now str is "123456789". + * + * The toAppendFit function acts like toAppend, but it precalculates the size + * required to perform the append operation, and reserves that space in the + * output string before actually inserting its arguments. This can sometimes + * save on string expansion, but beware: appending to the same string many times + * with toAppendFit is likely a pessimization, since it will resize the string + * once per append. + * + * The combination of the append and delim variants also exist: toAppendDelim + * and toAppendDelimFit are defined, with the obvious semantics. + * + ******************************************************************************* + * STRING -> TYPE CONVERSIONS + ******************************************************************************* + * Going in the other direction, and parsing a string into a C++ type, is also + * supported: + * to("123"); // Returns 123. + * + * Out of range (e.g. to("1000")), or invalidly formatted (e.g. + * to("four")) inputs will throw. If throw-on-error is undesirable (for + * instance: you're dealing with untrusted input, and want to protect yourself + * from users sending you down a very slow exception-throwing path), you can use + * tryTo, which will return an Expected. + * + * There are overloads of to() and tryTo() that take a StringPiece*. These parse + * out a type from the beginning of a string, and modify the passed-in + * StringPiece to indicate the portion of the string not consumed. + * + ******************************************************************************* + * NUMERIC / ENUM CONVERSIONS + ******************************************************************************* + * Conv also supports a to(S) overload, where T and S are numeric or enum + * types, that checks to see that the target type can represent its argument, + * and will throw if it cannot. This includes cases where a floating point -> + * integral conversion is attempted on a value with a non-zero fractional + * component, and integral -> floating point conversions that would lose + * precision. Enum conversions are range-checked for the underlying type of the + * enum, but there is no check that the input value is a valid choice of enum + * value. + * + ******************************************************************************* + * CUSTOM TYPE CONVERSIONS + ******************************************************************************* + * Users may customize the string conversion functionality for their own data + * types, . The key functions you should implement are: + * // Two functions to allow conversion to your type from a string. + * Expected parseTo(folly::StringPiece in, + * YourType& out); + * YourErrorType makeConversionError(YourErrorType in, StringPiece in); + * // Two functions to allow conversion from your type to a string. + * template + * void toAppend(const YourType& in, String* out); + * size_t estimateSpaceNeeded(const YourType& in); + * + * These are documented below, inline. */ #pragma once #include +#include #include #include #include @@ -32,7 +109,6 @@ #include #include #include -#include #include #include // V8 JavaScript implementation @@ -44,7 +120,9 @@ #include #include #include +#include #include +#include #include namespace folly { @@ -109,7 +187,7 @@ class ConversionError : public ConversionErrorBase { * return YourConversionError(messageString); * } ******************************************************************************/ -ConversionError makeConversionError(ConversionCode code, StringPiece sp); +ConversionError makeConversionError(ConversionCode code, StringPiece input); namespace detail { /** @@ -648,15 +726,13 @@ template typename std::enable_if< std::is_enum::value && IsSomeString::value>::type toAppend(Src value, Tgt* result) { - toAppend( - static_cast::type>(value), result); + toAppend(to_underlying(value), result); } template typename std::enable_if::value, size_t>::type estimateSpaceNeeded(Src value) { - return estimateSpaceNeeded( - static_cast::type>(value)); + return estimateSpaceNeeded(to_underlying(value)); } /******************************************************************************* @@ -699,8 +775,9 @@ toAppend( case DoubleToStringConverter::FIXED: conv.ToFixed(value, int(numDigits), &builder); break; + case DoubleToStringConverter::PRECISION: default: - CHECK(mode == DoubleToStringConverter::PRECISION); + assert(mode == DoubleToStringConverter::PRECISION); conv.ToPrecision(value, int(numDigits), &builder); break; } @@ -1216,8 +1293,8 @@ typename std::enable_if< Expected>::type convertTo(const Src& value) noexcept { if /* constexpr */ ( - folly::_t>(std::numeric_limits::max()) < - folly::_t>(std::numeric_limits::max())) { + std::make_unsigned_t(std::numeric_limits::max()) < + std::make_unsigned_t(std::numeric_limits::max())) { if (greater_than::max()>(value)) { return makeUnexpected(ConversionCode::ARITH_POSITIVE_OVERFLOW); } @@ -1335,11 +1412,7 @@ convertTo(const Src& value) noexcept { template inline std::string errorValue(const Src& value) { -#ifdef FOLLY_HAS_RTTI - return to("(", demangle(typeid(Tgt)), ") ", value); -#else - return to(value); -#endif + return to("(", pretty_name(), ") ", value); } template @@ -1551,8 +1624,7 @@ typename std::enable_if< !std::is_convertible::value, Expected>::type tryTo(const Src& value) { - using I = typename std::underlying_type::type; - return tryTo(static_cast(value)); + return tryTo(to_underlying(value)); } template @@ -1571,7 +1643,7 @@ typename std::enable_if< !std::is_convertible::value, Tgt>::type to(const Src& value) { - return to(static_cast::type>(value)); + return to(to_underlying(value)); } template diff --git a/ios/Pods/Folly/folly/CppAttributes.h b/ios/Pods/Folly/folly/CppAttributes.h index 00cc39332..cd02fa3fd 100644 --- a/ios/Pods/Folly/folly/CppAttributes.h +++ b/ios/Pods/Folly/folly/CppAttributes.h @@ -1,11 +1,11 @@ /* - * Copyright 2015-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/ios/Pods/Folly/folly/CpuId.h b/ios/Pods/Folly/folly/CpuId.h index 8d92f8b7b..517ccb571 100644 --- a/ios/Pods/Folly/folly/CpuId.h +++ b/ios/Pods/Folly/folly/CpuId.h @@ -1,11 +1,11 @@ /* - * Copyright 2012-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -105,108 +105,108 @@ class CpuId { #endif } -#define X(name, r, bit) \ - FOLLY_ALWAYS_INLINE bool name() const { \ - return ((r) & (1U << bit)) != 0; \ +#define FOLLY_DETAIL_CPUID_X(name, r, bit) \ + FOLLY_ALWAYS_INLINE bool name() const { \ + return ((r) & (1U << bit)) != 0; \ } // cpuid(1): Processor Info and Feature Bits. -#define C(name, bit) X(name, f1c_, bit) - C(sse3, 0) - C(pclmuldq, 1) - C(dtes64, 2) - C(monitor, 3) - C(dscpl, 4) - C(vmx, 5) - C(smx, 6) - C(eist, 7) - C(tm2, 8) - C(ssse3, 9) - C(cnxtid, 10) - C(fma, 12) - C(cx16, 13) - C(xtpr, 14) - C(pdcm, 15) - C(pcid, 17) - C(dca, 18) - C(sse41, 19) - C(sse42, 20) - C(x2apic, 21) - C(movbe, 22) - C(popcnt, 23) - C(tscdeadline, 24) - C(aes, 25) - C(xsave, 26) - C(osxsave, 27) - C(avx, 28) - C(f16c, 29) - C(rdrand, 30) -#undef C -#define D(name, bit) X(name, f1d_, bit) - D(fpu, 0) - D(vme, 1) - D(de, 2) - D(pse, 3) - D(tsc, 4) - D(msr, 5) - D(pae, 6) - D(mce, 7) - D(cx8, 8) - D(apic, 9) - D(sep, 11) - D(mtrr, 12) - D(pge, 13) - D(mca, 14) - D(cmov, 15) - D(pat, 16) - D(pse36, 17) - D(psn, 18) - D(clfsh, 19) - D(ds, 21) - D(acpi, 22) - D(mmx, 23) - D(fxsr, 24) - D(sse, 25) - D(sse2, 26) - D(ss, 27) - D(htt, 28) - D(tm, 29) - D(pbe, 31) -#undef D +#define FOLLY_DETAIL_CPUID_C(name, bit) FOLLY_DETAIL_CPUID_X(name, f1c_, bit) + FOLLY_DETAIL_CPUID_C(sse3, 0) + FOLLY_DETAIL_CPUID_C(pclmuldq, 1) + FOLLY_DETAIL_CPUID_C(dtes64, 2) + FOLLY_DETAIL_CPUID_C(monitor, 3) + FOLLY_DETAIL_CPUID_C(dscpl, 4) + FOLLY_DETAIL_CPUID_C(vmx, 5) + FOLLY_DETAIL_CPUID_C(smx, 6) + FOLLY_DETAIL_CPUID_C(eist, 7) + FOLLY_DETAIL_CPUID_C(tm2, 8) + FOLLY_DETAIL_CPUID_C(ssse3, 9) + FOLLY_DETAIL_CPUID_C(cnxtid, 10) + FOLLY_DETAIL_CPUID_C(fma, 12) + FOLLY_DETAIL_CPUID_C(cx16, 13) + FOLLY_DETAIL_CPUID_C(xtpr, 14) + FOLLY_DETAIL_CPUID_C(pdcm, 15) + FOLLY_DETAIL_CPUID_C(pcid, 17) + FOLLY_DETAIL_CPUID_C(dca, 18) + FOLLY_DETAIL_CPUID_C(sse41, 19) + FOLLY_DETAIL_CPUID_C(sse42, 20) + FOLLY_DETAIL_CPUID_C(x2apic, 21) + FOLLY_DETAIL_CPUID_C(movbe, 22) + FOLLY_DETAIL_CPUID_C(popcnt, 23) + FOLLY_DETAIL_CPUID_C(tscdeadline, 24) + FOLLY_DETAIL_CPUID_C(aes, 25) + FOLLY_DETAIL_CPUID_C(xsave, 26) + FOLLY_DETAIL_CPUID_C(osxsave, 27) + FOLLY_DETAIL_CPUID_C(avx, 28) + FOLLY_DETAIL_CPUID_C(f16c, 29) + FOLLY_DETAIL_CPUID_C(rdrand, 30) +#undef FOLLY_DETAIL_CPUID_C +#define FOLLY_DETAIL_CPUID_D(name, bit) FOLLY_DETAIL_CPUID_X(name, f1d_, bit) + FOLLY_DETAIL_CPUID_D(fpu, 0) + FOLLY_DETAIL_CPUID_D(vme, 1) + FOLLY_DETAIL_CPUID_D(de, 2) + FOLLY_DETAIL_CPUID_D(pse, 3) + FOLLY_DETAIL_CPUID_D(tsc, 4) + FOLLY_DETAIL_CPUID_D(msr, 5) + FOLLY_DETAIL_CPUID_D(pae, 6) + FOLLY_DETAIL_CPUID_D(mce, 7) + FOLLY_DETAIL_CPUID_D(cx8, 8) + FOLLY_DETAIL_CPUID_D(apic, 9) + FOLLY_DETAIL_CPUID_D(sep, 11) + FOLLY_DETAIL_CPUID_D(mtrr, 12) + FOLLY_DETAIL_CPUID_D(pge, 13) + FOLLY_DETAIL_CPUID_D(mca, 14) + FOLLY_DETAIL_CPUID_D(cmov, 15) + FOLLY_DETAIL_CPUID_D(pat, 16) + FOLLY_DETAIL_CPUID_D(pse36, 17) + FOLLY_DETAIL_CPUID_D(psn, 18) + FOLLY_DETAIL_CPUID_D(clfsh, 19) + FOLLY_DETAIL_CPUID_D(ds, 21) + FOLLY_DETAIL_CPUID_D(acpi, 22) + FOLLY_DETAIL_CPUID_D(mmx, 23) + FOLLY_DETAIL_CPUID_D(fxsr, 24) + FOLLY_DETAIL_CPUID_D(sse, 25) + FOLLY_DETAIL_CPUID_D(sse2, 26) + FOLLY_DETAIL_CPUID_D(ss, 27) + FOLLY_DETAIL_CPUID_D(htt, 28) + FOLLY_DETAIL_CPUID_D(tm, 29) + FOLLY_DETAIL_CPUID_D(pbe, 31) +#undef FOLLY_DETAIL_CPUID_D // cpuid(7): Extended Features. -#define B(name, bit) X(name, f7b_, bit) - B(bmi1, 3) - B(hle, 4) - B(avx2, 5) - B(smep, 7) - B(bmi2, 8) - B(erms, 9) - B(invpcid, 10) - B(rtm, 11) - B(mpx, 14) - B(avx512f, 16) - B(avx512dq, 17) - B(rdseed, 18) - B(adx, 19) - B(smap, 20) - B(avx512ifma, 21) - B(pcommit, 22) - B(clflushopt, 23) - B(clwb, 24) - B(avx512pf, 26) - B(avx512er, 27) - B(avx512cd, 28) - B(sha, 29) - B(avx512bw, 30) - B(avx512vl, 31) -#undef B -#define C(name, bit) X(name, f7c_, bit) - C(prefetchwt1, 0) - C(avx512vbmi, 1) -#undef C +#define FOLLY_DETAIL_CPUID_B(name, bit) FOLLY_DETAIL_CPUID_X(name, f7b_, bit) + FOLLY_DETAIL_CPUID_B(bmi1, 3) + FOLLY_DETAIL_CPUID_B(hle, 4) + FOLLY_DETAIL_CPUID_B(avx2, 5) + FOLLY_DETAIL_CPUID_B(smep, 7) + FOLLY_DETAIL_CPUID_B(bmi2, 8) + FOLLY_DETAIL_CPUID_B(erms, 9) + FOLLY_DETAIL_CPUID_B(invpcid, 10) + FOLLY_DETAIL_CPUID_B(rtm, 11) + FOLLY_DETAIL_CPUID_B(mpx, 14) + FOLLY_DETAIL_CPUID_B(avx512f, 16) + FOLLY_DETAIL_CPUID_B(avx512dq, 17) + FOLLY_DETAIL_CPUID_B(rdseed, 18) + FOLLY_DETAIL_CPUID_B(adx, 19) + FOLLY_DETAIL_CPUID_B(smap, 20) + FOLLY_DETAIL_CPUID_B(avx512ifma, 21) + FOLLY_DETAIL_CPUID_B(pcommit, 22) + FOLLY_DETAIL_CPUID_B(clflushopt, 23) + FOLLY_DETAIL_CPUID_B(clwb, 24) + FOLLY_DETAIL_CPUID_B(avx512pf, 26) + FOLLY_DETAIL_CPUID_B(avx512er, 27) + FOLLY_DETAIL_CPUID_B(avx512cd, 28) + FOLLY_DETAIL_CPUID_B(sha, 29) + FOLLY_DETAIL_CPUID_B(avx512bw, 30) + FOLLY_DETAIL_CPUID_B(avx512vl, 31) +#undef FOLLY_DETAIL_CPUID_B +#define FOLLY_DETAIL_CPUID_C(name, bit) FOLLY_DETAIL_CPUID_X(name, f7c_, bit) + FOLLY_DETAIL_CPUID_C(prefetchwt1, 0) + FOLLY_DETAIL_CPUID_C(avx512vbmi, 1) +#undef FOLLY_DETAIL_CPUID_C -#undef X +#undef FOLLY_DETAIL_CPUID_X private: uint32_t f1c_ = 0; diff --git a/ios/Pods/Folly/folly/DefaultKeepAliveExecutor.h b/ios/Pods/Folly/folly/DefaultKeepAliveExecutor.h index 5f8b8ea95..c5d7f6065 100644 --- a/ios/Pods/Folly/folly/DefaultKeepAliveExecutor.h +++ b/ios/Pods/Folly/folly/DefaultKeepAliveExecutor.h @@ -1,11 +1,11 @@ /* - * Copyright 2018-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -46,6 +46,15 @@ class DefaultKeepAliveExecutor : public virtual Executor { keepAliveReleaseBaton_.wait(); } + void joinAndResetKeepAlive() { + joinKeepAlive(); + auto keepAliveCount = + controlBlock_->keepAliveCount_.exchange(1, std::memory_order_relaxed); + DCHECK_EQ(keepAliveCount, 0); + keepAliveReleaseBaton_.reset(); + keepAlive_ = makeKeepAlive(this); + } + private: struct ControlBlock { std::atomic keepAliveCount_{1}; @@ -143,8 +152,7 @@ class DefaultKeepAliveExecutor : public virtual Executor { std::shared_ptr controlBlock_{std::make_shared()}; Baton<> keepAliveReleaseBaton_; - KeepAlive keepAlive_{ - makeKeepAlive(this)}; + KeepAlive keepAlive_{makeKeepAlive(this)}; }; } // namespace folly diff --git a/ios/Pods/Folly/folly/Demangle.cpp b/ios/Pods/Folly/folly/Demangle.cpp index c51632135..0df902c00 100644 --- a/ios/Pods/Folly/folly/Demangle.cpp +++ b/ios/Pods/Folly/folly/Demangle.cpp @@ -1,11 +1,11 @@ /* - * Copyright 2014-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -20,6 +20,7 @@ #include #include +#include #include #if FOLLY_DETAIL_HAVE_DEMANGLE_H @@ -33,6 +34,9 @@ namespace folly { #if FOLLY_DETAIL_HAVE_DEMANGLE_H fbstring demangle(const char* name) { + if (!name) { + return fbstring(); + } #ifdef FOLLY_DEMANGLE_MAX_SYMBOL_SIZE // GCC's __cxa_demangle() uses on-stack data structures for the // parser state which are linear in the number of components of the @@ -118,14 +122,4 @@ size_t demangle(const char* name, char* out, size_t outSize) { #endif -size_t strlcpy(char* dest, const char* const src, size_t size) { - size_t len = strlen(src); - if (size != 0) { - size_t n = std::min(len, size - 1); // always null terminate! - memcpy(dest, src, n); - dest[n] = '\0'; - } - return len; -} - } // namespace folly diff --git a/ios/Pods/Folly/folly/Demangle.h b/ios/Pods/Folly/folly/Demangle.h index 13537dd50..da13fb178 100644 --- a/ios/Pods/Folly/folly/Demangle.h +++ b/ios/Pods/Folly/folly/Demangle.h @@ -1,11 +1,11 @@ /* - * Copyright 2014-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -54,12 +54,9 @@ inline fbstring demangle(const std::type_info& type) { * libiberty), so it is possible for the fbstring version to work, while this * version returns the original, mangled name. */ -size_t demangle(const char* name, char* buf, size_t bufSize); +size_t demangle(const char* name, char* out, size_t outSize); inline size_t demangle(const std::type_info& type, char* buf, size_t bufSize) { return demangle(type.name(), buf, bufSize); } -// glibc doesn't have strlcpy -size_t strlcpy(char* dest, const char* const src, size_t size); - } // namespace folly diff --git a/ios/Pods/Folly/folly/DiscriminatedPtr.h b/ios/Pods/Folly/folly/DiscriminatedPtr.h index dab199787..0f49bd688 100644 --- a/ios/Pods/Folly/folly/DiscriminatedPtr.h +++ b/ios/Pods/Folly/folly/DiscriminatedPtr.h @@ -1,11 +1,11 @@ /* - * Copyright 2011-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/ios/Pods/Folly/folly/DynamicConverter.h b/ios/Pods/Folly/folly/DynamicConverter.h index 1f323fd0d..04aec594e 100644 --- a/ios/Pods/Folly/folly/DynamicConverter.h +++ b/ios/Pods/Folly/folly/DynamicConverter.h @@ -1,11 +1,11 @@ /* - * Copyright 2012-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -27,7 +27,9 @@ #include #include #include +#include #include +#include namespace folly { template @@ -110,7 +112,7 @@ struct Dereferencer { static inline void derefToCache( Optional* /* mem */, const dynamic::const_item_iterator& /* it */) { - throw TypeError("array", dynamic::Type::OBJECT); + throw_exception("array", dynamic::Type::OBJECT); } static inline void derefToCache( @@ -249,7 +251,7 @@ struct DynamicConverter> { auto it = d.items().begin(); return std::make_pair(convertTo(it->first), convertTo(it->second)); } else { - throw TypeError("array (size 2) or object (size 1)", d.type()); + throw_exception("array (size 2) or object (size 1)", d.type()); } } }; @@ -271,7 +273,7 @@ struct DynamicConverter< dynamicconverter_detail::conversionIterator(d.items().begin()), dynamicconverter_detail::conversionIterator(d.items().end())); } else { - throw TypeError("object or array", d.type()); + throw_exception("object or array", d.type()); } } }; @@ -295,7 +297,7 @@ struct DynamicConverter< dynamicconverter_detail::conversionIterator(d.items().begin()), dynamicconverter_detail::conversionIterator(d.items().end())); } else { - throw TypeError("object or array", d.type()); + throw_exception("object or array", d.type()); } return ret; } @@ -327,6 +329,16 @@ struct DynamicConstructor< } }; +// enums +template +struct DynamicConstructor< + C, + typename std::enable_if::value>::type> { + static dynamic construct(const C& x) { + return dynamic(to_underlying(x)); + } +}; + // maps template struct DynamicConstructor< diff --git a/ios/Pods/Folly/folly/Exception.h b/ios/Pods/Folly/folly/Exception.h index 19d7509d1..b050d641d 100644 --- a/ios/Pods/Folly/folly/Exception.h +++ b/ios/Pods/Folly/folly/Exception.h @@ -1,11 +1,11 @@ /* - * Copyright 2013-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -63,12 +63,12 @@ std::system_error makeSystemError(Args&&... args) { // Helper to throw std::system_error [[noreturn]] inline void throwSystemErrorExplicit(int err, const char* msg) { - throw makeSystemErrorExplicit(err, msg); + throw_exception(makeSystemErrorExplicit(err, msg)); } template [[noreturn]] void throwSystemErrorExplicit(int err, Args&&... args) { - throw makeSystemErrorExplicit(err, std::forward(args)...); + throw_exception(makeSystemErrorExplicit(err, std::forward(args)...)); } // Helper to throw std::system_error from errno and components of a string @@ -132,11 +132,11 @@ void checkFopenErrorExplicit(FILE* fp, int savedErrno, Args&&... args) { * If cond is not true, raise an exception of type E. E must have a ctor that * works with const char* (a description of the failure). */ -#define CHECK_THROW(cond, E) \ - do { \ - if (!(cond)) { \ - throw E("Check failed: " #cond); \ - } \ +#define CHECK_THROW(cond, E) \ + do { \ + if (!(cond)) { \ + folly::throw_exception("Check failed: " #cond); \ + } \ } while (0) } // namespace folly diff --git a/ios/Pods/Folly/folly/ExceptionString.h b/ios/Pods/Folly/folly/ExceptionString.h index 5c3a2760c..13042ead4 100644 --- a/ios/Pods/Folly/folly/ExceptionString.h +++ b/ios/Pods/Folly/folly/ExceptionString.h @@ -1,11 +1,11 @@ /* - * Copyright 2016-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -31,7 +31,7 @@ namespace folly { * defined. */ inline fbstring exceptionStr(const std::exception& e) { -#ifdef FOLLY_HAS_RTTI +#if FOLLY_HAS_RTTI fbstring rv(demangle(typeid(e))); rv += ": "; #else @@ -41,29 +41,27 @@ inline fbstring exceptionStr(const std::exception& e) { return rv; } -// Empirically, this indicates if the runtime supports -// std::exception_ptr, as not all (arm, for instance) do. -#if defined(__GNUC__) && defined(__GCC_ATOMIC_INT_LOCK_FREE) && \ - __GCC_ATOMIC_INT_LOCK_FREE > 1 inline fbstring exceptionStr(std::exception_ptr ep) { - try { - std::rethrow_exception(ep); - } catch (const std::exception& e) { - return exceptionStr(e); - } catch (...) { - return ""; + if (!kHasExceptions) { + return "Exception (catch unavailable)"; } + return catch_exception( + [&]() -> fbstring { + return catch_exception( + [&]() -> fbstring { std::rethrow_exception(ep); }, + [](auto&& e) { return exceptionStr(e); }); + }, + []() -> fbstring { return ""; }); } -#endif template auto exceptionStr(const E& e) -> typename std:: enable_if::value, fbstring>::type { -#ifdef FOLLY_HAS_RTTI +#if FOLLY_HAS_RTTI return demangle(typeid(e)); #else (void)e; - return "Exception (no RTTI available) "; + return "Exception (no RTTI available)"; #endif } diff --git a/ios/Pods/Folly/folly/ExceptionWrapper-inl.h b/ios/Pods/Folly/folly/ExceptionWrapper-inl.h index a585e74cc..16fb5d4b6 100644 --- a/ios/Pods/Folly/folly/ExceptionWrapper-inl.h +++ b/ios/Pods/Folly/folly/ExceptionWrapper-inl.h @@ -1,11 +1,11 @@ /* - * Copyright 2017-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * * Author: Eric Niebler @@ -201,7 +202,7 @@ inline void exception_wrapper::InPlace::delete_(exception_wrapper* that) { template [[noreturn]] inline void exception_wrapper::InPlace::throw_( exception_wrapper const* that) { - throw that->buff_.as(); // @nolint + throw that->buff_.as(); } template inline std::type_info const* exception_wrapper::InPlace::type_( @@ -226,7 +227,7 @@ inline exception_wrapper exception_wrapper::InPlace::get_exception_ptr_( template [[noreturn]] inline void exception_wrapper::SharedPtr::Impl::throw_() const { - throw ex_; // @nolint + throw ex_; } template inline std::exception const* @@ -343,7 +344,7 @@ inline exception_wrapper::exception_wrapper( namespace exception_wrapper_detail { template Ex&& dont_slice(Ex&& ex) { - assert(typeid(ex) == typeid(_t>) || + assert(typeid(ex) == typeid(std::decay_t) || !"Dynamic and static exception types don't match. Exception would " "be sliced when storing in exception_wrapper."); return std::forward(ex); @@ -479,12 +480,12 @@ template template struct exception_wrapper::ExceptionTypeOf { - using type = arg_type<_t>>; + using type = arg_type>; static_assert( std::is_reference::value, "Always catch exceptions by reference."); static_assert( - !IsConst || std::is_const<_t>>::value, + !IsConst || std::is_const>::value, "handle() or with_exception() called on a const exception_wrapper " "and asked to catch a non-const exception. Handler will never fire. " "Catch exception by const reference to fix this."); @@ -550,7 +551,7 @@ struct exception_wrapper::HandleStdExceptReduce { return [th = std::forward(th), &ca](auto&& continuation) -> StdEx* { if (auto e = const_cast(th(continuation))) { - if (auto e2 = dynamic_cast<_t>>(e)) { + if (auto e2 = dynamic_cast>(e)) { ca(*e2); } else { return e; diff --git a/ios/Pods/Folly/folly/ExceptionWrapper.h b/ios/Pods/Folly/folly/ExceptionWrapper.h index 3bdc7ae02..8fb287b7d 100644 --- a/ios/Pods/Folly/folly/ExceptionWrapper.h +++ b/ios/Pods/Folly/folly/ExceptionWrapper.h @@ -1,11 +1,11 @@ /* - * Copyright 2014-present Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Author: Eric Niebler */ @@ -52,7 +53,7 @@ FOLLY_GCC_DISABLE_NEW_SHADOW_WARNINGS namespace folly { #define FOLLY_REQUIRES_DEF(...) \ - _t(__VA_ARGS__), long>> + std::enable_if_t(__VA_ARGS__), long> #define FOLLY_REQUIRES(...) FOLLY_REQUIRES_DEF(__VA_ARGS__) = __LINE__ @@ -62,16 +63,15 @@ template