Graal встроил JavaScript в Java, как вызвать карту в списке / массив из Java? Является ли это возможным? - PullRequest
1 голос
/ 03 апреля 2019

Я играю с Graal за запуск javascript в качестве гостевого языка и хотел бы узнать, есть ли способ использовать функциональность javascript Array.map на объекте хоста (Java) или прокси. Ниже приведен демонстрационный код Kotlin, но он должен быть достаточно близок к коду Java.

fun main() {
    val context = Context.newBuilder().build()
    val javaOutputList = mutableListOf<Integer>()
    val javaList = listOf(2, 2, 3, 4, 5)
    val proxyJavaList = ProxyArray.fromList(javaList)

    context.polyglotBindings.apply {
        putMember("javaOutputList", javaOutputList)
        putMember("javaList", javaList)
        putMember("proxyJavaList", proxyJavaList)
    }

    val script = """
        var javaOutputList = Polyglot.import('javaOutputList');
        var javaList = Polyglot.import('javaList');
        var proxyJavaList = Polyglot.import('proxyJavaList');

        var abc = [1, 2, 3];
        abc.forEach(x => javaOutputList.add(x));      // WORKS

        //abc.map(x => x + 1)             // WORKS
        //javaList.map(x => x + 1)        // DOES NOT WORK (map not a method on list)
        proxyJavaList.map(x => x + 1)     // DOES NOT WORK (message not supported: INVOKE)
    """.trimIndent()

    val result = context.eval("js", script)

    val resultList = result.`as`(List::class.java)
    println("result: $resultList")
    println("javaOutputList: $javaOutputList")

}

Использование ProxyArray показалось мне наиболее перспективным, но я все еще не мог заставить его работать. Ожидается ли поддержка этой функции?

РЕДАКТИРОВАТЬ: с принятым ответом код работает, вот изменение для заинтересованных:

    val context = Context.newBuilder()
            //.allowExperimentalOptions(true)  // doesn't seem to be needed
            .option("js.experimental-foreign-object-prototype", "true")
            .build()

1 Ответ

4 голосов
/ 04 апреля 2019

Корень проблемы заключается в том, что у подобных объектам массивов, отличных от JavaScript, по умолчанию в цепочке прототипов нет Array.prototype. Таким образом, Array.prototype.map недоступен с использованием синтаксиса javaList.map / proxyJavaList.map.

Вы можете вызвать Array.prototype.map напрямую, например Array.prototype.map.call(javaList, x => x+1), или использовать экспериментальную опцию js.experimental-foreign-object-prototype=true (которую мы недавно добавили), которая добавляет Array.prototype в цепочку прототипов всех объектов, подобных массивам. javaList.map / proxyJavaList.map будет доступно тогда.

...