many changes

new feature to record both sides while call recording(upstream commit), fix armv7 failed to initialize error & fix a9 issues
This commit is contained in:
particle-box
2026-01-19 01:52:26 +05:30
parent 1ba7e411ca
commit 9fa733f98d
27 changed files with 812 additions and 319 deletions

View File

@@ -3,15 +3,19 @@ package me.eternal.purrfectsnap.bridge
import android.content.Intent
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.BiometricPrompt
import android.hardware.fingerprint.FingerprintManager
import android.os.Build
import android.os.Bundle
import android.os.CancellationSignal
import android.app.KeyguardManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import me.eternal.purrfectsnap.SharedContextHolder
import java.util.concurrent.Executors
class BiometricPromptActivity: ComponentActivity() {
private val deviceCredentialRequestCode = 2201
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -21,16 +25,50 @@ class BiometricPromptActivity: ComponentActivity() {
}
val remoteSideContext = SharedContextHolder.remote(this)
val executor = Executors.newSingleThreadExecutor()
val negativeText = remoteSideContext.translation.getOrNull("biometric_auth.cancel")
?.takeIf { it.isNotBlank() }
?: "Cancel"
val titleText = remoteSideContext.translation.getOrNull("biometric_auth.title")
?.takeIf { it.isNotBlank() }
?: "Unlock"
val subtitleText = remoteSideContext.translation.getOrNull("biometric_auth.subtitle")
?.takeIf { it.isNotBlank() }
?: "Confirm your screen lock"
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
val fingerprintManager = getSystemService(FingerprintManager::class.java)
val hasFingerprintSupport = runCatching {
fingerprintManager?.isHardwareDetected == true && fingerprintManager.hasEnrolledFingerprints()
}.getOrDefault(false)
if (!hasFingerprintSupport) {
val keyguardManager = getSystemService(KeyguardManager::class.java)
val canUseDeviceCredential = keyguardManager?.isKeyguardSecure == true
if (canUseDeviceCredential) {
val intent = keyguardManager.createConfirmDeviceCredentialIntent(titleText, subtitleText)
if (intent != null) {
startActivityForResult(intent, deviceCredentialRequestCode)
setContent {}
return
}
}
cancel()
setContent {}
return
}
}
BiometricPrompt.Builder(this@BiometricPromptActivity)
.setTitle(remoteSideContext.translation["biometric_auth.title"])
.setSubtitle(remoteSideContext.translation["biometric_auth.subtitle"])
.setTitle(titleText)
.setSubtitle(subtitleText)
.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@Suppress("DEPRECATION")
setDeviceCredentialAllowed(true)
} else {
setNegativeButton(negativeText, mainExecutor) { _, _ -> cancel() }
}
}
.build().authenticate(
@@ -39,7 +77,7 @@ class BiometricPromptActivity: ComponentActivity() {
cancel()
}
},
Executors.newSingleThreadExecutor(),
executor,
object: BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
cancel()
@@ -54,4 +92,16 @@ class BiometricPromptActivity: ComponentActivity() {
setContent {}
}
}
@Suppress("DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != deviceCredentialRequestCode) return
if (resultCode == RESULT_OK) {
setResult(RESULT_OK, Intent())
} else {
setResult(RESULT_CANCELED, Intent())
}
finish()
}
}

View File

@@ -7,6 +7,7 @@ import android.os.ParcelFileDescriptor
import kotlinx.coroutines.runBlocking
import me.eternal.purrfectsnap.RemoteSideContext
import me.eternal.purrfectsnap.SharedContextHolder
import me.eternal.purrfectsnap.bridge.call.CallDownloadSession
import me.eternal.purrfectsnap.bridge.snapclient.MessagingBridge
import me.eternal.purrfectsnap.common.data.MessagingFriendInfo
import me.eternal.purrfectsnap.common.data.MessagingGroupInfo
@@ -16,6 +17,7 @@ import me.eternal.purrfectsnap.common.ui.OverlayType
import me.eternal.purrfectsnap.common.util.toParcelable
import me.eternal.purrfectsnap.download.DownloadProcessor
import me.eternal.purrfectsnap.download.FFMpegProcessor
import me.eternal.purrfectsnap.download.call.CallDownloadSessionImpl
import me.eternal.purrfectsnap.storage.*
import me.eternal.purrfectsnap.task.Task
import me.eternal.purrfectsnap.task.TaskType
@@ -248,5 +250,16 @@ class BridgeService : Service() {
override fun getDebugProp(key: String, defaultValue: String?): String? {
return remoteSideContext.sharedPreferences.all["debug_$key"]?.toString() ?: defaultValue
}
override fun startCallDownload(
startTimestamp: Long,
author: String
): CallDownloadSession {
return CallDownloadSessionImpl(
context = remoteSideContext,
callStartTimestamp = startTimestamp,
author = author
)
}
}
}

View File

