QML 的底层实现之 JavaScript 变量编码



  • QML 是基于 ECMA-262(JavaScript),通过 Qt 的机制进行拓展的高级编程语言。下面会简单讲讲 JavaScript 变量是如何在 C++ 中定义的。

    先看如下一段话:

    We use two different ways of encoding JS values. One for 32bit and one for 64bit systems.

    In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double NaN (actually -qNaN)
    is indicated by a number that has the top 13 bits set. The other values are usually set to 0 by the
    processor, and are thus free for us to store other data. We keep pointers in there for managed objects,
    and encode the other types using the free space given to use by the unused bits for NaN values. This also
    works for pointers on 64 bit systems, as they all currently only have 48 bits of addressable memory.

    On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value that
    will make the number a NaN. The Masks below are used for encoding the other types.

    On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will get encoded
    with the 13 highest bits all 0. We are now using special values for bits 14-17 to encode our values. These
    can be used, as the highest valid pointer on a 64 bit system is 2^48-1.

    If they are all 0, we have a pointer to a Managed object. If bit 14 is set we have an integer.
    This makes testing for pointers and numbers very fast (we have a number if any of the highest 14 bits is set).

    Bit 15-17 is then used to encode other immediates.

    大意如下:

    使用两种不同的方式对 JS 值进行编码,一种是 32 位,一种是 64 位。

    在这两种情况下,使用 8 字节(int64)保存一个值或者一个包装了非数字变量的变体。使用前 13 个 bit 来标定 -qNaN,其他位一般置为零以便使用。在后 51 位中可以保存被管理对象的指针或者其他值,这是适用 64 位操作系统,因为现有的 64 位操作系统,一般只是用 48 位(2^48-1)进行寻址,这意味着有多余的 16 位( 2^16+1 )是不会被使用的。

    在 32 位系统上,直接将双精度浮点数进行保存。高 32 位配合位遮罩保存不同的非数字变量,例如 int 和指针。

    在 64 位上,通过 0xffff8000 << 32 这个位遮罩存取浮点数,这样的效果是,不直接对双精度进行编码,最高的 13 位直接置为零。现在可以使用 14~17 位对其他值进行编码,在64位系统上的最高有效指针是2 ^ 48-1。

    如果最高的 14 位全为零,那么低 48 位保存的是一个指针,如果第 14 位置为 1,保存的便是一个整数。这让数字和指针在运算过程中变得十分快速。(最高的 14 位中,任意一位为 1,保存的便是整数)

    然后使用 15-17 位来编码其他立即数。

    总结一下,就是使用 int64 作为 JavaScript 运行的 var,这个 var 可以保存数字或者指针。

    代码在这里 qv4value_p.h



  • @qyvlik 厉害,都研究到v4引擎来了。那么,作为我们QML开发者,需要注意那些呢?想知道知道。👷



  • @jiangcaiyang123 这种应该是 tagged pointer 的原理。



  • @qyvlik 能具体介绍一下吗?好像还是不懂的样子…………


Log in to reply
 

