GraalJS 兼容性

GraalJS 是一个符合 ECMAScript 标准的 JavaScript 语言运行时。本文档解释了它为用 JavaScript 编写的用户应用程序提供的公共 API。

ECMAScript 语言合规性 #

GraalJS 实现了 ECMAScript (ECMA-262) 规范,并完全兼容 ECMAScript 2024 规范(有时称为第15版)。当新功能被确认为 ECMAScript 2024 的一部分时,会频繁地添加到 GraalVM 中,详情请参阅 CHANGELOG.md。从 ECMAScript 5 开始的旧版本可以通过配置选项启用(按数字:--js.ecmascript-version=5 或按年份:--js.ecmascript-version=2024)。在生产环境中,您可能需要考虑指定一个固定的 ECMAScript 版本,因为未来版本的 GraalJS 一旦可用将使用更新的规范版本。

GraalJS 在全局作用域中提供了 ECMAScript 规范定义的以下函数对象,它们代表 JavaScript 核心库:Array、ArrayBuffer、Boolean、DataView、Date、Error、Function、JSON、Map、Math、Number、Object、Promise、Proxy、Reflect、RegExp、Set、SharedArrayBuffer、String、Symbol、TypedArray、WeakMap 和 WeakSet。

其他对象可通过选项获取,例如 --js.temporal。运行 js --help 可查看可用选项列表。

其中一些函数对象及其部分成员仅在选择特定规范版本执行时可用。有关所提供方法的列表,请查阅 ECMAScript 规范。对规范的扩展如下所述。

国际化 API (ECMA-402) #

GraalJS 附带了 ECMA-402 国际化 API 的实现,默认启用(可以使用以下选项禁用:--js.intl-402=false)。这包括以下扩展:

  • Intl.Collator
  • Intl.DateTimeFormat
  • Intl.DisplayNames
  • Intl.ListFormat
  • Intl.Locale
  • Intl.NumberFormat
  • Intl.PluralRules
  • Intl.RelativeTimeFormat
  • Intl.Segmenter

根据 ECMA-402 规范,一些其他内置函数(例如 toLocaleString)的功能也得到了更新。

JavaScript 模块 #

GraalJS 支持 ECMAScript 6 及更高版本定义的模块。请注意,对此功能的支持仍在不断增强。请务必使用最新的 ECMAScript 版本以获取所有最新功能。

通过多语言 Source 加载模块时,您可以使用非官方的 application/javascript+module MIME 类型来指定您正在加载一个模块。从文件加载 JavaScript 代码时,请确保模块是从 .mjs 扩展名的文件中加载的。使用 import 关键字加载不受此限制,可以从任何扩展名的文件 import

兼容性扩展 #

GraalJS 中提供了以下对象和方法,以兼容其他 JavaScript 引擎。请注意,这些方法的行为可能不完全符合所有现有引擎中这些方法的语义。

语言特性 #

条件 Catch 子句

如果启用了 js.syntax-extensions 选项,GraalJS 支持条件 catch 子句。

try {
    myMethod(); // can throw
} catch (e if e instanceof TypeError) {
    print("TypeError caught");
} catch (e) {
    print("another Error caught");
}

全局属性 #

load(source)

  • 加载(解析并执行)指定的 JavaScript 源代码

Source 可以是以下类型:

  • 字符串:源文件的路径或要执行的 URL。
  • java.lang.URL:如果 js.load-from-url 选项设置为 true,则查询该 URL 以获取要执行的源代码。
  • java.io.File:读取该文件以获取要执行的源代码。
  • JavaScript 对象:查询该对象以获取 namescript 属性,它们分别代表源名称和代码。
  • 所有其他类型:源被转换为字符串。

load 默认可用,通过将 js.load 选项设置为 false 可以停用。

