GraalVM - Использование значения Polyglot без контекста

Я пишу приложение поверх Graal, которое сможет выполнять небольшие скрипты на разных языках.

Я пытаюсь написать некоторые модульные тесты для класса, который я использую для преобразования / обработки результата вызова Context.eval() (type: Value) в объект Java. Из документации я знаю, что экземпляр Value всегда связан с Context, поэтому, когда я пытаюсь написать что-то вроде этого:

@Test
public void NumericFloatTest() throws ScriptExecutionException {

    GuestLanguageResultProcessor LangProcessor = new GuestLanguageResultProcessor();
    Float javaValue = (float) 43.25;
    Value numValue = Value.asValue(javaValue);
    LangProcessor.processResult(numValue);

    Object result = LangProcessor.processResult(numValue);

    assertThat(result.getClass()).isEqualTo(Float.class);
}

Я получаю следующую ошибку:

java.lang.IllegalStateException: No current context is available. Make sure the Java method is invoked by a Graal guest language or a context is entered using Context.enter().

Я предполагаю, что концептуально не имеет смысла иметь экземпляр "Value" без связанного бита гостевого кода, поэтому мой вопрос:

Как я могу протестировать мой класс GuestLanguageResultProcessor? Нужно ли "раздувать" мой модульный тест с созданием контекста?

Дополнительный вопрос для экспертов: я использую этот класс (GuestLanguageResultProcessor) также для извлечения значения Java из экземпляра значения Polyglot, чтобы я мог закрыть контекст. Другими словами, мне кажется, что прежде чем я смогу сделать Context.close(), мне нужно вызвать [value instance].asString() или.asWhwhat (), чтобы получить результат и иметь возможность закрыть контекст без получить IllegalStateException, как сказано в документации.

Я делаю это правильно? Есть ли лучший способ обработать получение результата и безопасно закрыть контекст?

Спасибо!

1 ответ

Решение

Как я могу протестировать мой класс GuestLanguageResultProcessor? Нужно ли "раздувать" мой модульный тест с созданием контекста?

Я боюсь, что вздутие живота необходимо. Я бы рекомендовал использовать следующий код, чтобы ваш тест работал. Это также может быть сделано в тестовом базовом классе, чтобы избежать повторения.

Context context;

@Before
public void setup() {
    context = Context.create();
    context.enter();
}

@After
public void setup() {
    context.leave();
    context.close();
}

@Test
public void NumericFloatTest() throws ScriptExecutionException {
    GuestLanguageResultProcessor LangProcessor = new GuestLanguageResultProcessor();
    Float javaValue = (float) 43.25;
    Value numValue = Value.asValue(javaValue);
    LangProcessor.processResult(numValue);

    Object result = LangProcessor.processResult(numValue);

    assertThat(result.getClass()).isEqualTo(Float.class);
}

Я делаю это правильно? Есть ли лучший способ обработать получение результата и безопасно закрыть контекст?

Экземпляры значений могут быть связаны с объектами гостевого языка, такими как объекты JavaScript, которые становятся недействительными, как только их контекст закрывается. Не всегда возможно преобразовать объекты гостевого языка в постоянное представление Java. Например, значения полиглотов могут относиться ко всему графу объектов JavaScript.

Если возможно, я бы рекомендовал держать контекст открытым, пока нужны значения, так как он не требует каких-либо преобразований.

Если это невозможно и вы имеете дело только с примитивами и массивами, вы можете попробовать использовать следующий метод. Вы также можете попытаться скопировать объекты в землю Java, получив доступ к ее членам.

Object copyToJavaLand(Value value) {
    if (value.isBoolean()) {
        return value.asBoolean();
    } else if (value.isString()) {
        return value.asString();
    } else if (value.isNumber()) {
        return value.as(Number.class);
    } else if (value.isHostObject()) {
        return value.asHostObject();
    } else if (value.isProxyObject()) {
        return value.asProxyObject();
    } else if (value.hasArrayElements()) {
        Object[] array = new Object[(int) value.getArraySize()];
        for (int i = 0; i < array.length; i++) {
            array[i] = copyToJavaLand(value.getArrayElement(i));
        }
        return array;
    }
    throw new IllegalArgumentException("Cannot copy value " + value + ".");
}

Обратите внимание, что этот метод не всегда безопасен. Например, если массивы ссылаются на себя, этот метод завершится с ошибкой переполнения стека.

Другие вопросы по тегам