6.7.0 - Alpha8 - 新增 cvt/fmt 模块及 (cvt/fmt/s13n).bytes 方法, 用于字节数据处理
This commit is contained in:
@@ -3,8 +3,13 @@
|
||||
"v6.7.0": {
|
||||
"released_date": "2025/10/26",
|
||||
"feature": [
|
||||
"cvt 模块, 用于数据单位转换 (参阅 项目文档 > [单位转换](https://docs.autojs6.com/#/cvt))",
|
||||
"fmt 模块, 用于数据格式化 (参阅 项目文档 > [格式化](https://docs.autojs6.com/#/fmt))",
|
||||
"zip 模块, 用于文件压缩与解压缩操作 (Ref to [Auto.js Pro](https://g.pro.autojs.org/)) (参阅 项目文档 > [Zip](https://docs.autojs6.com/#/zip))",
|
||||
"mediainfo 模块, 用于查看媒体文件的详细信息 (参阅 项目文档 > [媒体信息](https://docs.autojs6.com/#/mediainfo))",
|
||||
"cvt.bytes 方法, 用于字节数据单位转换 (参阅 项目文档 > [单位转换](https://docs.autojs6.com/#/cvt))",
|
||||
"fmt.bytes 方法, 用于字节数据格式化 (参阅 项目文档 > [数据格式化](https://docs.autojs6.com/#/fmt))",
|
||||
"s13n.bytes 方法, 用于标准化字节数据 (参阅 项目文档 > [标准化](https://docs.autojs6.com/#/s13n))",
|
||||
"UiObject#isShifted 方法, 用于检测控件位置变化",
|
||||
"device.getSharedDeviceId 方法, 用于跨应用获取统一共享设备 ID _[`issue #455`](http://issues.autojs6.com/455)_",
|
||||
"structuredClone 全局方法, 用于深拷贝 JavaScript 对象 (参阅 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/structuredClone))",
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.mozilla.javascript.NativeError
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import org.mozilla.javascript.UniqueTag.NOT_FOUND
|
||||
import org.mozilla.javascript.Wrapper
|
||||
import java.math.BigInteger
|
||||
|
||||
object AnyExtensions {
|
||||
|
||||
@@ -82,8 +83,8 @@ object AnyExtensions {
|
||||
fun <T> T?.jsSanitize() = if (this.isJsNullish()) null else this
|
||||
|
||||
fun Any?.jsUnwrapped(): Any? = when (this) {
|
||||
is String -> this
|
||||
is ConsString -> this.toString()
|
||||
is String, is ConsString -> Context.toString(this)
|
||||
is BigInteger -> this
|
||||
is Number -> Context.toNumber(this)
|
||||
is Boolean -> Context.toBoolean(this)
|
||||
is Wrapper -> this.unwrap().jsUnwrapped()
|
||||
|
||||
@@ -4,7 +4,7 @@ import android.content.Context
|
||||
import android.content.res.AssetManager
|
||||
import android.text.TextUtils
|
||||
import org.autojs.autojs.app.GlobalAppContext
|
||||
import org.autojs.autojs.core.pref.Language
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes
|
||||
import org.autojs.autojs.tool.Func1
|
||||
import org.autojs.autojs.util.EnvironmentUtils
|
||||
import org.autojs.autojs.util.FileUtils
|
||||
@@ -19,8 +19,6 @@ import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.nio.charset.Charset
|
||||
import kotlin.math.ln
|
||||
import kotlin.math.pow
|
||||
|
||||
/**
|
||||
* Created by Stardust on Apr 1, 2017.
|
||||
@@ -60,14 +58,9 @@ object PFiles {
|
||||
@JvmStatic
|
||||
fun create(path: String): Boolean {
|
||||
val f = File(path)
|
||||
return if (path.endsWith(separator)) {
|
||||
f.mkdir()
|
||||
} else {
|
||||
try {
|
||||
f.createNewFile()
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
}
|
||||
return when {
|
||||
path.endsWith(separator) -> f.mkdir()
|
||||
else -> runCatching { f.createNewFile() }.isSuccess
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +88,7 @@ object PFiles {
|
||||
fun ensureDir(path: String): Boolean {
|
||||
val i = path.lastIndexOf(separator)
|
||||
return if (i >= 0) {
|
||||
val folder = path.substring(0, i)
|
||||
val folder = path.take(i)
|
||||
val file = File(folder)
|
||||
file.exists() || file.mkdirs()
|
||||
} else false
|
||||
@@ -355,7 +348,7 @@ object PFiles {
|
||||
val fileName = getName(filePath)
|
||||
var b = fileName.lastIndexOf('.')
|
||||
if (b < 0) b = fileName.length
|
||||
return fileName.substring(0, b)
|
||||
return fileName.take(b)
|
||||
}
|
||||
|
||||
fun copyAssetToTmpFile(context: Context, path: String): File {
|
||||
@@ -450,14 +443,18 @@ object PFiles {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getHumanReadableSize(bytes: Long): String {
|
||||
val unit = 1024
|
||||
if (bytes < unit) return "$bytes B"
|
||||
val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val pre = "KMGTPE".substring(exp - 1, exp)
|
||||
return String.format(Language.getPrefLanguage().locale, "%.1f %sB", bytes / unit.toDouble().pow(exp.toDouble()), pre)
|
||||
@JvmOverloads
|
||||
fun getHumanReadableSize(bytes: Long, useIecIdentifier: Boolean = false): String {
|
||||
return Bytes.string(
|
||||
source = bytes.toDouble(),
|
||||
fromUnit = "B",
|
||||
toUnit = "AUTO",
|
||||
useIecIdentifier = useIecIdentifier,
|
||||
useSpace = true,
|
||||
fractionDigits = 1,
|
||||
trimTrailingZero = false,
|
||||
signature = "PFiles.getHumanReadableSize",
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -511,10 +508,6 @@ object PFiles {
|
||||
|
||||
@JvmStatic
|
||||
fun closeSilently(closeable: Closeable?) {
|
||||
try {
|
||||
closeable?.close()
|
||||
} catch (ignored: IOException) {
|
||||
/* Ignored. */
|
||||
}
|
||||
runCatching { closeable?.close() }
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ import org.autojs.autojs.runtime.api.augment.colors.Color
|
||||
import org.autojs.autojs.runtime.api.augment.colors.Colors
|
||||
import org.autojs.autojs.runtime.api.augment.console.Console
|
||||
import org.autojs.autojs.runtime.api.augment.continuation.Continuation
|
||||
import org.autojs.autojs.runtime.api.augment.converter.Converter
|
||||
import org.autojs.autojs.runtime.api.augment.cryptyo.Crypto
|
||||
import org.autojs.autojs.runtime.api.augment.device.Device
|
||||
import org.autojs.autojs.runtime.api.augment.dialogs.Dialogs
|
||||
@@ -52,6 +53,7 @@ import org.autojs.autojs.runtime.api.augment.events.Events
|
||||
import org.autojs.autojs.runtime.api.augment.events.Keys
|
||||
import org.autojs.autojs.runtime.api.augment.files.Files
|
||||
import org.autojs.autojs.runtime.api.augment.floaty.Floaty
|
||||
import org.autojs.autojs.runtime.api.augment.formatter.Formatter
|
||||
import org.autojs.autojs.runtime.api.augment.global.Global
|
||||
import org.autojs.autojs.runtime.api.augment.global.GlobalClasses
|
||||
import org.autojs.autojs.runtime.api.augment.global.IsNullish
|
||||
@@ -160,6 +162,8 @@ import org.autojs.autojs.runtime.api.Toaster as ApiToaster
|
||||
import org.autojs.autojs.runtime.api.UI as ApiUI
|
||||
import org.autojs.autojs.runtime.api.Util as ApiUtil
|
||||
import org.autojs.autojs.runtime.api.augment.autojs.Version as AutojsVersion
|
||||
import org.autojs.autojs.runtime.api.augment.converter.Bytes as BytesCvt
|
||||
import org.autojs.autojs.runtime.api.augment.formatter.Bytes as BytesFmt
|
||||
import org.autojs.autojs.runtime.api.augment.global.Legacy as GlobalLegacy
|
||||
import org.autojs.autojs.runtime.api.augment.notice.Channel as NoticeChannel
|
||||
import org.autojs.autojs.runtime.api.augment.util.Inspect as UtilInspect
|
||||
@@ -714,20 +718,20 @@ class ScriptRuntime private constructor(builder: Builder) {
|
||||
|
||||
private fun augment(target: ScriptableObject) {
|
||||
|
||||
Global(this).assignWithGlobal(target, topLevelScope, GlobalClasses)
|
||||
GlobalLegacy(this).assignWithGlobal(target, topLevelScope)
|
||||
IsNullish.augmentWithGlobal(target, topLevelScope, false)
|
||||
Util.augmentWithGlobal(target, topLevelScope, util, true).apply {
|
||||
UtilJava.augmentWithGlobal(this, topLevelScope, false)
|
||||
UtilVersion.augmentWithGlobal(this, topLevelScope, false)
|
||||
UtilVersionCodes.augmentWithGlobal(this, topLevelScope, VersionCodesInfo.obj, false)
|
||||
UtilInspect.augmentWithGlobal(this, topLevelScope, false)
|
||||
UtilMorseCode.augmentWithGlobal(this, topLevelScope, false)
|
||||
Global(this).assignWithRuntime(target, this, GlobalClasses)
|
||||
GlobalLegacy(this).assignWithRuntime(target, this)
|
||||
IsNullish.augmentWithRuntime(target, this, false)
|
||||
Util.augmentWithRuntime(target, this, util, true).also { util ->
|
||||
UtilJava.augmentWithRuntime(util, this, false)
|
||||
UtilVersion.augmentWithRuntime(util, this, false)
|
||||
UtilVersionCodes.augmentWithRuntime(util, this, VersionCodesInfo.obj, false)
|
||||
UtilInspect.augmentWithRuntime(util, this, false)
|
||||
UtilMorseCode.augmentWithRuntime(util, this, false)
|
||||
}
|
||||
Species.augmentWithGlobal(target, topLevelScope, true)
|
||||
Species.augmentWithRuntime(target, this, true)
|
||||
App(this).augment(target, app, true).also { augmentedApp = it }
|
||||
Autojs(this).augment(target, true).also { augmentedAutojs = it }.apply {
|
||||
AutojsVersion.augmentWithGlobal(this, topLevelScope, false)
|
||||
Autojs(this).augment(target, true).also { augmentedAutojs = it }.also { autojs ->
|
||||
AutojsVersion.augmentWithRuntime(autojs, this, false)
|
||||
}
|
||||
Shell(this).augment(target, true)
|
||||
Timers(this).augment(target, timers, true)
|
||||
@@ -735,26 +739,32 @@ class ScriptRuntime private constructor(builder: Builder) {
|
||||
Automator(this).augment(target, true)
|
||||
Selector(this).augment(target, true, READONLY)
|
||||
Events(this).augment(target, events, true)
|
||||
Keys.augmentWithGlobal(target, topLevelScope, true)
|
||||
Keys.augmentWithRuntime(target, this, true)
|
||||
Images(this).augment(target, true)
|
||||
Ocr(this).augment(target, true).apply {
|
||||
OcrMLKit(this@ScriptRuntime).augment(this, false).also { augmentedOcrMLKit = it }
|
||||
OcrPaddle(this@ScriptRuntime).augment(this, false).also { augmentedOcrPaddle = it }
|
||||
OcrRapid(this@ScriptRuntime).augment(this, false).also { augmentedOcrRapid = it }
|
||||
Ocr(this).augment(target, true).also { ocr ->
|
||||
OcrMLKit(this).augment(ocr, false).also { augmentedOcrMLKit = it }
|
||||
OcrPaddle(this).augment(ocr, false).also { augmentedOcrPaddle = it }
|
||||
OcrRapid(this).augment(ocr, false).also { augmentedOcrRapid = it }
|
||||
}
|
||||
Barcode(this).augment(target, true)
|
||||
QrCode(this).augment(target, true)
|
||||
Threads(this).augment(target, threads, true)
|
||||
UI(this).proxying(target, ui, true)
|
||||
Colors.augmentWithGlobal(target, topLevelScope, listOf(colors, Colors), true)
|
||||
Color.augmentWithGlobal(target, topLevelScope, false)
|
||||
Colors.augmentWithRuntime(target, this, listOf(colors, Colors), true)
|
||||
Color.augmentWithRuntime(target, this, false)
|
||||
Tasks(this).augment(target, true)
|
||||
Dialogs(this).augment(target, true)
|
||||
Continuation(this).augment(target, js_mod_continuation, true, READONLY)
|
||||
Http(this).augment(target, http, true)
|
||||
Web(this).augment(target, true)
|
||||
WebSocket(this).augment(target, WebSocketFields, false)
|
||||
S13n.augmentWithGlobal(target, topLevelScope, true)
|
||||
S13n.augmentWithRuntime(target, this, true)
|
||||
Converter.augmentWithRuntime(target, this, true).also { cvt ->
|
||||
BytesCvt.augmentWithRuntime(cvt, this, false)
|
||||
}
|
||||
Formatter.augmentWithRuntime(target, this, true).also { fmt ->
|
||||
BytesFmt.augmentWithRuntime(fmt, this, false)
|
||||
}
|
||||
Console(this).proxying(target, console, true).also { consoleProxyObject = it }
|
||||
Plugins(this).augment(target, true)
|
||||
Arrayx(this).augment(target, false)
|
||||
@@ -762,29 +772,29 @@ class ScriptRuntime private constructor(builder: Builder) {
|
||||
Mathx(this).augment(target, false)
|
||||
Jsox(this).augment(target, true)
|
||||
Files(this).augment(target, files, true)
|
||||
Crypto.augmentWithGlobal(target, topLevelScope, CoreCrypto, true)
|
||||
Crypto.augmentWithRuntime(target, this, CoreCrypto, true)
|
||||
RootAutomator(this).augment(target, false)
|
||||
Engines(this).augment(target, true)
|
||||
Floaty(this).augment(target, true)
|
||||
Storages.augmentWithGlobal(target, topLevelScope, true)
|
||||
Storages.augmentWithRuntime(target, this, true)
|
||||
Device(this).augment(target, device, true)
|
||||
Recorder(this).augment(target, recorder, true)
|
||||
Toast(this).augment(target, true)
|
||||
Media.augmentWithGlobal(target, topLevelScope, media, true)
|
||||
Sensors.augmentWithGlobal(target, topLevelScope, sensors, true)
|
||||
Base64.augmentWithGlobal(target, topLevelScope, true)
|
||||
Notice(this).augment(target, true).apply {
|
||||
NoticeChannel(this@ScriptRuntime).augment(this, false)
|
||||
Media.augmentWithRuntime(target, this, media, true)
|
||||
Sensors.augmentWithRuntime(target, this, sensors, true)
|
||||
Base64.augmentWithRuntime(target, this, true)
|
||||
Notice(this).augment(target, true).also { notice ->
|
||||
NoticeChannel(this).augment(notice, false)
|
||||
}
|
||||
Shizuku(this).augment(target, shizuku, true)
|
||||
OpenCC.augmentWithGlobal(target, topLevelScope, true)
|
||||
OpenCC.augmentWithRuntime(target, this, true)
|
||||
Mime(this).augment(target, mime, true)
|
||||
SysProps(this).augment(target, true)
|
||||
SQLite(this).augment(target, true)
|
||||
Zip(this).augment(target, true)
|
||||
NanoID.augmentWithGlobal(target, topLevelScope, true)
|
||||
Pinyin.augmentWithGlobal(target, topLevelScope, true)
|
||||
Pinyin4j.augmentWithGlobal(target, topLevelScope, true)
|
||||
NanoID.augmentWithRuntime(target, this, true)
|
||||
Pinyin.augmentWithRuntime(target, this, true)
|
||||
Pinyin4j.augmentWithRuntime(target, this, true)
|
||||
Mediainfo(this).augment(target, true)
|
||||
|
||||
augmentedApp.defineProp(Autojs::class.java.simpleName.lowercase(), augmentedAutojs)
|
||||
|
||||
@@ -192,8 +192,9 @@ class Files(private val scriptRuntime: ScriptRuntime) {
|
||||
return PFiles.isEmptyDir(path(path))
|
||||
}
|
||||
|
||||
fun getHumanReadableSize(bytes: Long): String {
|
||||
return PFiles.getHumanReadableSize(bytes)
|
||||
@JvmOverloads
|
||||
fun getHumanReadableSize(bytes: Long, useIecIdentifier: Boolean = false): String {
|
||||
return PFiles.getHumanReadableSize(bytes, useIecIdentifier)
|
||||
}
|
||||
|
||||
fun getSimplifiedPath(path: String?): String {
|
||||
|
||||
@@ -138,19 +138,19 @@ abstract class Augmentable(private val scriptRuntime: ScriptRuntime? = null) : F
|
||||
|
||||
fun originateKeyName() = also { mIsOriginalKeyName = true }
|
||||
|
||||
fun augmentWithGlobal(target: Scriptable, specifiedGlobal: ScriptableObject, withDollarPrefix: Boolean = true, additionalAttributes: Int = 0): ScriptableObject {
|
||||
return augment(target, withDollarPrefix, additionalAttributes, specifiedGlobal)
|
||||
fun augmentWithRuntime(target: Scriptable, specifiedRuntime: ScriptRuntime, withDollarPrefix: Boolean = true, additionalAttributes: Int = 0): ScriptableObject {
|
||||
return augment(target, withDollarPrefix, additionalAttributes, specifiedRuntime)
|
||||
}
|
||||
|
||||
fun augmentWithGlobal(target: Scriptable, specifiedGlobal: ScriptableObject, proto: Any, withDollarPrefix: Boolean = true, additionalAttributes: Int = 0): ScriptableObject {
|
||||
return augment(target, proto, withDollarPrefix, additionalAttributes, specifiedGlobal)
|
||||
fun augmentWithRuntime(target: Scriptable, specifiedRuntime: ScriptRuntime, proto: Any, withDollarPrefix: Boolean = true, additionalAttributes: Int = 0): ScriptableObject {
|
||||
return augment(target, proto, withDollarPrefix, additionalAttributes, specifiedRuntime)
|
||||
}
|
||||
|
||||
fun augment(target: Scriptable, withDollarPrefix: Boolean = true, additionalAttributes: Int = 0, specifiedGlobal: ScriptableObject? = null): ScriptableObject {
|
||||
return augment(target, emptyList<Any>(), withDollarPrefix, additionalAttributes, specifiedGlobal)
|
||||
fun augment(target: Scriptable, withDollarPrefix: Boolean = true, additionalAttributes: Int = 0, specifiedRuntime: ScriptRuntime? = null): ScriptableObject {
|
||||
return augment(target, emptyList<Any>(), withDollarPrefix, additionalAttributes, specifiedRuntime)
|
||||
}
|
||||
|
||||
fun augment(target: Scriptable, proto: Any, withDollarPrefix: Boolean = true, additionalAttributes: Int = 0, specifiedGlobal: ScriptableObject? = null): ScriptableObject {
|
||||
fun augment(target: Scriptable, proto: Any, withDollarPrefix: Boolean = true, additionalAttributes: Int = 0, specifiedRuntime: ScriptRuntime? = null): ScriptableObject {
|
||||
val callFunc: (args: Array<out Any?>) -> Any? = { args ->
|
||||
try {
|
||||
(this as Invokable).invoke(*args)
|
||||
@@ -191,7 +191,7 @@ abstract class Augmentable(private val scriptRuntime: ScriptRuntime? = null) : F
|
||||
else -> RhinoUtils.newObject(target)
|
||||
}
|
||||
|
||||
assign(newObj, proto, specifiedGlobal)
|
||||
assign(newObj, proto, specifiedRuntime)
|
||||
|
||||
val keys = mutableListOf(key)
|
||||
if (withDollarPrefix) keys += "\$$key"
|
||||
@@ -203,17 +203,17 @@ abstract class Augmentable(private val scriptRuntime: ScriptRuntime? = null) : F
|
||||
return newObj
|
||||
}
|
||||
|
||||
fun assignWithGlobal(target: ScriptableObject, specifiedGlobal: ScriptableObject, proto: Any? = null) {
|
||||
assign(target, proto, specifiedGlobal)
|
||||
fun assignWithRuntime(target: ScriptableObject, specifiedRuntime: ScriptRuntime, proto: Any? = null) {
|
||||
assign(target, proto, specifiedRuntime)
|
||||
}
|
||||
|
||||
/**
|
||||
* When the subclass calls assign, the value of key will be ignored.
|
||||
* zh-CN: 子类调用 assign 时将忽略 key 的值.
|
||||
*/
|
||||
fun assign(target: ScriptableObject, proto: Any? = null, specifiedGlobal: ScriptableObject? = null) {
|
||||
fun assign(target: ScriptableObject, proto: Any? = null, specifiedRuntime: ScriptRuntime? = null) {
|
||||
|
||||
val global: ScriptableObject = specifiedGlobal ?: scriptRuntime?.topLevelScope ?: ScriptableObject.getTopLevelScope(target) as ScriptableObject
|
||||
val global: ScriptableObject = (specifiedRuntime ?: scriptRuntime)?.topLevelScope ?: ScriptableObject.getTopLevelScope(target) as ScriptableObject
|
||||
|
||||
val protos = when (proto) {
|
||||
null -> emptyList()
|
||||
|
||||
@@ -40,7 +40,13 @@ import org.autojs.autojs.util.RhinoUtils.withRhinoContext
|
||||
import org.autojs.autojs.util.StringUtils.lowercaseFirstChar
|
||||
import org.autojs.autojs.util.StringUtils.uppercaseFirstChar
|
||||
import org.autojs.autojs6.R
|
||||
import org.mozilla.javascript.*
|
||||
import org.mozilla.javascript.BaseFunction
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.NativeArray
|
||||
import org.mozilla.javascript.NativeError
|
||||
import org.mozilla.javascript.NativeObject
|
||||
import org.mozilla.javascript.ScriptableObject
|
||||
import org.mozilla.javascript.Undefined
|
||||
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
class Console(scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRuntime) {
|
||||
@@ -226,38 +232,38 @@ class Console(scriptRuntime: ScriptRuntime) : AugmentableProxy(scriptRuntime) {
|
||||
|
||||
@JvmStatic
|
||||
@RhinoRuntimeFunctionInterface
|
||||
fun log(scriptRuntime: ScriptRuntime, args: Array<out Any?>) = unwrapArguments(args) {
|
||||
scriptRuntime.console.log(Util.formatRhino(*it))
|
||||
fun log(scriptRuntime: ScriptRuntime, args: Array<out Any?>) {
|
||||
return scriptRuntime.console.log(Util.formatRhino(*args))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoRuntimeFunctionInterface
|
||||
fun verbose(scriptRuntime: ScriptRuntime, args: Array<out Any?>) = unwrapArguments(args) {
|
||||
scriptRuntime.console.verbose(Util.formatRhino(*it))
|
||||
fun verbose(scriptRuntime: ScriptRuntime, args: Array<out Any?>) {
|
||||
return scriptRuntime.console.verbose(Util.formatRhino(*args))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoRuntimeFunctionInterface
|
||||
fun info(scriptRuntime: ScriptRuntime, args: Array<out Any?>) = unwrapArguments(args) {
|
||||
scriptRuntime.console.info(Util.formatRhino(*it))
|
||||
fun info(scriptRuntime: ScriptRuntime, args: Array<out Any?>) {
|
||||
return scriptRuntime.console.info(Util.formatRhino(*args))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoRuntimeFunctionInterface
|
||||
fun warn(scriptRuntime: ScriptRuntime, args: Array<out Any?>) = unwrapArguments(args) {
|
||||
scriptRuntime.console.warn(Util.formatRhino(*it))
|
||||
fun warn(scriptRuntime: ScriptRuntime, args: Array<out Any?>) {
|
||||
return scriptRuntime.console.warn(Util.formatRhino(*args))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoRuntimeFunctionInterface
|
||||
fun error(scriptRuntime: ScriptRuntime, args: Array<out Any?>) = unwrapArguments(args) {
|
||||
scriptRuntime.console.error(Util.formatRhino(*it))
|
||||
fun error(scriptRuntime: ScriptRuntime, args: Array<out Any?>) {
|
||||
return scriptRuntime.console.error(Util.formatRhino(*args))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoRuntimeFunctionInterface
|
||||
fun print(scriptRuntime: ScriptRuntime, args: Array<out Any?>) = unwrapArguments(args) {
|
||||
scriptRuntime.console.print(Log.DEBUG, Util.formatRhino(*it))
|
||||
fun print(scriptRuntime: ScriptRuntime, args: Array<out Any?>) {
|
||||
return scriptRuntime.console.print(Log.DEBUG, Util.formatRhino(*args))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
@file:Suppress("MayBeConstant")
|
||||
|
||||
package org.autojs.autojs.runtime.api.augment.converter
|
||||
|
||||
import org.autojs.autojs.annotation.RhinoSingletonFunctionInterface
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsBoolean
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNumber
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsObject
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsString
|
||||
import org.autojs.autojs.extension.AnyExtensions.jsBrief
|
||||
import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire
|
||||
import org.autojs.autojs.runtime.api.augment.Augmentable
|
||||
import org.autojs.autojs.runtime.api.augment.Invokable
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.AUTO
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.DEFAULT_BYTES_STRICT
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.IEC_DIV
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.SI_DIV
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.Tough
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.Tough.LOOSE
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.Tough.NONE
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.Tough.STRICT
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.UNITS
|
||||
import org.autojs.autojs.util.RhinoUtils.coerceBoolean
|
||||
import org.mozilla.javascript.ScriptableObject
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes as CoreBytes
|
||||
|
||||
object Bytes : Augmentable(), Invokable {
|
||||
|
||||
override val selfAssignmentProperties = listOf(
|
||||
"UNITS" to UNITS,
|
||||
"AUTO" to AUTO,
|
||||
"IEC_DIV" to IEC_DIV,
|
||||
"SI_DIV" to SI_DIV,
|
||||
)
|
||||
|
||||
override val selfAssignmentFunctions = listOf(
|
||||
::strict.name,
|
||||
::loose.name,
|
||||
)
|
||||
|
||||
override fun invoke(vararg args: Any?): Any = ensureArgumentsLengthInRange(args, 1..4) { call(args) }
|
||||
|
||||
@JvmStatic
|
||||
@RhinoSingletonFunctionInterface
|
||||
fun call(args: Array<out Any?>, tough: Tough = NONE): Any = ensureArgumentsLengthInRange(args, 1..4) { argList ->
|
||||
val (arg0, arg1, arg2, arg3) = argList
|
||||
|
||||
when (argList.size) {
|
||||
4 -> when {
|
||||
arg3.isJsObject() -> {
|
||||
val opts = arg3 as ScriptableObject
|
||||
listOf("source", "fromUnit", "toUnit", "options").forBytesNumber(
|
||||
source = arg0,
|
||||
fromUnit = opts.inquire("fromUnit", arg1),
|
||||
toUnit = opts.inquire("toUnit", arg2),
|
||||
fractionDigits = opts.inquire("fractionDigits"),
|
||||
autoCarryThreshold = opts.inquire("autoCarryThreshold"),
|
||||
strict = opts.inquire("strict"),
|
||||
tough = tough,
|
||||
)
|
||||
}
|
||||
arg3.isJsBoolean() -> {
|
||||
listOf("source", "fromUnit", "toUnit", "useIecIdentifier").forBytesNumber(
|
||||
source = arg0,
|
||||
fromUnit = arg1,
|
||||
toUnit = arg2,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg3.isJsNumber() -> {
|
||||
listOf("source", "fromUnit", "toUnit", "fractionDigits").forBytesNumber(
|
||||
source = arg0,
|
||||
fromUnit = arg1,
|
||||
toUnit = arg2,
|
||||
fractionDigits = arg3,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid argument[3] ${arg3.jsBrief()} for ${Converter.key}.bytes")
|
||||
}
|
||||
3 -> when {
|
||||
arg2.isJsObject() -> {
|
||||
val opts = arg2 as ScriptableObject
|
||||
listOf("source", "toUnit", "options").forBytesNumber(
|
||||
source = arg0,
|
||||
fromUnit = opts.inquire("fromUnit"),
|
||||
toUnit = opts.inquire("toUnit", arg1),
|
||||
fractionDigits = opts.inquire("fractionDigits"),
|
||||
autoCarryThreshold = opts.inquire("autoCarryThreshold"),
|
||||
strict = opts.inquire("strict"),
|
||||
tough = tough,
|
||||
)
|
||||
}
|
||||
arg2.isJsString() -> {
|
||||
listOf("source", "fromUnit", "toUnit").forBytesNumber(
|
||||
source = arg0,
|
||||
fromUnit = arg1,
|
||||
toUnit = arg2,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg2.isJsBoolean() -> {
|
||||
listOf("source", "toUnit", "useIecIdentifier").forBytesNumber(
|
||||
source = arg0,
|
||||
toUnit = arg1,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg2.isJsNumber() -> {
|
||||
listOf("source", "toUnit", "fractionDigits").forBytesNumber(
|
||||
source = arg0,
|
||||
toUnit = arg1,
|
||||
fractionDigits = arg2,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid argument[2] ${arg2.jsBrief()} for ${Converter.key}.bytes")
|
||||
}
|
||||
2 -> when {
|
||||
arg1.isJsObject() -> {
|
||||
val opts = arg1 as ScriptableObject
|
||||
listOf("source", "options").forBytesNumber(
|
||||
source = arg0,
|
||||
fromUnit = opts.inquire("fromUnit"),
|
||||
toUnit = opts.inquire("toUnit"),
|
||||
fractionDigits = opts.inquire("fractionDigits"),
|
||||
autoCarryThreshold = opts.inquire("autoCarryThreshold"),
|
||||
strict = opts.inquire("strict"),
|
||||
tough = tough,
|
||||
)
|
||||
}
|
||||
arg1.isJsString() -> {
|
||||
listOf("source", "toUnit").forBytesNumber(
|
||||
source = arg0,
|
||||
toUnit = arg1,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg1.isJsBoolean() -> {
|
||||
listOf("source", "useIecIdentifier").forBytesNumber(
|
||||
source = arg0,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg1.isJsNumber() -> {
|
||||
listOf("source", "fractionDigits").forBytesNumber(
|
||||
source = arg0,
|
||||
fractionDigits = arg1,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid argument[1] ${arg1.jsBrief()} for ${Converter.key}.bytes")
|
||||
}
|
||||
1 -> listOf("source").forBytesNumber(
|
||||
source = arg0,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
else -> throw IllegalArgumentException("Invalid arguments length ${argList.size} for ${Converter.key}.bytes")
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoSingletonFunctionInterface
|
||||
fun strict(args: Array<out Any?>): Any = ensureArgumentsLengthInRange(args, 1..4) {
|
||||
call(args, STRICT)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoSingletonFunctionInterface
|
||||
fun loose(args: Array<out Any?>): Any = ensureArgumentsLengthInRange(args, 1..4) {
|
||||
call(args, LOOSE)
|
||||
}
|
||||
|
||||
private fun List<String>.forBytesNumber(
|
||||
source: Any? = null,
|
||||
fromUnit: Any? = null,
|
||||
toUnit: Any? = null,
|
||||
fractionDigits: Any? = null,
|
||||
autoCarryThreshold: Any? = null,
|
||||
strict: Any? = null,
|
||||
tough: Tough = NONE,
|
||||
): Any {
|
||||
val (niceStrict, signature) = when (tough) {
|
||||
STRICT -> {
|
||||
val signature = "${Converter.key}.bytes.strict(${this.joinToString(", ")})"
|
||||
require(strict.isJsNullish()) {
|
||||
"Option \"strict\" ${strict.jsBrief()} must be nullish when in strict mode for $signature"
|
||||
}
|
||||
true to signature
|
||||
}
|
||||
LOOSE -> {
|
||||
val signature = "${Converter.key}.bytes.loose(${this.joinToString(", ")})"
|
||||
require(strict.isJsNullish()) {
|
||||
"Option \"strict\" ${strict.jsBrief()} must be nullish when in loose mode for $signature"
|
||||
}
|
||||
false to signature
|
||||
}
|
||||
NONE -> {
|
||||
val signature = "${Converter.key}.bytes(${this.joinToString(", ")})"
|
||||
coerceBoolean(strict, DEFAULT_BYTES_STRICT) to signature
|
||||
}
|
||||
}
|
||||
return CoreBytes.numberRhino(
|
||||
source,
|
||||
fromUnit,
|
||||
toUnit,
|
||||
fractionDigits,
|
||||
autoCarryThreshold,
|
||||
niceStrict,
|
||||
signature,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.autojs.autojs.runtime.api.augment.converter
|
||||
|
||||
import org.autojs.autojs.runtime.api.augment.Augmentable
|
||||
|
||||
object Converter : Augmentable() {
|
||||
|
||||
override val key = "cvt"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
@file:Suppress("MayBeConstant")
|
||||
|
||||
package org.autojs.autojs.runtime.api.augment.converter.core
|
||||
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNumber
|
||||
import org.autojs.autojs.extension.AnyExtensions.jsBrief
|
||||
import org.autojs.autojs.util.RhinoUtils.MAX_SAFE_INT_IEEE754_BD
|
||||
import org.autojs.autojs.util.RhinoUtils.MIN_SAFE_INT_IEEE754_BD
|
||||
import org.autojs.autojs.util.RhinoUtils.coerceBoolean
|
||||
import org.autojs.autojs.util.RhinoUtils.coerceIntNumber
|
||||
import org.autojs.autojs.util.RhinoUtils.coerceLongNumber
|
||||
import org.autojs.autojs.util.RhinoUtils.coerceNumber
|
||||
import org.autojs.autojs.util.RhinoUtils.coerceString
|
||||
import org.mozilla.javascript.Context
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import java.math.RoundingMode
|
||||
import kotlin.Double.Companion.NaN
|
||||
import kotlin.text.RegexOption.IGNORE_CASE
|
||||
|
||||
object Bytes {
|
||||
|
||||
// @formatter:off
|
||||
/**
|
||||
* ```md
|
||||
* | Value | IEC | Term |
|
||||
* |---------|-----|--------------------|
|
||||
* | 1024^1 | KiB | Kibibyte (Kilo) |
|
||||
* | 1024^2 | MiB | Mebibyte (Mega) |
|
||||
* | 1024^3 | GiB | Gibibyte (Giga) |
|
||||
* | 1024^4 | TiB | Tebibyte (Tera) |
|
||||
* | 1024^5 | PiB | Pebibyte (Peta) |
|
||||
* | 1024^6 | EiB | Exbibyte (Exa) |
|
||||
* | 1024^7 | ZiB | Zebibyte (Zetta) |
|
||||
* | 1024^8 | YiB | Yobibyte (Yotta) |
|
||||
* | 1024^9 | RiB | Robibyte (Ronna) |
|
||||
* | 1024^10 | QiB | Quebibyte (Quetta) |
|
||||
* ```
|
||||
*/
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@JvmField val UNITS = "KMGTPEZYRQ"
|
||||
@JvmField val AUTO = "AUTO"
|
||||
|
||||
@JvmField val IEC_DIV = 1024L
|
||||
@JvmField val SI_DIV = 1000L
|
||||
|
||||
@JvmField val DEFAULT_BYTES_FROM_UNIT = "B"
|
||||
@JvmField val DEFAULT_BYTES_TO_UNIT = AUTO
|
||||
@JvmField val DEFAULT_BYTES_USE_IEC_IDENTIFIER = false
|
||||
@JvmField val DEFAULT_BYTES_USE_IEC_IDENTIFIER_FOR_STRICT = true
|
||||
@JvmField val DEFAULT_BYTES_USE_SPACE = true
|
||||
@JvmField val DEFAULT_BYTES_FRACTION_DIGITS = 2
|
||||
@JvmField val DEFAULT_BYTES_TRIM_TRAILING_ZERO = false
|
||||
@JvmField val DEFAULT_BYTES_AUTO_CARRY_THRESHOLD = IEC_DIV
|
||||
@JvmField val DEFAULT_BYTES_STRICT = false
|
||||
// @formatter:on
|
||||
|
||||
private val BI_IEC = BigInteger.valueOf(IEC_DIV)
|
||||
private val BI_SI = BigInteger.valueOf(SI_DIV)
|
||||
private val POW_IEC = Array(UNITS.length + 1) { i -> BI_IEC.pow(i) }
|
||||
private val POW_SI = Array(UNITS.length + 1) { i -> BI_SI.pow(i) }
|
||||
|
||||
@JvmStatic
|
||||
fun numberRhino(
|
||||
source: Any? = null, /* Double */
|
||||
fromUnit: Any? = null, /* String */
|
||||
toUnit: Any? = null, /* String */
|
||||
fractionDigits: Any? = null, /* Int */
|
||||
autoCarryThreshold: Any? = null, /* Double */
|
||||
strict: Any? = null, /* Boolean */
|
||||
signature: String? = null,
|
||||
): Any {
|
||||
val r = NumberRetrievalHandler(source, fromUnit, toUnit, fractionDigits, autoCarryThreshold, strict, signature).retrieve()
|
||||
return number(
|
||||
r.niceSource, r.niceFromUnit, r.niceToUnit, r.niceFractionDigits, r.niceAutoCarryThreshold, r.niceStrict, signature ?: "bytes.number",
|
||||
).toJavaScriptBigIntOrNumber()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun number(
|
||||
source: Double,
|
||||
fromUnit: String = DEFAULT_BYTES_FROM_UNIT,
|
||||
toUnit: String = DEFAULT_BYTES_TO_UNIT,
|
||||
fractionDigits: Int = DEFAULT_BYTES_FRACTION_DIGITS,
|
||||
autoCarryThreshold: Long = DEFAULT_BYTES_AUTO_CARRY_THRESHOLD,
|
||||
strict: Boolean = DEFAULT_BYTES_STRICT,
|
||||
signature: String = "Bytes.number",
|
||||
) = parseArguments(source, fromUnit, toUnit, fractionDigits, autoCarryThreshold, strict, signature).let { (bytesBD, baseBI, toUnit) ->
|
||||
computeNumberValue(bytesBD, baseBI, toUnit, fractionDigits, autoCarryThreshold, strict, signature)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun stringRhino(
|
||||
source: Any? = null,
|
||||
fromUnit: Any? = null,
|
||||
toUnit: Any? = null,
|
||||
useIecIdentifier: Any? = null,
|
||||
useSpace: Any? = null,
|
||||
fractionDigits: Any? = null,
|
||||
trimTrailingZero: Any? = null,
|
||||
autoCarryThreshold: Any? = null,
|
||||
strict: Any? = null,
|
||||
signature: String? = null,
|
||||
): String {
|
||||
val r = StringRetrievalHandler(source, fromUnit, toUnit, useIecIdentifier, useSpace, fractionDigits, trimTrailingZero, autoCarryThreshold, strict, signature).retrieve()
|
||||
return string(r.niceSource, r.niceFromUnit, r.niceToUnit, r.niceUseIecIdentifier, r.niceUseSpace, r.niceFractionDigits, r.niceTrimTrailingZero, r.niceAutoCarryThreshold, r.niceStrict, signature ?: "bytes.string")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun string(
|
||||
source: Double,
|
||||
fromUnit: String = DEFAULT_BYTES_FROM_UNIT,
|
||||
toUnit: String = DEFAULT_BYTES_TO_UNIT,
|
||||
useIecIdentifier: Boolean? = null,
|
||||
useSpace: Boolean = DEFAULT_BYTES_USE_SPACE,
|
||||
fractionDigits: Int = DEFAULT_BYTES_FRACTION_DIGITS,
|
||||
trimTrailingZero: Boolean = DEFAULT_BYTES_TRIM_TRAILING_ZERO,
|
||||
autoCarryThreshold: Long = DEFAULT_BYTES_AUTO_CARRY_THRESHOLD,
|
||||
strict: Boolean = DEFAULT_BYTES_STRICT,
|
||||
signature: String = "Bytes.string",
|
||||
): String {
|
||||
val (bytesBD, baseBI, niceToUnit) = parseArguments(source, fromUnit, toUnit, fractionDigits, autoCarryThreshold, strict, signature)
|
||||
val bd = computeNumberValue(bytesBD, baseBI, niceToUnit, fractionDigits, autoCarryThreshold, strict, signature)
|
||||
val bdFormatted = bd.setScale(fractionDigits, RoundingMode.HALF_UP).toPlainString().let {
|
||||
if (!trimTrailingZero) it else it.replace(Regex("(\\.\\d*?)0+$"), "$1").removeSuffix(".")
|
||||
}
|
||||
val withIec = useIecIdentifier ?: when {
|
||||
strict -> DEFAULT_BYTES_USE_IEC_IDENTIFIER_FOR_STRICT
|
||||
else -> DEFAULT_BYTES_USE_IEC_IDENTIFIER
|
||||
}
|
||||
val space = if (useSpace) " " else ""
|
||||
|
||||
return when {
|
||||
strict -> when (niceToUnit) {
|
||||
AUTO -> {
|
||||
val (_, core) = autoPickBig(bytesBD, baseBI, BigDecimal.valueOf(autoCarryThreshold))
|
||||
val suffix = if (core.isEmpty()) "B" else "${core}iB"
|
||||
"$bdFormatted$space$suffix"
|
||||
}
|
||||
else -> {
|
||||
val coreRaw = niceToUnit.uppercase().removeSuffix("B")
|
||||
val isIEC = coreRaw.endsWith("I")
|
||||
when (val core = if (isIEC) coreRaw.removeSuffix("I") else coreRaw) {
|
||||
"" -> "$bdFormatted${space}B"
|
||||
else -> {
|
||||
val idx = UNITS.indexOf(core)
|
||||
require(idx != -1) { "Argument \"toUnit\" ${toUnit.jsBrief()} is invalid${signature.toSignatureSuffix()}" }
|
||||
when {
|
||||
isIEC -> "$bdFormatted${space}${core}iB"
|
||||
else -> "$bdFormatted${space}${core}B"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> when (niceToUnit) {
|
||||
AUTO -> {
|
||||
val (_, core) = autoPickBig(bytesBD, BI_IEC, BigDecimal.valueOf(autoCarryThreshold))
|
||||
val suffix = if (core.isEmpty()) "B" else if (withIec) "${core}iB" else "${core}B"
|
||||
"$bdFormatted$space$suffix"
|
||||
}
|
||||
else -> when (val core = niceToUnit.removeSuffix("B")) {
|
||||
"" -> "$bdFormatted${space}B"
|
||||
else -> {
|
||||
val idx = UNITS.indexOf(core)
|
||||
require(idx != -1) { "Argument \"toUnit\" ${toUnit.jsBrief()} is invalid${signature.toSignatureSuffix()}" }
|
||||
val suffix = if (withIec) "${core}iB" else "${core}B"
|
||||
"$bdFormatted$space$suffix"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseArguments(
|
||||
source: Double,
|
||||
fromUnit: String = DEFAULT_BYTES_FROM_UNIT,
|
||||
toUnit: String = DEFAULT_BYTES_TO_UNIT,
|
||||
fractionDigits: Int = DEFAULT_BYTES_FRACTION_DIGITS,
|
||||
autoCarryThreshold: Long = DEFAULT_BYTES_AUTO_CARRY_THRESHOLD,
|
||||
strict: Boolean = DEFAULT_BYTES_STRICT,
|
||||
signature: String,
|
||||
): UnitConversionDetails {
|
||||
val signatureSuffix = signature.toSignatureSuffix()
|
||||
|
||||
require(!source.isNaN() && source >= 0.0) {
|
||||
"Argument \"source\" ${source.jsBrief()} must be non-negative number$signatureSuffix"
|
||||
}
|
||||
|
||||
require(fractionDigits >= 0) {
|
||||
"Argument \"fractionDigits\" ${fractionDigits.jsBrief()} must be non-negative$signatureSuffix"
|
||||
}
|
||||
|
||||
val niceToUnit = when {
|
||||
strict -> toUnit.trim().uppercase()
|
||||
else -> toUnit.toSiUnit()
|
||||
}.takeUnless { it.isBlank() } ?: DEFAULT_BYTES_TO_UNIT
|
||||
|
||||
require(autoCarryThreshold > 0) {
|
||||
"Argument \"autoCarryThreshold\" ${autoCarryThreshold.jsBrief()} must be a positive finite number$signatureSuffix"
|
||||
}
|
||||
require(autoCarryThreshold == DEFAULT_BYTES_AUTO_CARRY_THRESHOLD || niceToUnit.equals(AUTO, ignoreCase = true)) {
|
||||
"Argument \"autoCarryThreshold\" is only allowed when argument \"toUnit\" is \"$AUTO\"$signatureSuffix"
|
||||
}
|
||||
|
||||
val (bytesBD, baseBI) = run parseExactBytes@{
|
||||
val valueBD = BigDecimal.valueOf(source)
|
||||
val niceFromUnit = when {
|
||||
strict -> fromUnit.trim()
|
||||
else -> fromUnit.toSiUnit()
|
||||
}.takeUnless { it.isBlank() }?.uppercase() ?: DEFAULT_BYTES_FROM_UNIT
|
||||
val coreRaw = when {
|
||||
niceFromUnit.endsWith("B") -> niceFromUnit.dropLast(1)
|
||||
else -> niceFromUnit
|
||||
}
|
||||
val isIec = strict && coreRaw.endsWith("I")
|
||||
val core = if (isIec) coreRaw.removeSuffix("I") else coreRaw
|
||||
val base = if (!strict) BI_IEC else if (isIec) BI_IEC else BI_SI
|
||||
val idx = if (core.isEmpty()) -1 else UNITS.indexOf(core)
|
||||
require(core.isEmpty() || idx != -1) { "Argument \"fromUnit\" ${fromUnit.jsBrief()} is invalid$signatureSuffix" }
|
||||
val factor = if (idx == -1) BigInteger.ONE else powBI(base, idx + 1)
|
||||
valueBD.multiply(BigDecimal(factor)) to base
|
||||
}
|
||||
|
||||
return UnitConversionDetails(bytesBD, baseBI, niceToUnit)
|
||||
}
|
||||
|
||||
// Calculate the order of AUTO (avoiding errors from ln/Double), determine whether to carry early (autoCarryThreshold)
|
||||
// zh-CN: 求 AUTO 的阶 (避免 ln/Double 带来的误差), 决策是否提前进位 (autoCarryThreshold).
|
||||
private fun autoPickBig(bytes: BigDecimal, base: BigInteger, carryThreshold: BigDecimal): Pair<Int, String> {
|
||||
if (bytes.compareTo(BigDecimal.ZERO) == 0) return 0 to ""
|
||||
var exp = 0
|
||||
val core = fun() = UNITS.substring((exp - 1).coerceAtLeast(0), exp)
|
||||
// Find max exp such that bytes >= base^exp.
|
||||
// zh-CN: 找到最大的 exp 使 bytes >= base^exp.
|
||||
for (i in 1..UNITS.length) {
|
||||
val th = BigDecimal(powBI(base, i))
|
||||
if (bytes >= th) exp = i else break
|
||||
}
|
||||
if (exp > 0) {
|
||||
val currentVal = bytes.divide(BigDecimal(powBI(base, exp)), DEFAULT_BYTES_FRACTION_DIGITS + 4, RoundingMode.HALF_UP)
|
||||
if (currentVal >= carryThreshold && exp < UNITS.length) {
|
||||
exp += 1
|
||||
return exp to core()
|
||||
}
|
||||
}
|
||||
return exp to core()
|
||||
}
|
||||
|
||||
private fun parseSource(source: Any?, fromUnit: Any?, strict: Boolean, signatureSuffix: String): Pair<Double, String> {
|
||||
var bytes: Double
|
||||
var parsedUnitRaw = ""
|
||||
when {
|
||||
source.isJsNumber() -> {
|
||||
val d = coerceNumber(source, NaN)
|
||||
require(!d.isNaN() && d >= 0.0) { "Argument \"source\" ${source.jsBrief()} must be non-negative number for $signatureSuffix" }
|
||||
bytes = d
|
||||
}
|
||||
else -> {
|
||||
val s = coerceString(source, "0").trim()
|
||||
val m = Regex("^(\\d+(?:\\.\\d+)?)\\s*([A-Za-z]*)$", IGNORE_CASE).find(s)
|
||||
?: throw IllegalArgumentException("Invalid bytes value: \"$source\"")
|
||||
bytes = try {
|
||||
Context.toNumber(m.groupValues[1])
|
||||
} catch (_: Throwable) {
|
||||
throw IllegalArgumentException("Invalid bytes value: \"${m.groupValues[1]}\"")
|
||||
}
|
||||
parsedUnitRaw = m.groupValues[2]
|
||||
}
|
||||
}
|
||||
val niceFromUnit = run parseFromUnit@{
|
||||
val incoming = coerceString(fromUnit, "").let { if (strict) it.trim() else it.toSiUnit() }
|
||||
val parsed = if (strict) parsedUnitRaw.trim() else parsedUnitRaw.toSiUnit()
|
||||
when {
|
||||
incoming.isBlank() -> parsed
|
||||
parsed.isBlank() -> incoming
|
||||
incoming.equals(parsed, ignoreCase = !strict) -> incoming
|
||||
else -> throw IllegalArgumentException("Ambiguous \"fromUnit\" values: [ $incoming, $parsed ]$signatureSuffix")
|
||||
}.ifBlank { DEFAULT_BYTES_FROM_UNIT }.uppercase()
|
||||
}
|
||||
return bytes to niceFromUnit
|
||||
}
|
||||
|
||||
private fun powBI(base: BigInteger, exp: Int): BigInteger = when {
|
||||
base == BI_IEC && exp in 0..UNITS.length -> POW_IEC[exp]
|
||||
base == BI_SI && exp in 0..UNITS.length -> POW_SI[exp]
|
||||
else -> base.pow(exp)
|
||||
}
|
||||
|
||||
private fun computeNumberValue(
|
||||
bytesBD: BigDecimal,
|
||||
baseBI: BigInteger,
|
||||
toUnit: String,
|
||||
fractionDigits: Int,
|
||||
autoCarryThreshold: Long,
|
||||
strict: Boolean,
|
||||
signature: String,
|
||||
): BigDecimal = when {
|
||||
strict -> when (toUnit) {
|
||||
AUTO -> {
|
||||
val (exp, _) = autoPickBig(bytesBD, baseBI, BigDecimal.valueOf(autoCarryThreshold))
|
||||
val value = if (exp == 0) bytesBD else bytesBD.divide(BigDecimal(powBI(baseBI, exp)), fractionDigits, RoundingMode.HALF_UP)
|
||||
value.stripTrailingZeros()
|
||||
}
|
||||
else -> {
|
||||
val coreRaw = toUnit.uppercase().removeSuffix("B")
|
||||
val isIEC = coreRaw.endsWith("I")
|
||||
when (val core = if (isIEC) coreRaw.removeSuffix("I") else coreRaw) {
|
||||
"" -> bytesBD.stripTrailingZeros()
|
||||
else -> {
|
||||
val idx = UNITS.indexOf(core)
|
||||
require(idx != -1) { "Argument \"toUnit\" ${toUnit.jsBrief()} is invalid${signature.toSignatureSuffix()}" }
|
||||
val base = if (isIEC) BI_IEC else BI_SI
|
||||
val value = bytesBD.divide(BigDecimal(powBI(base, idx + 1)), fractionDigits, RoundingMode.HALF_UP)
|
||||
value.stripTrailingZeros()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> when (toUnit) {
|
||||
AUTO -> {
|
||||
val (exp, _) = autoPickBig(bytesBD, BI_IEC, BigDecimal.valueOf(autoCarryThreshold))
|
||||
val value = if (exp == 0) bytesBD else bytesBD.divide(BigDecimal(powBI(BI_IEC, exp)), fractionDigits, RoundingMode.HALF_UP)
|
||||
value.stripTrailingZeros()
|
||||
}
|
||||
else -> when (val core = toUnit.removeSuffix("B")) {
|
||||
"" -> bytesBD
|
||||
else -> {
|
||||
val idx = UNITS.indexOf(core)
|
||||
require(idx != -1) { "Argument \"toUnit\" ${toUnit.jsBrief()} is invalid${signature.toSignatureSuffix()}" }
|
||||
val value = bytesBD.divide(BigDecimal(powBI(BI_IEC, idx + 1)), fractionDigits, RoundingMode.HALF_UP)
|
||||
value.stripTrailingZeros()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.toSiUnit() = this.trim().uppercase().replace(Regex("I(?=B$)"), "")
|
||||
|
||||
private fun String.toSignatureSuffix() = this.takeUnless { it.isBlank() }?.let { " for $it" } ?: ""
|
||||
|
||||
private fun BigDecimal.toJavaScriptBigIntOrNumber(): Any = when (this) {
|
||||
in MIN_SAFE_INT_IEEE754_BD..MAX_SAFE_INT_IEEE754_BD -> {
|
||||
this.toDouble()
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
|
||||
private interface RetrievalHandler {
|
||||
fun retrieve(): Any
|
||||
}
|
||||
|
||||
private open class NumberRetrievalHandler(
|
||||
private val source: Any? = null, /* Double */
|
||||
private val fromUnit: Any? = null, /* String */
|
||||
private val toUnit: Any? = null, /* String */
|
||||
private val fractionDigits: Any? = null, /* Int */
|
||||
private val autoCarryThreshold: Any? = null, /* Double */
|
||||
private val strict: Any? = null, /* Boolean */
|
||||
private val signature: String? = null,
|
||||
) : RetrievalHandler {
|
||||
|
||||
override fun retrieve(): NumberRhinoStandardizationDetails {
|
||||
val signatureSuffix = signature?.toSignatureSuffix() ?: ""
|
||||
val niceStrict = coerceBoolean(strict, DEFAULT_BYTES_STRICT)
|
||||
val niceToUnit = when {
|
||||
niceStrict -> coerceString(toUnit, DEFAULT_BYTES_TO_UNIT).trim().uppercase()
|
||||
else -> coerceString(toUnit, DEFAULT_BYTES_TO_UNIT).toSiUnit()
|
||||
}.takeIf { it.isNotEmpty() } ?: DEFAULT_BYTES_TO_UNIT
|
||||
|
||||
val (niceSource, niceFromUnit) = parseSource(source, fromUnit, niceStrict, signatureSuffix)
|
||||
|
||||
val niceFractionDigits = coerceIntNumber(fractionDigits, DEFAULT_BYTES_FRACTION_DIGITS).also {
|
||||
require(it >= 0) { "Argument \"fractionDigits\" ${fractionDigits.jsBrief()} must be non-negative$signatureSuffix" }
|
||||
}
|
||||
|
||||
require(autoCarryThreshold.isJsNullish() || niceToUnit.equals(AUTO, ignoreCase = true)) {
|
||||
"Option \"autoCarryThreshold\" is only allowed when argument \"toUnit\" is \"$AUTO\"$signatureSuffix"
|
||||
}
|
||||
val niceAutoCarryThreshold = coerceLongNumber(autoCarryThreshold, DEFAULT_BYTES_AUTO_CARRY_THRESHOLD).also {
|
||||
require(it > 0) {
|
||||
"Option \"autoCarryThreshold\" ${autoCarryThreshold.jsBrief()} must be a positive finite number$signatureSuffix"
|
||||
}
|
||||
}
|
||||
return NumberRhinoStandardizationDetails(niceSource, niceFromUnit, niceToUnit, niceFractionDigits, niceAutoCarryThreshold, niceStrict)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class StringRetrievalHandler(
|
||||
source: Any?,
|
||||
fromUnit: Any?,
|
||||
toUnit: Any?,
|
||||
private val useIecIdentifier: Any?,
|
||||
private val useSpace: Any?,
|
||||
fractionDigits: Any?,
|
||||
private val trimTrailingZero: Any?,
|
||||
autoCarryThreshold: Any?,
|
||||
strict: Any?,
|
||||
private val signature: String? = null,
|
||||
) : NumberRetrievalHandler(source, fromUnit, toUnit, fractionDigits, autoCarryThreshold, strict, signature) {
|
||||
|
||||
override fun retrieve(): StringRhinoStandardizationDetails {
|
||||
val sp = super.retrieve()
|
||||
|
||||
val niceSource = sp.niceSource
|
||||
val niceFromUnit = sp.niceFromUnit
|
||||
val niceToUnit = sp.niceToUnit
|
||||
val niceFractionDigits = sp.niceFractionDigits
|
||||
val niceAutoCarryThreshold = sp.niceAutoCarryThreshold
|
||||
val niceStrict = sp.niceStrict
|
||||
val signatureSuffix = signature?.toSignatureSuffix() ?: ""
|
||||
|
||||
require(!niceStrict || useIecIdentifier.isJsNullish()) {
|
||||
"Argument \"useIecIdentifier\" ${useIecIdentifier.jsBrief()} must be nullish when in strict mode$signatureSuffix"
|
||||
}
|
||||
|
||||
val niceUseSpace = coerceBoolean(useSpace, DEFAULT_BYTES_USE_SPACE)
|
||||
|
||||
val niceTrimTrailingZero = coerceBoolean(trimTrailingZero, DEFAULT_BYTES_TRIM_TRAILING_ZERO)
|
||||
val niceUseIecIdentifier = if (useIecIdentifier.isJsNullish()) null else Context.toBoolean(useIecIdentifier)
|
||||
|
||||
return StringRhinoStandardizationDetails(niceSource, niceFromUnit, niceToUnit, niceUseIecIdentifier, niceUseSpace, niceFractionDigits, niceTrimTrailingZero, niceAutoCarryThreshold, niceStrict)
|
||||
}
|
||||
}
|
||||
|
||||
private data class UnitConversionDetails(val bytesBD: BigDecimal, val baseBI: BigInteger, val toUnit: String)
|
||||
|
||||
private open class NumberRhinoStandardizationDetails(
|
||||
open val niceSource: Double,
|
||||
open val niceFromUnit: String,
|
||||
open val niceToUnit: String,
|
||||
open val niceFractionDigits: Int,
|
||||
open val niceAutoCarryThreshold: Long,
|
||||
open val niceStrict: Boolean,
|
||||
)
|
||||
|
||||
private class StringRhinoStandardizationDetails(
|
||||
override val niceSource: Double,
|
||||
override val niceFromUnit: String,
|
||||
override val niceToUnit: String,
|
||||
val niceUseIecIdentifier: Boolean?,
|
||||
val niceUseSpace: Boolean,
|
||||
override val niceFractionDigits: Int,
|
||||
val niceTrimTrailingZero: Boolean,
|
||||
override val niceAutoCarryThreshold: Long,
|
||||
override val niceStrict: Boolean,
|
||||
) : NumberRhinoStandardizationDetails(niceSource, niceFromUnit, niceToUnit, niceFractionDigits, niceAutoCarryThreshold, niceStrict)
|
||||
|
||||
enum class Tough { STRICT, LOOSE, NONE }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
@file:Suppress("MayBeConstant")
|
||||
|
||||
package org.autojs.autojs.runtime.api.augment.formatter
|
||||
|
||||
import org.autojs.autojs.annotation.RhinoSingletonFunctionInterface
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsBoolean
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNumber
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsObject
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsString
|
||||
import org.autojs.autojs.extension.AnyExtensions.jsBrief
|
||||
import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire
|
||||
import org.autojs.autojs.runtime.api.augment.Augmentable
|
||||
import org.autojs.autojs.runtime.api.augment.Invokable
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.AUTO
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.DEFAULT_BYTES_STRICT
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.IEC_DIV
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.SI_DIV
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.Tough
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.Tough.LOOSE
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.Tough.NONE
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.Tough.STRICT
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes.UNITS
|
||||
import org.autojs.autojs.util.RhinoUtils.coerceBoolean
|
||||
import org.mozilla.javascript.ScriptableObject
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes as CoreBytes
|
||||
|
||||
object Bytes : Augmentable(), Invokable {
|
||||
|
||||
override val selfAssignmentProperties = listOf(
|
||||
"UNITS" to UNITS,
|
||||
"AUTO" to AUTO,
|
||||
"IEC_DIV" to IEC_DIV,
|
||||
"SI_DIV" to SI_DIV,
|
||||
)
|
||||
|
||||
override val selfAssignmentFunctions = listOf(
|
||||
::strict.name,
|
||||
::loose.name,
|
||||
)
|
||||
|
||||
override fun invoke(vararg args: Any?): String = ensureArgumentsLengthInRange(args, 1..4) { call(args) }
|
||||
|
||||
@JvmStatic
|
||||
@RhinoSingletonFunctionInterface
|
||||
fun call(args: Array<out Any?>, tough: Tough = NONE): String = ensureArgumentsLengthInRange(args, 1..4) { argList ->
|
||||
val (arg0, arg1, arg2, arg3) = argList
|
||||
|
||||
when (argList.size) {
|
||||
4 -> when {
|
||||
arg3.isJsObject() -> {
|
||||
val opts = arg3 as ScriptableObject
|
||||
listOf("source", "fromUnit", "toUnit", "options").forBytesString(
|
||||
source = arg0,
|
||||
fromUnit = opts.inquire("fromUnit", arg1),
|
||||
toUnit = opts.inquire("toUnit", arg2),
|
||||
useIecIdentifier = opts.inquire("useIecIdentifier"),
|
||||
useSpace = opts.inquire("useSpace"),
|
||||
fractionDigits = opts.inquire("fractionDigits"),
|
||||
trimTrailingZero = opts.inquire("trimTrailingZero"),
|
||||
autoCarryThreshold = opts.inquire("autoCarryThreshold"),
|
||||
strict = opts.inquire("strict"),
|
||||
tough = tough,
|
||||
)
|
||||
}
|
||||
arg3.isJsBoolean() -> {
|
||||
listOf("source", "fromUnit", "toUnit", "useIecIdentifier").forBytesString(
|
||||
source = arg0,
|
||||
fromUnit = arg1,
|
||||
toUnit = arg2,
|
||||
useIecIdentifier = arg3,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg3.isJsNumber() -> {
|
||||
listOf("source", "fromUnit", "toUnit", "fractionDigits").forBytesString(
|
||||
source = arg0,
|
||||
fromUnit = arg1,
|
||||
toUnit = arg2,
|
||||
fractionDigits = arg3,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid argument[3] ${arg3.jsBrief()} for ${Formatter.key}.bytes")
|
||||
}
|
||||
3 -> when {
|
||||
arg2.isJsObject() -> {
|
||||
val opts = arg2 as ScriptableObject
|
||||
listOf("source", "toUnit", "options").forBytesString(
|
||||
source = arg0,
|
||||
fromUnit = opts.inquire("fromUnit"),
|
||||
toUnit = opts.inquire("toUnit", arg1),
|
||||
fractionDigits = opts.inquire("fractionDigits"),
|
||||
useIecIdentifier = opts.inquire("useIecIdentifier"),
|
||||
useSpace = opts.inquire("useSpace"),
|
||||
trimTrailingZero = opts.inquire("trimTrailingZero"),
|
||||
autoCarryThreshold = opts.inquire("autoCarryThreshold"),
|
||||
strict = opts.inquire("strict"),
|
||||
tough = tough,
|
||||
)
|
||||
}
|
||||
arg2.isJsString() -> {
|
||||
listOf("source", "fromUnit", "toUnit").forBytesString(
|
||||
source = arg0,
|
||||
fromUnit = arg1,
|
||||
toUnit = arg2,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg2.isJsBoolean() -> {
|
||||
listOf("source", "toUnit", "useIecIdentifier").forBytesString(
|
||||
source = arg0,
|
||||
toUnit = arg1,
|
||||
useIecIdentifier = arg2,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg2.isJsNumber() -> {
|
||||
listOf("source", "toUnit", "fractionDigits").forBytesString(
|
||||
source = arg0,
|
||||
toUnit = arg1,
|
||||
fractionDigits = arg2,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid argument[2] ${arg2.jsBrief()} for ${Formatter.key}.bytes")
|
||||
}
|
||||
2 -> when {
|
||||
arg1.isJsObject() -> {
|
||||
val opts = arg1 as ScriptableObject
|
||||
listOf("source", "options").forBytesString(
|
||||
source = arg0,
|
||||
fromUnit = opts.inquire("fromUnit"),
|
||||
toUnit = opts.inquire("toUnit"),
|
||||
fractionDigits = opts.inquire("fractionDigits"),
|
||||
useIecIdentifier = opts.inquire("useIecIdentifier"),
|
||||
useSpace = opts.inquire("useSpace"),
|
||||
trimTrailingZero = opts.inquire("trimTrailingZero"),
|
||||
autoCarryThreshold = opts.inquire("autoCarryThreshold"),
|
||||
strict = opts.inquire("strict"),
|
||||
tough = tough,
|
||||
)
|
||||
}
|
||||
arg1.isJsString() -> {
|
||||
listOf("source", "toUnit").forBytesString(
|
||||
source = arg0,
|
||||
toUnit = arg1,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg1.isJsBoolean() -> {
|
||||
listOf("source", "useIecIdentifier").forBytesString(
|
||||
source = arg0,
|
||||
useIecIdentifier = arg1,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
arg1.isJsNumber() -> {
|
||||
listOf("source", "fractionDigits").forBytesString(
|
||||
source = arg0,
|
||||
fractionDigits = arg1,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
}
|
||||
else -> throw IllegalArgumentException("Invalid argument[1] ${arg1.jsBrief()} for ${Formatter.key}.bytes")
|
||||
}
|
||||
1 -> listOf("source").forBytesString(
|
||||
source = arg0,
|
||||
strict = tough == STRICT,
|
||||
)
|
||||
else -> throw IllegalArgumentException("Invalid arguments length ${argList.size} for ${Formatter.key}.bytes")
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoSingletonFunctionInterface
|
||||
fun strict(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..4) {
|
||||
call(args, STRICT)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoSingletonFunctionInterface
|
||||
fun loose(args: Array<out Any?>): String = ensureArgumentsLengthInRange(args, 1..4) {
|
||||
call(args, LOOSE)
|
||||
}
|
||||
|
||||
private fun List<String>.forBytesString(
|
||||
source: Any? = null,
|
||||
fromUnit: Any? = null,
|
||||
toUnit: Any? = null,
|
||||
useIecIdentifier: Any? = null,
|
||||
useSpace: Any? = null,
|
||||
fractionDigits: Any? = null,
|
||||
trimTrailingZero: Any? = null,
|
||||
autoCarryThreshold: Any? = null,
|
||||
strict: Any? = null,
|
||||
tough: Tough = NONE,
|
||||
): String {
|
||||
val (niceStrict, signature) = when (tough) {
|
||||
STRICT -> {
|
||||
val signature = "${Formatter.key}.bytes.strict(${this.joinToString(", ")})"
|
||||
require(strict.isJsNullish()) {
|
||||
"Option \"strict\" ${strict.jsBrief()} must be nullish when in strict mode for $signature"
|
||||
}
|
||||
true to signature
|
||||
}
|
||||
LOOSE -> {
|
||||
val signature = "${Formatter.key}.bytes.loose(${this.joinToString(", ")})"
|
||||
require(strict.isJsNullish()) {
|
||||
"Option \"strict\" ${strict.jsBrief()} must be nullish when in loose mode for $signature"
|
||||
}
|
||||
false to signature
|
||||
}
|
||||
NONE -> {
|
||||
val signature = "${Formatter.key}.bytes(${this.joinToString(", ")})"
|
||||
coerceBoolean(strict, DEFAULT_BYTES_STRICT) to signature
|
||||
}
|
||||
}
|
||||
return CoreBytes.stringRhino(
|
||||
source,
|
||||
fromUnit,
|
||||
toUnit,
|
||||
useIecIdentifier,
|
||||
useSpace,
|
||||
fractionDigits,
|
||||
trimTrailingZero,
|
||||
autoCarryThreshold,
|
||||
niceStrict,
|
||||
signature,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.autojs.autojs.runtime.api.augment.formatter
|
||||
|
||||
import org.autojs.autojs.runtime.api.augment.Augmentable
|
||||
|
||||
object Formatter : Augmentable() {
|
||||
|
||||
override val key = "fmt"
|
||||
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
@file:Suppress("MayBeConstant")
|
||||
|
||||
package org.autojs.autojs.runtime.api.augment.s13n
|
||||
|
||||
import org.autojs.autojs.annotation.RhinoFunctionBody
|
||||
import org.autojs.autojs.annotation.RhinoSingletonFunctionInterface
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsObject
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsString
|
||||
import org.autojs.autojs.extension.AnyExtensions.jsBrief
|
||||
import org.autojs.autojs.extension.ScriptableExtensions.prop
|
||||
import org.autojs.autojs.extension.ScriptableObjectExtensions.inquire
|
||||
import org.autojs.autojs.runtime.api.augment.Augmentable
|
||||
import org.autojs.autojs.runtime.api.augment.colors.Colors
|
||||
import org.autojs.autojs.runtime.exception.WrappedIllegalArgumentException
|
||||
@@ -25,6 +30,7 @@ import kotlin.math.roundToInt
|
||||
import kotlin.reflect.full.declaredMemberFunctions
|
||||
import kotlin.text.RegexOption.IGNORE_CASE
|
||||
import android.graphics.Point as AndroidPoint
|
||||
import org.autojs.autojs.runtime.api.augment.converter.core.Bytes as CoreBytes
|
||||
import org.opencv.core.Point as OpencvPoint
|
||||
|
||||
object S13n : Augmentable() {
|
||||
@@ -60,6 +66,7 @@ object S13n : Augmentable() {
|
||||
::throwable.name,
|
||||
::point.name,
|
||||
::time.name,
|
||||
::bytes.name,
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@@ -139,7 +146,7 @@ object S13n : Augmentable() {
|
||||
} ?: when {
|
||||
arg1.isJsObject() -> {
|
||||
val options = arg1 as ScriptableObject
|
||||
time(arrayOf(source, options.prop("fromUnit"), options.prop("toUnit")))
|
||||
time(arrayOf(source, options.inquire("fromUnit"), options.inquire("toUnit")))
|
||||
}
|
||||
else -> {
|
||||
val num = coerceNumber(source, NaN).also { require(!it.isNaN()) { "Failed to make ${arg0.jsBrief()} a number time being" } }
|
||||
@@ -159,6 +166,47 @@ object S13n : Augmentable() {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@RhinoSingletonFunctionInterface
|
||||
fun bytes(args: Array<out Any?>): Any = ensureArgumentsLengthInRange(args, 1..2) { argList ->
|
||||
val (arg0, arg1) = argList
|
||||
|
||||
when (argList.size) {
|
||||
2 -> when {
|
||||
arg1.isJsString() -> listOf("source", "fromUnit").forBytes(source = arg0, fromUnit = arg1)
|
||||
else -> throw WrappedIllegalArgumentException("Invalid argument[1] ${arg1.jsBrief()} for $key.bytes")
|
||||
}
|
||||
1 -> listOf("source").forBytes(source = arg0)
|
||||
else -> throw WrappedIllegalArgumentException("Invalid arguments length ${argList.size} for $key.bytes")
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
@RhinoFunctionBody
|
||||
fun bytesRhino(
|
||||
source: Any? = null, /* Double */
|
||||
fromUnit: Any? = null, /* String */
|
||||
funcSignature: List<String> = emptyList(),
|
||||
): Any {
|
||||
val signature = when {
|
||||
funcSignature.isNotEmpty() -> "$key.bytes(${funcSignature.joinToString(", ")})"
|
||||
else -> "$key.bytes()"
|
||||
}
|
||||
return CoreBytes.numberRhino(
|
||||
source = source,
|
||||
fromUnit = fromUnit,
|
||||
toUnit = "B",
|
||||
fractionDigits = 0,
|
||||
signature = signature,
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<String>.forBytes(
|
||||
source: Any? = null, /* Double */
|
||||
fromUnit: Any? = null, /* String */
|
||||
) = bytesRhino(source, fromUnit, this)
|
||||
|
||||
private fun getTimeUnitSourceObject(unit: Any?): TimeUnit = when (unit) {
|
||||
is TimeUnit -> unit
|
||||
is String -> timeUnitRex.entries.find { it.value.matches(unit) }?.let {
|
||||
|
||||
@@ -2,11 +2,8 @@ package org.autojs.autojs.runtime.api.augment.util
|
||||
|
||||
import org.autojs.autojs.annotation.RhinoFunctionBody
|
||||
import org.autojs.autojs.core.automator.UiObjectCollection
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsBoolean
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNonNullObject
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNullish
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsNumber
|
||||
import org.autojs.autojs.extension.AnyExtensions.isJsString
|
||||
import org.autojs.autojs.extension.AnyExtensions.jsBrief
|
||||
import org.autojs.autojs.extension.ArrayExtensions.toNativeObject
|
||||
import org.autojs.autojs.extension.FlexibleArray.Companion.component1
|
||||
@@ -261,7 +258,13 @@ object Inspect : Augmentable(), Invokable {
|
||||
private fun formatValue(ctx: Ctx, `val`: Any?, recurseTimes: Int?): String {
|
||||
var value = `val`
|
||||
|
||||
while (value is Wrapper) value = value.unwrap()
|
||||
while (value is Wrapper) {
|
||||
val unwrapped = value.unwrap()
|
||||
if (unwrapped is Number) {
|
||||
return callToStringFunction(value as Scriptable)
|
||||
}
|
||||
value = unwrapped
|
||||
}
|
||||
|
||||
if (value is TopLevel) return "[object ${value.className}]"
|
||||
|
||||
@@ -579,7 +582,7 @@ object Inspect : Augmentable(), Invokable {
|
||||
// For some reason, typeof null is "object", so special case here.
|
||||
ctx.stylize("null", "null")
|
||||
}
|
||||
value.isJsString() -> {
|
||||
value is String -> {
|
||||
val content = Context.toString(js_json_stringify(value))
|
||||
.removeSurrounding("\"")
|
||||
.replace(Regex("'"), "\\\'")
|
||||
@@ -590,16 +593,15 @@ object Inspect : Augmentable(), Invokable {
|
||||
// ! to make a string quoted by single quotation marks.
|
||||
// ! zh-CN:
|
||||
// ! 我认为将字符串用单引号包裹起来并不是一个好主意.
|
||||
// ! PS: 我认为不应该翻译为 "我不认为...". :)
|
||||
// !
|
||||
// # ctx.stylize("'$content'", "string")
|
||||
|
||||
ctx.stylize(content, "string")
|
||||
}
|
||||
value.isJsNumber() -> {
|
||||
value is Number -> {
|
||||
ctx.stylize(value.toString(), "number")
|
||||
}
|
||||
value.isJsBoolean() -> {
|
||||
value is Boolean -> {
|
||||
ctx.stylize(Context.toString(value), "boolean")
|
||||
}
|
||||
else -> null
|
||||
|
||||
@@ -51,6 +51,7 @@ import org.mozilla.javascript.Wrapper
|
||||
import org.mozilla.javascript.json.JsonParser
|
||||
import java.io.Serializable
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.math.BigInteger
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.roundToLong
|
||||
@@ -66,6 +67,23 @@ object RhinoUtils {
|
||||
const val DEFAULT_CALLER = 0x03
|
||||
const val DEFAULT_CONSTRUCTOR = 0x04
|
||||
|
||||
/** [Long]: 2^53 - 1. */
|
||||
const val MAX_SAFE_INT_IEEE754_L = 9_007_199_254_740_991L
|
||||
/** [Long]: -(2^53 - 1). */
|
||||
const val MIN_SAFE_INT_IEEE754_L = -9_007_199_254_740_991L
|
||||
/** [Double]: 2^53 - 1. */
|
||||
const val MAX_SAFE_INT_IEEE754_D = 9_007_199_254_740_991.0
|
||||
/** [Double]: -(2^53 - 1). */
|
||||
const val MIN_SAFE_INT_IEEE754_D = -9_007_199_254_740_991.0
|
||||
/** [java.math.BigDecimal]: 2^53 - 1. */
|
||||
val MAX_SAFE_INT_IEEE754_BD = MAX_SAFE_INT_IEEE754_D.toBigDecimal()
|
||||
/** [java.math.BigDecimal]: -(2^53 - 1). */
|
||||
val MIN_SAFE_INT_IEEE754_BD = MIN_SAFE_INT_IEEE754_D.toBigDecimal()
|
||||
/** [java.math.BigInteger]: 2^53 - 1. */
|
||||
val MAX_SAFE_INT_IEEE754_BI = MAX_SAFE_INT_IEEE754_L.toBigInteger()
|
||||
/** [java.math.BigInteger]: -(2^53 - 1). */
|
||||
val MIN_SAFE_INT_IEEE754_BI = MIN_SAFE_INT_IEEE754_L.toBigInteger()
|
||||
|
||||
private val TAG = RhinoUtils::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
@@ -274,8 +292,8 @@ object RhinoUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun unwrap(o: Any?): Any? = when (o) {
|
||||
is String -> o
|
||||
is ConsString -> o.toString()
|
||||
is String, is ConsString -> Context.toString(o)
|
||||
is BigInteger -> o
|
||||
is Number -> Context.toNumber(o)
|
||||
is Boolean -> Context.toBoolean(o)
|
||||
is Wrapper -> unwrap(o.unwrap())
|
||||
|
||||
Reference in New Issue
Block a user