print(...arg)printErr(...arg)

  • 在控制台上打印参数(分别是 stdoutstderr
  • 提供尽力而为的人类可读输出

printprintErr 默认可用,通过将 js.print 选项设置为 false 可以停用。

console 全局对象的方法

提供了一个全局 console 对象,它提供多种用于调试目的的方法。这些方法力求提供与其他引擎类似的功能,但不保证结果完全相同。

请注意,当 GraalJS 在 Node.js 模式下执行时(例如,启动 node 可执行文件而不是 js),这些方法的行为会有所不同。Node.js 提供了自己的实现,并会替代使用。

  • console.logconsole.infoconsole.debugprint(...arg) 的别名
  • console.errorconsole.warn:类似于 print,但使用错误 IO 流
  • console.assert(check, message):当 check 为假值时打印 message
  • console.clear:如果可能,清除控制台窗口
  • console.count()console.countReset():计数并打印它被调用的次数,或重置此计数器
  • console.groupconsole.groupEnd:增加或减少后续控制台输出的缩进
  • console.time()console.timeLog()console.timeEnd():分别启动计时器,打印计时器已活动的时长,或打印时长并停止计时器。

console 对象默认可用,通过将选项 js.console 设置为 false 可以停用。

js Shell 中的附加全局函数 #

quit(status)

  • 退出引擎并返回指定的退出代码

read(file)

  • 读取 file 的内容

结果以字符串形式返回。

参数 file 可以是以下类型:

  • java.io.File:直接使用该文件。
  • 所有其他类型:file 被转换为字符串并解释为文件名。

readbuffer(file)

  • 读取 file 的内容,类似于 read 函数

结果以 JavaScript ArrayBuffer 对象形式返回。

readline()

  • 从输入流中读取一行输入

结果以字符串形式返回。

Object #

Object.prototype.__defineGetter__(prop, func)

  • thisprop 属性定义为 getter 函数 func

此功能在大多数 JavaScript 引擎中已被弃用。在最新的 ECMAScript 版本中,getter 和 setter 已被语言原生支持。

Object.prototype.__defineSetter__(prop, func)

  • thisprop 属性定义为 setter 函数 func

此功能在大多数 JavaScript 引擎中已被弃用。在最新的 ECMAScript 版本中,getter 和 setter 已被语言原生支持。

Object.prototype.__lookupGetter__(prop)

  • 返回由 __defineGetter__ 设置的对象属性 prop 的 getter 函数

此功能在大多数 JavaScript 引擎中已被弃用。在最新的 ECMAScript 版本中,getter 和 setter 已被语言原生支持。

Object.prototype.__lookupSetter__(prop)

  • 返回由 __defineSetter__ 设置的对象属性 prop 的 setter 函数

此功能在大多数 JavaScript 引擎中已被弃用。在最新的 ECMAScript 版本中,getter 和 setter 已被语言原生支持。

Nashorn 脚本模式 #

GraalJS 提供与 Nashorn 引擎兼容的脚本模式。通过 js.scripting 选项启用。请确保设置了 --experimental-options

js --experimental-options --js.scripting=true

在脚本模式下,全局对象中会添加多个属性和函数,包括 readFullyreadLine$ARG$ENV$EXEC

对于之前面向 NashornRhino 引擎的代码,有可用的迁移指南。

GraalJS 扩展 #

Graal 对象 #

Graal 对象作为全局对象的属性提供。它提供特定于 Graal 的信息。该属性的存在可以用来识别 GraalJS 是否是当前的语言引擎。

if (typeof Graal != 'undefined') {
    print(Graal.versionECMAScript);
    print(Graal.versionGraalVM);
    print(Graal.isGraalRuntime());
}

Graal 对象在 GraalJS 中默认可用,除非通过选项(js.graal-builtin=false)停用。

Graal.versionECMAScript

  • 提供 GraalJS ECMAScript 兼容模式的版本号(年份值)

Graal.versionGraalVM

  • 如果当前引擎在 GraalVM 上执行,则提供 GraalVM 的版本

Graal.isGraalRuntime()

  • 指示 GraalJS 是否在启用了 GraalVM 的运行时上执行
  • 如果为 true,热点代码将由 Graal 编译器编译,从而实现高吞吐性能。
  • 如果为 false,GraalJS 将不会被 Graal 编译器优化,通常会导致性能降低。

Graal.setUnhandledPromiseRejectionHandler(handler) #

  • 在使用选项(js.unhandled-rejections=handler)时提供未处理的 Promise 拒绝处理程序。
  • 处理程序会带两个参数被调用:(rejectionReason, unhandledPromise)。
  • Graal.setUnhandledPromiseRejectionHandler 可以通过传入 nullundefined 或空参数来清除处理程序。

Java #

Java 对象仅在允许 宿主类查找 时可用。要访问 Java 宿主类及其成员,它们首先需要通过 宿主访问策略 允许,并且在从原生可执行文件运行时,需要注册 运行时反射

请注意,某些功能需要设置 Nashorn 兼容模式(--js.nashorn-compat=true)。

Java.type(className)

Java.type 加载指定的 Java 类并返回一个可构造对象,该对象具有该类的静态成员(例如,方法和字段),并且可以与 new 关键字一起用于构造新实例。

var BigDecimal = Java.type('java.math.BigDecimal');
var point1 = new BigDecimal("0.1");
var two = BigDecimal.TWO;
console.log(point1.multiply(two).toString());

请注意,当直接与 new 运算符一起使用时,Java.type(...) 需要用括号括起来。

console.log(new (Java.type('java.math.BigDecimal'))("1.1").pow(15));

Java.from(javaData)

Java.from 将 Java 数据结构(Array、List)浅复制为 JavaScript 数组。

在许多情况下,这不是必需的;通常可以直接从 JavaScript 中使用 Java 数据结构。

Java.to(jsData, javaType)

Java.to 将参数转换为 Java 类型。

源对象 jsData 预计是 JavaScript 数组,或具有 length 属性的类数组对象。目标 javaType 可以是字符串(例如,"int[]")或类型对象(例如 Java.type("int[]"))。有效的目标类型是 Java 数组。当目标类型省略时,默认为 Object[]

var jsArray = ["a", "b", "c"];
var stringArrayType = Java.type("java.lang.String[]");
var javaArray = Java.to(jsArray, stringArrayType);
assertEquals('class java.lang.String[]', String(javaArray.getClass()));
var javaArray = Java.to(jsArray);
assertEquals('class java.lang.Object[]', String(javaArray.getClass()));

当 JavaScript 值必须转换为 Java 类型时,会执行 ECMAScript 定义的转换方法(例如 ToStringToDouble)。有损转换是不允许的,会导致 TypeError

Java.isJavaObject(obj)

  • 如果 obj 是一个 Java 宿主对象,则返回 true
  • 对于原生 JavaScript 对象以及其他多语言的对象,返回 false

Java.isType(obj)

  • 如果 obj 是一个表示 Java 类构造函数和静态成员的对象(通过 Java.type() 或包对象获得),则返回 true
  • 对于所有其他参数,返回 false

Java.typeName(obj)

  • obj 表示 Java 类型(isType(obj) === true)或 Java Class 实例时,返回 obj 的 Java Class 名称
  • 否则返回 undefined

Java.isJavaFunction(fn)

  • 返回 fn 是否是一个表示 Java 函数的 Java 语言对象
  • 对于所有其他类型,包括原生 JavaScript 函数以及其他多语言的函数,返回 false

此函数仅在 Nashorn 兼容模式下可用(--js.nashorn-compat=true)。

Java.isScriptObject(obj)

  • 返回 obj 是否是 JavaScript 语言的对象
  • 对于所有其他类型,包括 Java 和其他多语言的对象,返回 false

此函数仅在 Nashorn 兼容模式下可用(--js.nashorn-compat=true)。

Java.isScriptFunction(fn)

  • 返回 fn 是否是 JavaScript 函数
  • 对于所有其他类型,包括 Java 函数以及其他多语言的函数,返回 false

此函数仅在 Nashorn 兼容模式下可用(--js.nashorn-compat=true)。

Java.addToClasspath(location)

  • 将指定位置(.jar 文件或目录路径字符串)添加到 Java 的 classpath 中

多语言 #

Polyglot 对象的功能允许与来自其他多语言的值进行交互。

Polyglot 对象默认可用,除非通过将 js.polyglot-builtin 选项设置为 false 来停用。

Polyglot.export(key, value)

  • 将 JavaScript valuekey(字符串)名称导出到多语言绑定中
    function helloWorld() { print("Hello, JavaScript world"); }
    Polyglot.export("helloJSWorld", helloWorld);
    

如果多语言绑定中已存在由 key 标识的值,则会被新值覆盖。value 可以是任何有效的 Polyglot 值。

  • 如果 key 不是字符串或缺失,则抛出 TypeError

Polyglot.import(key)

  • 从多语言绑定中导入由 key(字符串)标识的值并返回
    var rubyHelloWorld = Polyglot.import("helloRubyWorld");
    rubyHelloWorld();
    

如果没有语言导出由 key 标识的值,则返回 undefined

  • 如果 key 不是字符串或缺失,则抛出 TypeError

Polyglot.eval(languageId, sourceCode)

  • 使用由 languageId 标识的解释器解析并评估 sourceCode

sourceCode 的值预计为字符串(或可转换为字符串的类型)。

  • 返回评估结果,具体取决于 sourceCode 和/或所评估语言的语义
    var pyArray = Polyglot.eval('python', 'import random; [random.uniform(0.0, 1.0) for _ in range(1000)]');
    

当传入无效的 languageId、语言无法评估 sourceCode 或执行的程序抛出异常时,可能会发生异常。

Polyglot.evalFile(languageId, sourceFileName)

  • 使用由 languageId 标识的解释器解析文件 sourceFileName

sourceFileName 的值预计为字符串(或可转换为字符串的类型),表示当前路径可访问的文件。

  • 返回一个可执行对象,通常是一个函数
    var rFunc = Polyglot.evalFile('R', 'myExample.r');
    var result = rFunc();
    

当传入无效的 languageId、找不到由 sourceFileName 标识的文件,或语言在解析期间抛出异常(例如,语法错误等解析时错误)时,可能会发生异常。评估程序抛出的异常只会在结果函数被评估时才会被抛出。

Polyglot 内置功能可用时,Polyglot.evalFile 函数默认可用,除非通过将 js.polyglot-evalfile 选项设置为 false 来停用。当 js.debug-builtin 被激活时,它也可用。

调试 #

  • 需要使用 js.debug-builtin 选项启动引擎

Debug 是一个 GraalJS 特定的函数对象,它为调试 JavaScript 代码和 JavaScript 引擎提供功能。此 API 可能会在不另行通知的情况下更改。请勿用于生产目的。

全局函数 #

printErr(...arg)

  • 行为与 print 完全相同

唯一的区别是使用错误流进行打印,而不是默认输出流。

loadWithNewGlobal(source, arguments)

  • 行为类似于 load 函数

相关的区别在于代码是在一个新的全局作用域(根据 ECMAScript 定义的 Realm)中进行评估的。

Source 可以是以下类型:

  • java.lang.URL:查询该 URL 以获取要执行的源代码。
  • JavaScript 对象:查询该对象以获取 namescript 属性。
  • 所有其他类型:源被转换为字符串。

arguments 的值在执行时提供给加载的代码。

联系我们