最近的回复

  • 看了你的脚本,看来PowerShell是一种shell的方言,支持function和自定义的符号。和bash还不一样。
    我以前也写过Apple Script,看来大家都在shell语言上加上自己的特性啊。
    顺便了解到你们主要用Visual Studio开发Qt应用的。😁 😁

    read more
  • @wentaojia2014
    代码中的 {1} 是自动生成的,注意删掉

    read more
  • 脚本玩家路过一下😁
    我写了一段Powershell脚本,可以一键打开/关闭调试信息和代码优化。
    原理就是解析.sln文件(xml格式),找到关联的所有.vcproj文件(也是xml格式),并把其中的字段修改掉。

    看脚本

    # filename: DebugInfoSwitch.ps1 Add-Type -AssemblyName System.Xml.Linq # 获取绝对路径 function GetAbsolutePath { [OutputType([string])] param([string]$filePath) $aPath = Resolve-Path $filePath return Split-Path $aPath } # 解析sln文件,提取出project列表 function ParseSolution { [OutputType([string[]])] param([string]$solutionFile) $parttern = [regex]"^Project*" [string[]] $projs = @() Get-Content $solutionFile | Where-Object {$_ -match $parttern} | ForEach-Object { $items = $_.ToString().Split(',') if ($items[1].EndsWith("vcxproj`"")) { $projs += $items[1] } } return $projs } #Write-Host($debugGroup | Format-Table | Out-String ) function UpdateDebugInfo { param([System.Xml.Linq.XNamespace]$xNamespace,[System.Xml.Linq.XElement] $group, [bool]$isOpen) [System.Xml.Linq.XElement]$xClCompile = $group.Element($xNamespace + "ClCompile") [System.Xml.Linq.XElement]$xLink = $group.Element($xNamespace + "Link") [System.Xml.Linq.XElement]$format = $xClCompile.Element($xNamespace + "DebugInformationFormat") [System.Xml.Linq.XElement]$optimization = $xClCompile.Element($xNamespace + "Optimization") [System.Xml.Linq.XElement]$generate = $xLink.Element($xNamespace + "GenerateDebugInformation") if ($null -eq $format) { $format = [System.Xml.Linq.XElement]::new($xNamespace + "DebugInformationFormat") $xClCompile.Add($format) } if ($null -eq $optimization) { $optimization = [System.Xml.Linq.XElement]::new($xNamespace + "Optimization") $xClCompile.Add($optimization) } if ($null -eq $generate) { $generate = [System.Xml.Linq.XElement]::new($xNamespace + "GenerateDebugInformation") $xLink.Add($generate) } if ($isOpen) { $format.SetValue("ProgramDatabase"); $optimization.SetValue("Disabled"); $generate.SetValue("true"); } else { $format.SetValue("None"); $optimization.SetValue("MaxSpeed"); $generate.SetValue("false"); } } function SwitchDebugInfo { param([string]$projPath, [bool]$open, [bool]$isRelease) if (Test-Path($projPath)) { [System.Xml.Linq.XDocument]$xDoc = [System.Xml.Linq.XDocument]::Load($projPath) [System.Xml.Linq.XNamespace]$xNamespace = $xDoc.Root.GetDefaultNamespace(); [System.Xml.Linq.XElement]$group = $null $groups = $xDoc.Root.Elements($xNamespace + "ItemDefinitionGroup") foreach ($i in $groups) { if ($isRelease) { if ($i.Attribute("Condition").Value -match "^*Release*") { $group = $i; break; } } else { if ($i.Attribute("Condition").Value -match "^*Debug*") { $group = $i; break; } } } UpdateDebugInfo $xNamespace $group $open $xDoc.Save($projPath) } } #脚本入口函数 function Main { param([string]$sln, [bool]$open, [bool]$isRelease) Write-Host("sln file ", $sln) $slnPath= GetAbsolutePath $sln $projList = ParseSolution $sln $t = $projList.Split(' ') | ForEach-Object { $s = $_.ToString() if ($s) { $projPath=-Join($slnPath, '\', ($s.SubString(1, $s.Length - 2) )) SwitchDebugInfo $projPath $open $isRelease } } } function Usage { Write-Host ("Usage: DebugInfoSwitch.ps1 slnFilePath mode[D/d for Debug, R/r for Release] open[Y/y for open, N/n for close]") Write-Host ("Example for open Release mode debugInfo: DebugInfoSwitch.ps1 xxx.sln R y") Write-Host ("Example for close Debug mode debugInfo: DebugInfoSwitch.ps1 xxx.sln d N") } #检查参数 [string]$sln = "" [bool]$open = $true [bool]$isRelease = $false if ( $args.Count -ne 3) { Usage return } $sln = $args[0] if ($args[1] -eq "D" -or $args[1] -eq "d") { $isRelease = $false } elseif ($args[1] -eq "R" -or $args[1] -eq "r") { $isRelease = $true } else { Usage return } if ($args[2] -eq "Y" -or $args[2] -eq "y") { $open = $true } elseif ($args[2] -eq "N" -or $args[2] -eq "n") { $open = $false } else { Usage return } #调用主函数 Main $sln $open $isRelease

    用的时候,再写个bat脚本,调用这个ps1并传参数就行了。
    比如打开Release模式的调试信息、同时关掉优化开关

    @echo off powershell.exe -NoProfile -ExecutionPolicy Bypass -File DebugInfoSwitch.ps1 ./you/path/to.sln R N

    关闭调试信息、打开优化开关,则是传相反的参数

    @echo off powershell.exe -NoProfile -ExecutionPolicy Bypass -File DebugInfoSwitch.ps1 ./you/path/to.sln R Y

    read more
  • 我将USD在Linux中依赖的文件和脚本放在了我创建的QQ群里,我们的QQ群是“上海USD研究小组”。加入本小组,可以快速地在USD中上手解决编译问题,以及快速得到同行的响应。
    上海USD研究小组

    read more

关注我们

微博
QQ群