@@ -59,6 +59,12 @@ class FFMpegProcessor(
pendingTask.updateProgress("Processing (frames=${it.videoFrameNumber}, fps=${it.videoFps}, time=${it.time}, bitrate=${it.bitrate}, speed=${it.speed})")
}
)
fun newFFMpegProcessor(context: RemoteSideContext, onStatistics: (Statistics) -> Unit = {}) = FFMpegProcessor(
logManager = context.log,
ffmpegOptions = context.config.root.downloader.ffmpegOptions,
onStatistics = onStatistics
)
}
enum class Action {
DOWNLOAD_DASH,
@@ -66,6 +72,7 @@ class FFMpegProcessor(
CONVERSION,
MERGE_MEDIA,
DOWNLOAD_AUDIO_STREAM,
MERGE_AUDIO_STREAMS,
}
data class Request(
@@ -76,6 +83,7 @@ class FFMpegProcessor(
val startTime: Long? = null, //only for DOWNLOAD_DASH
val duration: Long? = null, //only for DOWNLOAD_DASH
val audioStreamFormat: AudioStreamFormat? = null, //only for DOWNLOAD_AUDIO_STREAM
val inputDelayOffsets: Map<String, Long>? = null, // only for MERGE_AUDIO_STREAMS
var videoCodec: String? = null,
var audioCodec: String? = null,
@@ -225,6 +233,26 @@ class FFMpegProcessor(
globalArguments += "-ar" to args.audioStreamFormat.sampleRate.toString()
globalArguments += "-ac" to args.audioStreamFormat.channels.toString()
}
Action.MERGE_AUDIO_STREAMS -> {
inputArguments.clear()
outputArguments.clear()
val filterParts = StringBuilder()
args.inputs.forEachIndexed { index, input ->
inputArguments += "-i" to input
val offset = args.inputDelayOffsets?.get(input) ?: 0L
if (offset > 0) {
filterParts.append("[$index:a]adelay=$offset|$offset[a$index];")
} else {
filterParts.append("[$index:a]acopy[a$index];")
}
}
args.inputs.indices.forEach { index ->
filterParts.append("[a$index]")
}
filterParts.append("amix=inputs=${args.inputs.size}:duration=longest:normalize=0[aout]")
outputArguments += "-filter_complex" to "\"$filterParts\""
outputArguments += "-map" to "\"[aout]\""
}
}
outputArguments += args.output.absolutePath
try {

View File

@@ -0,0 +1,175 @@
package me.eternal.purrfectsnap.download.call
import android.os.ParcelFileDescriptor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import me.eternal.purrfectsnap.RemoteSideContext
import me.eternal.purrfectsnap.bridge.DownloadCallback
import me.eternal.purrfectsnap.bridge.call.CallDownloadSession
import me.eternal.purrfectsnap.common.data.download.AudioStreamFormat
import me.eternal.purrfectsnap.common.data.download.DownloadMetadata
import me.eternal.purrfectsnap.common.data.download.MediaDownloadSource
import me.eternal.purrfectsnap.common.data.download.createNewFilePath
import me.eternal.purrfectsnap.download.DownloadProcessor
import me.eternal.purrfectsnap.download.FFMpegProcessor
import me.eternal.purrfectsnap.task.PendingTaskListener
import me.eternal.purrfectsnap.task.Task
import me.eternal.purrfectsnap.task.TaskType
import java.util.UUID
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.math.absoluteValue
class CallDownloadSessionImpl(
private val context: RemoteSideContext,
private val callStartTimestamp: Long,
private val author: String,
): CallDownloadSession.Stub() {
private val coroutineScope = CoroutineScope(Dispatchers.IO)
private var callEnded: Boolean = false
private val streams = CopyOnWriteArrayList<CallStream>()
private var mergeJob: Job? = null
init {
context.log.verbose("Starting call callStartTimestamp=$callStartTimestamp")
}
inner class CallStream(
val startTimestampMillis: Long,
private val audioStreamFormat: AudioStreamFormat
) {
val job: Job
val writePfd: ParcelFileDescriptor
val outputFile = context.androidContext.cacheDir.resolve("call_${UUID.randomUUID()}.mp3").apply {
if (exists()) delete()
}
init {
val pipe = ParcelFileDescriptor.createPipe()
writePfd = pipe[1]
job = coroutineScope.launch {
runCatching {
FFMpegProcessor.newFFMpegProcessor(context).execute(
FFMpegProcessor.Request(
action = FFMpegProcessor.Action.DOWNLOAD_AUDIO_STREAM,
inputs = listOf("/proc/self/fd/${pipe[0].fd}"),
output = outputFile,
audioStreamFormat = audioStreamFormat
)
)
}.onFailure {
context.log.error("Error converting call audio stream", it)
runCatching {
outputFile.delete()
}
}
pipe.forEach { runCatching { it.close() } }
context.log.verbose("Call stream ended startTimestampMillis=$startTimestampMillis")
}
}
}
override fun createStream(
startTimestampMillis: Long,
channels: Int,
sampleRate: Int,
encoding: Int
): ParcelFileDescriptor? {
if (callEnded) return null
context.log.verbose("createFileDescriptor startTimestampMillis=$startTimestampMillis, channels=$channels, sampleRate=$sampleRate, encoding=$encoding")
val callStream = CallStream(
startTimestampMillis = startTimestampMillis,
audioStreamFormat = AudioStreamFormat(
channels = channels,
sampleRate = sampleRate,
encoding = encoding
)
)
synchronized(streams) {
streams.add(callStream)
}
return callStream.writePfd
}
override fun end() {
if (callEnded) return
callEnded = true
if (streams.isEmpty()) {
context.log.verbose("No call chunks to merge")
return
}
val outputFile = context.androidContext.cacheDir.resolve("call_${UUID.randomUUID()}_final.mp3")
val pendingTask = context.taskManager.createPendingTask(
Task(
type = TaskType.DOWNLOAD,
title = "Call Recording $author",
author = author,
hash = UUID.randomUUID().toString()
)
).apply {
addListener(PendingTaskListener(
onCancel = {
mergeJob?.cancel()
outputFile.delete()
streams.forEach { stream ->
stream.outputFile.delete()
}
}
))
}
mergeJob = coroutineScope.launch {
try {
streams.forEach { stream ->
stream.job.join()
}
val sortedStreams = streams.filter { it.outputFile.exists() }.sortedBy { it.startTimestampMillis }
FFMpegProcessor.newFFMpegProcessor(context, pendingTask).execute(
FFMpegProcessor.Request(
action = FFMpegProcessor.Action.MERGE_AUDIO_STREAMS,
inputs = sortedStreams.map { it.outputFile.absolutePath },
output = outputFile,
inputDelayOffsets = sortedStreams.associate { stream -> stream.outputFile.absolutePath to (stream.startTimestampMillis - callStartTimestamp) }
)
)
DownloadProcessor(context, object: DownloadCallback.Default() {
override fun onSuccess(outputPath: String) {
context.log.verbose("Downloaded call $outputPath")
}
}).saveMediaToGallery(pendingTask, outputFile, DownloadMetadata(
mediaIdentifier = UUID.randomUUID().toString(),
outputPath = createNewFilePath(
context.config.root,
UUID.randomUUID().toString().hashCode().absoluteValue.toString(16),
downloadSource = MediaDownloadSource.VOICE_CALL,
mediaAuthor = author,
creationTimestamp = System.currentTimeMillis()
),
mediaAuthor = author,
downloadSource = MediaDownloadSource.VOICE_CALL.translate(context.translation),
iconUrl = null
))
} finally {
streams.forEach { stream ->
stream.outputFile.delete()
}
outputFile.delete()
}
context.log.verbose("ending call")
}
}
}

View File

@@ -1,4 +1,4 @@
## v1.1.1
## v1.1.2
- Fix: Quick Tiles crash.
## v1.1.0

View File

@@ -12,6 +12,7 @@ import me.eternal.purrfectsnap.bridge.snapclient.MessagingBridge;
import me.eternal.purrfectsnap.bridge.AccountStorage;
import me.eternal.purrfectsnap.bridge.storage.FileHandleManager;
import me.eternal.purrfectsnap.bridge.location.LocationManager;
import me.eternal.purrfectsnap.bridge.call.CallDownloadSession;
import me.eternal.purrfectsnap.bridge.task.TaskInterface;
interface BridgeInterface {
@@ -105,4 +106,6 @@ interface BridgeInterface {
oneway void registerConfigStateListener(in ConfigStateListener listener);
@nullable String getDebugProp(String key, @nullable String defaultValue);
CallDownloadSession startCallDownload(long startTimestamp, String author);
}

View File

@@ -0,0 +1,6 @@
package me.eternal.purrfectsnap.bridge.call;
interface CallDownloadSession {
ParcelFileDescriptor createStream(long startTimestampMillis, int channels, int sampleRate, int encoding);
oneway void end();
}

View File

@@ -765,6 +765,10 @@
"name": "Auto Download Voice Notes",
"description": "Automatically downloads voice notes when playing them"
},
"call_recorder": {
"name": "Call Recorder",
"description": "Automatically records audio calls"
},
"download_profile_pictures": {
"name": "Download Profile Pictures",
"description": "Allows you to download Profile Pictures from the profile page"
@@ -1814,10 +1818,6 @@
"name": "Story Logger",
"description": "Provides a history of friends stories"
},
"call_recorder": {
"name": "Call Recorder",
"description": "Automatically records audio calls"
},
"account_switcher": {
"name": "Account Switcher",
"description": "Allows you to switch between accounts without logging out\nLong press on the search icon next to your Bitmoji profile to open the menu\nNote: This feature is experimental and will likely change in the future",
@@ -2143,6 +2143,11 @@
"back": "Back Camera",
"null": "Remember last used"
},
"call_recorder": {
"only_record_self": "Only Record Self",
"only_record_others": "Only Record Others",
"record_both": "Record Both Sides"
},
"front_custom_frame_rate": {
"null": "Device default FPS"
},

View File

@@ -50,4 +50,5 @@ class DownloaderConfig : ConfigContainer() {
set(mutableListOf("success", "progress", "failure"))
}
val customPathFormat = string("custom_path_format") { addNotices(FeatureNotice.UNSTABLE) }
}
val callRecorder = unique("call_recorder", "only_record_self", "only_record_others", "record_both") { requireRestart(); addNotices(FeatureNotice.UNSTABLE) }
}

View File

@@ -66,7 +66,6 @@ class Experimental : ConfigContainer() {
val convertMessageLocally = boolean("convert_message_locally") { requireRestart() }
val mediaFilePicker = boolean("media_file_picker") { requireRestart(); addNotices(FeatureNotice.UNSTABLE) }
val storyLogger = boolean("story_logger") { requireRestart(); addNotices(FeatureNotice.UNSTABLE); }
val callRecorder = boolean("call_recorder") { requireRestart(); addNotices(FeatureNotice.UNSTABLE); }
val accountSwitcher = container("account_switcher", AccountSwitcherConfig()) { requireRestart(); addNotices(FeatureNotice.UNSTABLE) }
val betterTranscript = container("better_transcript", BetterTranscriptConfig()) { requireRestart() }
val voiceNoteAutoPlay = boolean("voice_note_auto_play") { requireRestart() }

View File

@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.Resources
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.Process
@@ -161,7 +162,8 @@ class ModContext(
NativeConfig(
disableBitmoji = config.experimental.nativeHooks.disableBitmoji.get(),
disableMetrics = config.global.disableMetrics.get(),
valdiHooks = config.experimental.nativeHooks.valdiHooks.globalState == true,
valdiHooks = config.experimental.nativeHooks.valdiHooks.globalState == true &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q,
customEmojiFontPath = getCustomEmojiFontPath(this)
)
)

View File

@@ -7,6 +7,7 @@ import android.content.res.Resources
import android.os.Build
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Cancel
import java.lang.reflect.Method
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -49,6 +50,8 @@ class PurrfectSnap {
}
private lateinit var appContext: ModContext
private var isBridgeInitialized = false
private var android9ValdiBindDisabled = false
private var android9ValdiBindDisableLogged = false
private fun hookMainActivity(methodName: String, stage: HookStage = HookStage.AFTER, block: Activity.(param: HookAdapter) -> Unit) {
Activity::class.java.hook(methodName, stage, { isBridgeInitialized }) { param ->
@@ -156,6 +159,7 @@ class PurrfectSnap {
}
reloadConfig()
installAndroid9ValdiBindGuard()
initNative()
initWidgetListener()
scope.launch(Dispatchers.IO) {
@@ -244,6 +248,89 @@ class PurrfectSnap {
}
}
private fun installAndroid9ValdiBindGuard() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) return
val valdiNativeBridge = runCatching {
classLoader.loadClass("com.snapchat.client.valdi.NativeBridge")
}.getOrNull()
val targetClasses = listOf("AXj", "NZj", "MZj")
.mapNotNull { className ->
runCatching { classLoader.loadClass(className) }.getOrNull()
}
.distinct()
if (targetClasses.isEmpty()) return
fun defaultReturn(type: Class<*>): Any? = when (type) {
Boolean::class.javaPrimitiveType -> false
Byte::class.javaPrimitiveType -> 0.toByte()
Short::class.javaPrimitiveType -> 0.toShort()
Int::class.javaPrimitiveType -> 0
Long::class.javaPrimitiveType -> 0L
Float::class.javaPrimitiveType -> 0f
Double::class.javaPrimitiveType -> 0.0
Char::class.javaPrimitiveType -> 0.toChar()
Void.TYPE -> null
else -> null
}
fun installGuard(targetClass: Class<*>, methodName: String) {
targetClass.hook(methodName, HookStage.BEFORE) { param ->
val method = param.method() as? Method ?: return@hook
if (android9ValdiBindDisabled) {
if (!android9ValdiBindDisableLogged) {
android9ValdiBindDisableLogged = true
appContext.log.warn("Skipping Valdi bind on Android 9 due to missing native impl")
}
param.setResult(defaultReturn(method.returnType))
return@hook
}
runCatching {
param.invokeOriginal()
}.onSuccess { result ->
param.setResult(result)
}.onFailure { throwable ->
val rootCause = throwable.cause ?: throwable
val message = rootCause.message ?: throwable.message
val isMissingNativeImpl = rootCause is UnsatisfiedLinkError &&
message?.contains("NativeBridge.createContext") == true
val isValdiContextNpe = rootCause is NullPointerException &&
message?.contains("ValdiContext") == true
if (isMissingNativeImpl || isValdiContextNpe) {
android9ValdiBindDisabled = true
if (!android9ValdiBindDisableLogged) {
android9ValdiBindDisableLogged = true
appContext.log.warn("Skipping Valdi bind on Android 9 due to missing native impl")
if (isMissingNativeImpl) {
appContext.log.error("Android 9 Valdi NativeBridge.createContext missing native impl", rootCause)
}
}
param.setResult(defaultReturn(method.returnType))
return@hook
}
appContext.log.error("Android 9 Valdi bind hook failed", throwable)
param.setResult(defaultReturn(method.returnType))
}
}
}
targetClasses.forEach { targetClass ->
installGuard(targetClass, "f")
installGuard(targetClass, "g2")
installGuard(targetClass, "n2")
installGuard(targetClass, "d")
}
valdiNativeBridge?.hook("createContext", HookStage.AFTER) { param ->
val throwable = param.throwable() as? UnsatisfiedLinkError ?: return@hook
android9ValdiBindDisabled = true
if (!android9ValdiBindDisableLogged) {
android9ValdiBindDisableLogged = true
appContext.log.warn("Skipping Valdi bind on Android 9 due to missing native impl")
appContext.log.error("Android 9 Valdi NativeBridge.createContext missing native impl", throwable)
}
param.setResult(null)
}
}
private fun initNative() {
val nativeSigCacheFileHandle = appContext.fileHandlerManager.getFileHandle(FileHandleScope.INTERNAL.key, InternalFileHandleType.NATIVE_SIG_CACHE.key).toWrapper()

View File

@@ -14,6 +14,7 @@ import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withTimeoutOrNull
import me.eternal.purrfectsnap.bridge.*
import me.eternal.purrfectsnap.bridge.call.CallDownloadSession
import me.eternal.purrfectsnap.bridge.e2ee.E2eeInterface
import me.eternal.purrfectsnap.bridge.location.LocationManager
import me.eternal.purrfectsnap.bridge.logger.LoggerInterface
@@ -284,5 +285,12 @@ class BridgeClient(
fun registerConfigStateListener(listener: ConfigStateListener) = safeServiceCall { service.registerConfigStateListener(listener) }
fun getDebugProp(name: String, defaultValue: String? = null): String? = safeServiceCall { service.getDebugProp(name, defaultValue) }
fun startCallDownload(
startTimestamp: Long,
author: String,
): CallDownloadSession {
return safeServiceCall { service.startCallDownload(startTimestamp, author) }
}
}

View File

@@ -5,6 +5,7 @@ import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.os.Build
import me.eternal.purrfectsnap.common.util.snap.SnapWidgetBroadcastReceiverHelper
import me.eternal.purrfectsnap.core.ModContext
import me.eternal.purrfectsnap.core.event.events.impl.*
@@ -27,6 +28,10 @@ class EventDispatcher(
private val context: ModContext
) {
private fun hookViewBinder() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
context.log.warn("BindViewEvent hooks disabled on Android 9 and below")
return
}
context.mappings.useMapper(ViewBinderMapper::class) {
val cachedHooks = mutableListOf<String>()
fun cacheHook(clazz: Class<*>, block: Class<*>.() -> Unit) {

View File

@@ -6,6 +6,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import me.eternal.purrfectsnap.core.ModContext
import me.eternal.purrfectsnap.core.features.impl.*
import me.eternal.purrfectsnap.core.features.impl.downloader.CallRecorder
import me.eternal.purrfectsnap.core.features.impl.downloader.MediaDownloader
import me.eternal.purrfectsnap.core.features.impl.downloader.ProfilePictureDownloader
import me.eternal.purrfectsnap.core.features.impl.experiments.*

View File

@@ -0,0 +1,178 @@
package me.eternal.purrfectsnap.core.features.impl.downloader
import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.AudioTrack
import android.os.ParcelFileDescriptor
import me.eternal.purrfectsnap.bridge.call.CallDownloadSession
import me.eternal.purrfectsnap.core.features.Feature
import me.eternal.purrfectsnap.core.util.hook.HookStage
import me.eternal.purrfectsnap.core.util.hook.hook
import me.eternal.purrfectsnap.core.util.hook.hookConstructor
import me.eternal.purrfectsnap.core.util.ktx.getObjectField
import me.eternal.purrfectsnap.core.util.ktx.getObjectFieldOrNull
import me.eternal.purrfectsnap.core.wrapper.impl.SnapUUID
import java.io.OutputStream
import java.nio.ByteBuffer
import java.util.concurrent.ConcurrentHashMap
class CallRecorder : Feature("Call Recorder") {
private var wasInCall = false
private var callDownloadSession: CallDownloadSession? = null
inner class LazyStream(
private val audioFormat: AudioFormat,
private val startTimestamp: Long = System.currentTimeMillis(),
) {
private var stream: ParcelFileDescriptor.AutoCloseOutputStream? = null
fun get(): OutputStream? {
if (stream != null) return stream
if (callDownloadSession == null) return null
stream = ParcelFileDescriptor.AutoCloseOutputStream(
callDownloadSession?.createStream(
startTimestamp,
audioFormat.channelCount,
audioFormat.sampleRate,
audioFormat.encoding
) ?: return null
)
return stream
}
}
private fun initCallDownloadSession(conversationId: String) {
val author = (if (context.database.getConversationType(conversationId) == 1) {
context.database.getFeedEntryByConversationId(conversationId)?.feedDisplayName
} else {
context.database.getDMOtherParticipant(conversationId)?.let { context.database.getFriendInfo(it)?.mutableUsername }
}) ?: "unknown"
callDownloadSession = context.bridgeClient.startCallDownload(System.currentTimeMillis(), author)
}
private fun onCallStarted(conversationId: String) {
initCallDownloadSession(conversationId)
}
private fun onCallEnded(conversationId: String) {
callDownloadSession?.end()
}
override fun init() {
val callRecorderConfig = context.config.downloader.callRecorder.getNullable()
if (callRecorderConfig == null) return
val streams = ConcurrentHashMap<Int, LazyStream>() // audioTrack -> stream
runCatching {
findClass("com.snapchat.talkcorev3.CallingSessionState")
}.getOrNull()?.hookConstructor(HookStage.AFTER) { param ->
val instance = param.thisObject<Any>()
val callingState = instance.getObjectFieldOrNull("mLocalUser")?.getObjectField("mCallingState")
if (callingState.toString() == "IN_CALL") {
// TODO: implement for older Snapchat versions
}
} ?: findClass("com.snapchat.talkcorev3.TSCallingStateUpdateParams").hookConstructor(
HookStage.AFTER) { param ->
val instance = param.thisObject<Any>()
val conversationId = SnapUUID(instance.getObjectField("mConversationId")).toString()
if (instance.getObjectFieldOrNull("mInCall") == true) {
if (!wasInCall) {
wasInCall = true
onCallStarted(conversationId)
}
} else {
if (wasInCall) {
wasInCall = false
onCallEnded(conversationId)
}
}
}
AudioRecord::class.java.apply {
if (callRecorderConfig == "only_record_others") return@apply
declaredConstructors.first { it.parameterCount > 5 }.hook(HookStage.AFTER) { param ->
val audioAttributes = param.arg<AudioAttributes>(0)
context.log.verbose(audioAttributes.usage)
if (audioAttributes.usage != AudioAttributes.USAGE_UNKNOWN) return@hook
val audioFormat = param.arg<AudioFormat>(1)
val hashCode = param.thisObject<Any>().hashCode()
streams.put(hashCode, LazyStream(audioFormat))
context.log.verbose("AudioRecord called usage=${audioAttributes.usage}, format=$audioFormat")
}
getMethod("read", ByteBuffer::class.java, Int::class.javaPrimitiveType, Int::class.javaPrimitiveType).hook(
HookStage.AFTER) { param ->
val readBytes = param.getResult() as Int
if (readBytes <= 0) return@hook
streams[param.thisObject<Any>().hashCode()]?.let { handlers ->
val byteBuffer = param.arg<ByteBuffer>(0)
val position = byteBuffer.position()
val buffer = ByteArray(readBytes)
byteBuffer.get(buffer)
byteBuffer.position(position)
runCatching {
handlers.get()?.write(buffer, 0, buffer.size)
}.onFailure {
context.log.error("Failed to record call audio data", it)
}
}
}
hook("release", HookStage.BEFORE) {
runCatching {
streams.remove(it.thisObject<Any>().hashCode())?.get()?.close()
}
}
}
AudioTrack::class.java.apply {
if (callRecorderConfig == "only_record_self") return@apply
getConstructor(
AudioAttributes::class.java,
AudioFormat::class.java,
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType,
).hook(HookStage.AFTER) { param ->
val audioAttributes = param.arg<AudioAttributes>(0)
if (audioAttributes.usage != AudioAttributes.USAGE_VOICE_COMMUNICATION) return@hook
val audioFormat = param.arg<AudioFormat>(1)
val hashCode = param.thisObject<Any>().hashCode()
streams.put(hashCode, LazyStream(audioFormat))
context.log.verbose("AudioTrack called usage=${audioAttributes.usage}, format=$audioFormat")
}
getMethod("write", ByteBuffer::class.java, Int::class.javaPrimitiveType, Int::class.javaPrimitiveType).hook(
HookStage.BEFORE) { param ->
streams[param.thisObject<Any>().hashCode()]?.let { handlers ->
val byteBuffer = param.arg<ByteBuffer>(0)
val position = byteBuffer.position()
val buffer = ByteArray(param.arg(1))
byteBuffer.get(buffer)
byteBuffer.position(position)
runCatching {
handlers.get()?.write(buffer, 0, buffer.size)
}.onFailure {
context.log.error("Failed to record call audio data", it)
}
}
}
hook("release", HookStage.BEFORE) {
runCatching { streams.remove(it.thisObject<Any>().hashCode())?.get()?.close() }
}
}
}
}

View File

@@ -6,6 +6,7 @@ import android.widget.RelativeLayout
import me.eternal.purrfectsnap.common.util.protobuf.ProtoReader
import me.eternal.purrfectsnap.core.event.events.impl.AddViewEvent
import me.eternal.purrfectsnap.core.event.events.impl.NetworkApiRequestEvent
import me.eternal.purrfectsnap.core.event.events.impl.UnaryCallEvent
import me.eternal.purrfectsnap.core.features.Feature
import me.eternal.purrfectsnap.core.ui.ViewAppearanceHelper
@@ -19,11 +20,23 @@ class ProfilePictureDownloader : Feature("ProfilePictureDownloader") {
var avatarUrl: String? = null
onNextActivityCreate(defer = true) {
val profileViewClasses = setOf(
"com.snap.unifiedpublicprofile.UnifiedPublicProfileView",
"com.snap.modules.profile3.UserProfileV2RootComponent",
"com.snap.profile.ui.flatland.UnifiedProfileFlatlandProfileView"
)
context.event.subscribe(AddViewEvent::class) { event ->
if (event.view::class.java.name != "com.snap.unifiedpublicprofile.UnifiedPublicProfileView") return@subscribe
if (event.view::class.java.name !in profileViewClasses) return@subscribe
val buttonText = this@ProfilePictureDownloader.context.translation["profile_picture_downloader.button"]
if ((0 until event.parent.childCount).any {
val child = event.parent.getChildAt(it)
child is Button && (child as Button).text == buttonText
}) return@subscribe
event.parent.addView(Button(event.parent.context).apply {
text = this@ProfilePictureDownloader.context.translation["profile_picture_downloader.button"]
text = buttonText
layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT).apply {
setMargins(0, 200, 0, 0)
}
@@ -36,16 +49,21 @@ class ProfilePictureDownloader : Feature("ProfilePictureDownloader") {
backgroundUrl?.let { choices["background_option"] = it }
avatarUrl?.let { choices["avatar_option"] = it }
setItems(choices.keys.map {
this@ProfilePictureDownloader.context.translation["profile_picture_downloader.$it"]
}.toTypedArray()) { _, which ->
runCatching {
this@ProfilePictureDownloader.context.feature(MediaDownloader::class).downloadProfilePicture(
choices.values.elementAt(which),
friendUsername!!
)
}.onFailure {
this@ProfilePictureDownloader.context.log.error("Failed to download profile picture", it)
if (choices.isEmpty()) {
setMessage("No profile pictures available. Please wait for the profile to load.")
setPositiveButton("OK") { dialog, _ -> dialog.dismiss() }
} else {
setItems(choices.keys.map {
this@ProfilePictureDownloader.context.translation["profile_picture_downloader.$it"]
}.toTypedArray()) { _, which ->
runCatching {
this@ProfilePictureDownloader.context.feature(MediaDownloader::class).downloadProfilePicture(
choices.values.elementAt(which),
friendUsername ?: "unknown"
)
}.onFailure {
this@ProfilePictureDownloader.context.log.error("Failed to download profile picture", it)
}
}
}
}.show()
@@ -54,18 +72,31 @@ class ProfilePictureDownloader : Feature("ProfilePictureDownloader") {
}
context.event.subscribe(NetworkApiRequestEvent::class) { event ->
if (!event.url.endsWith("/rpc/getPublicProfile")) return@subscribe
event.onSuccess { buffer ->
ProtoReader(buffer ?: return@onSuccess).followPath(1, 1, 2) {
fun parseProfileData(buffer: ByteArray) {
runCatching {
ProtoReader(buffer).followPath(1, 1, 2) {
friendUsername = getString(2) ?: return@followPath
followPath(4) {
backgroundUrl = getString(2)
avatarUrl = getString(100)
}
}
}.onFailure {
context.log.error("Failed to parse profile picture data", it)
}
}
context.event.subscribe(NetworkApiRequestEvent::class) { event ->
if (!event.url.contains("getPublicProfile")) return@subscribe
event.onSuccess { buffer -> buffer?.let { parseProfileData(it) } }
}
context.event.subscribe(UnaryCallEvent::class) { event ->
if (!event.uri.contains("getPublicProfile")) return@subscribe
event.addResponseCallback {
parseProfileData(buffer)
}
}
}
}
}
}

View File

@@ -1,141 +0,0 @@
package me.eternal.purrfectsnap.core.features.impl.experiments
import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioTrack
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
import me.eternal.purrfectsnap.common.data.download.AudioStreamFormat
import me.eternal.purrfectsnap.common.data.download.MediaDownloadSource
import me.eternal.purrfectsnap.core.features.Feature
import me.eternal.purrfectsnap.core.features.impl.downloader.MediaDownloader
import me.eternal.purrfectsnap.core.util.hook.HookStage
import me.eternal.purrfectsnap.core.util.hook.hook
import me.eternal.purrfectsnap.core.util.hook.hookConstructor
import me.eternal.purrfectsnap.core.util.ktx.getObjectField
import me.eternal.purrfectsnap.core.util.ktx.getObjectFieldOrNull
import me.eternal.purrfectsnap.core.util.media.HttpServer
import java.io.PipedInputStream
import java.io.PipedOutputStream
import java.nio.ByteBuffer
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
class CallRecorder : Feature("Call Recorder") {
private val httpServer = HttpServer(
timeout = Integer.MAX_VALUE
)
override fun init() {
if (!context.config.experimental.callRecorder.get()) return
val streamHandlers = ConcurrentHashMap<Int, MutableList<(data: ByteArray) -> Unit>>() // audioTrack -> handlers
val participants = CopyOnWriteArrayList<String>()
runCatching {
findClass("com.snapchat.talkcorev3.CallingSessionState")
}.getOrNull()?.hookConstructor(HookStage.AFTER) { param ->
val instance = param.thisObject<Any>()
val callingState = instance.getObjectFieldOrNull("mLocalUser")?.getObjectField("mCallingState")
if (callingState.toString() == "IN_CALL") {
participants.clear()
participants.addAll((instance.getObjectField("mParticipants") as Map<*, *>).keys.map { it.toString() })
}
} ?: findClass("com.snapchat.talkcorev3.TSCallingStateUpdateParams").hookConstructor(HookStage.AFTER) { param ->
val instance = param.thisObject<Any>()
if (instance.getObjectFieldOrNull("mInCall") == true) {
participants.clear()
participants.addAll((instance.getObjectField("mParticipants") as Set<*>).map { it.toString() })
}
}
AudioTrack::class.java.apply {
getConstructor(
AudioAttributes::class.java,
AudioFormat::class.java,
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType,
).hook(HookStage.BEFORE) { param ->
val audioAttributes = param.arg<AudioAttributes>(0)
if (audioAttributes.usage != AudioAttributes.USAGE_VOICE_COMMUNICATION) return@hook
val audioFormat = param.arg<AudioFormat>(1)
val hashCode = param.thisObject<Any>().hashCode()
lateinit var streamUrl: String
streamUrl = httpServer.ensureServerStarted()?.putContent(
object: HttpServer.HttpContent() {
override val contentType: String = "audio/wav"
override val chunked: Boolean = true
override val contentLength: Long? = null
override val newBody: () -> HttpServer.HttpBody = {
object: HttpServer.HttpBody() {
val outputStream = PipedOutputStream()
val inputStream = PipedInputStream(outputStream)
val handler: (byteArray: ByteArray) -> Unit = handler@{ byteArray ->
if (byteArray.isEmpty()) {
httpServer.removeUrl(streamUrl)
return@handler
}
runCatching {
outputStream.write(byteArray)
outputStream.flush()
}.onFailure {
context.log.warn("Failed to write to streaming url ${it.localizedMessage}")
}
}
override val onOpen: () -> Unit = {
streamHandlers.getOrPut(hashCode) { CopyOnWriteArrayList() }.add(handler)
}
override val readBytes: (byteArray: ByteArray) -> Int = { byteArray ->
runBlocking {
withTimeoutOrNull(3000L) {
inputStream.read(byteArray)
} ?: -1
}
}
override val onClose: () -> Unit = {
context.log.verbose("Streaming url closed")
streamHandlers[hashCode]?.remove(handler)
outputStream.close()
inputStream.close()
}
}
}
}
) ?: return@hook
context.log.verbose("streaming url = $streamUrl, sampleRate = ${audioFormat.sampleRate}, audioFormat = ${audioFormat.encoding}")
context.feature(MediaDownloader::class).provideDownloadManagerClient(
UUID.randomUUID().toString(),
participants.mapNotNull { context.database.getFriendInfo(it)?.mutableUsername }.joinToString("-"),
System.currentTimeMillis(),
MediaDownloadSource.VOICE_CALL
).downloadStream(streamUrl, AudioStreamFormat(audioFormat.channelCount, audioFormat.sampleRate, audioFormat.encoding))
}
getMethod("write", ByteBuffer::class.java, Int::class.javaPrimitiveType, Int::class.javaPrimitiveType).hook(HookStage.BEFORE) { param ->
streamHandlers[param.thisObject<Any>().hashCode()]?.let { handlers ->
val byteBuffer = param.arg<ByteBuffer>(0)
val position = byteBuffer.position()
val buffer = ByteArray(param.arg(1))
byteBuffer.get(buffer)
byteBuffer.position(position)
handlers.forEach { it(buffer) }
}
}
hook("release", HookStage.BEFORE) {
streamHandlers.remove(it.thisObject<Any>().hashCode())?.forEach { it(ByteArray(0)) }
}
}
}
}

View File

@@ -17,6 +17,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.launch
import android.os.Build
import me.eternal.purrfectsnap.common.bridge.FileHandleScope
import me.eternal.purrfectsnap.common.bridge.toWrapper
import me.eternal.purrfectsnap.common.ui.createComposeAlertDialog
@@ -113,6 +114,10 @@ class ValdiHooks: Feature("ValdiHooks") {
@Suppress("UNCHECKED_CAST")
override fun init() {
if (config.globalState != true) return
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
context.log.warn("ValdiHooks disabled on Android 9 and below")
return
}
if (PurrfectSnap.classCache.valdiFunction == null) {
context.log.warn("ComposerFunction/ValdiFunction class not found, ValdiHooks feature disabled")

View File

@@ -1,5 +1,6 @@
package me.eternal.purrfectsnap.core.features.impl.tweaks
import android.os.Build
import android.view.ViewGroup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -27,6 +28,7 @@ class VoiceNoteOverride: Feature("Voice Note Override") {
val playbackMap = sortedMapOf<Long, Any>()
val classLoader = context.androidContext.classLoader
var valdiCreateContextWarned = false
fun tryFallbackCreateContext(param: HookAdapter): Any? {
val fallbackClass = runCatching {
@@ -149,6 +151,17 @@ class VoiceNoteOverride: Feature("Voice Note Override") {
PurrfectSnap.classCache.nativeBridge.hook("createContext", HookStage.AFTER) { param ->
val throwable = param.throwable() as? UnsatisfiedLinkError ?: return@hook
val isAndroid9OrBelow = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
val isValdiBridge = PurrfectSnap.classCache.nativeBridge.name == "com.snapchat.client.valdi.NativeBridge"
if (isAndroid9OrBelow && isValdiBridge) {
if (!valdiCreateContextWarned) {
valdiCreateContextWarned = true
context.log.warn("NativeBridge.createContext missing native impl on Android 9; skipping fallback")
}
param.clearThrowable()
param.setResult(null)
return@hook
}
context.log.error("NativeBridge.createContext missing native impl; attempting fallback", throwable)
val fallback = tryFallbackCreateContext(param)
param.setResult(fallback)

View File

@@ -103,13 +103,24 @@ val nativeAbisProp = (findProperty("nativeAbis") as? String)
?.takeIf { it.isNotBlank() }
?: System.getenv("NATIVE_ABIS")
?: "arm64-v8a,armeabi-v7a"
val enabledNativeAbis = nativeAbisProp
var enabledNativeAbis = nativeAbisProp
.split(',', ';')
.map { it.trim() }
.filter { it.isNotEmpty() }
.toSet()
.ifEmpty { setOf("arm64-v8a", "armeabi-v7a") }
val requestedTasks = gradle.startParameter.taskNames.joinToString(" ")
val wantsArmv7 = requestedTasks.contains("armv7", ignoreCase = true)
val wantsArmv8 = requestedTasks.contains("armv8", ignoreCase = true)
if (wantsArmv7 && !wantsArmv8) {
enabledNativeAbis = setOf("armeabi-v7a")
} else if (wantsArmv8 && !wantsArmv7) {
enabledNativeAbis = setOf("arm64-v8a")
} else if (wantsArmv7 && wantsArmv8) {
enabledNativeAbis = enabledNativeAbis + setOf("armeabi-v7a", "arm64-v8a")
}
val cargoTargets = listOf(
CargoTarget(
triple = "aarch64-linux-android",
@@ -247,17 +258,16 @@ val syncTasks = cargoTargets.mapIndexed { index, target ->
inputs.property("outputLibName", outputLibName)
val wslCandidate = File(wslStagingDir, "native/rust/target/${target.triple}/release/libpurrfectsnap.so")
val localCandidate = layout.projectDirectory.file("rust/target/${target.triple}/release/libpurrfectsnap.so").asFile
val sourceLibProvider = providers.provider {
if (wslCandidate.exists()) wslCandidate else localCandidate
}
from(sourceLibProvider) {
val sourceLibs = files(wslCandidate, localCandidate)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(sourceLibs) {
rename { outputLibName }
}
from(sourceLibProvider) {
from(sourceLibs) {
rename { "libpurrfectsnap.so" }
}
into(layout.buildDirectory.dir("rustJniLibs/android/${target.abi}"))
inputs.files(sourceLibProvider)
inputs.files(sourceLibs)
val checksumsDir = layout.buildDirectory.dir("checksums")
doLast {
val file = File(destinationDir, outputLibName)
@@ -313,6 +323,7 @@ android {
defaultConfig {
buildConfigField("String", "NATIVE_NAME", "\"$nativeBuildHash\".toString()")
buildConfigField("String", "MODULE_PACKAGE_NAME", "\"${rootProject.ext["applicationId"]}\"")
minSdk = 28
}

View File

@@ -1,6 +1,7 @@
package me.eternal.purrfectsnap.nativelib
import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import java.io.File
import kotlin.math.absoluteValue
@@ -15,40 +16,52 @@ class NativeLib {
private set
private var libraryLoaded = false
private fun findNativeLibraryDir(): File? {
private fun findNativeLibraryDirs(): List<File> {
val app = runCatching {
val cls = Class.forName("android.app.ActivityThread")
val method = cls.getMethod("currentApplication")
method.invoke(null) as? android.app.Application
}.getOrNull() ?: return null
val dir = app.applicationInfo.nativeLibraryDir ?: return null
return File(dir)
}.getOrNull() ?: return emptyList()
val dirs = mutableListOf<File>()
val moduleDir = runCatching {
app.createPackageContext(BuildConfig.MODULE_PACKAGE_NAME, Context.CONTEXT_IGNORE_SECURITY)
.applicationInfo.nativeLibraryDir
}.getOrNull()
moduleDir?.let { dirs.add(File(it)) }
app.applicationInfo.nativeLibraryDir?.let { dirs.add(File(it)) }
return dirs.distinctBy { it.absolutePath }
}
private fun tryLoadFromNativeDir(): Boolean {
val dir = findNativeLibraryDir() ?: return false
if (!dir.isDirectory) return false
val dirs = findNativeLibraryDirs().filter { it.isDirectory }
if (dirs.isEmpty()) return false
val candidates = listOf(
"lib${BuildConfig.NATIVE_NAME}.so",
"libpurrfectsnap.so"
).map { File(dir, it) }
for (dir in dirs) {
val candidates = listOf(
"lib${BuildConfig.NATIVE_NAME}.so",
"libpurrfectsnap.so"
).map { File(dir, it) }
val fallback = dir.listFiles()?.firstOrNull { it.name.startsWith("lib") && it.name.endsWith(".so") && it.name.contains("purrfectsnap") }
val fallback = dir.listFiles()
?.firstOrNull { it.name.startsWith("lib") && it.name.endsWith(".so") && it.name.contains("purrfectsnap") }
val ordered = buildList<File> {
addAll(candidates)
fallback?.let { if (!contains(it)) add(it) }
}
val ordered = buildList<File> {
addAll(candidates)
fallback?.let { if (!contains(it)) add(it) }
}
for (file in ordered) {
if (!file.exists()) continue
val ok = runCatching {
System.load(file.absolutePath)
libraryLoaded = true
true
}.getOrDefault(false)
if (ok) return true
for (file in ordered) {
if (!file.exists()) continue
val ok = runCatching {
System.load(file.absolutePath)
libraryLoaded = true
true
}.getOrDefault(false)
if (ok) return true
}
}
return false
}

View File

@@ -3182,7 +3182,7 @@ var SyntaxKind = /* @__PURE__ */ ((SyntaxKind4) => {
SyntaxKind4[SyntaxKind4["WhileStatement"] = 248] = "WhileStatement";
SyntaxKind4[SyntaxKind4["ForStatement"] = 249] = "ForStatement";
SyntaxKind4[SyntaxKind4["ForInStatement"] = 250] = "ForInStatement";
SyntaxKind4[SyntaxKind4["ForOfStatement"] = 251] = "ForOfStatement";
SyntaxKind4[SyntaxKind4["ForOfStatement"] = 252] = "ForOfStatement";
SyntaxKind4[SyntaxKind4["ContinueStatement"] = 252] = "ContinueStatement";
SyntaxKind4[SyntaxKind4["BreakStatement"] = 253] = "BreakStatement";
SyntaxKind4[SyntaxKind4["ReturnStatement"] = 254] = "ReturnStatement";
@@ -12315,7 +12315,7 @@ function isIterationStatement(node, lookInLabeledStatements) {
switch (node.kind) {
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 247 /* DoStatement */:
case 248 /* WhileStatement */:
return true;
@@ -12337,7 +12337,7 @@ function isExternalModuleIndicator(result) {
return isAnyImportOrReExport(result) || isExportAssignment(result) || hasSyntacticModifier(result, 32 /* Export */);
}
function isForInOrOfStatement(node) {
return node.kind === 250 /* ForInStatement */ || node.kind === 251 /* ForOfStatement */;
return node.kind === 250 /* ForInStatement */ || node.kind === 252 /* ForOfStatement */;
}
function isConciseBody(node) {
return isBlock(node) || isExpression(node);
@@ -12441,7 +12441,7 @@ function canHaveLocals(node) {
case 181 /* ConstructSignature */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 263 /* FunctionDeclaration */:
case 219 /* FunctionExpression */:
case 185 /* FunctionType */:
@@ -12471,7 +12471,7 @@ function isDeclarationStatementKind(kind) {
return kind === 263 /* FunctionDeclaration */ || kind === 283 /* MissingDeclaration */ || kind === 264 /* ClassDeclaration */ || kind === 265 /* InterfaceDeclaration */ || kind === 266 /* TypeAliasDeclaration */ || kind === 267 /* EnumDeclaration */ || kind === 268 /* ModuleDeclaration */ || kind === 273 /* ImportDeclaration */ || kind === 272 /* ImportEqualsDeclaration */ || kind === 279 /* ExportDeclaration */ || kind === 278 /* ExportAssignment */ || kind === 271 /* NamespaceExportDeclaration */;
}
function isStatementKindButNotDeclarationKind(kind) {
return kind === 253 /* BreakStatement */ || kind === 252 /* ContinueStatement */ || kind === 260 /* DebuggerStatement */ || kind === 247 /* DoStatement */ || kind === 245 /* ExpressionStatement */ || kind === 243 /* EmptyStatement */ || kind === 250 /* ForInStatement */ || kind === 251 /* ForOfStatement */ || kind === 249 /* ForStatement */ || kind === 246 /* IfStatement */ || kind === 257 /* LabeledStatement */ || kind === 254 /* ReturnStatement */ || kind === 256 /* SwitchStatement */ || kind === 258 /* ThrowStatement */ || kind === 259 /* TryStatement */ || kind === 244 /* VariableStatement */ || kind === 248 /* WhileStatement */ || kind === 255 /* WithStatement */ || kind === 354 /* NotEmittedStatement */;
return kind === 253 /* BreakStatement */ || kind === 252 /* ContinueStatement */ || kind === 260 /* DebuggerStatement */ || kind === 247 /* DoStatement */ || kind === 245 /* ExpressionStatement */ || kind === 243 /* EmptyStatement */ || kind === 250 /* ForInStatement */ || kind === 252 /* ForOfStatement */ || kind === 249 /* ForStatement */ || kind === 246 /* IfStatement */ || kind === 257 /* LabeledStatement */ || kind === 254 /* ReturnStatement */ || kind === 256 /* SwitchStatement */ || kind === 258 /* ThrowStatement */ || kind === 259 /* TryStatement */ || kind === 244 /* VariableStatement */ || kind === 248 /* WhileStatement */ || kind === 255 /* WithStatement */ || kind === 354 /* NotEmittedStatement */;
}
function isDeclaration(node) {
if (node.kind === 169 /* TypeParameter */) {
@@ -12897,7 +12897,7 @@ function isStatementWithLocals(node) {
case 270 /* CaseBlock */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return true;
}
return false;
@@ -13754,7 +13754,7 @@ function isBlockScope(node, parentNode) {
case 268 /* ModuleDeclaration */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 177 /* Constructor */:
case 175 /* MethodDeclaration */:
case 178 /* GetAccessor */:
@@ -14248,7 +14248,7 @@ function forEachReturnStatement(body, visitor) {
case 248 /* WhileStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 255 /* WithStatement */:
case 256 /* SwitchStatement */:
case 297 /* CaseClause */:
@@ -14791,7 +14791,7 @@ function isInExpressionContext(node) {
const forStatement = parent;
return forStatement.initializer === node && forStatement.initializer.kind !== 262 /* VariableDeclarationList */ || forStatement.condition === node || forStatement.incrementor === node;
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
const forInOrOfStatement = parent;
return forInOrOfStatement.initializer === node && forInOrOfStatement.initializer.kind !== 262 /* VariableDeclarationList */ || forInOrOfStatement.expression === node;
case 217 /* TypeAssertionExpression */:
@@ -15325,7 +15325,7 @@ function canHaveJSDoc(node) {
case 282 /* ExportSpecifier */:
case 245 /* ExpressionStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 249 /* ForStatement */:
case 263 /* FunctionDeclaration */:
case 219 /* FunctionExpression */:
@@ -15489,7 +15489,7 @@ function getAssignmentTarget(node) {
const unaryOperator = unaryExpression.operator;
return unaryOperator === 46 /* PlusPlusToken */ || unaryOperator === 47 /* MinusMinusToken */ ? unaryExpression : void 0;
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
const forInOrOfStatement = parent;
return forInOrOfStatement.initializer === node ? forInOrOfStatement : void 0;
case 218 /* ParenthesizedExpression */:
@@ -15532,7 +15532,7 @@ function getAssignmentTargetKind(node) {
case 226 /* PostfixUnaryExpression */:
return 2 /* Compound */;
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return 1 /* Definite */;
}
}
@@ -15564,7 +15564,7 @@ function isNodeWithPossibleHoistedDeclaration(node) {
case 257 /* LabeledStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 247 /* DoStatement */:
case 248 /* WhileStatement */:
case 259 /* TryStatement */:
@@ -17424,7 +17424,7 @@ function accessKind(node) {
case 210 /* ArrayLiteralExpression */:
return accessKind(parent);
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return node === parent.initializer ? 1 /* Write */ : 0 /* Read */;
default:
return 0 /* Read */;
@@ -23083,7 +23083,7 @@ function createNodeFactory(flags, baseFactory2) {
return node.initializer !== initializer || node.expression !== expression || node.statement !== statement ? update(createForInStatement(initializer, expression, statement), node) : node;
}
function createForOfStatement(awaitModifier, initializer, expression, statement) {
const node = createBaseNode(251 /* ForOfStatement */);
const node = createBaseNode(252 /* ForOfStatement */);
node.awaitModifier = awaitModifier;
node.initializer = initializer;
node.expression = parenthesizerRules().parenthesizeExpressionForDisallowedComma(expression);
@@ -26810,7 +26810,7 @@ function isForInStatement(node) {
return node.kind === 250 /* ForInStatement */;
}
function isForOfStatement(node) {
return node.kind === 251 /* ForOfStatement */;
return node.kind === 252 /* ForOfStatement */;
}
function isReturnStatement(node) {
return node.kind === 254 /* ReturnStatement */;
@@ -28447,7 +28447,7 @@ var forEachChildTable = {
[250 /* ForInStatement */]: function forEachChildInForInStatement(node, cbNode, _cbNodes) {
return visitNode2(cbNode, node.initializer) || visitNode2(cbNode, node.expression) || visitNode2(cbNode, node.statement);
},
[251 /* ForOfStatement */]: function forEachChildInForOfStatement(node, cbNode, _cbNodes) {
[252 /* ForOfStatement */]: function forEachChildInForOfStatement(node, cbNode, _cbNodes) {
return visitNode2(cbNode, node.awaitModifier) || visitNode2(cbNode, node.initializer) || visitNode2(cbNode, node.expression) || visitNode2(cbNode, node.statement);
},
[252 /* ContinueStatement */]: forEachChildInContinueOrBreakStatement,
@@ -30220,7 +30220,7 @@ var Parser;
case 253 /* BreakStatement */:
case 252 /* ContinueStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 249 /* ForStatement */:
case 248 /* WhileStatement */:
case 255 /* WithStatement */:
@@ -42754,7 +42754,7 @@ function createBinder() {
bindForStatement(node);
break;
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
bindForInOrForOfStatement(node);
break;
case 246 /* IfStatement */:
@@ -43144,7 +43144,7 @@ function createBinder() {
bind(node.expression);
addAntecedent(preLoopLabel, currentFlow);
currentFlow = preLoopLabel;
if (node.kind === 251 /* ForOfStatement */) {
if (node.kind === 252 /* ForOfStatement */) {
bind(node.awaitModifier);
}
addAntecedent(postLoopLabel, currentFlow);
@@ -45137,7 +45137,7 @@ function getContainerFlags(node) {
case 300 /* CatchClause */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 270 /* CaseBlock */:
return 2 /* IsBlockScopedContainer */ | 32 /* HasLocals */;
case 242 /* Block */:
@@ -47938,7 +47938,7 @@ function createTypeChecker(host) {
switch (declaration2.parent.parent.kind) {
case 244 /* VariableStatement */:
case 249 /* ForStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
if (isSameScopeDescendentOf(usage2, declaration2, declContainer)) {
return true;
}
@@ -55950,7 +55950,7 @@ function createTypeChecker(host) {
)));
return indexType.flags & (262144 /* TypeParameter */ | 4194304 /* Index */) ? getExtractStringType(indexType) : stringType;
}
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === 251 /* ForOfStatement */) {
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === 252 /* ForOfStatement */) {
const forOfStatement = declaration.parent.parent;
return checkRightHandSideOfForOf(forOfStatement) || anyType;
}
@@ -69719,7 +69719,7 @@ function createTypeChecker(host) {
return isDestructuringDefaultAssignment ? getTypeWithDefault(getAssignedType(node), node.right) : getTypeOfExpression(node.right);
}
function isDestructuringAssignmentTarget(parent) {
return parent.parent.kind === 227 /* BinaryExpression */ && parent.parent.left === parent || parent.parent.kind === 251 /* ForOfStatement */ && parent.parent.initializer === parent;
return parent.parent.kind === 227 /* BinaryExpression */ && parent.parent.left === parent || parent.parent.kind === 252 /* ForOfStatement */ && parent.parent.initializer === parent;
}
function getAssignedTypeOfArrayLiteralElement(node, element) {
return getTypeOfDestructuredArrayElement(getAssignedType(node), node.elements.indexOf(element));
@@ -69738,7 +69738,7 @@ function createTypeChecker(host) {
switch (parent.kind) {
case 250 /* ForInStatement */:
return stringType;
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return checkRightHandSideOfForOf(parent) || errorType;
case 227 /* BinaryExpression */:
return getAssignedTypeOfBinaryExpression(parent);
@@ -69772,7 +69772,7 @@ function createTypeChecker(host) {
if (node.parent.parent.kind === 250 /* ForInStatement */) {
return stringType;
}
if (node.parent.parent.kind === 251 /* ForOfStatement */) {
if (node.parent.parent.kind === 252 /* ForOfStatement */) {
return checkRightHandSideOfForOf(node.parent.parent) || errorType;
}
return errorType;
@@ -70010,7 +70010,7 @@ function createTypeChecker(host) {
if (isDeclarationWithExplicitTypeAnnotation(declaration)) {
return getTypeOfSymbol(symbol);
}
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === 251 /* ForOfStatement */) {
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === 252 /* ForOfStatement */) {
const statement = declaration.parent.parent;
const expressionType = getTypeOfDottedName(
statement.expression,
@@ -71449,7 +71449,7 @@ function createTypeChecker(host) {
case 248 /* WhileStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 255 /* WithStatement */:
case 256 /* SwitchStatement */:
case 259 /* TryStatement */:
@@ -82766,7 +82766,7 @@ function createTypeChecker(host) {
case 270 /* CaseBlock */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
checkUnusedLocalsAndParameters(node, addDiagnostic);
break;
case 177 /* Constructor */:
@@ -86503,7 +86503,7 @@ function createTypeChecker(host) {
return checkForStatement(node);
case 250 /* ForInStatement */:
return checkForInStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return checkForOfStatement(node);
case 252 /* ContinueStatement */:
case 253 /* BreakStatement */:
@@ -87497,7 +87497,7 @@ function createTypeChecker(host) {
}
function getTypeOfAssignmentPattern(expr) {
Debug.assert(expr.kind === 211 /* ObjectLiteralExpression */ || expr.kind === 210 /* ArrayLiteralExpression */);
if (expr.parent.kind === 251 /* ForOfStatement */) {
if (expr.parent.kind === 252 /* ForOfStatement */) {
const iteratedType = checkRightHandSideOfForOf(expr.parent);
return checkDestructuringAssignment(expr, iteratedType || errorType);
}
@@ -89489,7 +89489,7 @@ function createTypeChecker(host) {
if (checkGrammarStatementInAmbientContext(forInOrOfStatement)) {
return true;
}
if (forInOrOfStatement.kind === 251 /* ForOfStatement */ && forInOrOfStatement.awaitModifier) {
if (forInOrOfStatement.kind === 252 /* ForOfStatement */ && forInOrOfStatement.awaitModifier) {
if (!(forInOrOfStatement.flags & 65536 /* AwaitContext */)) {
const sourceFile = getSourceFileOfNode(forInOrOfStatement);
if (isInTopLevelContext(forInOrOfStatement)) {
@@ -89798,7 +89798,7 @@ function createTypeChecker(host) {
return grammarErrorOnNode(node, Diagnostics._0_declarations_may_not_have_binding_patterns, "using");
}
}
if (node.parent.parent.kind !== 250 /* ForInStatement */ && node.parent.parent.kind !== 251 /* ForOfStatement */) {
if (node.parent.parent.kind !== 250 /* ForInStatement */ && node.parent.parent.kind !== 252 /* ForOfStatement */) {
if (nodeFlags & 33554432 /* Ambient */) {
checkAmbientInitializer(node);
} else if (!node.initializer) {
@@ -89890,7 +89890,7 @@ function createTypeChecker(host) {
case 255 /* WithStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return false;
case 257 /* LabeledStatement */:
return allowBlockDeclarations(parent.parent);
@@ -91350,7 +91350,7 @@ var visitEachChildTable = {
visitIterationBody(node.statement, visitor, context, nodeVisitor)
);
},
[251 /* ForOfStatement */]: function visitEachChildOfForOfStatement(node, visitor, context, _nodesVisitor, nodeVisitor, tokenVisitor) {
[252 /* ForOfStatement */]: function visitEachChildOfForOfStatement(node, visitor, context, _nodesVisitor, nodeVisitor, tokenVisitor) {
return context.factory.updateForOfStatement(
node,
tokenVisitor ? nodeVisitor(node.awaitModifier, tokenVisitor, isAwaitKeyword) : node.awaitModifier,
@@ -100339,7 +100339,7 @@ function transformES2017(context) {
return visitForStatementInAsyncBody(node);
case 250 /* ForInStatement */:
return visitForInStatementInAsyncBody(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatementInAsyncBody(node);
case 300 /* CatchClause */:
return visitCatchClauseInAsyncBody(node);
@@ -101193,7 +101193,7 @@ function transformES2018(context) {
0 /* IterationStatementExcludes */,
2 /* IterationStatementIncludes */
);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(
node,
/*outermostLabeledStatement*/
@@ -101364,7 +101364,7 @@ function transformES2018(context) {
function visitLabeledStatement(node) {
if (enclosingFunctionFlags & 2 /* Async */) {
const statement = unwrapInnermostStatementOfLabel(node);
if (statement.kind === 251 /* ForOfStatement */ && statement.awaitModifier) {
if (statement.kind === 252 /* ForOfStatement */ && statement.awaitModifier) {
return visitForOfStatement(statement, node);
}
return factory2.restoreEnclosingLabel(visitNode(statement, visitor, isStatement, factory2.liftToBlock), node);
@@ -102705,7 +102705,7 @@ function transformESNext(context) {
return visitBlock(node);
case 249 /* ForStatement */:
return visitForStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(node);
case 256 /* SwitchStatement */:
return visitSwitchStatement(node);
@@ -103943,7 +103943,7 @@ var entities = new Map(Object.entries({
oslash: 248,
ugrave: 249,
uacute: 250,
ucirc: 251,
ucirc: 252,
uuml: 252,
yacute: 253,
thorn: 254,
@@ -104369,7 +104369,7 @@ function transformES2015(context) {
/*outermostLabeledStatement*/
void 0
);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(
node,
/*outermostLabeledStatement*/
@@ -105979,7 +105979,7 @@ function transformES2015(context) {
return visitForStatement(node, outermostLabeledStatement);
case 250 /* ForInStatement */:
return visitForInStatement(node, outermostLabeledStatement);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(node, outermostLabeledStatement);
}
}
@@ -106453,7 +106453,7 @@ function transformES2015(context) {
return convertForStatement(node, initializerFunction, convertedLoopBody);
case 250 /* ForInStatement */:
return convertForInStatement(node, convertedLoopBody);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return convertForOfStatement(node, convertedLoopBody);
case 247 /* DoStatement */:
return convertDoStatement(node, convertedLoopBody);
@@ -106511,7 +106511,7 @@ function transformES2015(context) {
switch (node.kind) {
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
const initializer = node.initializer;
if (initializer && initializer.kind === 262 /* VariableDeclarationList */) {
loopInitializer = initializer;
@@ -110048,7 +110048,7 @@ function transformModule(context) {
);
case 250 /* ForInStatement */:
return visitForInStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(node);
case 247 /* DoStatement */:
return visitDoStatement(node);
@@ -112320,7 +112320,7 @@ function transformSystemModule(context) {
);
case 250 /* ForInStatement */:
return visitForInStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(node);
case 247 /* DoStatement */:
return visitDoStatement(node);
@@ -116842,7 +116842,7 @@ function createPrinter(printerOptions = {}, handlers = {}) {
return emitForStatement(node);
case 250 /* ForInStatement */:
return emitForInStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return emitForOfStatement(node);
case 252 /* ContinueStatement */:
return emitContinueStatement(node);
@@ -119983,7 +119983,7 @@ function createPrinter(printerOptions = {}, handlers = {}) {
generateNames(node.elseStatement);
break;
case 249 /* ForStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 250 /* ForInStatement */:
generateNames(node.initializer);
generateNames(node.statement);

View File

@@ -3927,7 +3927,7 @@ declare namespace ts {
WhileStatement = 248,
ForStatement = 249,
ForInStatement = 250,
ForOfStatement = 251,
ForOfStatement = 252,
ContinueStatement = 252,
BreakStatement = 253,
ReturnStatement = 254,

View File

@@ -5771,7 +5771,7 @@ var SyntaxKind = /* @__PURE__ */ ((SyntaxKind5) => {
SyntaxKind5[SyntaxKind5["WhileStatement"] = 248] = "WhileStatement";
SyntaxKind5[SyntaxKind5["ForStatement"] = 249] = "ForStatement";
SyntaxKind5[SyntaxKind5["ForInStatement"] = 250] = "ForInStatement";
SyntaxKind5[SyntaxKind5["ForOfStatement"] = 251] = "ForOfStatement";
SyntaxKind5[SyntaxKind5["ForOfStatement"] = 252] = "ForOfStatement";
SyntaxKind5[SyntaxKind5["ContinueStatement"] = 252] = "ContinueStatement";
SyntaxKind5[SyntaxKind5["BreakStatement"] = 253] = "BreakStatement";
SyntaxKind5[SyntaxKind5["ReturnStatement"] = 254] = "ReturnStatement";
@@ -15881,7 +15881,7 @@ function isIterationStatement(node, lookInLabeledStatements) {
switch (node.kind) {
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 247 /* DoStatement */:
case 248 /* WhileStatement */:
return true;
@@ -15903,7 +15903,7 @@ function isExternalModuleIndicator(result) {
return isAnyImportOrReExport(result) || isExportAssignment(result) || hasSyntacticModifier(result, 32 /* Export */);
}
function isForInOrOfStatement(node) {
return node.kind === 250 /* ForInStatement */ || node.kind === 251 /* ForOfStatement */;
return node.kind === 250 /* ForInStatement */ || node.kind === 252 /* ForOfStatement */;
}
function isConciseBody(node) {
return isBlock(node) || isExpression(node);
@@ -16018,7 +16018,7 @@ function canHaveLocals(node) {
case 181 /* ConstructSignature */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 263 /* FunctionDeclaration */:
case 219 /* FunctionExpression */:
case 185 /* FunctionType */:
@@ -16048,7 +16048,7 @@ function isDeclarationStatementKind(kind) {
return kind === 263 /* FunctionDeclaration */ || kind === 283 /* MissingDeclaration */ || kind === 264 /* ClassDeclaration */ || kind === 265 /* InterfaceDeclaration */ || kind === 266 /* TypeAliasDeclaration */ || kind === 267 /* EnumDeclaration */ || kind === 268 /* ModuleDeclaration */ || kind === 273 /* ImportDeclaration */ || kind === 272 /* ImportEqualsDeclaration */ || kind === 279 /* ExportDeclaration */ || kind === 278 /* ExportAssignment */ || kind === 271 /* NamespaceExportDeclaration */;
}
function isStatementKindButNotDeclarationKind(kind) {
return kind === 253 /* BreakStatement */ || kind === 252 /* ContinueStatement */ || kind === 260 /* DebuggerStatement */ || kind === 247 /* DoStatement */ || kind === 245 /* ExpressionStatement */ || kind === 243 /* EmptyStatement */ || kind === 250 /* ForInStatement */ || kind === 251 /* ForOfStatement */ || kind === 249 /* ForStatement */ || kind === 246 /* IfStatement */ || kind === 257 /* LabeledStatement */ || kind === 254 /* ReturnStatement */ || kind === 256 /* SwitchStatement */ || kind === 258 /* ThrowStatement */ || kind === 259 /* TryStatement */ || kind === 244 /* VariableStatement */ || kind === 248 /* WhileStatement */ || kind === 255 /* WithStatement */ || kind === 354 /* NotEmittedStatement */;
return kind === 253 /* BreakStatement */ || kind === 252 /* ContinueStatement */ || kind === 260 /* DebuggerStatement */ || kind === 247 /* DoStatement */ || kind === 245 /* ExpressionStatement */ || kind === 243 /* EmptyStatement */ || kind === 250 /* ForInStatement */ || kind === 252 /* ForOfStatement */ || kind === 249 /* ForStatement */ || kind === 246 /* IfStatement */ || kind === 257 /* LabeledStatement */ || kind === 254 /* ReturnStatement */ || kind === 256 /* SwitchStatement */ || kind === 258 /* ThrowStatement */ || kind === 259 /* TryStatement */ || kind === 244 /* VariableStatement */ || kind === 248 /* WhileStatement */ || kind === 255 /* WithStatement */ || kind === 354 /* NotEmittedStatement */;
}
function isDeclaration(node) {
if (node.kind === 169 /* TypeParameter */) {
@@ -16490,7 +16490,7 @@ function isStatementWithLocals(node) {
case 270 /* CaseBlock */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return true;
}
return false;
@@ -17395,7 +17395,7 @@ function isBlockScope(node, parentNode) {
case 268 /* ModuleDeclaration */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 177 /* Constructor */:
case 175 /* MethodDeclaration */:
case 178 /* GetAccessor */:
@@ -17935,7 +17935,7 @@ function forEachReturnStatement(body, visitor) {
case 248 /* WhileStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 255 /* WithStatement */:
case 256 /* SwitchStatement */:
case 297 /* CaseClause */:
@@ -18481,7 +18481,7 @@ function isInExpressionContext(node) {
const forStatement = parent2;
return forStatement.initializer === node && forStatement.initializer.kind !== 262 /* VariableDeclarationList */ || forStatement.condition === node || forStatement.incrementor === node;
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
const forInOrOfStatement = parent2;
return forInOrOfStatement.initializer === node && forInOrOfStatement.initializer.kind !== 262 /* VariableDeclarationList */ || forInOrOfStatement.expression === node;
case 217 /* TypeAssertionExpression */:
@@ -19051,7 +19051,7 @@ function canHaveJSDoc(node) {
case 282 /* ExportSpecifier */:
case 245 /* ExpressionStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 249 /* ForStatement */:
case 263 /* FunctionDeclaration */:
case 219 /* FunctionExpression */:
@@ -19224,7 +19224,7 @@ function getAssignmentTarget(node) {
const unaryOperator = unaryExpression.operator;
return unaryOperator === 46 /* PlusPlusToken */ || unaryOperator === 47 /* MinusMinusToken */ ? unaryExpression : void 0;
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
const forInOrOfStatement = parent2;
return forInOrOfStatement.initializer === node ? forInOrOfStatement : void 0;
case 218 /* ParenthesizedExpression */:
@@ -19267,7 +19267,7 @@ function getAssignmentTargetKind(node) {
case 226 /* PostfixUnaryExpression */:
return 2 /* Compound */;
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return 1 /* Definite */;
}
}
@@ -19299,7 +19299,7 @@ function isNodeWithPossibleHoistedDeclaration(node) {
case 257 /* LabeledStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 247 /* DoStatement */:
case 248 /* WhileStatement */:
case 259 /* TryStatement */:
@@ -21316,7 +21316,7 @@ function accessKind(node) {
case 210 /* ArrayLiteralExpression */:
return accessKind(parent2);
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return node === parent2.initializer ? 1 /* Write */ : 0 /* Read */;
default:
return 0 /* Read */;
@@ -23389,7 +23389,7 @@ function isSourceElement(node) {
case 248 /* WhileStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 252 /* ContinueStatement */:
case 253 /* BreakStatement */:
case 254 /* ReturnStatement */:
@@ -27191,7 +27191,7 @@ function createNodeFactory(flags, baseFactory2) {
return node.initializer !== initializer || node.expression !== expression || node.statement !== statement ? update(createForInStatement(initializer, expression, statement), node) : node;
}
function createForOfStatement(awaitModifier, initializer, expression, statement) {
const node = createBaseNode(251 /* ForOfStatement */);
const node = createBaseNode(252 /* ForOfStatement */);
node.awaitModifier = awaitModifier;
node.initializer = initializer;
node.expression = parenthesizerRules().parenthesizeExpressionForDisallowedComma(expression);
@@ -30992,7 +30992,7 @@ function isForInStatement(node) {
return node.kind === 250 /* ForInStatement */;
}
function isForOfStatement(node) {
return node.kind === 251 /* ForOfStatement */;
return node.kind === 252 /* ForOfStatement */;
}
function isContinueStatement(node) {
return node.kind === 252 /* ContinueStatement */;
@@ -32693,7 +32693,7 @@ var forEachChildTable = {
[250 /* ForInStatement */]: function forEachChildInForInStatement(node, cbNode, _cbNodes) {
return visitNode2(cbNode, node.initializer) || visitNode2(cbNode, node.expression) || visitNode2(cbNode, node.statement);
},
[251 /* ForOfStatement */]: function forEachChildInForOfStatement(node, cbNode, _cbNodes) {
[252 /* ForOfStatement */]: function forEachChildInForOfStatement(node, cbNode, _cbNodes) {
return visitNode2(cbNode, node.awaitModifier) || visitNode2(cbNode, node.initializer) || visitNode2(cbNode, node.expression) || visitNode2(cbNode, node.statement);
},
[252 /* ContinueStatement */]: forEachChildInContinueOrBreakStatement,
@@ -34481,7 +34481,7 @@ var Parser;
case 253 /* BreakStatement */:
case 252 /* ContinueStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 249 /* ForStatement */:
case 248 /* WhileStatement */:
case 255 /* WithStatement */:
@@ -47265,7 +47265,7 @@ function createBinder() {
bindForStatement(node);
break;
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
bindForInOrForOfStatement(node);
break;
case 246 /* IfStatement */:
@@ -47655,7 +47655,7 @@ function createBinder() {
bind(node.expression);
addAntecedent(preLoopLabel, currentFlow);
currentFlow = preLoopLabel;
if (node.kind === 251 /* ForOfStatement */) {
if (node.kind === 252 /* ForOfStatement */) {
bind(node.awaitModifier);
}
addAntecedent(postLoopLabel, currentFlow);
@@ -49648,7 +49648,7 @@ function getContainerFlags(node) {
case 300 /* CatchClause */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 270 /* CaseBlock */:
return 2 /* IsBlockScopedContainer */ | 32 /* HasLocals */;
case 242 /* Block */:
@@ -52549,7 +52549,7 @@ function createTypeChecker(host) {
switch (declaration2.parent.parent.kind) {
case 244 /* VariableStatement */:
case 249 /* ForStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
if (isSameScopeDescendentOf(usage2, declaration2, declContainer)) {
return true;
}
@@ -60561,7 +60561,7 @@ function createTypeChecker(host) {
)));
return indexType.flags & (262144 /* TypeParameter */ | 4194304 /* Index */) ? getExtractStringType(indexType) : stringType;
}
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === 251 /* ForOfStatement */) {
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === 252 /* ForOfStatement */) {
const forOfStatement = declaration.parent.parent;
return checkRightHandSideOfForOf(forOfStatement) || anyType;
}
@@ -74330,7 +74330,7 @@ function createTypeChecker(host) {
return isDestructuringDefaultAssignment ? getTypeWithDefault(getAssignedType(node), node.right) : getTypeOfExpression(node.right);
}
function isDestructuringAssignmentTarget(parent2) {
return parent2.parent.kind === 227 /* BinaryExpression */ && parent2.parent.left === parent2 || parent2.parent.kind === 251 /* ForOfStatement */ && parent2.parent.initializer === parent2;
return parent2.parent.kind === 227 /* BinaryExpression */ && parent2.parent.left === parent2 || parent2.parent.kind === 252 /* ForOfStatement */ && parent2.parent.initializer === parent2;
}
function getAssignedTypeOfArrayLiteralElement(node, element) {
return getTypeOfDestructuredArrayElement(getAssignedType(node), node.elements.indexOf(element));
@@ -74349,7 +74349,7 @@ function createTypeChecker(host) {
switch (parent2.kind) {
case 250 /* ForInStatement */:
return stringType;
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return checkRightHandSideOfForOf(parent2) || errorType;
case 227 /* BinaryExpression */:
return getAssignedTypeOfBinaryExpression(parent2);
@@ -74383,7 +74383,7 @@ function createTypeChecker(host) {
if (node.parent.parent.kind === 250 /* ForInStatement */) {
return stringType;
}
if (node.parent.parent.kind === 251 /* ForOfStatement */) {
if (node.parent.parent.kind === 252 /* ForOfStatement */) {
return checkRightHandSideOfForOf(node.parent.parent) || errorType;
}
return errorType;
@@ -74621,7 +74621,7 @@ function createTypeChecker(host) {
if (isDeclarationWithExplicitTypeAnnotation(declaration)) {
return getTypeOfSymbol(symbol);
}
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === 251 /* ForOfStatement */) {
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === 252 /* ForOfStatement */) {
const statement = declaration.parent.parent;
const expressionType = getTypeOfDottedName(
statement.expression,
@@ -76060,7 +76060,7 @@ function createTypeChecker(host) {
case 248 /* WhileStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 255 /* WithStatement */:
case 256 /* SwitchStatement */:
case 259 /* TryStatement */:
@@ -87377,7 +87377,7 @@ function createTypeChecker(host) {
case 270 /* CaseBlock */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
checkUnusedLocalsAndParameters(node, addDiagnostic);
break;
case 177 /* Constructor */:
@@ -91114,7 +91114,7 @@ function createTypeChecker(host) {
return checkForStatement(node);
case 250 /* ForInStatement */:
return checkForInStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return checkForOfStatement(node);
case 252 /* ContinueStatement */:
case 253 /* BreakStatement */:
@@ -92108,7 +92108,7 @@ function createTypeChecker(host) {
}
function getTypeOfAssignmentPattern(expr) {
Debug.assert(expr.kind === 211 /* ObjectLiteralExpression */ || expr.kind === 210 /* ArrayLiteralExpression */);
if (expr.parent.kind === 251 /* ForOfStatement */) {
if (expr.parent.kind === 252 /* ForOfStatement */) {
const iteratedType = checkRightHandSideOfForOf(expr.parent);
return checkDestructuringAssignment(expr, iteratedType || errorType);
}
@@ -94100,7 +94100,7 @@ function createTypeChecker(host) {
if (checkGrammarStatementInAmbientContext(forInOrOfStatement)) {
return true;
}
if (forInOrOfStatement.kind === 251 /* ForOfStatement */ && forInOrOfStatement.awaitModifier) {
if (forInOrOfStatement.kind === 252 /* ForOfStatement */ && forInOrOfStatement.awaitModifier) {
if (!(forInOrOfStatement.flags & 65536 /* AwaitContext */)) {
const sourceFile = getSourceFileOfNode(forInOrOfStatement);
if (isInTopLevelContext(forInOrOfStatement)) {
@@ -94409,7 +94409,7 @@ function createTypeChecker(host) {
return grammarErrorOnNode(node, Diagnostics._0_declarations_may_not_have_binding_patterns, "using");
}
}
if (node.parent.parent.kind !== 250 /* ForInStatement */ && node.parent.parent.kind !== 251 /* ForOfStatement */) {
if (node.parent.parent.kind !== 250 /* ForInStatement */ && node.parent.parent.kind !== 252 /* ForOfStatement */) {
if (nodeFlags & 33554432 /* Ambient */) {
checkAmbientInitializer(node);
} else if (!node.initializer) {
@@ -94501,7 +94501,7 @@ function createTypeChecker(host) {
case 255 /* WithStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return false;
case 257 /* LabeledStatement */:
return allowBlockDeclarations(parent2.parent);
@@ -95961,7 +95961,7 @@ var visitEachChildTable = {
visitIterationBody(node.statement, visitor, context, nodeVisitor)
);
},
[251 /* ForOfStatement */]: function visitEachChildOfForOfStatement(node, visitor, context, _nodesVisitor, nodeVisitor, tokenVisitor) {
[252 /* ForOfStatement */]: function visitEachChildOfForOfStatement(node, visitor, context, _nodesVisitor, nodeVisitor, tokenVisitor) {
return context.factory.updateForOfStatement(
node,
tokenVisitor ? nodeVisitor(node.awaitModifier, tokenVisitor, isAwaitKeyword) : node.awaitModifier,
@@ -105132,7 +105132,7 @@ function transformES2017(context) {
return visitForStatementInAsyncBody(node);
case 250 /* ForInStatement */:
return visitForInStatementInAsyncBody(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatementInAsyncBody(node);
case 300 /* CatchClause */:
return visitCatchClauseInAsyncBody(node);
@@ -105986,7 +105986,7 @@ function transformES2018(context) {
0 /* IterationStatementExcludes */,
2 /* IterationStatementIncludes */
);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(
node,
/*outermostLabeledStatement*/
@@ -106157,7 +106157,7 @@ function transformES2018(context) {
function visitLabeledStatement(node) {
if (enclosingFunctionFlags & 2 /* Async */) {
const statement = unwrapInnermostStatementOfLabel(node);
if (statement.kind === 251 /* ForOfStatement */ && statement.awaitModifier) {
if (statement.kind === 252 /* ForOfStatement */ && statement.awaitModifier) {
return visitForOfStatement(statement, node);
}
return factory2.restoreEnclosingLabel(visitNode(statement, visitor, isStatement, factory2.liftToBlock), node);
@@ -107498,7 +107498,7 @@ function transformESNext(context) {
return visitBlock(node);
case 249 /* ForStatement */:
return visitForStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(node);
case 256 /* SwitchStatement */:
return visitSwitchStatement(node);
@@ -108736,7 +108736,7 @@ var entities = new Map(Object.entries({
oslash: 248,
ugrave: 249,
uacute: 250,
ucirc: 251,
ucirc: 252,
uuml: 252,
yacute: 253,
thorn: 254,
@@ -109162,7 +109162,7 @@ function transformES2015(context) {
/*outermostLabeledStatement*/
void 0
);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(
node,
/*outermostLabeledStatement*/
@@ -110772,7 +110772,7 @@ function transformES2015(context) {
return visitForStatement(node, outermostLabeledStatement);
case 250 /* ForInStatement */:
return visitForInStatement(node, outermostLabeledStatement);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(node, outermostLabeledStatement);
}
}
@@ -111246,7 +111246,7 @@ function transformES2015(context) {
return convertForStatement(node, initializerFunction, convertedLoopBody);
case 250 /* ForInStatement */:
return convertForInStatement(node, convertedLoopBody);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return convertForOfStatement(node, convertedLoopBody);
case 247 /* DoStatement */:
return convertDoStatement(node, convertedLoopBody);
@@ -111304,7 +111304,7 @@ function transformES2015(context) {
switch (node.kind) {
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
const initializer = node.initializer;
if (initializer && initializer.kind === 262 /* VariableDeclarationList */) {
loopInitializer = initializer;
@@ -114841,7 +114841,7 @@ function transformModule(context) {
);
case 250 /* ForInStatement */:
return visitForInStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(node);
case 247 /* DoStatement */:
return visitDoStatement(node);
@@ -117113,7 +117113,7 @@ function transformSystemModule(context) {
);
case 250 /* ForInStatement */:
return visitForInStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return visitForOfStatement(node);
case 247 /* DoStatement */:
return visitDoStatement(node);
@@ -121646,7 +121646,7 @@ function createPrinter(printerOptions = {}, handlers = {}) {
return emitForStatement(node);
case 250 /* ForInStatement */:
return emitForInStatement(node);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return emitForOfStatement(node);
case 252 /* ContinueStatement */:
return emitContinueStatement(node);
@@ -124787,7 +124787,7 @@ function createPrinter(printerOptions = {}, handlers = {}) {
generateNames(node.elseStatement);
break;
case 249 /* ForStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 250 /* ForInStatement */:
generateNames(node.initializer);
generateNames(node.statement);
@@ -139884,7 +139884,7 @@ function isCompletedNode(n, sourceFile) {
return false;
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 248 /* WhileStatement */:
return isCompletedNode(n.statement, sourceFile);
case 247 /* DoStatement */:
@@ -140674,7 +140674,7 @@ function isArrayLiteralOrObjectLiteralDestructuringPattern(node) {
if (node.parent.kind === 227 /* BinaryExpression */ && node.parent.left === node && node.parent.operatorToken.kind === 64 /* EqualsToken */) {
return true;
}
if (node.parent.kind === 251 /* ForOfStatement */ && node.parent.initializer === node) {
if (node.parent.kind === 252 /* ForOfStatement */ && node.parent.initializer === node) {
return true;
}
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.kind === 304 /* PropertyAssignment */ ? node.parent.parent : node.parent)) {
@@ -143632,7 +143632,7 @@ var DocumentHighlights;
// falls through
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 248 /* WhileStatement */:
case 247 /* DoStatement */:
return !statement.label || isLabeledBy(node, statement.label.escapedText);
@@ -143712,7 +143712,7 @@ var DocumentHighlights;
switch (owner.kind) {
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 247 /* DoStatement */:
case 248 /* WhileStatement */:
return getLoopBreakContinueOccurrences(owner);
@@ -154280,7 +154280,7 @@ function spanInSourceFileAtLocation(sourceFile, position) {
return spanInForStatement(node);
case 250 /* ForInStatement */:
return textSpanEndingAtNextToken(node, node.expression);
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return spanInInitializerOfForLike(node);
case 256 /* SwitchStatement */:
return textSpanEndingAtNextToken(node, node.expression);
@@ -154378,7 +154378,7 @@ function spanInSourceFileAtLocation(sourceFile, position) {
case 171 /* Decorator */:
return spanInNode(node.parent);
case 249 /* ForStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return textSpan(node);
case 227 /* BinaryExpression */:
if (node.parent.operatorToken.kind === 28 /* CommaToken */) {
@@ -154441,7 +154441,7 @@ function spanInSourceFileAtLocation(sourceFile, position) {
if (isBindingPattern(variableDeclaration.name)) {
return spanInBindingPattern(variableDeclaration.name);
}
if (hasOnlyExpressionInitializer(variableDeclaration) && variableDeclaration.initializer || hasSyntacticModifier(variableDeclaration, 32 /* Export */) || parent2.parent.kind === 251 /* ForOfStatement */) {
if (hasOnlyExpressionInitializer(variableDeclaration) && variableDeclaration.initializer || hasSyntacticModifier(variableDeclaration, 32 /* Export */) || parent2.parent.kind === 252 /* ForOfStatement */) {
return textSpanFromVariableDeclaration(variableDeclaration);
}
if (isVariableDeclarationList(variableDeclaration.parent) && variableDeclaration.parent.declarations[0] !== variableDeclaration) {
@@ -154500,7 +154500,7 @@ function spanInSourceFileAtLocation(sourceFile, position) {
return spanInNodeIfStartsOnSameLine(block.parent, block.statements[0]);
// Set span on previous token if it starts on same line otherwise on the first statement of the block
case 249 /* ForStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return spanInNodeIfStartsOnSameLine(findPrecedingToken(block.pos, sourceFile, block.parent), block.statements[0]);
}
return spanInNode(block.statements[0]);
@@ -154630,7 +154630,7 @@ function spanInSourceFileAtLocation(sourceFile, position) {
case 248 /* WhileStatement */:
case 247 /* DoStatement */:
case 249 /* ForStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 214 /* CallExpression */:
case 215 /* NewExpression */:
case 218 /* ParenthesizedExpression */:
@@ -154659,7 +154659,7 @@ function spanInSourceFileAtLocation(sourceFile, position) {
return spanInNode(node2.parent);
}
function spanInOfKeyword(node2) {
if (node2.parent.kind === 251 /* ForOfStatement */) {
if (node2.parent.kind === 252 /* ForOfStatement */) {
return spanInNextNode(node2);
}
return spanInNode(node2.parent);
@@ -162485,7 +162485,7 @@ function canPrefix(token) {
case 261 /* VariableDeclaration */: {
const varDecl = token.parent;
switch (varDecl.parent.parent.kind) {
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 250 /* ForInStatement */:
return true;
}
@@ -169764,7 +169764,7 @@ function getCompletionData(program, log, sourceFile, compilerOptions, position,
isNewIdentifierLocation = false;
const rootDeclaration = getRootDeclaration(objectLikeContainer.parent);
if (!isVariableLike(rootDeclaration)) return Debug.fail("Root declaration is not variable-like.");
let canGetType = hasInitializer(rootDeclaration) || !!getEffectiveTypeAnnotationNode(rootDeclaration) || rootDeclaration.parent.parent.kind === 251 /* ForOfStatement */;
let canGetType = hasInitializer(rootDeclaration) || !!getEffectiveTypeAnnotationNode(rootDeclaration) || rootDeclaration.parent.parent.kind === 252 /* ForOfStatement */;
if (!canGetType && rootDeclaration.kind === 170 /* Parameter */) {
if (isExpression(rootDeclaration.parent)) {
canGetType = !!typeChecker.getContextualType(rootDeclaration.parent);
@@ -172505,7 +172505,7 @@ function getContextNode(node) {
return node.parent;
case 227 /* BinaryExpression */:
return isExpressionStatement(node.parent) ? node.parent : node;
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 250 /* ForInStatement */:
return {
start: node.initializer,
@@ -177133,7 +177133,7 @@ function getOutliningSpanForNode(n, sourceFile) {
switch (n.parent.kind) {
case 247 /* DoStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 249 /* ForStatement */:
case 246 /* IfStatement */:
case 248 /* WhileStatement */:
@@ -180339,7 +180339,7 @@ var deleteDeclaration;
}
const gp = parent2.parent;
switch (gp.kind) {
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 250 /* ForInStatement */:
changes.replaceNode(sourceFile, node, factory.createObjectLiteralExpression());
break;
@@ -181131,7 +181131,7 @@ function isBinaryOpContext(context) {
case 169 /* TypeParameter */:
return context.currentTokenSpan.kind === 103 /* InKeyword */ || context.nextTokenSpan.kind === 103 /* InKeyword */ || context.currentTokenSpan.kind === 64 /* EqualsToken */ || context.nextTokenSpan.kind === 64 /* EqualsToken */;
// Technically, "of" is not a binary operator, but format it the same way as "in"
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
return context.currentTokenSpan.kind === 165 /* OfKeyword */ || context.nextTokenSpan.kind === 165 /* OfKeyword */;
}
return false;
@@ -181262,7 +181262,7 @@ function isControlDeclContext(context) {
case 256 /* SwitchStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 248 /* WhileStatement */:
case 259 /* TryStatement */:
case 247 /* DoStatement */:
@@ -181409,7 +181409,7 @@ function isStatementConditionContext(context) {
case 246 /* IfStatement */:
case 249 /* ForStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 247 /* DoStatement */:
case 248 /* WhileStatement */:
return true;
@@ -183041,7 +183041,7 @@ var SmartIndenter;
case 247 /* DoStatement */:
case 248 /* WhileStatement */:
case 250 /* ForInStatement */:
case 251 /* ForOfStatement */:
case 252 /* ForOfStatement */:
case 249 /* ForStatement */:
case 246 /* IfStatement */:
case 263 /* FunctionDeclaration */:

View File

@@ -43,7 +43,7 @@
"@dprint/typescript": "0.93.4",
"@esfx/canceltoken": "^1.0.0",
"@eslint/js": "^9.20.0",
"@octokit/rest": "^21.1.1",
"@octokit/rest": "^21.1.2",
"@types/chai": "^4.3.20",
"@types/diff": "^7.0.1",
"@types/minimist": "^1.2.5",
@@ -76,7 +76,7 @@
"mocha-fivemat-progress-reporter": "^0.1.0",
"monocart-coverage-reports": "^2.12.1",
"ms": "^2.1.3",
"picocolors": "^1.1.1",
"picocolors": "^1.1.2",
"playwright": "^1.50.1",
"source-map-support": "^0.5.21",
"tslib": "^2.8.1",