ESC
科技 1 分钟阅读

微软2007年就砍掉了FoxPro,无论如何,FoxPro复活了

微软2007年停止开发Visual FoxPro后,FoxDev Studio让这门语言重获新生:从零编写的64位运行时,Rust编写并编译为WebAssembly的编译器与字节码解释器,React直接绘制的界面,突破2GB表大小限制,新增FoxScript支持lambda与内置HTTP服务器,旧.fll库通过32位桥接进程照常兼容。

来源:Hacker News

无论它曾与什么对话,如今依然能对话

你的应用所依赖的系统调用、自动化对象和老旧的插件库依然照常工作,那些没人愿意触碰的部分得以原封不动。

IDE运行在Visual FoxPro自带的示例项目上。每张图片都是真实运行会话。

真正搞垮老应用的往往是细微差异:一个数字打印出来宽出了一列,一个事件晚到了一瞬,一个错误码对不上号。所以这里的行为兼容是通过直接询问Visual FoxPro本身并匹配其答案来确定的,而不是靠查阅参考文档然后祈祷。

最终产出的是一个从零编写的运行时:启动迅速、自成一体,并且坦诚说明哪些角落尚未覆盖。

Visual FoxPro停留在版本9,也停留在32位。这是同一门语言,重建在一个自2007年以来从未冻结的基础之上。四个部分使之成为可能。

64位32位:屏幕上的表单——React,直接从下方对象树绘制,只重绘一个控件,而非整个表单;活跃的对象树——每个控件,连同其属性与事件THISFORM.lblGreeting.Caption = cMsg;虚拟机——你的代码,编译为字节码,运行于WebAssembly,让出请求,从不阻塞;宿主——进程内的文件、数据表、COM与64位库,SET LIBRARY TO,fllhost.exe——一个32位进程,承载你的.fll。

64位,端到端

32位带来的天花板消失了

Visual FoxPro是一个32位程序,而这决定了比表面看起来更多的事情。这就是为什么数据表止步于2GB,备注文件止步于2GB,为什么一份大报表会在内存充裕的机器上内存耗尽。这些限制是埋在文件处理代码中的有符号32位整数,而不是什么人做出的授权决定。

FoxDev Studio全面采用64位。每个文件偏移量都是64位,而且数据表根本不会被读入内存,因此过去会戛然而止的同一个.dbf文件可以继续扩展到数百GB。在依赖它之前需要知道一点:一旦表超过2GB,就无法再在Visual FoxPro中打开了。如果你仍在两边同时工作,这是一扇单向门。

Visual FoxPro:2,147,483,647字节——表止步于2 GB,最高位承载符号,所以是2 GB而不是4 GB;FoxDev Studio:9,223,372,036,854,775,807字节——偏移量永远不会成为瓶颈,每次读写的每个字节都以这种方式寻址。这能为一张表换来什么:下面每个方格都是2 GB,即一张Visual FoxPro表所能容纳的全部。2 GB,279个方格:一张FoxDev表,130字节记录,558 GB。按1 KB记录计算可达4.4 TB,相当于2,200个这样的方格。如今限制它的只有DBF文件头自身的记录数字段。

虚拟机

一个编译器,和一台为运行其产物而造的机器

Visual FoxPro当年把你的程序编译成p-code,并附带一个运行时来执行它。如今是同样的安排重做一遍:一个用Rust编写、编译为WebAssembly的编译器和字节码解释器,因此同一台机器可以在应用运行的任何地方执行你的代码。编辑器正是通过这个编译器来检查你输入的内容,所以编辑器标出的下划线与运行时拒绝的东西不可能出现分歧。

一个运行中的程序就是一个fiber(纤程)。当它需要来自外部世界的某些东西时(一个消息框、一个模态表单、下一条记录),它不会调用并阻塞;而是让出执行权,工作在机器离开调用栈期间完成,答案再被交回来。这就是为什么MESSAGEBOX()能让程序停下来却不冻结其背后的窗口,为什么READ EVENTS等待时不空转,为什么SetFocus能触发GotFocus,为什么Init能在表单还在构建时就运行——一切都按FoxPro一贯的顺序。

一个运行中的表单是一棵活跃的对象树,带有你所期望的各类属性,界面由React直接从这棵树绘制出来。每个对象只观察自己,因此THISFORM.lblGreeting.Caption = cMsg只重绘一个标签,而不是整个表单。在控件密集的屏幕上,这就是瞬时与迟滞的差别。

设计器编辑的也是同一棵树,只是提前一步。不存在需要与第一个模型保持同步的第二个模型——而这通常正是表单与其设计器开始各说各话的地方。

一个.fll是32位映像,而64位应用中的每个进程都是64位,因此应用内部没有任何东西能直接打开它。与其告诉你这不可能,SET LIBRARY TO会启动一个小的32位进程,其唯一职责就是承载你的库,运行时与它通信。这些调用是同步的,因为程序可能在表达式执行到一半时调用库,迟到的答案就不再是答案。它经过了真实库的检验:加密库、FoxTools,以及用微软自己的API示例构建的库。

64位的东西不需要这座桥:DECLARE ... DLL在同一进程内就能访问现代库,自动化对象的访问方式也与从前一样。老路依然畅通;只是它不再是唯一的路了。

你写下的每一行代码依然保有它原本的含义。FoxScript只在其上做加法:一种可以交给别的东西稍后运行的代码块,以及一种让已经了解你业务逻辑的代码直接响应Web请求的方式。

&& 你的旧插件库依然像从前一样加载
SET LIBRARY TO "vfpencryption71.fll" ADDITIVE

LOCAL oServer
oServer = FoxScript.Http.CreateServer()

oServer.Get("/api/v1/customers/:id", LAMBDA(req, res)
 LOCAL lnId
 lnId = VAL(req.Params("id"))

 SELECT * FROM customer WHERE cust_id = lnId INTO CURSOR c_cust

 IF RECCOUNT("c_cust") > 0
 res.Status(200).Json(FoxScript.Data.CursorToJson("c_cust"))
 ELSE
 res.Status(404).Json('{"error": "Not found"}')
 ENDIF

 USE IN c_cust
ENDLAMBDA)

oServer.Listen(8080)
READ EVENTS

上面每一行都运行在与你的表单相同的运行时上。查询、游标和库调用都是普通的FoxPro;lambda和服务器才是FoxScript新增的部分:不需要第二门语言,也不需要在旁边另起一个服务。关键字与HTTP API均有完整文档。