GraalJS 兼容性

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

ECMAScript 语言兼容性 #

GraalJS 实现 ECMAScript(ECMA-262)规范,与 ECMAScript 2024 规范(有时称为第 15 版)完全兼容。当新功能被确认包含在 ECMAScript 2024 中时,它们会经常被添加到 GraalVM 中,有关详细信息,请参阅 CHANGELOG.md。可以使用配置选项(按数字:--js.ecmascript-version=5 或按年份:--js.ecmascript-version=2024)启用从 ECMAScript 5 开始的旧版本。在生产环境中,您可能需要考虑指定要使用的固定 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

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

JavaScript 模块 #

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

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

兼容性扩展 #

以下对象和方法在 GraalJS 中可用,以与其他 JavaScript 引擎兼容。请注意,此类方法的行为可能并不严格地匹配所有现有引擎中这些方法的语义。

语言功能 #

条件捕获子句

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

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,但使用错误 I/O 流
  • 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)

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

结果以 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 数据结构(数组、列表)的浅层副本,作为 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)

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

多语言 #

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 定义)中执行的。

Source 可以是以下类型

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

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

与我们联系