From 95e4c2e7b48eb4d928ec9c90f3a046f13112451b Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Mon, 8 Sep 2025 15:58:08 +0200 Subject: [PATCH 01/14] Adding hasHostObject and getHostObject interop messages. --- .../src/org/graalvm/polyglot/Value.java | 3 +- .../polyglot/impl/AbstractPolyglotImpl.java | 10 -- .../api/exception/ExceptionAccessor.java | 15 ++- .../api/interop/HeapIsolationException.java | 110 ++++++++++++++++++ .../truffle/api/interop/InteropLibrary.java | 83 +++++++++++++ .../api/test/host/CallerSensitiveTest.java | 8 +- .../test/host/FunctionalInterfaceTest.java | 4 +- .../api/test/host/HostAdapterTest.java | 34 +++--- .../api/test/host/HostExceptionTest.java | 32 +++-- .../api/test/host/HostInteropErrorTest.java | 4 +- .../api/test/host/ProxyLanguageEnvTest.java | 13 ++- .../api/test/host/TestMemberAccess.java | 2 +- .../test/interop/InteropAssertionsTest.java | 85 ++++++++++++++ .../polyglot/ContextBuilderExtendAPITest.java | 2 +- .../api/test/polyglot/FileSystemsTest.java | 6 +- .../test/polyglot/HostClassLoadingTest.java | 9 +- .../polyglot/LanguageSPIHostInteropTest.java | 52 +++++---- .../api/test/polyglot/ProxySPITest.java | 4 +- .../api/test/polyglot/ValueAPITest.java | 12 +- .../wrapper/GuestToHostLanguageService.java | 25 ---- .../api/test/wrapper/HostEntryPoint.java | 18 ++- .../oracle/truffle/api/TruffleLanguage.java | 23 +++- .../oracle/truffle/api/TruffleStackTrace.java | 15 +-- .../com/oracle/truffle/api/impl/Accessor.java | 8 +- .../com/oracle/truffle/host/HostAccessor.java | 8 +- .../com/oracle/truffle/host/HostFunction.java | 1 - .../truffle/host/HostLanguageService.java | 32 ----- .../oracle/truffle/host/HostMethodScope.java | 3 +- .../com/oracle/truffle/host/HostObject.java | 54 ++++----- .../oracle/truffle/host/HostToTypeNode.java | 34 +++++- .../backend/libffi/SerializeArgumentNode.java | 21 +++- .../polyglot/DefaultPolyglotHostService.java | 8 +- .../truffle/polyglot/EngineAccessor.java | 48 ++++---- .../polyglot/OtherContextGuestObject.java | 9 +- .../polyglot/PolyglotExceptionImpl.java | 50 +++++--- .../polyglot/PolyglotValueDispatch.java | 86 +++++++++++--- .../tstring/TStringBenchDummyLanguage.java | 57 ++++++--- 37 files changed, 688 insertions(+), 300 deletions(-) create mode 100644 truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java index 8c2257677b82..3b9d209bcf4a 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java @@ -1586,7 +1586,8 @@ public boolean isHostObject() { /** * Returns the original Java host language object. * - * @throws UnsupportedOperationException if {@link #isHostObject()} is false. + * @throws UnsupportedOperationException if {@link #isHostObject()} is false or the + * Java host language object is allocated in a foreign heap. * @throws PolyglotException if a guest language error occurred during execution. * @throws IllegalStateException if the underlying context was closed. * @since 19.0 diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java index 322d5bb09c4c..08ba3efec38b 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java @@ -1036,30 +1036,20 @@ public abstract void initializeHostContext(Object internalContext, Object contex public abstract boolean isHostValue(Object value); - public abstract Object unboxHostObject(Object hostValue); - public abstract Object unboxProxyObject(Object hostValue); - public abstract Throwable unboxHostException(Throwable hostValue); - public abstract Object toHostObject(Object context, Object value); public abstract RuntimeException toHostException(Object hostContext, Throwable exception); - public abstract boolean isHostException(Object exception); - public abstract boolean isHostFunction(Object obj); - public abstract boolean isHostObject(Object obj); - public abstract boolean isHostSymbol(Object obj); public abstract Object createHostAdapter(Object hostContextObject, Object[] types, Object classOverrides); public abstract boolean isHostProxy(Object value); - public abstract Error toHostResourceError(Throwable hostException); - public abstract int findNextGuestToHostStackTraceElement(StackTraceElement firstElement, StackTraceElement[] hostStack, int nextElementIndex); public abstract Object migrateValue(Object hostContext, Object value, Object valueContext); diff --git a/truffle/src/com.oracle.truffle.api.exception/src/com/oracle/truffle/api/exception/ExceptionAccessor.java b/truffle/src/com.oracle.truffle.api.exception/src/com/oracle/truffle/api/exception/ExceptionAccessor.java index 0c28307edf54..0dc3cfab9b84 100644 --- a/truffle/src/com.oracle.truffle.api.exception/src/com/oracle/truffle/api/exception/ExceptionAccessor.java +++ b/truffle/src/com.oracle.truffle.api.exception/src/com/oracle/truffle/api/exception/ExceptionAccessor.java @@ -47,7 +47,7 @@ import java.util.ListIterator; import java.util.function.Function; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractHostLanguageService; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleStackTrace; @@ -183,9 +183,8 @@ public Object getExceptionStackTrace(Object receiver, Object polyglotContext) { private static Object[] mergeHostGuestFrames(Throwable throwable, List guestStack, boolean inHost, Object polyglotEngine) { StackTraceElement[] hostStack = null; - AbstractHostLanguageService hostService = ACCESSOR.engineSupport().getHostService(polyglotEngine); - if (hostService.isHostException(throwable)) { - Throwable original = hostService.unboxHostException(throwable); + if (ACCESSOR.engineSupport().isHostException(throwable)) { + Throwable original = unboxHostException(throwable); hostStack = original.getStackTrace(); } else if (throwable instanceof AbstractTruffleException) { Throwable lazyStackTrace = ((AbstractTruffleException) throwable).getLazyStackTrace(); @@ -246,6 +245,14 @@ public Object apply(TruffleStackTraceElement element) { return elementsList.toArray(); } + private static Throwable unboxHostException(Throwable throwable) { + try { + return ACCESSOR.engineSupport().asHostException(throwable); + } catch (Exception e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + private static int indexOfLastGuestToHostFrame(List guestStack) { for (var iterator = guestStack.listIterator(guestStack.size()); iterator.hasPrevious();) { int index = iterator.previousIndex(); diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java new file mode 100644 index 000000000000..f8fb9763c411 --- /dev/null +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.api.interop; + +import com.oracle.truffle.api.CompilerDirectives; + +/** + * Exception thrown when a {@link TruffleObject} cannot unbox a host object because the object + * resides in a foreign heap. + * + * @since 26.0 + */ +@SuppressWarnings("serial") +public final class HeapIsolationException extends InteropException { + + private HeapIsolationException() { + super(null); + } + + private HeapIsolationException(Throwable cause) { + super(null, cause); + } + + /** + * {@inheritDoc} + * + * @since 26.0 + */ + @Override + public String getMessage() { + return "Host object in a foreign heap cannot be unboxed."; + } + + /** + * Creates a new {@link HeapIsolationException} indicating that a host object cannot be unboxed + * because it was allocated in a foreign heap. For example, when + * {@link InteropLibrary#getHostObject(Object)} is invoked from within a polyglot isolate. + *

+ * This factory method is intended for use in {@link CompilerDirectives#inCompiledCode() + * compiled code paths}. + * + * @since 26.0 + */ + public static HeapIsolationException create() { + return new HeapIsolationException(); + } + + /** + * Creates a new {@link HeapIsolationException} indicating that a host object cannot be unboxed + * because it was allocated in a foreign heap. For example, when + * {@link InteropLibrary#getHostObject(Object)} is invoked from within a polyglot isolate. + *

+ * In addition, a cause may be provided. The cause should only be set if the guest language code + * caused this problem. An example for this is a language specific proxy mechanism that invokes + * guest language code to describe an object. If the guest language code fails to execute and + * this interop exception is a valid interpretation of the error, then the error should be + * provided as cause. The cause can then be used by the source language as new exception cause + * if the {@link InteropException} is translated to a source language error. If the + * {@link InteropException} is discarded, then the cause will most likely get discarded by the + * source language as well. Note that the cause must be of type + * {@link com.oracle.truffle.api.exception.AbstractTruffleException} otherwise an + * {@link IllegalArgumentException} is thrown. + *

+ * This factory method is intended for use in {@link CompilerDirectives#inCompiledCode() + * compiled code paths}. + * + * @since 26.0 + */ + public static HeapIsolationException create(Throwable cause) { + return new HeapIsolationException(cause); + } +} diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java index 9d3fe707d143..fb4a4f376ea6 100644 --- a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java @@ -3008,6 +3008,39 @@ public Object getScopeParent(Object receiver) throws UnsupportedMessageException throw UnsupportedMessageException.create(); } + /** + * Returns {@code true} if the argument is wrapped Java host language object. This method must + * not cause any observable side-effects. If this method is implemented then also + * {@link #getHostObject(Object)} must be implemented. + * + * @see #getHostObject(Object) + * @since 26.0 + */ + @Abstract(ifExported = "getHostObject") + public boolean hasHostObject(Object receiver) { + return false; + } + + /** + * Returns the Java host object representation of the given Truffle guest object. + *

+ * Implementations of this method must not produce any observable side effects. If this method + * is implemented, {@link #hasHostObject(Object)} must also be implemented and return + * {@code true} for the same receiver. + * + * @throws UnsupportedMessageException if {@link #hasHostObject(Object)} returns {@code false} + * for the given receiver. + * @throws HeapIsolationException if the guest object represents a host object located in a + * foreign heap, for example in a polyglot isolate. + * + * @see #hasHostObject(Object) + * @since 26.0 + */ + @Abstract(ifExported = "hasHostObject") + public Object getHostObject(Object receiver) throws UnsupportedMessageException, HeapIsolationException { + throw UnsupportedMessageException.create(); + } + /** * Returns the library factory for the interop library. Short-cut for * {@link LibraryFactory#resolve(Class) ResolvedLibrary.resolve(InteropLibrary.class)}. @@ -5517,6 +5550,36 @@ private boolean assertHasNoLanguageId(Object receiver) { return true; } + @Override + public boolean hasHostObject(Object receiver) { + if (CompilerDirectives.inCompiledCode()) { + return delegate.hasHostObject(receiver); + } + assert preCondition(receiver); + boolean result = delegate.hasHostObject(receiver); + if (result) { + try { + delegate.getHostObject(receiver); + } catch (InteropException e) { + assert e instanceof HeapIsolationException : violationInvariant(receiver); + } catch (Exception e) { + } + } else { + assert assertHasNoHostObject(receiver); + } + assert validProtocolReturn(receiver, result); + return result; + } + + private boolean assertHasNoHostObject(Object receiver) { + try { + delegate.getHostObject(receiver); + assert false : violationInvariant(receiver); + } catch (UnsupportedMessageException | HeapIsolationException e) { + } + return true; + } + @Override public String getLanguageId(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { @@ -5535,6 +5598,26 @@ public String getLanguageId(Object receiver) throws UnsupportedMessageException throw e; } } + + @Override + public Object getHostObject(Object receiver) throws HeapIsolationException, UnsupportedMessageException { + if (CompilerDirectives.inCompiledCode()) { + return delegate.getHostObject(receiver); + } + assert preCondition(receiver); + boolean wasHasHostObject = delegate.hasHostObject(receiver); + try { + Object result = delegate.getHostObject(receiver); + assert wasHasHostObject : violationInvariant(receiver); + return result; + } catch (UnsupportedMessageException e) { + assert !wasHasHostObject : violationInvariant(receiver); + throw e; + } catch (InteropException e) { + assert e instanceof HeapIsolationException : violationInvariant(receiver); + throw e; + } + } } } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/CallerSensitiveTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/CallerSensitiveTest.java index ea00b9fc966a..e51716e2d95e 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/CallerSensitiveTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/CallerSensitiveTest.java @@ -69,13 +69,13 @@ public void testLogger() throws InteropException { TruffleObject getLogger = (TruffleObject) INTEROP.readMember(loggerClass, "getLogger"); logger = (TruffleObject) INTEROP.execute(getLogger, loggerName); - assertTrue(env.isHostObject(logger)); - assertTrue(env.asHostObject(logger) instanceof Logger); + assertTrue(INTEROP.hasHostObject(logger)); + assertTrue(INTEROP.getHostObject(logger) instanceof Logger); assertEquals(loggerName, asJavaObject(Logger.class, logger).getName()); logger = (TruffleObject) INTEROP.invokeMember(loggerClass, "getLogger", loggerName); - assertTrue(env.isHostObject(logger)); - assertTrue(env.asHostObject(logger) instanceof Logger); + assertTrue(INTEROP.hasHostObject(logger)); + assertTrue(INTEROP.getHostObject(logger) instanceof Logger); assertEquals(loggerName, asJavaObject(Logger.class, logger).getName()); } } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/FunctionalInterfaceTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/FunctionalInterfaceTest.java index 1e1cbb03eea0..fb6454c70f41 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/FunctionalInterfaceTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/FunctionalInterfaceTest.java @@ -110,8 +110,8 @@ public void testLegacyFunctionalInterface() throws InteropException { public void testThread() throws InteropException { TruffleObject threadClass = (TruffleObject) env.lookupHostSymbol("java.lang.Thread"); Object result = INTEROP.instantiate(threadClass, new TestExecutable()); - assertTrue(env.isHostObject(result)); - Object thread = env.asHostObject(result); + assertTrue(INTEROP.hasHostObject(result)); + Object thread = INTEROP.getHostObject(result); assertTrue(thread instanceof Thread); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java index e3930077df03..4dd8274ead7d 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java @@ -137,7 +137,7 @@ private Object asHostType(TruffleLanguage.Env env, Class c) { } private static Object verifyHostAdapterClass(TruffleLanguage.Env env, Object hostAdapterClass) { - assertTrue(env.isHostObject(hostAdapterClass)); + assertTrue(INTEROP.hasHostObject(hostAdapterClass)); assertTrue(env.isHostSymbol(hostAdapterClass)); assertTrue(INTEROP.isMetaObject(hostAdapterClass)); assertTrue(INTEROP.isInstantiable(hostAdapterClass)); @@ -162,10 +162,10 @@ Object createHostAdapterClassWithClassOverrides(TruffleLanguage.Env env, Class b.allowHostAccess(explicitHostAccessAllowImplementations(Callable.class)))) { TruffleLanguage.Env env = c.env; Object adapter = createHostAdapterClass(env, new Class[]{Callable.class}); - Object instance = instantianteHostAdapter(env, adapter, env.asGuestValue(ProxyObject.fromMap(Collections.singletonMap("call", (ProxyExecutable) (args) -> 42)))); + Object instance = instantianteHostAdapter(adapter, env.asGuestValue(ProxyObject.fromMap(Collections.singletonMap("call", (ProxyExecutable) (args) -> 42)))); assertEquals(42, INTEROP.invokeMember(instance, "call")); assertTrue(INTEROP.isMetaInstance(env.asHostSymbol(Callable.class), instance)); @@ -186,16 +186,16 @@ public void testCreateHostAdapterFromClass() throws InteropException { try (TestContext c = new TestContext((b) -> b.allowHostAccess(explicitHostAccessAllowImplementations(Extensible.class)))) { TruffleLanguage.Env env = c.env; Object adapter = createHostAdapterClass(env, new Class[]{Extensible.class}); - Object instance1 = instantianteHostAdapter(env, adapter, env.asGuestValue(ProxyObject.fromMap(Collections.singletonMap("abstractMethod", (ProxyExecutable) (args) -> "override")))); + Object instance1 = instantianteHostAdapter(adapter, env.asGuestValue(ProxyObject.fromMap(Collections.singletonMap("abstractMethod", (ProxyExecutable) (args) -> "override")))); assertEquals("override", INTEROP.invokeMember(instance1, "abstractMethod")); assertEquals("base", INTEROP.invokeMember(instance1, "baseMethod")); - Object instance2 = instantianteHostAdapter(env, adapter, env.asGuestValue(ProxyObject.fromMap(Collections.singletonMap("baseMethod", (ProxyExecutable) (args) -> "override")))); + Object instance2 = instantianteHostAdapter(adapter, env.asGuestValue(ProxyObject.fromMap(Collections.singletonMap("baseMethod", (ProxyExecutable) (args) -> "override")))); assertEquals("override", INTEROP.invokeMember(instance2, "baseMethod")); assertFails(() -> { return INTEROP.invokeMember(instance2, "abstractMethod"); - }, AbstractTruffleException.class, e -> assertTrue(e.toString(), env.isHostException(e))); + }, AbstractTruffleException.class, e -> assertTrue(e.toString(), INTEROP.hasHostObject(e) && INTEROP.isException(e))); assertTrue(INTEROP.isMetaInstance(env.asHostSymbol(Extensible.class), instance1)); assertTrue(INTEROP.isMetaInstance(env.asHostSymbol(Extensible.class), instance2)); @@ -213,7 +213,7 @@ public void testCreateHostAdapterFromClassAndInterfaces() throws InteropExceptio impl.put("baseMethod", (ProxyExecutable) (args) -> "baseMethodImpl"); impl.put("call", (ProxyExecutable) (args) -> "callImpl"); impl.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl"); - Object instance = instantianteHostAdapter(env, adapter, env.asGuestValue(ProxyObject.fromMap(impl))); + Object instance = instantianteHostAdapter(adapter, env.asGuestValue(ProxyObject.fromMap(impl))); assertEquals("abstractMethodImpl", INTEROP.invokeMember(instance, "abstractMethod")); assertEquals("baseMethodImpl", INTEROP.invokeMember(instance, "baseMethod")); @@ -245,7 +245,7 @@ public void testCreateHostAdapterFromClassWithConstructorParams() throws Interop Map impl = new HashMap<>(); impl.put("abstractMethod", (ProxyExecutable) (args) -> "abstractMethodImpl"); impl.put("finalMethod", (ProxyExecutable) (args) -> "finalMethodImpl"); - Object instance = instantianteHostAdapter(env, adapter, "concreteName", env.asGuestValue(ProxyObject.fromMap(impl))); + Object instance = instantianteHostAdapter(adapter, "concreteName", env.asGuestValue(ProxyObject.fromMap(impl))); assertEquals("abstractMethodImpl", INTEROP.invokeMember(instance, "abstractMethod")); assertEquals("final", INTEROP.invokeMember(instance, "finalMethod")); assertEquals("concreteName", INTEROP.readMember(instance, "name")); @@ -347,7 +347,7 @@ public void testCreateHostAdapterThis() throws InteropException { impl.put("baseMethod", (ProxyExecutable) (args) -> "baseMethodImpl"); impl.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl"); Object guestObject = env.asGuestValue(ProxyObject.fromMap(impl)); - Object instance = instantianteHostAdapter(env, adapter, guestObject); + Object instance = instantianteHostAdapter(adapter, guestObject); assertEquals("abstractMethodImpl", INTEROP.invokeMember(instance, "abstractMethod")); assertEquals("baseMethodImpl", INTEROP.invokeMember(instance, "baseMethod")); @@ -375,7 +375,7 @@ public void testHostAdapterWithCustomHostAccess() throws InteropException { impl.put("baseMethod", (ProxyExecutable) (args) -> "baseMethodImpl"); impl.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl"); Object guestObject = env.asGuestValue(ProxyObject.fromMap(impl)); - Object instance = instantianteHostAdapter(env, adapter, guestObject); + Object instance = instantianteHostAdapter(adapter, guestObject); assertEquals("abstractMethodImpl", INTEROP.invokeMember(instance, "abstractMethod")); assertEquals("baseMethodImpl", INTEROP.invokeMember(instance, "baseMethod")); @@ -398,7 +398,7 @@ public void testStackedHostAdaptersWithClassOverrides() throws InteropException impl1.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl1"); Object guestObject1 = env.asGuestValue(ProxyObject.fromMap(impl1)); Object adapterClass1 = createHostAdapterClassWithClassOverrides(env, supertypes, guestObject1); - Object parent = instantianteHostAdapter(env, adapterClass1); + Object parent = instantianteHostAdapter(adapterClass1); assertEquals("abstractMethodImpl1", INTEROP.invokeMember(parent, "abstractMethod")); assertEquals("baseMethodImpl1", INTEROP.invokeMember(parent, "baseMethod")); @@ -409,8 +409,8 @@ public void testStackedHostAdaptersWithClassOverrides() throws InteropException impl2.put("baseMethod", (ProxyExecutable) (args) -> "baseMethodImpl2"); impl2.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl2"); Object guestObject2 = env.asGuestValue(ProxyObject.fromMap(impl2)); - Object adapterClass2 = createHostAdapterClass(env, new Class[]{Interface.class, (Class) env.asHostObject(adapterClass1)}); - Object instance = instantianteHostAdapter(env, adapterClass2, guestObject2); + Object adapterClass2 = createHostAdapterClass(env, new Class[]{Interface.class, (Class) INTEROP.getHostObject(adapterClass1)}); + Object instance = instantianteHostAdapter(adapterClass2, guestObject2); assertEquals("abstractMethodImpl2", INTEROP.invokeMember(instance, "abstractMethod")); assertEquals("baseMethodImpl2", INTEROP.invokeMember(instance, "baseMethod")); @@ -435,7 +435,7 @@ public void testStackedHostAdaptersWithoutClassOverrides() throws InteropExcepti impl1.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl1"); Object guestObject1 = env.asGuestValue(ProxyObject.fromMap(impl1)); Object adapterClass1 = createHostAdapterClass(env, supertypes); - Object parent = instantianteHostAdapter(env, adapterClass1, guestObject1); + Object parent = instantianteHostAdapter(adapterClass1, guestObject1); assertEquals("abstractMethodImpl1", INTEROP.invokeMember(parent, "abstractMethod")); assertEquals("baseMethodImpl1", INTEROP.invokeMember(parent, "baseMethod")); @@ -446,8 +446,8 @@ public void testStackedHostAdaptersWithoutClassOverrides() throws InteropExcepti impl2.put("baseMethod", (ProxyExecutable) (args) -> "baseMethodImpl2"); impl2.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl2"); Object guestObject2 = env.asGuestValue(ProxyObject.fromMap(impl2)); - Object adapterClass2 = createHostAdapterClass(env, new Class[]{Interface.class, (Class) env.asHostObject(adapterClass1)}); - Object instance = instantianteHostAdapter(env, adapterClass2, guestObject1, guestObject2); + Object adapterClass2 = createHostAdapterClass(env, new Class[]{Interface.class, (Class) INTEROP.getHostObject(adapterClass1)}); + Object instance = instantianteHostAdapter(adapterClass2, guestObject1, guestObject2); assertEquals("abstractMethodImpl2", INTEROP.invokeMember(instance, "abstractMethod")); assertEquals("baseMethodImpl2", INTEROP.invokeMember(instance, "baseMethod")); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java index 9ceb666504b2..bf714cc9f71f 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java @@ -63,6 +63,7 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; +import com.oracle.truffle.api.interop.HeapIsolationException; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.HostAccess; import org.graalvm.polyglot.PolyglotException; @@ -100,7 +101,6 @@ import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.test.polyglot.ProxyLanguage; -import com.oracle.truffle.api.test.polyglot.ProxyLanguage.LanguageContext; import com.oracle.truffle.tck.tests.TruffleTestAssumptions; public class HostExceptionTest { @@ -186,12 +186,14 @@ public void after() { } @Test(expected = IllegalArgumentException.class) + @SuppressWarnings("deprecation") public void testAsHostExceptionIllegalArgument() { TruffleTestAssumptions.assumeWeakEncapsulation(); env.asHostException(new Exception()); } @Test(expected = IllegalArgumentException.class) + @SuppressWarnings("deprecation") public void testAsHostExceptionNull() { TruffleTestAssumptions.assumeWeakEncapsulation(); env.asHostException(null); @@ -577,7 +579,7 @@ public void testHostExceptionCause() { assertTrue("should have exception cause", INTEROP.hasExceptionCause(hostEx)); Object cause = INTEROP.getExceptionCause(hostEx); assertTrue("cause should be an exception", INTEROP.isException(cause)); - assertTrue("cause should be a host exception", env.isHostObject(cause)); + assertTrue("cause should be a host exception", INTEROP.hasHostObject(cause)); Class causeClass = NoSuchFieldException.class; assertTrue("cause should be instanceof " + causeClass.getSimpleName(), INTEROP.isMetaInstance(env.asHostSymbol(causeClass), cause)); assertFalse("cause should not have another cause", INTEROP.hasExceptionCause(cause)); @@ -628,7 +630,7 @@ public void testThrowHostExceptionObject() { assertTrue("should have exception cause", INTEROP.hasExceptionCause(hostEx)); Object cause = INTEROP.getExceptionCause(hostEx); assertTrue("cause should be an exception", INTEROP.isException(cause)); - assertTrue("cause should be a host exception", env.isHostObject(cause)); + assertTrue("cause should be a host exception", INTEROP.hasHostObject(cause)); Class causeClass = NoSuchFieldException.class; assertTrue("cause should be instanceof " + causeClass.getSimpleName(), INTEROP.isMetaInstance(env.asHostSymbol(causeClass), cause)); assertFalse("cause should not have another cause", INTEROP.hasExceptionCause(cause)); @@ -897,7 +899,7 @@ public void testGuestExceptionCaughtByHost() { hostExceptionVerifier = null; customExceptionVerifier = (guestEx) -> { - assertFalse(guestEx.toString(), env.isHostException(guestEx)); + assertFalse(guestEx.toString(), INTEROP.hasHostObject(guestEx) && INTEROP.isException(guestEx)); assertTrue(guestEx.toString(), INTEROP.isException(guestEx)); assertEquals(List.of(expectedMessage, @@ -976,7 +978,7 @@ public void testHideHostStackFrames() { hostExceptionVerifier = null; customExceptionVerifier = (guestEx) -> { - assertFalse(guestEx.toString(), env.isHostException(guestEx)); + assertFalse(guestEx.toString(), INTEROP.hasHostObject(guestEx) && INTEROP.isException(guestEx)); assertTrue(guestEx.toString(), INTEROP.isException(guestEx)); List expectedStack = List.of(expectedMessage, @@ -1218,10 +1220,10 @@ public Object execute(VirtualFrame frame) { } private void verifyHostException(Throwable ex) { - assertTrue(env.isHostObject(ex)); + assertTrue(INTEROP.hasHostObject(ex)); assertNotNull("Unexpected exception: " + ex, expectedException); - assertThat(env.asHostObject(ex), instanceOf(expectedException)); - assertThat(LanguageContext.get(null).getEnv().asHostException(ex), instanceOf(expectedException)); + assertThat(getHostObject(ex), instanceOf(expectedException)); + assertThat(getHostObject(ex), instanceOf(expectedException)); try { assertTrue(InteropLibrary.getUncached().isMetaInstance(env.asHostSymbol(Throwable.class), ex)); } catch (UnsupportedMessageException e) { @@ -1229,11 +1231,19 @@ private void verifyHostException(Throwable ex) { } } + private static Object getHostObject(Object guestObject) { + try { + return INTEROP.getHostObject(guestObject); + } catch (UnsupportedMessageException | HeapIsolationException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + @TruffleBoundary Object checkAndUnwrapException(Throwable ex) { // Avoid catching an AssertionError wrapped as a host exception. - if (env.isHostException(ex)) { - Throwable t = env.asHostException(ex); + if (INTEROP.hasHostObject(ex) && INTEROP.isException(ex)) { + Throwable t = (Throwable) getHostObject(ex); if (t instanceof AssertionError) { throw (AssertionError) t; } @@ -1314,7 +1324,7 @@ public Object execute(VirtualFrame frame) { throw CompilerDirectives.shouldNotReachHere(e); } catch (Exception ex) { if (interop.isException(ex)) { - assertTrue(env.isHostObject(ex)); + assertTrue(INTEROP.hasHostObject(ex)); try { throw interop.throwException(ex); } catch (UnsupportedMessageException e) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropErrorTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropErrorTest.java index 55789c44973f..0e4a48a883d1 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropErrorTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropErrorTest.java @@ -210,10 +210,10 @@ public void testClassCastExceptionInHostMethod() throws InteropException { Object foo = INTEROP.readMember(hostObj, "cce"); AbstractPolyglotTest.assertFails(() -> INTEROP.invokeMember(hostObj, "cce", 42), RuntimeException.class, (e) -> { - assertTrue(env.isHostException(e)); + assertTrue(INTEROP.hasHostObject(e) && INTEROP.isException(e)); }); AbstractPolyglotTest.assertFails(() -> INTEROP.execute(foo, 42), RuntimeException.class, (e) -> { - assertTrue(env.isHostException(e)); + assertTrue(INTEROP.hasHostObject(e) && INTEROP.isException(e)); }); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/ProxyLanguageEnvTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/ProxyLanguageEnvTest.java index 9e56dd0a48fc..e2fac111650c 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/ProxyLanguageEnvTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/ProxyLanguageEnvTest.java @@ -47,6 +47,8 @@ import java.util.List; import java.util.concurrent.Callable; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.interop.HeapIsolationException; import org.graalvm.polyglot.Context; import org.junit.After; import org.junit.Before; @@ -90,13 +92,20 @@ void assertThrowsExceptionWithCause(Callable callable, Class> causes = new ArrayList<>(); Throwable cause = e; + InteropLibrary interopLibrary = InteropLibrary.getUncached(); while (cause != null) { if (cause.getClass() == exception) { return; } causes.add(cause.getClass()); - if (env.isHostException(cause)) { - cause = env.asHostException(cause); + if (interopLibrary.hasHostObject(cause) && interopLibrary.isException(cause)) { + try { + cause = (Throwable) interopLibrary.getHostObject(cause); + } catch (HeapIsolationException ex) { + cause = cause.getCause(); + } catch (UnsupportedMessageException ex) { + throw CompilerDirectives.shouldNotReachHere(ex); + } } else { cause = cause.getCause(); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/TestMemberAccess.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/TestMemberAccess.java index 933862b51db9..cb22a39d598e 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/TestMemberAccess.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/TestMemberAccess.java @@ -430,7 +430,7 @@ public void testOverloadedConstructor2() throws InteropException { public void testOverloadedConstructor3() throws InteropException { TruffleObject clazz = asTruffleHostSymbol(TestConstructorException.class); Object testObj = INTEROP.instantiate(clazz, "test", 42); - assertTrue(testObj instanceof TruffleObject && env.asHostObject(testObj) instanceof TestConstructorException); + assertTrue(testObj instanceof TruffleObject && INTEROP.getHostObject(testObj) instanceof TestConstructorException); assertThrowsExceptionWithCause(() -> INTEROP.instantiate(clazz, "test"), IOException.class); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java index 415a87f8ebc1..48696ab938d9 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java @@ -53,6 +53,8 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import com.oracle.truffle.api.interop.HeapIsolationException; +import org.graalvm.collections.Pair; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.proxy.ProxyArray; import org.graalvm.polyglot.proxy.ProxyExecutable; @@ -1953,6 +1955,89 @@ public void testIsInvocableMemberWithReadSideEffects() throws UnsupportedMessage assertEquals(42, memberLib.invokeMember(obj, memberName)); } + @Test + public void testHasHostObject() { + GetHostObjectTest hostObject = new GetHostObjectTest(); + InteropLibrary hostObjectLib = createLibrary(InteropLibrary.class, hostObject); + + hostObject.hasHostObject = true; + hostObject.hostObjectProvider = () -> { + throw UnsupportedMessageException.create(); + }; + assertFails(() -> hostObjectLib.hasHostObject(hostObject), AssertionError.class); + + hostObject.hostObjectProvider = () -> Pair.create(42, "42"); + assertTrue(hostObjectLib.hasHostObject(hostObject)); + + hostObject.hostObjectProvider = () -> { + throw HeapIsolationException.create(); + }; + assertTrue(hostObjectLib.hasHostObject(hostObject)); + + hostObject.hasHostObject = false; + hostObject.hostObjectProvider = null; + assertFalse(hostObjectLib.hasHostObject(hostObject)); + + hostObject.hostObjectProvider = () -> Pair.create(42, "42"); + assertFails(() -> hostObjectLib.hasHostObject(hostObject), AssertionError.class); + + hostObject.hostObjectProvider = () -> { + throw HeapIsolationException.create(); + }; + assertFalse(hostObjectLib.hasHostObject(hostObject)); + } + + @Test + public void testGetHostObject() throws UnsupportedMessageException, HeapIsolationException { + GetHostObjectTest hostObject = new GetHostObjectTest(); + InteropLibrary l = createLibrary(InteropLibrary.class, hostObject); + + hostObject.hasHostObject = false; + hostObject.hostObjectProvider = null; + assertFails(() -> l.getHostObject(hostObject), UnsupportedMessageException.class); + + hostObject.hasHostObject = true; + Object value = new Object(); + hostObject.hostObjectProvider = () -> value; + assertSame(value, l.getHostObject(hostObject)); + + hostObject.hasHostObject = true; + hostObject.hostObjectProvider = null; + assertFails(() -> l.getHostObject(hostObject), AssertionError.class); + + hostObject.hasHostObject = false; + hostObject.hostObjectProvider = () -> value; + assertFails(() -> l.getHostObject(hostObject), AssertionError.class); + } + + @ExportLibrary(InteropLibrary.class) + static class GetHostObjectTest implements TruffleObject { + + boolean hasHostObject; + HostObjectProvider hostObjectProvider; + + @ExportMessage + boolean hasHostObject() { + return hasHostObject; + } + + @ExportMessage + Object getHostObject() throws UnsupportedMessageException, HeapIsolationException { + if (hostObjectProvider != null) { + return hostObjectProvider.call(); + } else { + throw UnsupportedMessageException.create(); + } + } + } + + @FunctionalInterface + interface HostObjectProvider extends Callable { + + @Override + Object call() throws UnsupportedMessageException, HeapIsolationException; + } + @SuppressWarnings("static-method") @ExportLibrary(InteropLibrary.class) static class IsInvocableUnknown implements TruffleObject { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java index b6f2c049a109..8485fe380ac9 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java @@ -485,7 +485,7 @@ static class ContextExtendTestLanguage extends AbstractExecutableTestLanguage { @TruffleBoundary protected Object execute(RootNode node, Env env, Object[] contextArguments, Object[] frameArguments) { try { - if (frameArguments.length == 1 && env.isHostObject(frameArguments[0])) { + if (frameArguments.length == 1 && interop.hasHostObject(frameArguments[0])) { return interop.invokeMember(frameArguments[0], "getName"); } if (frameArguments.length == 1 && interop.isString(frameArguments[0]) && interop.asString(frameArguments[0]).equals("return-array")) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java index e9b9cf88b4a9..c6da53c820ac 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java @@ -1942,12 +1942,12 @@ protected Object execute(RootNode node, Env env, Object[] contextArguments, Obje Assert.fail("Should not reach here."); } catch (Exception e) { if (isInternal) { - Assert.assertFalse(env.isHostException(e)); + Assert.assertFalse(interop.hasHostObject(e) && interop.isException(e)); Assert.assertTrue(e instanceof RuntimeException); } else { - Assert.assertTrue(env.isHostException(e)); + Assert.assertTrue(interop.hasHostObject(e) && interop.isException(e)); if (weakEncapsulation) { - Assert.assertTrue(env.asHostException(e) instanceof NullPointerException); + Assert.assertTrue(interop.getHostObject(e) instanceof NullPointerException); } } } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostClassLoadingTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostClassLoadingTest.java index 5477f9b5843e..471d9d996d6c 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostClassLoadingTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostClassLoadingTest.java @@ -93,6 +93,7 @@ public class HostClassLoadingTest extends AbstractPolyglotTest { static final String TEST_REPLACE_CLASS_NAME = "HostClassLoadingTestClazz1"; private static final String TEST_REPLACE_CLASS_NAME_2 = "HostClassLoadingTestClazz2"; private static final String TEST_REPLACE_QUALIFIED_CLASS_NAME = HostClassLoadingTestClass1.class.getPackage().getName() + ".HostClassLoadingTestClazz1"; + private static final InteropLibrary INTEROP = InteropLibrary.getFactory().getUncached(); // static number that has the same lifetime as HostClassLoadingTestClass1.class. private static int hostStaticFieldValue = 42; @@ -463,14 +464,14 @@ public void testResourceBundleFolder() throws IOException { } @Test - public void testProtectionDomainJar() throws IOException { + public void testProtectionDomainJar() throws IOException, InteropException { setupEnv(); Class hostClass = HostClassLoadingTestClass1.class; Path tempDir = renameHostClass(hostClass, TEST_REPLACE_CLASS_NAME); Path jar = createJar(tempDir); languageEnv.addToHostClassPath(languageEnv.getPublicTruffleFile(jar.toString())); Object newSymbol = languageEnv.lookupHostSymbol(hostClass.getPackage().getName() + "." + TEST_REPLACE_CLASS_NAME); - Class clz = (Class) languageEnv.asHostObject(newSymbol); + Class clz = (Class) INTEROP.getHostObject(newSymbol); ProtectionDomain protectionDomain = clz.getProtectionDomain(); assertNotNull(protectionDomain); CodeSource codeSource = protectionDomain.getCodeSource(); @@ -481,13 +482,13 @@ public void testProtectionDomainJar() throws IOException { } @Test - public void testProtectionDomainFolder() throws IOException { + public void testProtectionDomainFolder() throws IOException, InteropException { setupEnv(); Class hostClass = HostClassLoadingTestClass1.class; Path tempDir = renameHostClass(hostClass, TEST_REPLACE_CLASS_NAME); languageEnv.addToHostClassPath(languageEnv.getPublicTruffleFile(tempDir.toString())); Object newSymbol = languageEnv.lookupHostSymbol(hostClass.getPackage().getName() + "." + TEST_REPLACE_CLASS_NAME); - Class clz = (Class) languageEnv.asHostObject(newSymbol); + Class clz = (Class) INTEROP.getHostObject(newSymbol); ProtectionDomain protectionDomain = clz.getProtectionDomain(); assertNotNull(protectionDomain); CodeSource codeSource = protectionDomain.getCodeSource(); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java index 5704eac6b9ce..5db116ecce68 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java @@ -53,6 +53,8 @@ import java.util.List; import java.util.concurrent.Callable; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.interop.HeapIsolationException; import org.hamcrest.CoreMatchers; import org.junit.Before; import org.junit.BeforeClass; @@ -102,9 +104,9 @@ public static class TestClass { } @Test - public void conversionToClassYieldsTheClass() { - Object javaValue = languageEnv.asHostObject(languageEnv.asGuestValue(TestClass.class)); - Object javaSymbol = languageEnv.asHostObject(languageEnv.lookupHostSymbol(TestClass.class.getName())); + public void conversionToClassYieldsTheClass() throws Exception { + Object javaValue = INTEROP.getHostObject(languageEnv.asGuestValue(TestClass.class)); + Object javaSymbol = INTEROP.getHostObject(languageEnv.lookupHostSymbol(TestClass.class.getName())); assertTrue(javaValue instanceof Class); assertSame("Both class objects are the same", javaValue, javaSymbol); @@ -117,10 +119,10 @@ public void conversionToClassNull() { } @Test - public void nullAsJavaObject() { + public void nullAsJavaObject() throws Exception { Object nullObject = languageEnv.asGuestValue(null); - assertTrue(languageEnv.isHostObject(nullObject)); - assertNull(languageEnv.asHostObject(nullObject)); + assertTrue(INTEROP.hasHostObject(nullObject)); + assertNull(INTEROP.getHostObject(nullObject)); } @SuppressWarnings("unchecked") @@ -153,7 +155,7 @@ public void invokeJavaLangObjectFields() throws InteropException { Object string = INTEROP.invokeMember(obj, "toString"); assertTrue(string instanceof String && ((String) string).startsWith(Data.class.getName() + "@")); Object clazz = INTEROP.invokeMember(obj, "getClass"); - assertTrue(clazz instanceof TruffleObject && languageEnv.asHostObject(clazz) == Data.class); + assertTrue(clazz instanceof TruffleObject && INTEROP.getHostObject(clazz) == Data.class); assertEquals(true, INTEROP.invokeMember(obj, "equals", obj)); assertTrue(INTEROP.invokeMember(obj, "hashCode") instanceof Integer); @@ -245,8 +247,8 @@ private void assertNumberMembers(Number testNumber) throws InteropException { assertInvocable(testNumber.hashCode(), guestValue, "hashCode"); assertInvocable(testNumber.toString(), guestValue, "toString"); - assertTrue(languageEnv.isHostObject(guestValue)); - assertEquals(testNumber, languageEnv.asHostObject(guestValue)); + assertTrue(INTEROP.hasHostObject(guestValue)); + assertEquals(testNumber, INTEROP.getHostObject(guestValue)); } private static void assertInvocable(Object expectedValue, Object receiver, String method, Object... args) throws InteropException { @@ -280,7 +282,7 @@ private static void assertHasKey(Object receiver, String key) throws InteropExce throw new AssertionError("Key not found " + key + ". Keys are " + allKeys); } - private void assertStringMembers(String unboxValue, Object stringObject) throws InteropException { + private static void assertStringMembers(String unboxValue, Object stringObject) throws InteropException { String string = INTEROP.asString(stringObject); assertEquals(unboxValue, string); @@ -291,11 +293,11 @@ private void assertStringMembers(String unboxValue, Object stringObject) throws assertInvocable(string.equals(unboxValue), stringObject, "equals", unboxValue); assertInvocable(string.hashCode(), stringObject, "hashCode"); assertInvocable(string.toString(), stringObject, "toString"); - assertTrue(languageEnv.isHostObject(stringObject)); - assertEquals(string, languageEnv.asHostObject(stringObject)); + assertTrue(INTEROP.hasHostObject(stringObject)); + assertEquals(string, INTEROP.getHostObject(stringObject)); } - private void assertBooleanMembers(Object unboxValue, Object booleanObject) throws InteropException { + private static void assertBooleanMembers(Object unboxValue, Object booleanObject) throws InteropException { Boolean b = INTEROP.asBoolean(booleanObject); assertEquals(unboxValue, b); @@ -305,11 +307,11 @@ private void assertBooleanMembers(Object unboxValue, Object booleanObject) throw assertInvocable(b.hashCode(), booleanObject, "hashCode"); assertInvocable(b.toString(), booleanObject, "toString"); - assertTrue(languageEnv.isHostObject(booleanObject)); - assertEquals(b, languageEnv.asHostObject(booleanObject)); + assertTrue(INTEROP.hasHostObject(booleanObject)); + assertEquals(b, INTEROP.getHostObject(booleanObject)); } - private void assertCharacterMembers(Character unboxValue, Object charObject) throws InteropException { + private static void assertCharacterMembers(Character unboxValue, Object charObject) throws InteropException { String b = INTEROP.asString(charObject); assertEquals(unboxValue.toString(), b); @@ -319,8 +321,8 @@ private void assertCharacterMembers(Character unboxValue, Object charObject) thr assertInvocable(b.hashCode(), charObject, "hashCode"); assertInvocable(b.toString(), charObject, "toString"); - assertTrue(languageEnv.isHostObject(charObject)); - assertEquals(unboxValue, languageEnv.asHostObject(charObject)); + assertTrue(INTEROP.hasHostObject(charObject)); + assertEquals(unboxValue, INTEROP.getHostObject(charObject)); } @Test @@ -350,13 +352,17 @@ private static void assertError(Callable callable, Class } } - private void assertThrowsExceptionWithCause(Callable callable, Class exception) { + private static void assertThrowsExceptionWithCause(Callable callable, Class exception) { try { callable.call(); fail("Expected " + exception.getSimpleName() + " but no exception was thrown"); } catch (Exception e) { - assertTrue(languageEnv.isHostException(e)); - assertSame(exception, languageEnv.asHostException(e).getClass()); + assertTrue(INTEROP.hasHostObject(e) && INTEROP.isException(e)); + try { + assertSame(exception, INTEROP.getHostObject(e).getClass()); + } catch (UnsupportedMessageException | HeapIsolationException ex) { + throw CompilerDirectives.shouldNotReachHere(ex); + } } } @@ -365,12 +371,12 @@ public void testIsHostFunction() throws InteropException { TruffleObject system = (TruffleObject) languageEnv.lookupHostSymbol(System.class.getName()); Object exit = INTEROP.readMember(system, "exit"); assertTrue(exit instanceof TruffleObject); - assertFalse(languageEnv.isHostObject(exit)); + assertFalse(INTEROP.hasHostObject(exit)); assertTrue(languageEnv.isHostFunction(exit)); Object out = INTEROP.readMember(system, "out"); assertTrue(exit instanceof TruffleObject); - assertTrue(languageEnv.isHostObject(out)); + assertTrue(INTEROP.hasHostObject(out)); assertFalse(languageEnv.isHostFunction(out)); assertFalse(languageEnv.isHostFunction(system)); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxySPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxySPITest.java index 5babfbb62b88..9cb12fd65a6a 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxySPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxySPITest.java @@ -927,7 +927,7 @@ private static void assertEmpty(Object proxyInner) { } } - private void assertHostError(InteropCallable r) { + private static void assertHostError(InteropCallable r) throws InteropException { try { r.call(); Assert.fail(); @@ -937,7 +937,7 @@ private void assertHostError(InteropCallable r) { InteropLibrary interop = InteropLibrary.getUncached(); Assert.assertTrue(interop.isException(e)); Assert.assertEquals("Host Error", e.getMessage()); - Assert.assertTrue(languageEnv.asHostException(e) instanceof TestError); + Assert.assertTrue(INTEROP.getHostObject(e) instanceof TestError); } } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java index deb899a2aff6..ffda6dc6d114 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java @@ -306,7 +306,7 @@ public void testBooleans() { @Test public void testNull() { assertValueInContexts(context.asValue(null), HOST_OBJECT, NULL); - assertValueInContexts(createDelegateInteropWrapper(context, context.asValue(null)), NULL); + assertValueInContexts(createDelegateInteropWrapper(context, context.asValue(null)), HOST_OBJECT, NULL); } private static class BigIntegerSubClass extends BigInteger { @@ -419,10 +419,7 @@ public void testHostObject() { Value v = context.asValue(value); assertValueInContexts(v, expectedTraits.toArray(new Trait[0])); - - expectedTraits.remove(HOST_OBJECT); assertValueInContexts(createDelegateInteropWrapper(context, v), expectedTraits.toArray(new Trait[0])); - } } @@ -519,10 +516,7 @@ public void testBuffers() { final Value value = context.asValue(buffer); assertValueInContexts(value, BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS); - - // with the wrapper these buffers are no longer host buffers and trigger context to - // context migration code - assertValueInContexts(createDelegateInteropWrapper(context, value), BUFFER_ELEMENTS, MEMBERS); + assertValueInContexts(createDelegateInteropWrapper(context, value), BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS); } } @@ -2333,7 +2327,7 @@ private static void expandObjectVariants(Context sourceContext, List obj public void testHostException() { Value exceptionValue = context.asValue(new RuntimeException("expected")); assertValueInContexts(exceptionValue, HOST_OBJECT, MEMBERS, EXCEPTION); - assertValueInContexts(createDelegateInteropWrapper(context, exceptionValue), MEMBERS, EXCEPTION); + assertValueInContexts(createDelegateInteropWrapper(context, exceptionValue), HOST_OBJECT, MEMBERS, EXCEPTION); try { exceptionValue.throwException(); fail("should have thrown"); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/GuestToHostLanguageService.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/GuestToHostLanguageService.java index 6d4236acb601..d18f1b0e63f8 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/GuestToHostLanguageService.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/GuestToHostLanguageService.java @@ -108,21 +108,11 @@ public boolean isHostValue(Object value) { throw new UnsupportedOperationException(); } - @Override - public Object unboxHostObject(Object hostValue) { - throw new UnsupportedOperationException(); - } - @Override public Object unboxProxyObject(Object hostValue) { throw new UnsupportedOperationException(); } - @Override - public Throwable unboxHostException(Throwable hostValue) { - throw new UnsupportedOperationException(); - } - @Override public Object toHostObject(Object context, Object value) { throw new UnsupportedOperationException(); @@ -133,21 +123,11 @@ public RuntimeException toHostException(Object hostContext, Throwable exception) throw new UnsupportedOperationException(); } - @Override - public boolean isHostException(Object exception) { - return false; - } - @Override public boolean isHostFunction(Object obj) { return false; } - @Override - public boolean isHostObject(Object obj) { - return false; - } - @Override public boolean isHostSymbol(Object obj) { return false; @@ -168,11 +148,6 @@ public Object migrateValue(Object hostContext, Object value, Object valueContext return null; } - @Override - public Error toHostResourceError(Throwable hostException) { - return null; - } - @Override public int findNextGuestToHostStackTraceElement(StackTraceElement firstElement, StackTraceElement[] hostStack, int nextElementIndex) { return -1; diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEntryPoint.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEntryPoint.java index 023ee5db491b..a9176d7f17be 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEntryPoint.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEntryPoint.java @@ -46,6 +46,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicLong; +import com.oracle.truffle.api.interop.InteropException; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Engine; import org.graalvm.polyglot.EnvironmentAccess; @@ -154,13 +155,13 @@ public Object remoteMessage(long contextId, long receiverId, Message message, Ob Object result; try { result = lib.send(receiver, message, localValues); + } catch (InteropException e) { + throw sneakyThrow(e); + } catch (AbstractTruffleException e) { + // also send over stack traces and messages + return new GuestExceptionPointer(guestToHost(e), e.getMessage()); } catch (Exception e) { - if (e instanceof AbstractTruffleException) { - // also send over stack traces and messages - return new GuestExceptionPointer(guestToHost(e), e.getMessage()); - } else { - throw new RuntimeException("Internal error thrown by remote message.", e); - } + throw new RuntimeException("Internal error thrown by remote message.", e); } return marshallAtGuest(result); } finally { @@ -168,6 +169,11 @@ public Object remoteMessage(long contextId, long receiverId, Message message, Ob } } + @SuppressWarnings("unchecked") + private static RuntimeException sneakyThrow(Throwable ex) throws T { + throw (T) ex; + } + static class GuestExceptionPointer { final long id; diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index f4ed255ad5d1..b254c158c8b9 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -2237,13 +2237,17 @@ public Object lookupHostSymbol(String symbolName) { * Returns true if the argument is Java host language object wrapped using * Truffle interop. * + * @deprecated Use + * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#hasHostObject(Object) + * hosHostObject}. * @see #asHostObject(Object) * @since 19.0 */ + @Deprecated @SuppressWarnings("static-method") public boolean isHostObject(Object value) { try { - return LanguageAccessor.engineAccess().isHostObject(polyglotLanguageContext, value); + return LanguageAccessor.engineAccess().isHostObject(value); } catch (Throwable t) { throw engineToLanguageException(t); } @@ -2254,15 +2258,19 @@ public boolean isHostObject(Object value) { * host language object. Throws {@link ClassCastException} if the provided argument is not a * {@link #isHostObject(Object) host object}. * + * @deprecated Use + * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#getHostObject(Object) + * getHostObject}. * @since 19.0 */ + @Deprecated public Object asHostObject(Object value) { if (!isHostObject(value)) { CompilerDirectives.transferToInterpreterAndInvalidate(); throw new ClassCastException(); } try { - return LanguageAccessor.engineAccess().asHostObject(polyglotLanguageContext, value); + return LanguageAccessor.engineAccess().asHostObject(value); } catch (Throwable t) { throw engineToLanguageException(t); } @@ -2362,13 +2370,16 @@ public Object findMetaObject(Object value) { * @param exception the {@link Throwable} to test * @return {@code true} if the {@code exception} is a host exception, {@code false} * otherwise + * @deprecated Use + * {@code InteropLibrary.isHostObject(obj) && InteropLibrary.isException(obj)}. * @see #asHostException(Throwable) * @since 19.0 */ + @Deprecated @SuppressWarnings("static-method") public boolean isHostException(Throwable exception) { try { - return LanguageAccessor.engineAccess().isHostException(polyglotLanguageContext, exception); + return LanguageAccessor.engineAccess().isHostException(exception); } catch (Throwable t) { throw engineToLanguageException(t); } @@ -2380,16 +2391,20 @@ public boolean isHostException(Throwable exception) { * Host exceptions may be thrown by interoperability messages. The host exception may be * unwrapped using {@link #asHostException(Throwable)}. * + * @deprecated Use + * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#getHostObject(Object) + * getHostObject}. * @param exception the host exception to unwrap * @return the original Java exception * @throws IllegalArgumentException if the {@code exception} is not a host exception * @see #isHostException(Throwable) * @since 19.0 */ + @Deprecated @SuppressWarnings("static-method") public Throwable asHostException(Throwable exception) { try { - return LanguageAccessor.engineAccess().asHostException(polyglotLanguageContext, exception); + return LanguageAccessor.engineAccess().asHostException(exception); } catch (Throwable t) { throw engineToLanguageException(t); } diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java index 07d0e4cd9cb7..0684d7d675f9 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java @@ -47,7 +47,6 @@ import java.util.Objects; import org.graalvm.polyglot.PolyglotException; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractHostLanguageService; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.Frame; @@ -270,13 +269,15 @@ public static TruffleStackTrace fillIn(Throwable throwable) { } private static boolean isHostException(Throwable throwable) { - Object polyglotEngine = LanguageAccessor.ENGINE.getCurrentPolyglotEngine(); - if (polyglotEngine == null) { - return false; + try { + return LanguageAccessor.ENGINE.isHostException(throwable); + } catch (Throwable t) { + if (LanguageAccessor.ENGINE.isCancelExecution(t)) { + return false; + } else { + throw t; + } } - AbstractHostLanguageService hostService = LanguageAccessor.ENGINE.getHostService(polyglotEngine); - // hostService is null during context pre-initialization - return hostService != null && hostService.isHostException(throwable); } private static final class TracebackElement { diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java index 92e033a50b57..4dc363405bb2 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java @@ -507,9 +507,9 @@ public abstract Thread createThread(Object polyglotLanguageContext, Runnable run public abstract RuntimeException wrapHostException(Node callNode, Object languageContext, Throwable exception); - public abstract boolean isHostException(Object polyglotLanguageContext, Throwable exception); + public abstract boolean isHostException(Throwable exception); - public abstract Throwable asHostException(Object polyglotLanguageContext, Throwable exception); + public abstract Throwable asHostException(Throwable exception) throws Exception; public abstract Object getCurrentHostContext(); @@ -567,9 +567,9 @@ public abstract Thread createThread(Object polyglotLanguageContext, Runnable run public abstract Set getValidMimeTypes(Object engineObject, String language); - public abstract Object asHostObject(Object languageContext, Object value); + public abstract Object asHostObject(Object value) throws Exception; - public abstract boolean isHostObject(Object languageContext, Object value); + public abstract boolean isHostObject(Object value); public abstract boolean isHostFunction(Object languageContext, Object value); diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java index 67fe38282720..46751911bd43 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java @@ -115,7 +115,13 @@ public boolean isDisconnectedHostObject(Object obj) { @Override public Object unboxDisconnectedHostObject(Object hostValue) { - return HostObject.valueOf(null, hostValue); + Object v = HostLanguage.unwrapIfScoped(null, hostValue); + if (v instanceof HostObject) { + return ((HostObject) v).obj; + } else if (v instanceof HostException) { + return ((HostException) v).delegate.obj; + } + return v; } @Override diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java index 3df874363797..6382b2985e0e 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java @@ -145,5 +145,4 @@ public boolean equals(Object o) { public int hashCode() { return method.hashCode(); } - } diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java index 2e84806c1945..1b464c42ca3f 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java @@ -170,24 +170,11 @@ public boolean isHostValue(Object value) { (obj instanceof HostProxy); } - @Override - public Object unboxHostObject(Object hostValue) { - return HostObject.valueOf(language, hostValue); - } - @Override public Object unboxProxyObject(Object hostValue) { return HostProxy.toProxyHostObject(language, hostValue); } - @Override - public Throwable unboxHostException(Throwable hostValue) { - if (hostValue instanceof HostException) { - return ((HostException) hostValue).getOriginal(); - } - return null; - } - @Override public Object toHostObject(Object hostContext, Object value) { HostContext context = (HostContext) hostContext; @@ -199,21 +186,11 @@ public Object asHostDynamicClass(Object context, Class value) { return null; } - @Override - public boolean isHostException(Object exception) { - return exception instanceof HostException; - } - @Override public boolean isHostFunction(Object value) { return HostFunction.isInstance(language, value); } - @Override - public boolean isHostObject(Object value) { - return HostObject.isInstance(language, value); - } - @Override public boolean isHostProxy(Object value) { return HostProxy.isProxyGuestObject(language, value); @@ -283,15 +260,6 @@ public Object migrateValue(Object targetContext, Object value, Object valueConte } } - @Override - public Error toHostResourceError(Throwable hostException) { - Throwable t = unboxHostException(hostException); - if (t instanceof StackOverflowError || t instanceof OutOfMemoryError) { - return (Error) t; - } - return null; - } - @Override public int findNextGuestToHostStackTraceElement(StackTraceElement firstElement, StackTraceElement[] hostStack, int nextElementIndex) { StackTraceElement element = firstElement; diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java index b63011c1c166..b71991212f82 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java @@ -65,6 +65,7 @@ final class HostMethodScope { private static final ScopedObject[] EMTPY_SCOPE_ARRAY = new ScopedObject[0]; private static final Unsafe UNSAFE = getUnsafe(); + private static final Message MESSAGE_GET_HOST_OBJECT = Message.resolve(InteropLibrary.class, "getHostObject"); private ScopedObject[] scope; private int nextDynamicIndex; @@ -246,7 +247,7 @@ Object send(Message message, Object[] args, "Alternatively, use Value.pin() to prevent a scoped object from being released after the host call completed."); } Object returnValue = library.send(d, message, args); - if (message.getReturnType() == Object.class && !(d instanceof PinnedObject)) { + if (message.getReturnType() == Object.class && !(d instanceof PinnedObject) && message != MESSAGE_GET_HOST_OBJECT) { /* * Object return type indicates for an interop message that any interop value may be * returned. diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java index 20d5c320bdba..84f0eb278a00 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java @@ -61,6 +61,7 @@ import java.util.NoSuchElementException; import java.util.Objects; +import com.oracle.truffle.api.interop.HeapIsolationException; import org.graalvm.polyglot.impl.AbstractPolyglotImpl.APIAccess; import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractHostAccess; @@ -169,34 +170,6 @@ static Object withContext(HostLanguage language, Object originalValue, HostConte } } - static boolean isJavaInstance(HostLanguage language, Class targetType, Object javaObject) { - Object unboxed = unboxHostObject(language, javaObject); - if (unboxed != null) { - return targetType.isInstance(unboxed); - } - return false; - } - - static Object unboxHostObject(HostLanguage language, Object value) { - Object v = HostLanguage.unwrapIfScoped(language, value); - if (v instanceof HostObject) { - return ((HostObject) v).obj; - } else if (v instanceof HostException) { - return ((HostException) v).delegate.obj; - } - return null; - } - - static Object valueOf(HostLanguage language, Object value) { - Object v = HostLanguage.unwrapIfScoped(language, value); - if (v instanceof HostObject) { - return ((HostObject) v).obj; - } else if (v instanceof HostException) { - return ((HostException) v).delegate.obj; - } - return v; - } - @Override public int hashCode() { return System.identityHashCode(obj); @@ -3343,13 +3316,19 @@ Object getMetaSimpleName() throws UnsupportedMessageException { @TruffleBoundary boolean isMetaInstance(Object other, @Bind Node node, - @CachedLibrary("this") InteropLibrary library, @Shared @Cached InlinedBranchProfile error) throws UnsupportedMessageException { if (isClass()) { Class c = asClass(); - HostLanguage language = context != null ? HostLanguage.get(library) : null; - if (HostObject.isInstance(language, other)) { - Object otherHostObj = HostObject.valueOf(language, other); + HostLanguage language = context != null ? HostLanguage.get(node) : null; + InteropLibrary otherInterop = InteropLibrary.getUncached(other); + if (otherInterop.hasHostObject(other)) { + Object otherHostObj; + try { + otherHostObj = otherInterop.getHostObject(other); + } catch (HeapIsolationException e) { + error.enter(node); + throw UnsupportedMessageException.create(); + } if (otherHostObj == null) { return false; } else { @@ -3490,6 +3469,17 @@ static int identityHashCode(HostObject receiver) { return System.identityHashCode(receiver.obj); } + @ExportMessage + @SuppressWarnings("static-method") + boolean hasHostObject() { + return true; + } + + @ExportMessage + Object getHostObject() { + return this.obj; + } + @Override public boolean equals(Object o) { if (o instanceof HostObject) { diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java index 78a03afc06b0..e0d37f971436 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java @@ -64,6 +64,8 @@ import java.util.Objects; import java.util.function.Function; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.interop.HeapIsolationException; import org.graalvm.polyglot.HostAccess.MutableTargetMapping; import org.graalvm.polyglot.Value; @@ -183,9 +185,10 @@ private static Object convertImpl(Node node, Object value, Class targetType, return convertedValue; } } - HostLanguage language = HostLanguage.get(interop); - if (HostObject.isJavaInstance(language, targetType, value)) { - return HostObject.valueOf(language, value); + + Object hostValue; + if ((hostValue = toJavaInstance(value, targetType, interop)) != null) { + return hostValue; } if (useCustomTargetTypes) { @@ -201,6 +204,8 @@ private static Object convertImpl(Node node, Object value, Class targetType, return convertedValue; } } + + HostLanguage language = HostLanguage.get(interop); if (targetType == language.valueClass && context != null) { return language.valueClass.isInstance(value) ? value : context.asValue(interop, value); } else if (interop.isNull(value)) { @@ -269,7 +274,7 @@ static boolean canConvert(Node node, Object value, Class targetType, Type gen return true; } } - if (HostObject.isJavaInstance(language, targetType, value)) { + if (toJavaInstance(value, targetType, interop) != null) { return true; } @@ -427,8 +432,9 @@ private static T asJavaObject(Node node, HostContext hostContext, Object val InteropLibrary interop = InteropLibrary.getFactory().getUncached(value); assert !interop.isNull(value); // already handled Object obj; - if (HostObject.isJavaInstance(hostContext.language, targetType, value)) { - obj = HostObject.valueOf(hostContext.language, value); + Object hostObject; + if ((hostObject = toJavaInstance(value, targetType, interop)) != null) { + obj = hostObject; } else if (targetType == Object.class) { obj = convertToObject(node, hostContext, value, interop); } else if (targetType == List.class || targetType == Collection.class) { @@ -651,6 +657,22 @@ private static T asJavaObject(Node node, HostContext hostContext, Object val return targetType.cast(obj); } + private static Object toJavaInstance(Object value, Class targetType, InteropLibrary interop) { + if (interop.hasHostObject(value)) { + try { + Object hostObject = interop.getHostObject(value); + if (hostObject != null && targetType.isInstance(hostObject)) { + return hostObject; + } + } catch (HeapIsolationException e) { + return null; + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } + return null; + } + private static Object asPolyglotException(HostContext hostContext, Object value, InteropLibrary interop) { try { interop.throwException(value); diff --git a/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java b/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java index bc7f75496974..1747c600c961 100644 --- a/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java +++ b/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java @@ -50,6 +50,7 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.HeapIsolationException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidBufferOffsetException; import com.oracle.truffle.api.interop.TruffleObject; @@ -628,17 +629,25 @@ abstract static class SerializeArrayNode extends SerializeArgumentNode { } } - final boolean isHostObject(Object value) { - return LibFFIContext.get(this).env.isHostObject(value); + final boolean isHostObject(Object value, InteropLibrary interop) { + return interop.hasHostObject(value); } - final Object asHostObject(Object value) { - return LibFFIContext.get(this).env.asHostObject(value); + final Object asHostObject(Object value, InteropLibrary interop) { + try { + return interop.getHostObject(value); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } catch (HeapIsolationException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new UnsupportedOperationException(e); + } } - @Specialization(guards = {"isHostObject(value)", "tag != null"}) + @Specialization(guards = {"isHostObject(value, interop)", "tag != null"}) void doHostObject(@SuppressWarnings("unused") Object value, NativeArgumentBuffer buffer, - @Bind("asHostObject(value)") Object hostObject, + @CachedLibrary(limit = "3") InteropLibrary interop, + @Bind("asHostObject(value, interop)") Object hostObject, @Bind("getTypeTag.execute(hostObject)") TypeTag tag) { buffer.putObject(tag, hostObject, type.size); } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/DefaultPolyglotHostService.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/DefaultPolyglotHostService.java index 9224ae1a5489..a6d6f39dd611 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/DefaultPolyglotHostService.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/DefaultPolyglotHostService.java @@ -44,6 +44,7 @@ import static com.oracle.truffle.polyglot.PolyglotFastThreadLocals.LANGUAGE_CONTEXT_OFFSET; import static com.oracle.truffle.polyglot.PolyglotFastThreadLocals.computeLanguageIndexFromStaticIndex; +import com.oracle.truffle.api.interop.InteropLibrary; import org.graalvm.polyglot.impl.AbstractPolyglotImpl; import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractHostLanguageService; import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractPolyglotHostService; @@ -76,10 +77,15 @@ public void notifyEngineClosed(Object engineReceiver, boolean cancelIfExecuting) @Override public RuntimeException hostToGuestException(AbstractHostLanguageService host, Throwable throwable) { - assert !host.isHostException(throwable); + assert !isHostException(throwable); return host.toHostException(PolyglotFastThreadLocals.getLanguageContext(null, computeLanguageIndexFromStaticIndex(HOST_LANGUAGE_INDEX, LANGUAGE_CONTEXT_OFFSET)), throwable); } + private static boolean isHostException(Throwable throwable) { + InteropLibrary interop = InteropLibrary.getUncached(throwable); + return interop.hasHostObject(throwable) && interop.isException(throwable); + } + @Override public void notifyPolyglotThreadStart(Object contextReceiver, Thread threadToStart) { } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java index 60df2b5f83fe..cba4c2d53008 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java @@ -1259,24 +1259,22 @@ public RuntimeException wrapHostException(Node location, Object languageContext, } @Override - public boolean isHostException(Object languageContext, Throwable exception) { - PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context; - PolyglotEngineImpl engine = context.engine; - // During context pre-initialization, engine.host is null, languages are not allowed to - // use host interop. But the call to isHostException is supported and returns false - // because languages cannot create a HostObject. - return !engine.inEnginePreInitialization && engine.host.isHostException(exception); + public boolean isHostException(Throwable exception) { + InteropLibrary interop = InteropLibrary.getUncached(exception); + return interop.hasHostObject(exception) && interop.isException(exception); } @Override - public Throwable asHostException(Object languageContext, Throwable exception) { - PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context; - Throwable host = context.engine.host.unboxHostException(exception); - if (host == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException("Provided value not a host exception."); + public Throwable asHostException(Throwable exception) throws Exception { + if (exception != null) { + InteropLibrary interop = InteropLibrary.getUncached(exception); + boolean isHostException = interop.hasHostObject(exception) && interop.isException(exception); + if (isHostException) { + return (Throwable) interop.getHostObject(exception); + } } - return host; + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException("Provided value not a host exception."); } @Override @@ -1543,10 +1541,14 @@ public Object getLoggerOwner(Object loggerCache) { } @Override - public Object asHostObject(Object languageContext, Object obj) { - PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context; - assert isHostObject(languageContext, obj); - return context.engine.host.unboxHostObject(obj); + public Object asHostObject(Object obj) throws Exception { + InteropLibrary interop = InteropLibrary.getUncached(obj); + if (interop.hasHostObject(obj)) { + return interop.getHostObject(obj); + } else { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException("Provided value not a host object."); + } } @Override @@ -1560,13 +1562,9 @@ public boolean isHostFunction(Object languageContext, Object obj) { } @Override - public boolean isHostObject(Object languageContext, Object obj) { - PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context; - PolyglotEngineImpl engine = context.engine; - // During context pre-initialization, engine.host is null, languages are not allowed to - // use host interop. But the call to isHostObject is supported and returns false because - // languages cannot create a HostObject. - return !engine.inEnginePreInitialization && engine.host.isHostObject(obj); + public boolean isHostObject(Object obj) { + InteropLibrary interop = InteropLibrary.getUncached(obj); + return interop.hasHostObject(obj); } @Override diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java index fde3cbb578b6..9a8998c9686f 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java @@ -66,6 +66,7 @@ final class OtherContextGuestObject implements TruffleObject { static final Object OTHER_VALUE = new Object(); static final ReflectionLibrary OTHER_VALUE_UNCACHED = ReflectionLibrary.getFactory().getUncached(OTHER_VALUE); + private static final Message MESSAGE_GET_HOST_OBJECT = Message.resolve(InteropLibrary.class, "getHostObject"); final PolyglotContextImpl receiverContext; final Object delegate; @@ -159,7 +160,7 @@ static Object sendImpl(Node node, PolyglotSharingLayer layer, Object receiver, M if (message.getReturnType() == void.class) { return null; } - return migrateReturn(returnValue, receiverContext, delegateContext); + return migrateReturn(returnValue, message, receiverContext, delegateContext); } catch (Throwable e) { seenError.enter(node); throw migrateException(receiverContext, e, delegateContext); @@ -264,8 +265,10 @@ private static Object fallbackSend(Message message, Object[] args) throws Except return OTHER_VALUE_UNCACHED.send(OTHER_VALUE, message, args); } - private static Object migrateReturn(Object arg, PolyglotContextImpl receiverContext, PolyglotContextImpl delegateContext) { - if (arg instanceof TruffleObject) { + private static Object migrateReturn(Object arg, Message message, PolyglotContextImpl receiverContext, PolyglotContextImpl delegateContext) { + if (message == MESSAGE_GET_HOST_OBJECT) { + return arg; + } else if (arg instanceof TruffleObject) { return receiverContext.migrateValue(arg, delegateContext); } else { assert InteropLibrary.isValidProtocolValue(arg) : "unexpected interop primitive"; diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java index 4820305b91b6..73f6c93ee496 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java @@ -131,7 +131,7 @@ final class PolyglotExceptionImpl { } else { creationStackTrace = null; } - Error resourceLimitError = getResourceLimitError(engine, exception); + Error resourceLimitError = getResourceLimitError(exception); String exceptionQualifiedName = null; String exceptionMessage = null; InteropLibrary interop; @@ -158,7 +158,7 @@ final class PolyglotExceptionImpl { } else { this.sourceLocation = null; } - if (entered && languageContext != null && languageContext.isCreated() && !isHostException(engine, exception)) { + if (entered && languageContext != null && languageContext.isCreated() && !isHostException(exception)) { this.guestObject = languageContext.asValue(exception); } else { this.guestObject = null; @@ -173,7 +173,7 @@ final class PolyglotExceptionImpl { * InterruptExecution was thrown before the context was made invalid. */ boolean interruptException = (exception instanceof PolyglotEngineImpl.InterruptExecution) || (exception != null && exception.getCause() instanceof InterruptedException) || - (isHostException(engine, exception) && asHostException() instanceof InterruptedException); + (isHostException(exception) && asHostException() instanceof InterruptedException); boolean truffleException = exception instanceof com.oracle.truffle.api.exception.AbstractTruffleException; boolean cancelInducedTruffleOrInterruptException = (polyglotContextState != null && (polyglotContextState.isCancelling() || polyglotContextState == PolyglotContextImpl.State.CLOSED_CANCELLED) && @@ -228,11 +228,11 @@ final class PolyglotExceptionImpl { qualifiedName = exceptionQualifiedName; } - private static Error getResourceLimitError(PolyglotEngineImpl engine, Throwable e) { + private static Error getResourceLimitError(Throwable e) { if (e instanceof CancelExecution) { return ((CancelExecution) e).isResourceLimit() ? (Error) e : null; - } else if (isHostException(engine, e)) { - Error toCheck = engine.host.toHostResourceError(e); + } else if (isHostException(e)) { + Error toCheck = toHostResourceError(e); assert toCheck == null || toCheck instanceof StackOverflowError || toCheck instanceof OutOfMemoryError; return toCheck; } else if (e instanceof StackOverflowError || e instanceof OutOfMemoryError) { @@ -241,6 +241,14 @@ private static Error getResourceLimitError(PolyglotEngineImpl engine, Throwable return null; } + private static Error toHostResourceError(Throwable hostException) { + Throwable t = unboxHostException(hostException); + if (t instanceof StackOverflowError || t instanceof OutOfMemoryError) { + return (Error) t; + } + return null; + } + private Object newSourceSection(com.oracle.truffle.api.source.SourceSection section) { com.oracle.truffle.api.source.Source truffleSource = section.getSource(); Object source = polyglot.getAPIAccess().newSource(polyglot.getSourceDispatch(), truffleSource); @@ -277,7 +285,7 @@ public boolean isInterrupted() { } public boolean isHostException() { - return isHostException(engine, exception); + return isHostException(exception); } public Throwable asHostException() { @@ -285,7 +293,7 @@ public Throwable asHostException() { throw PolyglotEngineException.unsupported( String.format("Unsupported operation PolyglotException.asHostException(). You can ensure that the operation is supported using PolyglotException.isHostException()")); } - return engine.host.unboxHostException(exception); + return unboxHostException(exception); } void printStackTrace(PrintWriter s) { @@ -536,9 +544,9 @@ static Iterator createStackFrameIterator(PolyglotExceptionImpl impl) { APIAccess apiAccess = impl.polyglot.getAPIAccess(); StackTraceElement[] hostStack = null; - if (isHostException(impl.engine, impl.exception)) { - Throwable original = impl.engine.host.unboxHostException(impl.exception); - hostStack = original.getStackTrace(); + if (isHostException(impl.exception)) { + Throwable original = unboxHostException(impl.exception); + hostStack = original != null ? original.getStackTrace() : impl.exception.getStackTrace(); } else if (EngineAccessor.EXCEPTION.isException(impl.exception)) { Throwable lazyStack = EngineAccessor.EXCEPTION.getLazyStackTrace(impl.exception); if (lazyStack != null) { @@ -581,11 +589,21 @@ public Object apply(TruffleStackTraceElement guestFrame) { }); } - private static boolean isHostException(PolyglotEngineImpl engine, Throwable cause) { - /* - * Note that engine.host can be null if the error happens during initialization. - */ - return engine != null && engine.host != null && engine.host.isHostException(cause); + private static boolean isHostException(Throwable cause) { + try { + InteropLibrary interop = InteropLibrary.getUncached(cause); + return interop.hasHostObject(cause) && interop.isException(cause); + } catch (CancelExecution cancelled) { + return false; + } + } + + private static Throwable unboxHostException(Throwable cause) { + try { + return (Throwable) InteropLibrary.getUncached(cause).getHostObject(cause); + } catch (Exception e) { + throw CompilerDirectives.shouldNotReachHere(e); + } } static class MergedHostGuestIterator implements Iterator { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java index 2638b726f215..c6a819e48dbb 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java @@ -59,6 +59,7 @@ import java.util.NoSuchElementException; import java.util.Set; +import com.oracle.truffle.api.interop.HeapIsolationException; import org.graalvm.polyglot.impl.AbstractPolyglotImpl; import org.graalvm.polyglot.impl.AbstractPolyglotImpl.APIAccess; import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueDispatch; @@ -92,6 +93,7 @@ import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.AsClassLiteralNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.AsDateNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.AsDurationNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.AsHostObjectNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.AsInstantNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.AsNativePointerNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.AsTimeNodeGen; @@ -132,6 +134,7 @@ import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsDateNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsDurationNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsExceptionNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsHostObjectNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsMetaInstanceNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsMetaObjectNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsNativePointerNodeGen; @@ -2293,6 +2296,8 @@ static final class InteropValue extends PolyglotValueDispatch { final CallTarget getHashEntriesIterator; final CallTarget getHashKeysIterator; final CallTarget getHashValuesIterator; + final CallTarget isHostObject; + final CallTarget asHostObject; final CallTarget asClassLiteral; final CallTarget asTypeLiteral; @@ -2375,7 +2380,8 @@ static final class InteropValue extends PolyglotValueDispatch { this.getHashEntriesIterator = createTarget(GetHashEntriesIteratorNodeGen.create(this)); this.getHashKeysIterator = createTarget(GetHashKeysIteratorNodeGen.create(this)); this.getHashValuesIterator = createTarget(GetHashValuesIteratorNodeGen.create(this)); - + this.isHostObject = createTarget(IsHostObjectNodeGen.create(this)); + this.asHostObject = createTarget(AsHostObjectNodeGen.create(this)); } @SuppressWarnings("unchecked") @@ -2592,15 +2598,7 @@ public Duration asDuration(Object languageContext, Object receiver) { @Override public boolean isHostObject(Object languageContext, Object receiver) { - PolyglotLanguageContext context = (PolyglotLanguageContext) languageContext; - Object prev = hostEnter(context); - try { - return getEngine().host.isHostObject(receiver); - } catch (Throwable e) { - throw guestToHostException(context, e, true); - } finally { - hostLeave(context, prev); - } + return (boolean) RUNTIME.callProfiled(this.isHostObject, languageContext, receiver); } private PolyglotEngineImpl getEngine() { @@ -2631,11 +2629,7 @@ public Object asProxyObject(Object languageContext, Object receiver) { @Override public Object asHostObject(Object languageContext, Object receiver) { - if (isHostObject(languageContext, receiver)) { - return getEngine().host.unboxHostObject(receiver); - } else { - return super.asHostObject(languageContext, receiver); - } + return RUNTIME.callProfiled(this.asHostObject, languageContext, receiver); } @Override @@ -5587,6 +5581,66 @@ static Object doCached(PolyglotLanguageContext context, Object receiver, Object[ } } - } + abstract static class IsHostObjectNode extends InteropNode { + + protected IsHostObjectNode(InteropValue interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "isHostObject"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, @Bind Node node, + @CachedLibrary("receiver") InteropLibrary objects) { + return objects.hasHostObject(receiver); + } + } + + abstract static class AsHostObjectNode extends InteropNode { + + protected AsHostObjectNode(InteropValue interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "asHostObject"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, @Bind Node node, + @CachedLibrary("receiver") InteropLibrary objects, + @Cached InlinedBranchProfile unsupported, + @Cached InlinedBranchProfile isolatedHeap) { + try { + return objects.getHostObject(receiver); + } catch (UnsupportedMessageException e) { + unsupported.enter(node); + return asHostObjectUnsupported(context, receiver); + } catch (HeapIsolationException e) { + isolatedHeap.enter(node); + throw asHostObjectIsolatedHeap(context, receiver); + } + } + @TruffleBoundary + static RuntimeException asHostObjectIsolatedHeap(PolyglotLanguageContext context, Object receiver) { + String polyglotMessage = String.format("Unsupported operation Value.asHostObject() for %s. The referenced host object resides in an isolated heap.", getValueInfo(context, receiver)); + return PolyglotEngineException.classCast(polyglotMessage); + } + } + } } diff --git a/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java b/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java index 7faa5f5197a1..a98225bc3f8e 100644 --- a/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java +++ b/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java @@ -41,11 +41,16 @@ package org.graalvm.truffle.benchmark.tstring; import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.HeapIsolationException; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.strings.TruffleString; @@ -237,8 +242,8 @@ abstract static class RawIterateBytesNode extends Node { public abstract int execute(Object input); @Specialization - int bench(Object hostObject) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + int bench(Object hostObject, @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); int ret = 0; for (int i = 0; i < input.length; i++) { ret += Byte.toUnsignedInt(input[i]); @@ -325,8 +330,9 @@ abstract static class CalcStringAttributesUTF8Node extends Node { @Specialization TruffleString bench(Object hostObject, int length, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_8, false); } } @@ -337,8 +343,9 @@ abstract static class CalcStringAttributesUTF16Node extends Node { @Specialization TruffleString bench(Object hostObject, int length, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16, false); } } @@ -349,8 +356,9 @@ abstract static class FromByteArrayUTF16Node extends Node { @Specialization TruffleString bench(Object hostObject, int length, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16, true); } } @@ -361,8 +369,9 @@ abstract static class FromByteArrayUTF32Node extends Node { @Specialization TruffleString bench(Object hostObject, int length, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32, true); } } @@ -373,8 +382,9 @@ abstract static class FromByteArrayUTF16FENode extends Node { @Specialization TruffleString bench(Object hostObject, int length, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16BE, false); } } @@ -386,8 +396,9 @@ abstract static class FromByteArrayUTF16FESwitchNode extends Node { @Specialization TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); return switchEncodingNode.execute(fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16BE, false), TruffleString.Encoding.UTF_16); } } @@ -398,8 +409,9 @@ abstract static class FromByteArrayUTF32FENode extends Node { @Specialization TruffleString bench(Object hostObject, int length, - @Cached TruffleString.FromByteArrayNode fromByteArrayNode) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32BE, false); } } @@ -411,8 +423,9 @@ abstract static class FromByteArrayUTF32FESwitchNode extends Node { @Specialization TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @CachedLibrary(limit = "3") InteropLibrary interop) { + byte[] input = (byte[]) getHostObject(hostObject, interop); return switchEncodingNode.execute(fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32BE, false), TruffleString.Encoding.UTF_32); } } @@ -455,4 +468,12 @@ public static DummyLanguageContext get(Node node) { return REFERENCE.get(node); } } + + static Object getHostObject(Object guestObject, InteropLibrary interop) { + try { + return interop.getHostObject(guestObject); + } catch (UnsupportedMessageException | HeapIsolationException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } } From 958b9c5507ad61ee10b85c2ff03c8b01843aa11c Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Tue, 11 Nov 2025 10:36:01 +0100 Subject: [PATCH 02/14] After rebase fixes. --- .../src/com/oracle/truffle/host/HostMethodScope.java | 2 +- .../com/oracle/truffle/polyglot/OtherContextGuestObject.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java index b71991212f82..59bf5a6ed4a0 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java @@ -65,7 +65,7 @@ final class HostMethodScope { private static final ScopedObject[] EMTPY_SCOPE_ARRAY = new ScopedObject[0]; private static final Unsafe UNSAFE = getUnsafe(); - private static final Message MESSAGE_GET_HOST_OBJECT = Message.resolve(InteropLibrary.class, "getHostObject"); + private static final Message MESSAGE_GET_HOST_OBJECT = Message.resolveExact(InteropLibrary.class, "getHostObject", Object.class); private ScopedObject[] scope; private int nextDynamicIndex; diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java index 9a8998c9686f..1cff8c2a526c 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java @@ -66,7 +66,7 @@ final class OtherContextGuestObject implements TruffleObject { static final Object OTHER_VALUE = new Object(); static final ReflectionLibrary OTHER_VALUE_UNCACHED = ReflectionLibrary.getFactory().getUncached(OTHER_VALUE); - private static final Message MESSAGE_GET_HOST_OBJECT = Message.resolve(InteropLibrary.class, "getHostObject"); + private static final Message MESSAGE_GET_HOST_OBJECT = Message.resolveExact(InteropLibrary.class, "getHostObject", Object.class); final PolyglotContextImpl receiverContext; final Object delegate; From 81a3698ef4c124a87738b7d05db079bedf94bf4e Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Tue, 11 Nov 2025 10:37:34 +0100 Subject: [PATCH 03/14] Fixed Value#asHostObject(), Value#asNativePointer() and Value#asProxyObject() javadoc. --- sdk/CHANGELOG.md | 2 ++ .../src/org/graalvm/polyglot/Value.java | 9 +++++---- .../oracle/truffle/polyglot/PolyglotValueDispatch.java | 2 +- .../src/com/oracle/truffle/tck/tests/ValueAssert.java | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 9e48c728de14..1a806f6275be 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -10,6 +10,8 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f * GR-65404 Remove `PolyglotLauncher` as it is no longer used. * GR-68613: JavaScript polyglot isolate now includes support for the WebAssembly (Wasm) language. * GR-69590: Closing a garbage-collected engine or context now logs only the first failure by default. To log all failures, use `engine.CloseOnGCFailureAction.PrintAll`. +* GR-35913: Updated the Javadoc of `Value#asHostObject()`, `Value#asNativePointer()`, and `Value#asProxyObject()` to clarify that these methods throw a `ClassCastException` rather than an `UnsupportedOperationException` when the value is not of the expected type. +* GR-35913: `Value#asHostObject()` throws `UnsupportedOperationException` if object is allocated in a foreign heap. ## Version 25.0.0 * GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens. diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java index 3b9d209bcf4a..ed6b80e3bf57 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java @@ -1554,7 +1554,7 @@ public boolean isNativePointer() { /** * Returns the value of the pointer as long value. * - * @throws UnsupportedOperationException if the value is not a pointer. + * @throws ClassCastException if the value is not a pointer. * @throws PolyglotException if a guest language error occurred during execution. * @throws IllegalStateException if the underlying context was closed. * @since 19.0 @@ -1586,8 +1586,9 @@ public boolean isHostObject() { /** * Returns the original Java host language object. * - * @throws UnsupportedOperationException if {@link #isHostObject()} is false or the - * Java host language object is allocated in a foreign heap. + * @throws ClassCastException if {@link #isHostObject()} is false + * @throws UnsupportedOperationException if Java host language object is allocated in a foreign + * heap. * @throws PolyglotException if a guest language error occurred during execution. * @throws IllegalStateException if the underlying context was closed. * @since 19.0 @@ -1621,7 +1622,7 @@ public boolean isProxyObject() { * Returns the unboxed instance of the {@link Proxy}. Proxies are not automatically boxed to * {@link #isHostObject() host objects} on host language call boundaries (Java methods). * - * @throws UnsupportedOperationException if a value is not a proxy object. + * @throws ClassCastException if a value is not a proxy object. * @throws PolyglotException if a guest language error occurred during execution. * @throws IllegalStateException if the underlying context was closed. * @since 19.0 diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java index c6a819e48dbb..fbcbcf59b049 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java @@ -5639,7 +5639,7 @@ static Object doCached(PolyglotLanguageContext context, Object receiver, Object[ @TruffleBoundary static RuntimeException asHostObjectIsolatedHeap(PolyglotLanguageContext context, Object receiver) { String polyglotMessage = String.format("Unsupported operation Value.asHostObject() for %s. The referenced host object resides in an isolated heap.", getValueInfo(context, receiver)); - return PolyglotEngineException.classCast(polyglotMessage); + return PolyglotEngineException.unsupported(polyglotMessage); } } } diff --git a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java index 4302fa7d90d9..6641c546c97c 100644 --- a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java +++ b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java @@ -371,7 +371,7 @@ public static void assertUnsupported(Value value, Trait... supported) { break; case HOST_OBJECT: assertFalse(value.isHostObject()); - assertFails(() -> value.asHostObject(), ClassCastException.class); + assertFails(() -> value.asHostObject(), Exception.class, UnsupportedCharsetException.class); break; case PROXY_OBJECT: assertFalse(value.isProxyObject()); From ece5ddd7c1cc869f7e6d5a0db990eb7523439d03 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Tue, 11 Nov 2025 15:23:15 +0100 Subject: [PATCH 04/14] Handle CancelExecution from polyglot isolate enter. --- .../oracle/truffle/api/TruffleStackTrace.java | 10 +---- .../polyglot/PolyglotExceptionImpl.java | 8 +--- .../oracle/truffle/polyglot/PolyglotImpl.java | 44 ++++++++++++------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java index 0684d7d675f9..37d917700d8f 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java @@ -269,15 +269,7 @@ public static TruffleStackTrace fillIn(Throwable throwable) { } private static boolean isHostException(Throwable throwable) { - try { - return LanguageAccessor.ENGINE.isHostException(throwable); - } catch (Throwable t) { - if (LanguageAccessor.ENGINE.isCancelExecution(t)) { - return false; - } else { - throw t; - } - } + return LanguageAccessor.ENGINE.isHostException(throwable); } private static final class TracebackElement { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java index 73f6c93ee496..86877ff6ea84 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java @@ -590,12 +590,8 @@ public Object apply(TruffleStackTraceElement guestFrame) { } private static boolean isHostException(Throwable cause) { - try { - InteropLibrary interop = InteropLibrary.getUncached(cause); - return interop.hasHostObject(cause) && interop.isException(cause); - } catch (CancelExecution cancelled) { - return false; - } + InteropLibrary interop = InteropLibrary.getUncached(cause); + return interop.hasHostObject(cause) && interop.isException(cause); } private static Throwable unboxHostException(Throwable cause) { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java index b4a1fbcb1d94..82bf41134be0 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java @@ -100,6 +100,7 @@ import com.oracle.truffle.api.strings.TruffleString; import com.oracle.truffle.api.strings.TruffleString.Encoding; import com.oracle.truffle.polyglot.EngineAccessor.AbstractClassLoaderSupplier; +import com.oracle.truffle.polyglot.PolyglotEngineImpl.CancelExecution; import com.oracle.truffle.polyglot.PolyglotEngineImpl.LogConfig; import com.oracle.truffle.polyglot.PolyglotLoggers.EngineLoggerProvider; @@ -928,22 +929,35 @@ static RuntimeException guestToHostException(PolyglotLangu PolyglotExceptionImpl suppressedImpl = null; PolyglotContextImpl.State localContextState = context.state; PolyglotImpl polyglot = context.engine.impl; - if (localContextState.isInvalidOrClosed()) { - exceptionImpl = new PolyglotExceptionImpl(polyglot, context.engine, localContextState, context.invalidResourceLimit, context.exitCode, languageContext, e, false, false); - } else { - try { - exceptionImpl = new PolyglotExceptionImpl(languageContext.getImpl(), languageContext.context.engine, localContextState, false, 0, - languageContext, e, true, entered); - } catch (Throwable t) { - /* - * It is possible that we fail to produce a guest value or interop message failed. - * We report the original exception without using interop messages. We also convert - * the exception thrown from the PolyglotExceptionImpl constructor to a new - * PolyglotException and add it to resulting exception suppressed exceptions. - */ - exceptionImpl = new PolyglotExceptionImpl(context.engine, localContextState, false, 0, e); - suppressedImpl = new PolyglotExceptionImpl(context.engine, localContextState, false, 0, t); + try { + if (localContextState.isInvalidOrClosed()) { + exceptionImpl = new PolyglotExceptionImpl(polyglot, context.engine, localContextState, context.invalidResourceLimit, context.exitCode, languageContext, e, false, false); + } else { + try { + exceptionImpl = new PolyglotExceptionImpl(languageContext.getImpl(), languageContext.context.engine, localContextState, false, 0, + languageContext, e, true, entered); + } catch (Throwable t) { + /* + * It is possible that we fail to produce a guest value or interop message + * failed. We report the original exception without using interop messages. We + * also convert the exception thrown from the PolyglotExceptionImpl constructor + * to a new PolyglotException and add it to resulting exception suppressed + * exceptions. + */ + exceptionImpl = new PolyglotExceptionImpl(context.engine, localContextState, false, 0, e); + suppressedImpl = new PolyglotExceptionImpl(context.engine, localContextState, false, 0, t); + } } + } catch (CancelExecution cancelExecution) { + /* + * The interop protocol is used to create a PolyglotExceptionImpl. A polyglot isolate + * always attempts to enter a context before processing an interop message. If the + * context has been cancelled, the enter operation throws a CancelExecution exception + * that is propagated to PolyglotExceptionImpl constructor. In such cases, the + * CancelExecution exception is wrapped in a PolyglotException and rethrown to the + * embedder. + */ + exceptionImpl = new PolyglotExceptionImpl(context.engine, localContextState, false, 0, cancelExecution); } APIAccess access = polyglot.getAPIAccess(); RuntimeException polyglotException = access.newLanguageException(exceptionImpl.getMessage(), polyglot.exceptionDispatch, exceptionImpl, context.getContextAPIOrNull()); From 44880cf1cd258448f791b7e68184344e2bf219e4 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Wed, 12 Nov 2025 17:21:57 +0100 Subject: [PATCH 05/14] Deprecated Env#isHostFunction. --- .../polyglot/impl/AbstractPolyglotImpl.java | 4 - .../polyglot/LanguageSPIHostInteropTest.java | 6 +- .../wrapper/GuestToHostLanguageService.java | 10 --- .../oracle/truffle/api/TruffleLanguage.java | 42 +++++---- .../com/oracle/truffle/api/impl/Accessor.java | 2 +- .../com/oracle/truffle/host/HostAccessor.java | 15 ++-- .../com/oracle/truffle/host/HostFunction.java | 17 ++-- .../truffle/host/HostLanguageService.java | 16 ---- .../oracle/truffle/host/HostToTypeNode.java | 2 +- .../truffle/polyglot/EngineAccessor.java | 12 +-- .../oracle/truffle/tck/tests/ValueAssert.java | 86 +++++++++++-------- 11 files changed, 103 insertions(+), 109 deletions(-) diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java index 08ba3efec38b..4574bf3cae38 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java @@ -1034,16 +1034,12 @@ public abstract void initializeHostContext(Object internalContext, Object contex public abstract T toHostType(Object hostNode, Object targetNode, Object hostContext, Object value, Class targetType, Type genericType); - public abstract boolean isHostValue(Object value); - public abstract Object unboxProxyObject(Object hostValue); public abstract Object toHostObject(Object context, Object value); public abstract RuntimeException toHostException(Object hostContext, Throwable exception); - public abstract boolean isHostFunction(Object obj); - public abstract boolean isHostSymbol(Object obj); public abstract Object createHostAdapter(Object hostContextObject, Object[] types, Object classOverrides); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java index 5db116ecce68..ee8b7cac7b85 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java @@ -367,19 +367,23 @@ private static void assertThrowsExceptionWithCause(Callable callable, Class T toHostType(Object hostNode, Object targetNode, Object hostContext, throw new UnsupportedOperationException(); } - @Override - public boolean isHostValue(Object value) { - throw new UnsupportedOperationException(); - } - @Override public Object unboxProxyObject(Object hostValue) { throw new UnsupportedOperationException(); @@ -123,11 +118,6 @@ public RuntimeException toHostException(Object hostContext, Throwable exception) throw new UnsupportedOperationException(); } - @Override - public boolean isHostFunction(Object obj) { - return false; - } - @Override public boolean isHostSymbol(Object obj) { return false; diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index b254c158c8b9..e9636cc5eb8d 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -2329,11 +2329,14 @@ public Object asBoxedGuestValue(Object guestObject) { * Truffle interop. * * @since 19.0 + * @deprecated Use + * {@code InteropLibrary.isHostObject(obj) && InteropLibrary.isExecutable(obj) && !InteropLibrary.hasMembers(obj)} */ + @Deprecated(since = "25.1") @SuppressWarnings("static-method") public boolean isHostFunction(Object value) { try { - return LanguageAccessor.engineAccess().isHostFunction(polyglotLanguageContext, value); + return LanguageAccessor.engineAccess().isHostFunction(value); } catch (Throwable t) { throw engineToLanguageException(t); } @@ -2363,17 +2366,17 @@ public Object findMetaObject(Object value) { /** * Tests whether an exception is a host exception thrown by a Java Interop method * invocation. - * + *

* Host exceptions may be thrown by interoperability messages. The host exception may be * unwrapped using {@link #asHostException(Throwable)}. * * @param exception the {@link Throwable} to test * @return {@code true} if the {@code exception} is a host exception, {@code false} * otherwise - * @deprecated Use - * {@code InteropLibrary.isHostObject(obj) && InteropLibrary.isException(obj)}. * @see #asHostException(Throwable) * @since 19.0 + * @deprecated Use + * {@code InteropLibrary.isHostObject(obj) && InteropLibrary.isException(obj)}. */ @Deprecated @SuppressWarnings("static-method") @@ -2387,18 +2390,18 @@ public boolean isHostException(Throwable exception) { /** * Unwraps a host exception thrown by a Java method invocation. - * + *

* Host exceptions may be thrown by interoperability messages. The host exception may be * unwrapped using {@link #asHostException(Throwable)}. * - * @deprecated Use - * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#getHostObject(Object) - * getHostObject}. * @param exception the host exception to unwrap * @return the original Java exception * @throws IllegalArgumentException if the {@code exception} is not a host exception * @see #isHostException(Throwable) * @since 19.0 + * @deprecated Use + * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#getHostObject(Object) + * getHostObject}. */ @Deprecated @SuppressWarnings("static-method") @@ -2583,10 +2586,9 @@ public boolean isPolyglotBindingsAccessAllowed() { * Allows it to be determined if this {@link org.graalvm.polyglot.Context} can execute code * written in a language with a given MIME type. * + * @return a boolean that indicates if the MIME type is supported * @see Source#getMimeType() * @see #parsePublic(Source, String...) - * - * @return a boolean that indicates if the MIME type is supported * @since 0.11 */ @TruffleBoundary @@ -2744,7 +2746,7 @@ public OutputStream err() { * language} may also be associated with additional services. One can request * implementations of such services by calling this method with the type identifying the * requested service and its API. - * + *

* Services that can be obtained via this method include * {@link com.oracle.truffle.api.instrumentation.Instrumenter} and others. * @@ -3063,13 +3065,13 @@ public TruffleFile getPublicTruffleFile(URI uri) { * * @param path the absolute or relative path to create {@link TruffleFile} for * @return {@link TruffleFile} - * @since 19.3.0 * @throws UnsupportedOperationException when the {@link FileSystem} supports only * {@link URI} * @throws IllegalArgumentException if the {@code path} string cannot be converted to a * {@link Path} * @see #getTruffleFileInternal(String, Predicate) * @see #getPublicTruffleFile(java.lang.String) + * @since 19.3.0 */ @TruffleBoundary public TruffleFile getInternalTruffleFile(String path) { @@ -3090,13 +3092,13 @@ public TruffleFile getInternalTruffleFile(String path) { * * @param uri the {@link URI} to create {@link TruffleFile} for * @return {@link TruffleFile} - * @since 19.3.0 * @throws UnsupportedOperationException when {@link URI} scheme is not supported * @throws IllegalArgumentException if preconditions on the {@code uri} do not hold. * @throws java.nio.file.FileSystemNotFoundException is the file system, identified by the * {@code uri}, does not exist and cannot be created automatically * @see #getTruffleFileInternal(URI, Predicate) * @see #getPublicTruffleFile(java.net.URI) + * @since 19.3.0 */ @TruffleBoundary public TruffleFile getInternalTruffleFile(URI uri) { @@ -3151,11 +3153,12 @@ public TruffleFile getInternalTruffleFile(URI uri) { * {@link URI} * @throws IllegalArgumentException if the {@code path} string cannot be converted to a * {@link Path} - * @since 21.1.0 * @see #getTruffleFileInternal(URI, Predicate) * @see #getPublicTruffleFile(String) * @see #getInternalTruffleFile(String) + * @since 21.1.0 * + * */ @TruffleBoundary public TruffleFile getTruffleFileInternal(String path, Predicate filter) { @@ -3176,11 +3179,12 @@ public TruffleFile getTruffleFileInternal(String path, Predicate fi * @throws IllegalArgumentException if preconditions on the {@code uri} do not hold. * @throws java.nio.file.FileSystemNotFoundException is the file system, identified by the * {@code uri}, does not exist and cannot be created automatically - * @since 21.1.0 * @see #getTruffleFileInternal(String, Predicate) * @see #getPublicTruffleFile(URI) * @see #getInternalTruffleFile(URI) + * @since 21.1.0 * + * */ @TruffleBoundary public TruffleFile getTruffleFileInternal(URI uri, Predicate filter) { @@ -3595,7 +3599,6 @@ public Object createHostAdapterClassWithStaticOverrides(Class[] types, Object * @throws UnsupportedOperationException if creating adapter classes is not supported on * this runtime at all, which is currently the case for native images. * @throws NullPointerException if {@code types} is null - * * @see #createHostAdapterWithClassOverrides(Object[], Object) * @since 22.1 */ @@ -3637,7 +3640,6 @@ public Object createHostAdapter(Object[] types) { * @throws UnsupportedOperationException if creating adapter classes is not supported on * this runtime at all, which is currently the case for native images. * @throws NullPointerException if either {@code types} or {@code classOverrides} is null. - * * @see #createHostAdapter(Object[]) * @since 22.1 */ @@ -3716,6 +3718,7 @@ public TruffleFile getInternalResource(String resourceId) throws IOException { * @return a {@link TruffleLogger} * @since 21.1 * + * */ @TruffleBoundary public TruffleLogger getLogger(String loggerName) { @@ -4066,7 +4069,7 @@ static RuntimeException engineToLanguageException(T * current {@link Node}, if available, as parameter. *

* Example intended usage: - * + *

* See {@link ContextReference} for a full usage example. * * @since 0.25 revised in 21.3 @@ -4353,8 +4356,8 @@ public enum ContextPolicy { /** * Mode of exit operation. * - * @since 22.0 * @see #exitContext(Object, ExitMode, int) + * @since 22.0 */ public enum ExitMode { /** @@ -4362,6 +4365,7 @@ public enum ExitMode { * * @since 22.0 * + * */ NATURAL, /** diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java index 4dc363405bb2..213df87a6a6b 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java @@ -571,7 +571,7 @@ public abstract Thread createThread(Object polyglotLanguageContext, Runnable run public abstract boolean isHostObject(Object value); - public abstract boolean isHostFunction(Object languageContext, Object value); + public abstract boolean isHostFunction(Object value); public abstract boolean isHostSymbol(Object languageContext, Object guestObject); diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java index 46751911bd43..79a1b00bff51 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java @@ -110,18 +110,19 @@ public boolean isDisconnectedHostProxy(Object value) { @Override public boolean isDisconnectedHostObject(Object obj) { - return HostObject.isInstance(null, obj); + return obj instanceof HostObject || obj instanceof HostException || obj instanceof HostFunction; } @Override public Object unboxDisconnectedHostObject(Object hostValue) { - Object v = HostLanguage.unwrapIfScoped(null, hostValue); - if (v instanceof HostObject) { - return ((HostObject) v).obj; - } else if (v instanceof HostException) { - return ((HostException) v).delegate.obj; + if (hostValue instanceof HostObject) { + return ((HostObject) hostValue).obj; + } else if (hostValue instanceof HostException) { + return ((HostException) hostValue).delegate.obj; + } else if (hostValue instanceof HostFunction) { + throw new UnsupportedOperationException(); } - return v; + return hostValue; } @Override diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java index 6382b2985e0e..c7e6948fd277 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java @@ -46,6 +46,7 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.ArityException; +import com.oracle.truffle.api.interop.HeapIsolationException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedTypeException; @@ -67,18 +68,22 @@ final class HostFunction implements TruffleObject { this.context = context; } - public static boolean isInstance(HostLanguage language, TruffleObject obj) { - return isInstance(language, (Object) obj); + @SuppressWarnings("static-method") + @ExportMessage + boolean isExecutable() { + return true; } - public static boolean isInstance(HostLanguage language, Object obj) { - return HostLanguage.unwrapIfScoped(language, obj) instanceof HostFunction; + @SuppressWarnings("static-method") + @ExportMessage + boolean hasHostObject() { + return true; } @SuppressWarnings("static-method") @ExportMessage - boolean isExecutable() { - return true; + Object getHostObject() throws HeapIsolationException { + throw HeapIsolationException.create(); } @ExportMessage diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java index 1b464c42ca3f..c9374c38ca21 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java @@ -161,15 +161,6 @@ private boolean validHostValue(Object node, Object hostValue) { return unboxed == hostValue; } - @Override - public boolean isHostValue(Object value) { - Object obj = HostLanguage.unwrapIfScoped(language, value); - return (obj instanceof HostObject) || - (obj instanceof HostFunction) || - (obj instanceof HostException) || - (obj instanceof HostProxy); - } - @Override public Object unboxProxyObject(Object hostValue) { return HostProxy.toProxyHostObject(language, hostValue); @@ -186,11 +177,6 @@ public Object asHostDynamicClass(Object context, Class value) { return null; } - @Override - public boolean isHostFunction(Object value) { - return HostFunction.isInstance(language, value); - } - @Override public boolean isHostProxy(Object value) { return HostProxy.isProxyGuestObject(language, value); @@ -238,7 +224,6 @@ public RuntimeException toHostException(Object context, Throwable exception) { public Object migrateValue(Object targetContext, Object value, Object valueContext) { assert targetContext != valueContext; if (value instanceof TruffleObject) { - assert value instanceof TruffleObject; if (HostObject.isInstance(language, value)) { return HostObject.withContext(language, value, (HostContext) HostAccessor.ENGINE.getHostContext(targetContext)); } else if (value instanceof HostProxy) { @@ -248,7 +233,6 @@ public Object migrateValue(Object targetContext, Object value, Object valueConte * The only way this can happen is with Value.asValue(TruffleObject). If it happens * otherwise, its wrong. */ - assert value instanceof TruffleObject; return value; } else { // cannot migrate diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java index e0d37f971436..9bc421ac3684 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java @@ -322,7 +322,7 @@ static boolean canConvert(Node node, Object value, Class targetType, Type gen } if (value instanceof TruffleObject) { - if (priority < HOST_PROXY && HostObject.isInstance(language, value)) { + if (priority < HOST_PROXY && interop.hasHostObject(value)) { return false; } else { if (priority >= FUNCTION_PROXY && HostInteropReflect.isFunctionalInterface(targetType) && diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java index cba4c2d53008..165bb9f07e54 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java @@ -628,7 +628,7 @@ public boolean isMultiThreaded(Object guestObject) { } if (isPrimitive(guestObject)) { return false; - } else if (context.engine.host.isHostValue(guestObject) || guestObject instanceof PolyglotBindings) { + } else if (InteropLibrary.getFactory().getUncached(guestObject).hasHostObject(guestObject) || context.engine.host.isHostProxy(guestObject) || guestObject instanceof PolyglotBindings) { return true; } PolyglotLanguage language = findObjectLanguage(context.engine, guestObject); @@ -1552,13 +1552,9 @@ public Object asHostObject(Object obj) throws Exception { } @Override - public boolean isHostFunction(Object languageContext, Object obj) { - PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context; - PolyglotEngineImpl engine = context.engine; - // During context pre-initialization, engine.host is null, languages are not allowed to - // use host interop. But the call to isHostFunction is supported and returns false - // because languages cannot create a HostObject. - return !engine.inEnginePreInitialization && engine.host.isHostFunction(obj); + public boolean isHostFunction(Object obj) { + InteropLibrary interop = InteropLibrary.getUncached(obj); + return interop.hasHostObject(obj) && interop.isExecutable(obj) && !interop.hasMembers(obj); } @Override diff --git a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java index 6641c546c97c..c385cf1293e0 100644 --- a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java +++ b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java @@ -210,7 +210,7 @@ public static void assertUnsupported(Value value, Trait... supported) { assertFails(() -> value.asDouble(), NullPointerException.class); } else { - if (value.isHostObject() && value.asHostObject() instanceof Number) { + if (isReachableHostObject(value) && value.asHostObject() instanceof Number) { assertSame(value.asHostObject(), value.as(Number.class)); } else { assertFails(() -> value.as(Number.class), ClassCastException.class); @@ -282,7 +282,7 @@ public static void assertUnsupported(Value value, Trait... supported) { if (value.isNull()) { assertNull(value.as(Map.class)); } else { - if ((!value.isHostObject() || (!(value.asHostObject() instanceof Map))) && !value.hasHashEntries()) { + if ((!isReachableHostObject(value) || (!(value.asHostObject() instanceof Map))) && !value.hasHashEntries()) { assertFails(() -> value.as(Map.class), ClassCastException.class); } } @@ -301,7 +301,7 @@ public static void assertUnsupported(Value value, Trait... supported) { if (value.hasMembers()) { assertFails(() -> value.as(FUNCTION).apply(null), UnsupportedOperationException.class); assertFails(() -> value.as(IsFunctionalInterfaceVarArgs.class).foobarbaz(123), UnsupportedOperationException.class); - } else if (!value.isHostObject() || (!(value.asHostObject() instanceof Function))) { + } else if (!isReachableHostObject(value) || (!(value.asHostObject() instanceof Function))) { assertFails(() -> value.as(FUNCTION), ClassCastException.class); assertFails(() -> value.as(IsFunctionalInterfaceVarArgs.class), ClassCastException.class); } @@ -320,7 +320,7 @@ public static void assertUnsupported(Value value, Trait... supported) { if (value.hasMembers()) { assertFails(() -> value.as(FUNCTION).apply(null), UnsupportedOperationException.class); assertFails(() -> value.as(IsFunctionalInterfaceVarArgs.class).foobarbaz(123), UnsupportedOperationException.class); - } else if (!value.isHostObject() || (!(value.asHostObject() instanceof Function))) { + } else if (!isReachableHostObject(value) || (!(value.asHostObject() instanceof Function))) { assertFails(() -> value.as(FUNCTION), ClassCastException.class); assertFails(() -> value.as(IsFunctionalInterfaceVarArgs.class), ClassCastException.class); } @@ -335,7 +335,7 @@ public static void assertUnsupported(Value value, Trait... supported) { assertFails(() -> value.setArrayElement(0, null), UnsupportedOperationException.class); assertFails(() -> value.getArraySize(), UnsupportedOperationException.class); if (!value.isNull()) { - if ((!value.isHostObject() || (!(value.asHostObject() instanceof List) && !(value.asHostObject() instanceof Object[])))) { + if ((!isReachableHostObject(value) || (!(value.asHostObject() instanceof List) && !(value.asHostObject() instanceof Object[])))) { assertFails(() -> value.as(List.class), ClassCastException.class); assertFails(() -> value.as(Object[].class), ClassCastException.class); } @@ -362,7 +362,7 @@ public static void assertUnsupported(Value value, Trait... supported) { assertFails(() -> value.writeBufferDouble(ByteOrder.LITTLE_ENDIAN, 0, 0.0), UnsupportedOperationException.class); if (!value.isNull()) { - if ((!value.isHostObject() || (!(value.asHostObject() instanceof ByteBuffer)))) { + if ((!isReachableHostObject(value) || (!(value.asHostObject() instanceof ByteBuffer)))) { assertFails(() -> value.as(ByteBuffer.class), ClassCastException.class); } } else { @@ -371,7 +371,7 @@ public static void assertUnsupported(Value value, Trait... supported) { break; case HOST_OBJECT: assertFalse(value.isHostObject()); - assertFails(() -> value.asHostObject(), Exception.class, UnsupportedCharsetException.class); + assertFails(() -> value.asHostObject(), ClassCastException.class, UnsupportedOperationException.class); break; case PROXY_OBJECT: assertFalse(value.isProxyObject()); @@ -479,7 +479,7 @@ public static void assertUnsupported(Value value, Trait... supported) { if (value.isNull()) { assertNull(value.as(Map.class)); } else { - if ((!value.isHostObject() || (!(value.asHostObject() instanceof Map))) && !value.hasMembers()) { + if ((!isReachableHostObject(value) || (!(value.asHostObject() instanceof Map))) && !value.hasMembers()) { assertFails(() -> value.as(Map.class), ClassCastException.class); } } @@ -490,6 +490,19 @@ public static void assertUnsupported(Value value, Trait... supported) { } } + private static boolean isReachableHostObject(Value value) { + if (!value.isHostObject()) { + return false; + } + try { + value.asHostObject(); + return true; + } catch (UnsupportedOperationException unsupported) { + // HeapIsolationException - unboxing is not supported. + return false; + } + } + @SuppressWarnings("unchecked") private static void assertValueImpl(Value value, int depth, boolean hasHostAccess, Trait... expectedTypes) { if (depth > 1) { @@ -567,36 +580,37 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces break; case HOST_OBJECT: assertTrue(msg, value.isHostObject()); - Object hostObject = value.asHostObject(); - assertFalse(hostObject instanceof Proxy); - boolean isStaticClass = false; - if (hasHostAccess && hostObject != null && value.hasMembers() && !java.lang.reflect.Proxy.isProxyClass(hostObject.getClass())) { - if (hostObject instanceof Class) { - isStaticClass = value.hasMember("class"); - if (isStaticClass) { - assertClassMembers(value, (Class) hostObject, true); + if (isReachableHostObject(value)) { + Object hostObject = value.asHostObject(); + assertFalse(hostObject instanceof Proxy); + boolean isStaticClass = false; + if (hasHostAccess && hostObject != null && value.hasMembers() && !java.lang.reflect.Proxy.isProxyClass(hostObject.getClass())) { + if (hostObject instanceof Class) { + isStaticClass = value.hasMember("class"); + if (isStaticClass) { + assertClassMembers(value, (Class) hostObject, true); + } else { + assertClassMembers(value, Class.class, false); + assertTrue(value.hasMember("static")); + } } else { - assertClassMembers(value, Class.class, false); - assertTrue(value.hasMember("static")); - } - } else { - // Asserts that value exposes the same members as the host object's - // class first public inclusive ancestor. - for (Class clazz = hostObject.getClass(); clazz != null; clazz = clazz.getSuperclass()) { - if (Modifier.isPublic(clazz.getModifiers())) { - assertClassMembers(value, clazz, false); - break; + // Asserts that value exposes the same members as the host object's + // class first public inclusive ancestor. + for (Class clazz = hostObject.getClass(); clazz != null; clazz = clazz.getSuperclass()) { + if (Modifier.isPublic(clazz.getModifiers())) { + assertClassMembers(value, clazz, false); + break; + } } } } + if (isStaticClass) { + assertNotEquals(Value.asValue(hostObject), value); + } else { + assertEquals(Value.asValue(hostObject), value); + } + assertEquals(Value.asValue(hostObject).hashCode(), value.hashCode()); } - if (isStaticClass) { - assertNotEquals(Value.asValue(hostObject), value); - } else { - assertEquals(Value.asValue(hostObject), value); - } - assertEquals(Value.asValue(hostObject).hashCode(), value.hashCode()); - break; case PROXY_OBJECT: assertTrue(msg, value.isProxyObject()); @@ -621,7 +635,7 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces if (value.isNull()) { assertNull(value.as(STRING_OBJECT_MAP)); - } else if (value.isHostObject() && value.asHostObject() instanceof Map) { + } else if (isReachableHostObject(value) && value.asHostObject() instanceof Map) { Map expectedValues = value.asHostObject(); assertEquals(value.as(OBJECT_OBJECT_MAP), expectedValues); } else if (value.hasHashEntries()) { @@ -756,7 +770,7 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces } private static boolean isSameHostObject(Value a, Value b) { - return a.isHostObject() && b.isHostObject() && a.asHostObject() == b.asHostObject(); + return isReachableHostObject(a) && isReachableHostObject(b) && a.asHostObject() == b.asHostObject(); } @SuppressWarnings("unchecked") @@ -777,7 +791,7 @@ private static void assertValueArrayElements(Value value, int depth, boolean has List objectList1 = value.as(OBJECT_LIST); List objectList2 = Arrays.asList(value.as(Object[].class)); - if (!value.isHostObject() || !(value.asHostObject() instanceof List)) { + if (!isReachableHostObject(value) || !(value.asHostObject() instanceof List)) { assertFalse(objectList1.equals(objectList2)); } assertTrue(objectList1.equals(objectList1)); From 41c1d560272b0795266245517cfe4f92163c8aaa Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Thu, 13 Nov 2025 15:55:51 +0100 Subject: [PATCH 06/14] Added InteropLibrary#hasStaticReceiver InteropLibrary#getStaticReceiver. --- sdk/CHANGELOG.md | 1 + .../src/org/graalvm/polyglot/Value.java | 47 ++++++++++ .../polyglot/impl/AbstractPolyglotImpl.java | 6 ++ truffle/CHANGELOG.md | 1 + .../oracle/truffle/api/debug/DebugValue.java | 24 +++++ .../truffle/api/interop/InteropLibrary.java | 84 +++++++++++++++++ .../api/test/polyglot/HostAccessTest.java | 24 ++--- .../api/test/polyglot/ValueAPITest.java | 28 +++--- .../polyglot/ValueHostConversionTest.java | 9 +- .../test/polyglot/ValueHostInteropTest.java | 8 ++ .../com/oracle/truffle/host/HostObject.java | 16 ++++ .../polyglot/PolyglotValueDispatch.java | 90 ++++++++++++++++++- .../oracle/truffle/tck/tests/ValueAssert.java | 15 +++- 13 files changed, 322 insertions(+), 31 deletions(-) diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 1a806f6275be..bea04cdc4fbf 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -12,6 +12,7 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f * GR-69590: Closing a garbage-collected engine or context now logs only the first failure by default. To log all failures, use `engine.CloseOnGCFailureAction.PrintAll`. * GR-35913: Updated the Javadoc of `Value#asHostObject()`, `Value#asNativePointer()`, and `Value#asProxyObject()` to clarify that these methods throw a `ClassCastException` rather than an `UnsupportedOperationException` when the value is not of the expected type. * GR-35913: `Value#asHostObject()` throws `UnsupportedOperationException` if object is allocated in a foreign heap. +* GR-71402: Added `Value#hasStaticReceiver` and `Value#getStaticReceiver` returning the Value's static receiver. ## Version 25.0.0 * GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens. diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java index ed6b80e3bf57..8ba492fbd752 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java @@ -1008,6 +1008,53 @@ public boolean removeMember(String identifier) { } } + /** + * Returns {@code true} if this value provides a {@linkplain #getStaticReceiver() static + * receiver}. A static receiver represents the static or class-level members associated with + * this value's type, such as static fields or methods. + *

+ * This method may only return {@code true} if {@link #hasMembers()} also returns {@code true}. + * + * @throws IllegalStateException if the context is already {@linkplain Context#close() closed} + * @throws PolyglotException if a guest language error occurs during execution + * @see #getStaticReceiver() + * @since 25.1 + */ + public boolean hasStaticReceiver() { + return dispatch.hasStaticReceiver(this.context, receiver); + } + + /** + * Returns the static receiver associated with this value. A static receiver is an object that + * exposes static members, members whose values or behaviors are independent of any particular + * instance of this value. + *

+ * The returned static receiver can be used to access static members using + * {@link #getMember(String)}, {@link #getMemberKeys()}, or + * {@link #invokeMember(String, Object...)}. + *

+ * When this value {@linkplain #hasMembers() has members}, its static receiver is also expected + * to provide (static) members and/or declared members representing the value's static context. + *

+ * Examples: + *

    + *
  • For a Java class instance, the static receiver exposes the class's static fields and + * methods.
  • + *
  • For a Python object, the static receiver exposes class-level attributes and methods.
  • + *
+ * + * @throws UnsupportedOperationException if and only if this value does not + * {@linkplain #hasStaticReceiver() have a static receiver} + * @throws IllegalStateException if the context is already {@linkplain Context#close() closed} + * @throws PolyglotException if a guest language error occurs during execution + * @see #hasStaticReceiver() + * @see #getMemberKeys() + * @since 25.1 + */ + public Value getStaticReceiver() { + return (Value) dispatch.getStaticReceiver(this.context, receiver); + } + // executable /** diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java index 4574bf3cae38..fc5e8c79595f 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java @@ -1139,6 +1139,12 @@ public Set getMemberKeys(Object context, Object receiver) { public abstract boolean removeMember(Object context, Object receiver, String key); + public boolean hasStaticReceiver(Object context, Object receiver) { + return false; + } + + public abstract Object getStaticReceiver(Object context, Object receiver); + public boolean canExecute(Object context, Object receiver) { return false; } diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 52d4864af6d0..74434d410730 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -39,6 +39,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-70086 Deprecated `Message.resolve(Class, String)`. Use `Message.resolveExact(Class, String, Class...)` with argument types instead. This deprecation was necessary as library messages are no longer unique by message name, if the previous message was deprecated. * GR-71299 Improved the responsiveness of the Truffle compilation queue by refining the computation of the execution rate of compilation units in the queue. * Added `engine.TraversingQueueRateHalfLife` to allow fine-tuning of the compilation queue responsiveness. +* GR-71402: Added `InteropLibrary#hasStaticReceiver` and `InteropLibrary#getStaticReceiver` returning the static receiver for given object. * GR-36894: Added `DynamicObject` nodes for dealing with `DynamicObject` properties and shapes, as a more lightweight replacement for `DynamicObjectLibrary`, including: * `DynamicObject.GetNode`: gets the value of a property or a default value if absent diff --git a/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugValue.java b/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugValue.java index f5f4579225be..b24248072bc6 100644 --- a/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugValue.java +++ b/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugValue.java @@ -1031,6 +1031,30 @@ public void accept(Breakpoint b) { return breakpoints[0] != null ? breakpoints[0] : Collections.emptyList(); } + /** + * Returns a value that provides static members whose value is independent on a specific + * instance. Returns {@code null} when no static receiver is available. + * + * @throws DebugException when guest language code throws an exception + * @since 25.1 + */ + public final DebugValue getStaticReceiver() { + if (!isReadable()) { + return null; + } + Object view = getLanguageView(); + try { + if (INTEROP.hasStaticReceiver(view)) { + return new HeapValue(getSession(), resolveLanguage(), null, INTEROP.getStaticReceiver(view)); + } + } catch (ThreadDeath td) { + throw td; + } catch (Throwable ex) { + throw DebugException.create(getSession(), ex, resolveLanguage(), null, true, null); + } + return null; + } + /** * Provides properties representing an internal structure of this value. The returned collection * is not thread-safe. If the value is not {@link #isReadable() readable} then null diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java index fb4a4f376ea6..e648870798cd 100644 --- a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java @@ -141,6 +141,7 @@ *
  • {@link #isInstantiable(Object) instantiable} *
  • {@link #isPointer(Object) pointer} *
  • {@link #hasMembers(Object) members} + *
  • {@link #hasStaticReceiver(Object receiver) static receiver} *
  • {@link #hasHashEntries(Object) hash entries} *
  • {@link #hasArrayElements(Object) array elements} *
  • {@link #hasBufferElements(Object) buffer elements} @@ -962,6 +963,56 @@ public boolean hasMemberWriteSideEffects(Object receiver, String member) { return false; } + /** + * Returns {@code true} if the given receiver provides a {@linkplain #getStaticReceiver(Object) + * static receiver}. A static receiver represents the static or class-level members associated + * with the receiver's type, such as static fields or methods. + *

    + * This message may only return {@code true} if {@link #hasMembers(Object)} returns + * {@code true}. Invoking this message must not cause any observable side effects. + *

    + * By default, this method returns {@code false}. + * + * @see #getStaticReceiver(Object) + * @since 25.1 + */ + @Abstract(ifExported = {"getStaticReceiver"}) + public boolean hasStaticReceiver(Object receiver) { + return false; + } + + /** + * Returns the static receiver associated with the given receiver. A static receiver is an + * object that exposes static members, members whose values or behaviors are independent of any + * particular instance of the receiver. + *

    + * The static receiver typically serves as an artificial or meta-level object that provides + * access to instance-independent members declared by the receiver's type or meta object. The + * returned object can be used as the receiver for interop messages such as + * {@link #readMember(Object, String) readMember}, {@link #writeMember(Object, String, Object) + * writeMember}, and {@link #invokeMember(Object, String, Object...) invokeMember} when + * accessing static members. + *

    + * Examples: + *

      + *
    • In Java, the static receiver would expose static fields and methods of a class.
    • + *
    • In Python, the static receiver would expose class-level variables and methods.
    • + *
    + *

    + * When the receiver {@linkplain #hasMembers(Object) has members}, the corresponding static + * receiver is also expected to have (static) members and/or declared members representing the + * receiver's static context. + * + * @throws UnsupportedMessageException if and only if the receiver does not + * {@linkplain #hasStaticReceiver(Object) have a static receiver} + * @see #hasStaticReceiver(Object) + * @since 25.1 + */ + @Abstract(ifExported = {"hasStaticReceiver"}) + public Object getStaticReceiver(Object receiver) throws UnsupportedMessageException { + throw UnsupportedMessageException.create(); + } + // Hashes /** * Returns {@code true} if the receiver may have hash entries. Therefore, at least one of @@ -3905,6 +3956,39 @@ public boolean isMemberInternal(Object receiver, String identifier) { return result; } + @Override + public boolean hasStaticReceiver(Object receiver) { + assert preCondition(receiver); + boolean result = delegate.hasStaticReceiver(receiver); + assert validProtocolReturn(receiver, result); + return result; + } + + @Override + public Object getStaticReceiver(Object receiver) throws UnsupportedMessageException { + if (CompilerDirectives.inCompiledCode()) { + return delegate.getStaticReceiver(receiver); + } + assert preCondition(receiver); + boolean hadStaticReceiver = delegate.hasStaticReceiver(receiver); + try { + Object result = delegate.getStaticReceiver(receiver); + assert hadStaticReceiver || isMultiThreaded(receiver) : violationInvariant(receiver); + assert validInteropReturn(receiver, result); + assert verifyStaticReceiver(receiver, result); + return result; + } catch (InteropException e) { + assert e instanceof UnsupportedMessageException : violationPost(receiver, e); + assert isMultiThreaded(receiver) || !hadStaticReceiver : violationInvariant(receiver); + throw e; + } + } + + private static boolean verifyStaticReceiver(Object instanceReceiver, Object staticReceiver) throws UnsupportedMessageException { + assert UNCACHED.hasMembers(instanceReceiver) && UNCACHED.hasMembers(staticReceiver) : violationPost(instanceReceiver, staticReceiver); + return true; + } + @Override public boolean hasHashEntries(Object receiver) { assert preCondition(receiver); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostAccessTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostAccessTest.java index 8b379ad2add1..52e36a8cfba3 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostAccessTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostAccessTest.java @@ -378,7 +378,7 @@ public void testArrayAccessEnabled() { } catch (UnsupportedOperationException e) { } assertEquals(2 /* arr.length and arr.clone(). */, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); } @Test @@ -397,7 +397,7 @@ private static void assertArrayAccessDisabled(Context context) { int[] array = new int[]{1, 2, 3}; Value value = context.asValue(array); assertSame(array, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); } @Test @@ -423,7 +423,7 @@ private static void assertBufferAccessEnabled(Context context) { assertEquals(42, value.readBufferByte(0)); value.writeBufferByte(1, (byte) 24); assertEquals(24, value.readBufferByte(1)); - ValueAssert.assertValue(value, false, Trait.BUFFER_ELEMENTS, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.BUFFER_ELEMENTS, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); } @Test @@ -432,7 +432,7 @@ public void testBufferAccessDisabled() { ByteBuffer buffer = ByteBuffer.allocate(2); Value value = context.asValue(buffer); assertSame(buffer, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); } /* @@ -482,7 +482,7 @@ public void testListAccessEnabled() { assertEquals(0, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); assertArrayAccessDisabled(context); } @@ -508,7 +508,7 @@ public void testIterableAccessEnabled() { assertFalse(iterator.hasIteratorNextElement()); assertSame(iterable, value.asHostObject()); assertEquals(0, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); assertListAccessDisabled(context, true); assertArrayAccessDisabled(context); } @@ -533,7 +533,7 @@ public void testIteratorAccessEnabled() { assertFalse(value.hasIteratorNextElement()); assertSame(iterator, value.asHostObject()); assertEquals(0, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.ITERATOR, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.ITERATOR, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); assertIterableAccessDisabled(context); assertListAccessDisabled(context, false); assertArrayAccessDisabled(context); @@ -565,7 +565,7 @@ public void testMapAccessEnabled() { assertEquals(1, entry.getArrayElement(0).asInt()); assertEquals(Integer.toBinaryString(1), entry.getArrayElement(1).asString()); assertEquals(0, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.HASH, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.HASH, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); assertArrayAccessDisabled(context); } @@ -591,7 +591,7 @@ private static void assertListAccessDisabled(Context context, boolean iterableAc List array = new ArrayList<>(Arrays.asList(1, 2, 3)); Value value = context.asValue(array); assertSame(array, value.asHostObject()); - List expectedTypes = new ArrayList<>(Arrays.asList(Trait.MEMBERS, Trait.HOST_OBJECT)); + List expectedTypes = new ArrayList<>(Arrays.asList(Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER)); if (iterableAccess) { expectedTypes.add(Trait.ITERABLE); } @@ -602,21 +602,21 @@ private static void assertIterableAccessDisabled(Context context) { Iterable iterable = new IterableImpl<>(1, 2, 3); Value value = context.asValue(iterable); assertSame(iterable, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); } private static void assertIteratorAccessDisabled(Context context) { Iterator iterator = new IteratorImpl<>(1, 2, 3); Value value = context.asValue(iterator); assertSame(iterator, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); } private static void assertMapAccessDisabled(Context context) { Map map = Collections.singletonMap(1, "string"); Value value = context.asValue(map); assertSame(map, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); } public static final class TargetClass1 { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java index ffda6dc6d114..39a89f80eec1 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java @@ -40,6 +40,7 @@ */ package com.oracle.truffle.api.test.polyglot; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STATIC_RECEIVER; import static com.oracle.truffle.tck.tests.ValueAssert.assertValue; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.ARRAY_ELEMENTS; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN; @@ -245,14 +246,14 @@ public void testString() { @Test public void testDatesTimesZonesAndDuration() { - assertValueInContexts(context.asValue(LocalDate.now()), HOST_OBJECT, MEMBERS, DATE, EXECUTABLE); - assertValueInContexts(context.asValue(LocalTime.now()), HOST_OBJECT, MEMBERS, TIME, EXECUTABLE); - assertValueInContexts(context.asValue(LocalDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, EXECUTABLE); - assertValueInContexts(context.asValue(Instant.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, EXECUTABLE); - assertValueInContexts(context.asValue(ZonedDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE); - assertValueInContexts(context.asValue(ZoneId.of("UTC")), HOST_OBJECT, MEMBERS, TIMEZONE); - assertValueInContexts(context.asValue(Date.from(Instant.now())), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE); - assertValueInContexts(context.asValue(Duration.ofMillis(100)), HOST_OBJECT, MEMBERS, DURATION); + assertValueInContexts(context.asValue(LocalDate.now()), HOST_OBJECT, MEMBERS, DATE, EXECUTABLE, STATIC_RECEIVER); + assertValueInContexts(context.asValue(LocalTime.now()), HOST_OBJECT, MEMBERS, TIME, EXECUTABLE, STATIC_RECEIVER); + assertValueInContexts(context.asValue(LocalDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, EXECUTABLE, STATIC_RECEIVER); + assertValueInContexts(context.asValue(Instant.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, EXECUTABLE, STATIC_RECEIVER); + assertValueInContexts(context.asValue(ZonedDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, STATIC_RECEIVER); + assertValueInContexts(context.asValue(ZoneId.of("UTC")), HOST_OBJECT, MEMBERS, TIMEZONE, STATIC_RECEIVER); + assertValueInContexts(context.asValue(Date.from(Instant.now())), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, STATIC_RECEIVER); + assertValueInContexts(context.asValue(Duration.ofMillis(100)), HOST_OBJECT, MEMBERS, DURATION, STATIC_RECEIVER); assertValueInContexts(context.asValue(ProxyDate.from(LocalDate.now())), DATE, PROXY_OBJECT); assertValueInContexts(context.asValue(ProxyTime.from(LocalTime.now())), TIME, PROXY_OBJECT); @@ -387,6 +388,7 @@ public void testHostObject() { List expectedTraits = new ArrayList<>(); expectedTraits.add(MEMBERS); expectedTraits.add(HOST_OBJECT); + expectedTraits.add(STATIC_RECEIVER); if (value.getClass() == BigInteger.class) { expectedTraits.add(NUMBER); @@ -451,7 +453,7 @@ public void testHostObject() { @Test public void testArrays() { for (Object array : ARRAYS) { - assertValueInContexts(context.asValue(array), ARRAY_ELEMENTS, ITERABLE, HOST_OBJECT, MEMBERS); + assertValueInContexts(context.asValue(array), ARRAY_ELEMENTS, ITERABLE, HOST_OBJECT, MEMBERS, STATIC_RECEIVER); } } @@ -515,8 +517,8 @@ public void testBuffers() { for (final ByteBuffer buffer : testBuffers) { final Value value = context.asValue(buffer); - assertValueInContexts(value, BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS); - assertValueInContexts(createDelegateInteropWrapper(context, value), BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS); + assertValueInContexts(value, BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS, STATIC_RECEIVER); + assertValueInContexts(createDelegateInteropWrapper(context, value), BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS, STATIC_RECEIVER); } } @@ -2326,8 +2328,8 @@ private static void expandObjectVariants(Context sourceContext, List obj @Test public void testHostException() { Value exceptionValue = context.asValue(new RuntimeException("expected")); - assertValueInContexts(exceptionValue, HOST_OBJECT, MEMBERS, EXCEPTION); - assertValueInContexts(createDelegateInteropWrapper(context, exceptionValue), HOST_OBJECT, MEMBERS, EXCEPTION); + assertValueInContexts(exceptionValue, HOST_OBJECT, MEMBERS, EXCEPTION, STATIC_RECEIVER); + assertValueInContexts(createDelegateInteropWrapper(context, exceptionValue), HOST_OBJECT, MEMBERS, EXCEPTION, STATIC_RECEIVER); try { exceptionValue.throwException(); fail("should have thrown"); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java index 920b7a8840a7..a50a18bc1e8c 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java @@ -40,6 +40,7 @@ */ package com.oracle.truffle.api.test.polyglot; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STATIC_RECEIVER; import static com.oracle.truffle.tck.tests.ValueAssert.assertUnsupported; import static com.oracle.truffle.tck.tests.ValueAssert.assertValue; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN; @@ -162,7 +163,7 @@ public void testHostObjectIdentityRestore() { assertTrue(value.isHostObject()); assertSame(obj, context.asValue(value.as(Object.class)).asHostObject()); - assertUnsupported(value, HOST_OBJECT, MEMBERS); + assertUnsupported(value, HOST_OBJECT, MEMBERS, STATIC_RECEIVER); } /** @@ -258,7 +259,7 @@ public void testClassProperties() { assertTrue(newInstance.getMetaObject().newInstance().asHostObject() instanceof JavaRecord); assertSame(JavaRecord.class, newInstance.getMetaObject().asHostObject()); - assertValue(recordClass, Trait.INSTANTIABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.META); + assertValue(recordClass, Trait.INSTANTIABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.META, STATIC_RECEIVER); } @Test @@ -316,8 +317,8 @@ public void testObjectProperties() { assertTrue(record.hasMember("wait")); assertTrue(record.hasMember("notifyAll")); - assertValue(record, Trait.MEMBERS, Trait.HOST_OBJECT); - assertValue(record.getMetaObject(), Trait.INSTANTIABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.META); + assertValue(record, Trait.MEMBERS, Trait.HOST_OBJECT, STATIC_RECEIVER); + assertValue(record.getMetaObject(), Trait.INSTANTIABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.META, STATIC_RECEIVER); } @Test diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java index b6a2b1ac1d53..f387e6ec342f 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java @@ -358,6 +358,14 @@ public void accessAllPublicPropertiesDirectly() { assertEquals("One field x", "x", propertyNames[0]); assertEquals("One method to access x", "readX", propertyNames[1]); + Value staticPojo = pojo.getStaticReceiver(); + assertTrue(staticPojo.hasMembers()); + propertyNames = staticPojo.getMemberKeys().toArray(); + assertEquals("One static field, one method and class", 3, propertyNames.length); + assertEquals("One field y", "y", propertyNames[0]); + assertEquals("One method to access y", "readY", propertyNames[1]); + assertEquals("The class", "class", propertyNames[2]); + Value readX = pojo.getMember("readX"); assertTrue(readX.canExecute()); diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java index 84f0eb278a00..0a5f75b678bb 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java @@ -3414,6 +3414,22 @@ Object readArrayElement(long idx, } } + @ExportMessage + boolean hasStaticReceiver() { + return obj != null && !isStaticClass(); + } + + @ExportMessage + Object getStaticReceiver() throws UnsupportedMessageException { + if (hasStaticReceiver()) { + Class clz = getLookupClass(); + return new HostObject(clz, context, clz); + } else { + CompilerDirectives.transferToInterpreter(); + throw UnsupportedMessageException.create(); + } + } + boolean isStaticClass() { return extraInfo instanceof Class; } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java index fbcbcf59b049..2fcf50ce6e4b 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java @@ -120,6 +120,7 @@ import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetMemberNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetMetaQualifiedNameNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetMetaSimpleNameNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetStaticReceiverNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasArrayElementsNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasBufferElementsNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasHashEntriesNodeGen; @@ -128,6 +129,7 @@ import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasIteratorNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasMemberNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasMembersNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasStaticReceiverNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.InvokeNoArgsNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.InvokeNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsBufferWritableNodeGen; @@ -588,6 +590,24 @@ static RuntimeException removeMemberUnsupported(PolyglotLanguageContext context, throw unsupported(context, receiver, "removeMember(String, Object)", null); } + @Override + public Object getStaticReceiver(Object languageContext, Object receiver) { + PolyglotLanguageContext context = (PolyglotLanguageContext) languageContext; + Object prev = hostEnter(context); + try { + return getStaticReceiverUnsupported(context, receiver); + } catch (Throwable e) { + throw guestToHostException(context, e, true); + } finally { + hostLeave(context, prev); + } + } + + @TruffleBoundary + static Object getStaticReceiverUnsupported(PolyglotLanguageContext context, Object receiver) { + throw unsupported(context, receiver, "getStaticReceiver(Object)", "hasStaticReceiver"); + } + @Override public Object execute(Object languageContext, Object receiver, Object[] arguments) { PolyglotLanguageContext context = (PolyglotLanguageContext) languageContext; @@ -2298,9 +2318,11 @@ static final class InteropValue extends PolyglotValueDispatch { final CallTarget getHashValuesIterator; final CallTarget isHostObject; final CallTarget asHostObject; - final CallTarget asClassLiteral; final CallTarget asTypeLiteral; + final CallTarget hasStaticReceiver; + final CallTarget getStaticReceiver; + final Class receiverType; InteropValue(PolyglotImpl polyglot, PolyglotLanguageInstance languageInstance, Object receiverObject, Class receiverType) { @@ -2382,6 +2404,8 @@ static final class InteropValue extends PolyglotValueDispatch { this.getHashValuesIterator = createTarget(GetHashValuesIteratorNodeGen.create(this)); this.isHostObject = createTarget(IsHostObjectNodeGen.create(this)); this.asHostObject = createTarget(AsHostObjectNodeGen.create(this)); + this.hasStaticReceiver = createTarget(HasStaticReceiverNodeGen.create(this)); + this.getStaticReceiver = createTarget(GetStaticReceiverNodeGen.create(this)); } @SuppressWarnings("unchecked") @@ -2546,6 +2570,16 @@ public Set getMemberKeys(Object languageContext, Object receiver) { return new MemberSet(this.getEngine().getAPIAccess(), languageContext, receiver, keys); } + @Override + public boolean hasStaticReceiver(Object languageContext, Object receiver) { + return (boolean) RUNTIME.callProfiled(this.hasStaticReceiver, languageContext, receiver); + } + + @Override + public Object getStaticReceiver(Object languageContext, Object receiver) { + return RUNTIME.callProfiled(this.getStaticReceiver, languageContext, receiver); + } + @Override public long asNativePointer(Object languageContext, Object receiver) { return (long) RUNTIME.callProfiled(this.asNativePointer, languageContext, receiver); @@ -5642,5 +5676,59 @@ static RuntimeException asHostObjectIsolatedHeap(PolyglotLanguageContext context return PolyglotEngineException.unsupported(polyglotMessage); } } + + abstract static class HasStaticReceiverNode extends InteropNode { + + protected HasStaticReceiverNode(InteropValue interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "hasStaticReceiver"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary objects) { + return objects.hasStaticReceiver(receiver); + } + } + + abstract static class GetStaticReceiverNode extends InteropNode { + + protected GetStaticReceiverNode(InteropValue interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "getStaticReceiver"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @Bind Node node, + @CachedLibrary("receiver") InteropLibrary objects, + @Cached ToHostValueNode toHost, + @Cached InlinedBranchProfile unsupported) { + try { + return toHost.execute(node, context, objects.getStaticReceiver(receiver)); + } catch (UnsupportedMessageException e) { + unsupported.enter(node); + return getStaticReceiverUnsupported(context, receiver); + } + } + } } } diff --git a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java index c385cf1293e0..5e4701810b06 100644 --- a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java +++ b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java @@ -58,6 +58,7 @@ import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NULL; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NUMBER; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.PROXY_OBJECT; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STATIC_RECEIVER; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STRING; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIME; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIMEZONE; @@ -287,6 +288,10 @@ public static void assertUnsupported(Value value, Trait... supported) { } } + break; + case STATIC_RECEIVER: + assertFalse(value.hasStaticReceiver()); + assertFails(value::getStaticReceiver, UnsupportedOperationException.class); break; case EXECUTABLE: assertFalse(value.toString(), value.canExecute()); @@ -662,6 +667,10 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces assertEquals(value.toString(), value.as(Map.class).toString()); } break; + case STATIC_RECEIVER: + assertTrue(msg, value.hasStaticReceiver()); + assertNotNull(value.getStaticReceiver()); + break; case NATIVE: assertTrue(msg, value.isNativePointer()); value.asNativePointer(); @@ -1160,6 +1169,9 @@ private static Trait[] detectSupportedTypes(Value value) { if (value.hasMembers()) { valueTypes.add(MEMBERS); } + if (value.hasStaticReceiver()) { + valueTypes.add(STATIC_RECEIVER); + } if (value.hasArrayElements()) { valueTypes.add(ARRAY_ELEMENTS); } @@ -1230,7 +1242,8 @@ public enum Trait { META, ITERABLE, ITERATOR, - HASH + HASH, + STATIC_RECEIVER } } From be632cc11ba3869686c8a891fe0b99944d0a0392 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Thu, 13 Nov 2025 17:12:09 +0100 Subject: [PATCH 07/14] Fixed sigtest files. --- sdk/src/org.graalvm.polyglot/snapshot.sigtest | 2 ++ .../src/com.oracle.truffle.api.debug/snapshot.sigtest | 1 + .../com.oracle.truffle.api.interop/snapshot.sigtest | 10 ++++++++++ truffle/src/com.oracle.truffle.api/snapshot.sigtest | 5 +++++ .../src/com/oracle/truffle/api/TruffleLanguage.java | 8 ++++---- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/sdk/src/org.graalvm.polyglot/snapshot.sigtest b/sdk/src/org.graalvm.polyglot/snapshot.sigtest index 1e48653b9786..63728c8e9ec8 100644 --- a/sdk/src/org.graalvm.polyglot/snapshot.sigtest +++ b/sdk/src/org.graalvm.polyglot/snapshot.sigtest @@ -598,6 +598,7 @@ meth public boolean hasIteratorNextElement() meth public boolean hasMember(java.lang.String) meth public boolean hasMembers() meth public boolean hasMetaParents() +meth public boolean hasStaticReceiver() meth public boolean isBoolean() meth public boolean isBufferWritable() meth public boolean isDate() @@ -659,6 +660,7 @@ meth public org.graalvm.polyglot.Value getIteratorNextElement() meth public org.graalvm.polyglot.Value getMember(java.lang.String) meth public org.graalvm.polyglot.Value getMetaObject() meth public org.graalvm.polyglot.Value getMetaParents() +meth public org.graalvm.polyglot.Value getStaticReceiver() meth public short asShort() meth public short readBufferShort(java.nio.ByteOrder,long) meth public static org.graalvm.polyglot.Value asValue(java.lang.Object) diff --git a/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest index 05960677b690..efa85f2aec6d 100644 --- a/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest @@ -212,6 +212,7 @@ meth public final boolean isNull() meth public final com.oracle.truffle.api.debug.DebugValue asInLanguage(com.oracle.truffle.api.nodes.LanguageInfo) meth public final com.oracle.truffle.api.debug.DebugValue getMetaObject() meth public final com.oracle.truffle.api.debug.DebugValue getProperty(java.lang.String) +meth public final com.oracle.truffle.api.debug.DebugValue getStaticReceiver() meth public final com.oracle.truffle.api.nodes.LanguageInfo getOriginalLanguage() meth public final com.oracle.truffle.api.source.SourceSection getSourceLocation() meth public final java.lang.String asString() diff --git a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest index eb45f1b7459f..d031fd23bcea 100644 --- a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest @@ -20,6 +20,12 @@ meth public static com.oracle.truffle.api.interop.ExceptionType valueOf(java.lan meth public static com.oracle.truffle.api.interop.ExceptionType[] values() supr java.lang.Enum +CLSS public final com.oracle.truffle.api.interop.HeapIsolationException +meth public java.lang.String getMessage() +meth public static com.oracle.truffle.api.interop.HeapIsolationException create() +meth public static com.oracle.truffle.api.interop.HeapIsolationException create(java.lang.Throwable) +supr com.oracle.truffle.api.interop.InteropException + CLSS public abstract com.oracle.truffle.api.interop.InteropException meth public final java.lang.Throwable fillInStackTrace() meth public final java.lang.Throwable getCause() @@ -53,6 +59,7 @@ meth public boolean hasExceptionMessage(java.lang.Object) meth public boolean hasExceptionStackTrace(java.lang.Object) meth public boolean hasExecutableName(java.lang.Object) meth public boolean hasHashEntries(java.lang.Object) +meth public boolean hasHostObject(java.lang.Object) meth public boolean hasIterator(java.lang.Object) meth public boolean hasIteratorNextElement(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public boolean hasLanguage(java.lang.Object) @@ -65,6 +72,7 @@ meth public boolean hasMetaObject(java.lang.Object) meth public boolean hasMetaParents(java.lang.Object) meth public boolean hasScopeParent(java.lang.Object) meth public boolean hasSourceLocation(java.lang.Object) +meth public boolean hasStaticReceiver(java.lang.Object) meth public boolean isArrayElementInsertable(java.lang.Object,long) meth public boolean isArrayElementModifiable(java.lang.Object,long) meth public boolean isArrayElementReadable(java.lang.Object,long) @@ -131,6 +139,7 @@ meth public java.lang.Object getExecutableName(java.lang.Object) throws com.orac meth public java.lang.Object getHashEntriesIterator(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getHashKeysIterator(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getHashValuesIterator(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public java.lang.Object getHostObject(java.lang.Object) throws com.oracle.truffle.api.interop.HeapIsolationException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getIterator(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getIteratorNextElement(java.lang.Object) throws com.oracle.truffle.api.interop.StopIterationException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getMembers(java.lang.Object,boolean) throws com.oracle.truffle.api.interop.UnsupportedMessageException @@ -139,6 +148,7 @@ meth public java.lang.Object getMetaParents(java.lang.Object) throws com.oracle. meth public java.lang.Object getMetaQualifiedName(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getMetaSimpleName(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getScopeParent(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public java.lang.Object getStaticReceiver(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object readArrayElement(java.lang.Object,long) throws com.oracle.truffle.api.interop.InvalidArrayIndexException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object readHashValue(java.lang.Object,java.lang.Object) throws com.oracle.truffle.api.interop.UnknownKeyException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object readHashValueOrDefault(java.lang.Object,java.lang.Object,java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException diff --git a/truffle/src/com.oracle.truffle.api/snapshot.sigtest b/truffle/src/com.oracle.truffle.api/snapshot.sigtest index 3f2c84e0f650..30f42911a368 100644 --- a/truffle/src/com.oracle.truffle.api/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api/snapshot.sigtest @@ -578,9 +578,12 @@ meth public boolean isCreateProcessAllowed() meth public boolean isCreateThreadAllowed() meth public boolean isFileIOAllowed() meth public boolean isHostException(java.lang.Throwable) + anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="25.1") meth public boolean isHostFunction(java.lang.Object) + anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="25.1") meth public boolean isHostLookupAllowed() meth public boolean isHostObject(java.lang.Object) + anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="25.1") meth public boolean isHostSymbol(java.lang.Object) meth public boolean isIOAllowed() anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="23.0") @@ -616,6 +619,7 @@ meth public java.io.OutputStream out() meth public java.lang.Object asBoxedGuestValue(java.lang.Object) meth public java.lang.Object asGuestValue(java.lang.Object) meth public java.lang.Object asHostObject(java.lang.Object) + anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="25.1") meth public java.lang.Object asHostSymbol(java.lang.Class) meth public java.lang.Object createHostAdapter(java.lang.Object[]) meth public java.lang.Object createHostAdapterClass(java.lang.Class[]) @@ -643,6 +647,7 @@ meth public java.lang.Thread createThread(java.lang.Runnable,com.oracle.truffle. meth public java.lang.Thread createThread(java.lang.Runnable,com.oracle.truffle.api.TruffleContext,java.lang.ThreadGroup,long) anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="") meth public java.lang.Throwable asHostException(java.lang.Throwable) + anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="25.1") meth public java.time.ZoneId getTimeZone() meth public java.util.Map getInstruments() meth public java.util.Map getInternalLanguages() diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index e9636cc5eb8d..683e98d5b065 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -2243,7 +2243,7 @@ public Object lookupHostSymbol(String symbolName) { * @see #asHostObject(Object) * @since 19.0 */ - @Deprecated + @Deprecated(since = "25.1") @SuppressWarnings("static-method") public boolean isHostObject(Object value) { try { @@ -2263,7 +2263,7 @@ public boolean isHostObject(Object value) { * getHostObject}. * @since 19.0 */ - @Deprecated + @Deprecated(since = "25.1") public Object asHostObject(Object value) { if (!isHostObject(value)) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -2378,7 +2378,7 @@ public Object findMetaObject(Object value) { * @deprecated Use * {@code InteropLibrary.isHostObject(obj) && InteropLibrary.isException(obj)}. */ - @Deprecated + @Deprecated(since = "25.1") @SuppressWarnings("static-method") public boolean isHostException(Throwable exception) { try { @@ -2403,7 +2403,7 @@ public boolean isHostException(Throwable exception) { * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#getHostObject(Object) * getHostObject}. */ - @Deprecated + @Deprecated(since = "25.1") @SuppressWarnings("static-method") public Throwable asHostException(Throwable exception) { try { From 05fcbde6622152ec01e06dc4919397c7345f5d46 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Fri, 14 Nov 2025 09:19:30 +0100 Subject: [PATCH 08/14] Deprecated TruffleLanguiage.Env.isHostSymbol. --- .../polyglot/impl/AbstractPolyglotImpl.java | 2 -- truffle/CHANGELOG.md | 3 +++ .../truffle/api/test/host/HostAdapterTest.java | 3 +++ .../api/test/host/HostExceptionTest.java | 2 +- .../wrapper/GuestToHostLanguageService.java | 5 ----- .../src/com.oracle.truffle.api/snapshot.sigtest | 1 + .../com/oracle/truffle/api/TruffleLanguage.java | 17 ++++++----------- .../com/oracle/truffle/api/impl/Accessor.java | 2 +- .../truffle/host/HostLanguageService.java | 9 --------- .../oracle/truffle/polyglot/EngineAccessor.java | 10 +++------- 10 files changed, 18 insertions(+), 36 deletions(-) diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java index fc5e8c79595f..b88952664bdd 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java @@ -1040,8 +1040,6 @@ public abstract void initializeHostContext(Object internalContext, Object contex public abstract RuntimeException toHostException(Object hostContext, Throwable exception); - public abstract boolean isHostSymbol(Object obj); - public abstract Object createHostAdapter(Object hostContextObject, Object[] types, Object classOverrides); public abstract boolean isHostProxy(Object value); diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 74434d410730..c8e620597367 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -62,6 +62,9 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-71088 Added `CompilerDirectives.EarlyEscapeAnalysis` annotation that runs partial escape analysis early before partial evaluation enabling partial-evaluation-constant scalar replacements. * GR-71870 Truffle DSL no longer supports mixed exclusive and shared inlined caches. Sharing will now be disabled if mixing was used. To resolve the new warnings it is typically necessary to use either `@Exclusive` or `@Shared` for all caches. * GR-71887: Bytecode DSL: Added a `ClearLocal` operation for fast clearing of local values. +* GR-71402: Added `InteropLibrary#hasHostObject` and `InteropLibrary#getHostObject` for accessing the Java host-object representation of a Truffle guest object. Deprecated `Env#isHostObject`, `Env#isHostException`, `Env#isHostFunction`, `Env#isHostSymbol`, `Env#asHostObject`, and `Env#asHostException` in favor of the new InteropLibrary messages. +* GR-71402: Added `InteropLibrary#hasStaticReceiver` and `InteropLibrary#getStaticReceiver` returning the static receiver for given object. + ## Version 25.0 * GR-31495 Added ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. Languages may describe available source options by implementing `TruffleLanguage.getSourceOptionDescriptors()` and `TruffleInstrument.getSourceOptionDescriptors()` respectively. diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java index 4dd8274ead7d..d317d62d0603 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java @@ -43,6 +43,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -136,9 +137,11 @@ private Object asHostType(TruffleLanguage.Env env, Class c) { } } + @SuppressWarnings("deprecation") private static Object verifyHostAdapterClass(TruffleLanguage.Env env, Object hostAdapterClass) { assertTrue(INTEROP.hasHostObject(hostAdapterClass)); assertTrue(env.isHostSymbol(hostAdapterClass)); + assertFalse(INTEROP.isNull(hostAdapterClass) || INTEROP.hasStaticReceiver(hostAdapterClass)); assertTrue(INTEROP.isMetaObject(hostAdapterClass)); assertTrue(INTEROP.isInstantiable(hostAdapterClass)); return hostAdapterClass; diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java index bf714cc9f71f..aee91751c076 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java @@ -530,7 +530,7 @@ public void testHostExceptionIsHostSymbol() { TruffleTestAssumptions.assumeWeakEncapsulation(); expectedException = RuntimeException.class; customExceptionVerifier = (t) -> { - assertFalse(env.isHostSymbol(t)); + assertFalse(INTEROP.hasHostObject(t) && !INTEROP.isNull(t) && !INTEROP.hasStaticReceiver(t)); }; Value catcher = context.eval(ProxyLanguage.ID, CATCHER); Runnable thrower = HostExceptionTest::thrower; diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/GuestToHostLanguageService.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/GuestToHostLanguageService.java index dd4a3e86d81d..99f83aa20d68 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/GuestToHostLanguageService.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/GuestToHostLanguageService.java @@ -118,11 +118,6 @@ public RuntimeException toHostException(Object hostContext, Throwable exception) throw new UnsupportedOperationException(); } - @Override - public boolean isHostSymbol(Object obj) { - return false; - } - @Override public Object createHostAdapter(Object hostContextObject, Object[] types, Object classOverrides) { return null; diff --git a/truffle/src/com.oracle.truffle.api/snapshot.sigtest b/truffle/src/com.oracle.truffle.api/snapshot.sigtest index 30f42911a368..42de8dbb4c2f 100644 --- a/truffle/src/com.oracle.truffle.api/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api/snapshot.sigtest @@ -585,6 +585,7 @@ meth public boolean isHostLookupAllowed() meth public boolean isHostObject(java.lang.Object) anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="25.1") meth public boolean isHostSymbol(java.lang.Object) + anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="25.1") meth public boolean isIOAllowed() anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="23.0") meth public boolean isInnerContextOptionsAllowed() diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index 683e98d5b065..fae457089178 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -2330,7 +2330,7 @@ public Object asBoxedGuestValue(Object guestObject) { * * @since 19.0 * @deprecated Use - * {@code InteropLibrary.isHostObject(obj) && InteropLibrary.isExecutable(obj) && !InteropLibrary.hasMembers(obj)} + * {@code interopLibrary.isHostObject(obj) && interopLibrary.isExecutable(obj) && !interopLibrary.hasMembers(obj)} */ @Deprecated(since = "25.1") @SuppressWarnings("static-method") @@ -2376,7 +2376,7 @@ public Object findMetaObject(Object value) { * @see #asHostException(Throwable) * @since 19.0 * @deprecated Use - * {@code InteropLibrary.isHostObject(obj) && InteropLibrary.isException(obj)}. + * {@code interopLibrary.isHostObject(obj) && interopLibrary.isException(obj)}. */ @Deprecated(since = "25.1") @SuppressWarnings("static-method") @@ -2419,11 +2419,14 @@ public Throwable asHostException(Throwable exception) { * * @see #lookupHostSymbol(String) * @since 19.0 + * @deprecated Use + * {@code interopLibrary.isHostObject(obj) && !interopLibrary.isNull(obj) && !interopLibrary.hasStaticReceiver(obj)}. */ + @Deprecated(since = "25.1") @SuppressWarnings("static-method") public boolean isHostSymbol(Object guestObject) { try { - return LanguageAccessor.engineAccess().isHostSymbol(polyglotLanguageContext, guestObject); + return LanguageAccessor.engineAccess().isHostSymbol(guestObject); } catch (Throwable t) { throw engineToLanguageException(t); } @@ -3157,8 +3160,6 @@ public TruffleFile getInternalTruffleFile(URI uri) { * @see #getPublicTruffleFile(String) * @see #getInternalTruffleFile(String) * @since 21.1.0 - * - * */ @TruffleBoundary public TruffleFile getTruffleFileInternal(String path, Predicate filter) { @@ -3183,8 +3184,6 @@ public TruffleFile getTruffleFileInternal(String path, Predicate fi * @see #getPublicTruffleFile(URI) * @see #getInternalTruffleFile(URI) * @since 21.1.0 - * - * */ @TruffleBoundary public TruffleFile getTruffleFileInternal(URI uri, Predicate filter) { @@ -3717,8 +3716,6 @@ public TruffleFile getInternalResource(String resourceId) throws IOException { * empty a root logger for language or instrument is returned * @return a {@link TruffleLogger} * @since 21.1 - * - * */ @TruffleBoundary public TruffleLogger getLogger(String loggerName) { @@ -4364,8 +4361,6 @@ public enum ExitMode { * Natural exit that occurs during normal context close. * * @since 22.0 - * - * */ NATURAL, /** diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java index 213df87a6a6b..f120ca4acac3 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java @@ -573,7 +573,7 @@ public abstract Thread createThread(Object polyglotLanguageContext, Runnable run public abstract boolean isHostFunction(Object value); - public abstract boolean isHostSymbol(Object languageContext, Object guestObject); + public abstract boolean isHostSymbol(Object guestObject); public abstract S lookupService(Object polyglotLanguageContext, LanguageInfo language, LanguageInfo accessingLanguage, Class type); diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java index c9374c38ca21..f90f41030d0d 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostLanguageService.java @@ -182,15 +182,6 @@ public boolean isHostProxy(Object value) { return HostProxy.isProxyGuestObject(language, value); } - @Override - public boolean isHostSymbol(Object obj) { - Object o = HostLanguage.unwrapIfScoped(language, obj); - if (o instanceof HostObject) { - return ((HostObject) o).isStaticClass(); - } - return false; - } - @Override public Object createHostAdapter(Object context, Object[] hostTypes, Object classOverrides) { CompilerAsserts.neverPartOfCompilation(); diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java index 165bb9f07e54..57b1ef2cf9be 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java @@ -1564,13 +1564,9 @@ public boolean isHostObject(Object obj) { } @Override - public boolean isHostSymbol(Object languageContext, Object obj) { - PolyglotContextImpl context = ((PolyglotLanguageContext) languageContext).context; - PolyglotEngineImpl engine = context.engine; - // During context pre-initialization, engine.host is null, languages are not allowed to - // use host interop. But the call to isHostSymbol is supported and returns false because - // languages cannot create a HostObject. - return !engine.inEnginePreInitialization && engine.host.isHostSymbol(obj); + public boolean isHostSymbol(Object obj) { + InteropLibrary interop = InteropLibrary.getUncached(obj); + return interop.hasHostObject(obj) && !interop.isNull(obj) && !interop.hasStaticReceiver(obj); } @Override From 5b42886c43bb093cbc631a921a933588eafb7a25 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Fri, 14 Nov 2025 13:47:41 +0100 Subject: [PATCH 09/14] Fixed deprecated methods usages. --- .../espresso/nodes/interop/ToReference.java | 20 ++++++++----------- .../tests/interop/PolyglotBuiltinTest.java | 8 +++++--- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java index 443d6de4c6d7..4f2ba659bf39 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java @@ -656,7 +656,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.hasIterator(value)", "interop.hasMetaObject(value)", - "isHostObject(getContext(), value)", + "interop.hasHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -734,7 +734,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.hasIterator(value)", "interop.hasMetaObject(value)", - "isHostObject(getContext(), value)", + "interop.hasHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -812,7 +812,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.isIterator(value)", "interop.hasMetaObject(value)", - "isHostObject(getContext(), value)", + "interop.hasHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -890,7 +890,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.hasHashEntries(value)", "interop.hasMetaObject(value)", - "isHostObject(getContext(), value)", + "interop.hasHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -966,7 +966,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.hasMetaObject(value)", - "isHostObject(getContext(), value)", + "interop.hasHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -1715,7 +1715,7 @@ public StaticObject doEspresso(StaticObject value, @Specialization(guards = { "!isStaticObject(value)", "!interop.isNull(value)", - "isHostObject(getContext(), value)" + "interop.hasHostObject(value)" }) StaticObject doForeignInterface(Object value, @Bind Node node, @@ -1780,7 +1780,7 @@ public StaticObject doEspresso(StaticObject value, @Specialization(guards = { "!isStaticObject(value)", "!interop.isNull(value)", - "isHostObject(getContext(), value)" + "interop.hasHostObject(value)" }) StaticObject doForeignConverter(Object value, @Bind Node node, @@ -1855,7 +1855,7 @@ public StaticObject doEspresso(StaticObject value, @Specialization(guards = { "!isStaticObject(value)", "!interop.isNull(value)", - "isHostObject(getContext(), value)" + "interop.hasHostObject(value)" }) StaticObject doForeignInternalConverter(Object value, @Bind Node node, @@ -2799,8 +2799,4 @@ static boolean isEspressoException(Object obj) { static boolean isTypeMappingEnabled(EspressoContext context) { return context.getPolyglotTypeMappings().hasMappings(); } - - static boolean isHostObject(EspressoContext context, Object value) { - return context.getEnv().isHostObject(value); - } } diff --git a/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java b/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java index 6a917af9ce7a..0167586906a5 100644 --- a/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java +++ b/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java @@ -35,6 +35,7 @@ import java.math.BigInteger; import java.util.HashMap; +import com.oracle.truffle.api.interop.InteropException; import org.hamcrest.MatcherAssert; import org.junit.Assert; import org.junit.Assume; @@ -161,13 +162,14 @@ public TestHostInteropNode() { } @Test - public void testHostInterop(@Inject(TestHostInteropNode.class) CallTarget testHostInterop) { + public void testHostInterop(@Inject(TestHostInteropNode.class) CallTarget testHostInterop) throws InteropException { Assume.assumeFalse("skipping host interop test in native mode", TruffleOptions.AOT); Object ret = testHostInterop.call(); - Assert.assertTrue("isHostObject", runWithPolyglot.getTruffleTestEnv().isHostObject(ret)); - Assert.assertSame("ret", BigInteger.class, runWithPolyglot.getTruffleTestEnv().asHostObject(ret)); + InteropLibrary interop = InteropLibrary.getUncached(ret); + Assert.assertTrue("hasHostObject", interop.hasHostObject(ret)); + Assert.assertSame("ret", BigInteger.class, interop.getHostObject(ret)); } public static class TestEvalNoLang extends SulongTestNode { From 4ce9c200e62b9daf5aaf304f4a65fcf725c34a93 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Tue, 18 Nov 2025 10:18:19 +0100 Subject: [PATCH 10/14] Fixed gates. --- .../src/org/graalvm/polyglot/Value.java | 2 +- .../tests/interop/PolyglotBuiltinTest.java | 2 +- .../com/oracle/truffle/host/HostObject.java | 2 +- .../backend/libffi/SerializeArgumentNode.java | 13 +++---- .../tstring/TStringBenchDummyLanguage.java | 36 +++++++++---------- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java index 8ba492fbd752..92187b7244d6 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 diff --git a/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java b/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java index 0167586906a5..7918960d88b7 100644 --- a/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java +++ b/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. * * All rights reserved. * diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java index 0a5f75b678bb..5386d34b1254 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java @@ -3312,7 +3312,7 @@ Object getMetaSimpleName() throws UnsupportedMessageException { } } - @ExportMessage + @ExportMessage(limit = "LIMIT") @TruffleBoundary boolean isMetaInstance(Object other, @Bind Node node, diff --git a/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java b/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java index 1747c600c961..7c9ccd28cf28 100644 --- a/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java +++ b/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java @@ -629,24 +629,19 @@ abstract static class SerializeArrayNode extends SerializeArgumentNode { } } - final boolean isHostObject(Object value, InteropLibrary interop) { - return interop.hasHostObject(value); - } - - final Object asHostObject(Object value, InteropLibrary interop) { + final Object asHostObject(Object value, InteropLibrary interop) throws UnsupportedTypeException { try { return interop.getHostObject(value); } catch (UnsupportedMessageException e) { throw CompilerDirectives.shouldNotReachHere(e); } catch (HeapIsolationException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new UnsupportedOperationException(e); + throw UnsupportedTypeException.create(new Object[]{value}); } } - @Specialization(guards = {"isHostObject(value, interop)", "tag != null"}) + @Specialization(guards = {"interop.hasHostObject(value)", "tag != null"}, limit = "3") void doHostObject(@SuppressWarnings("unused") Object value, NativeArgumentBuffer buffer, - @CachedLibrary(limit = "3") InteropLibrary interop, + @CachedLibrary("value") InteropLibrary interop, @Bind("asHostObject(value, interop)") Object hostObject, @Bind("getTypeTag.execute(hostObject)") TypeTag tag) { buffer.putObject(tag, hostObject, type.size); diff --git a/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java b/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java index a98225bc3f8e..f6216a4c66f7 100644 --- a/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java +++ b/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java @@ -241,8 +241,8 @@ abstract static class RawIterateBytesNode extends Node { public abstract int execute(Object input); - @Specialization - int bench(Object hostObject, @CachedLibrary(limit = "3") InteropLibrary interop) { + @Specialization(limit = "3") + int bench(Object hostObject, @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); int ret = 0; for (int i = 0; i < input.length; i++) { @@ -328,10 +328,10 @@ abstract static class CalcStringAttributesUTF8Node extends Node { abstract TruffleString execute(Object hostObject, int length); - @Specialization + @Specialization(limit = "3") TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @CachedLibrary(limit = "3") InteropLibrary interop) { + @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_8, false); } @@ -341,10 +341,10 @@ abstract static class CalcStringAttributesUTF16Node extends Node { abstract TruffleString execute(Object hostObject, int length); - @Specialization + @Specialization(limit = "3") TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @CachedLibrary(limit = "3") InteropLibrary interop) { + @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16, false); } @@ -354,10 +354,10 @@ abstract static class FromByteArrayUTF16Node extends Node { abstract TruffleString execute(Object hostObject, int length); - @Specialization + @Specialization(limit = "3") TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @CachedLibrary(limit = "3") InteropLibrary interop) { + @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16, true); } @@ -367,10 +367,10 @@ abstract static class FromByteArrayUTF32Node extends Node { abstract TruffleString execute(Object hostObject, int length); - @Specialization + @Specialization(limit = "3") TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @CachedLibrary(limit = "3") InteropLibrary interop) { + @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32, true); } @@ -380,10 +380,10 @@ abstract static class FromByteArrayUTF16FENode extends Node { abstract TruffleString execute(Object hostObject, int length); - @Specialization + @Specialization(limit = "3") TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @CachedLibrary(limit = "3") InteropLibrary interop) { + @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16BE, false); } @@ -393,11 +393,11 @@ abstract static class FromByteArrayUTF16FESwitchNode extends Node { abstract TruffleString execute(Object hostObject, int length); - @Specialization + @Specialization(limit = "3") TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, - @CachedLibrary(limit = "3") InteropLibrary interop) { + @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); return switchEncodingNode.execute(fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16BE, false), TruffleString.Encoding.UTF_16); } @@ -407,10 +407,10 @@ abstract static class FromByteArrayUTF32FENode extends Node { abstract TruffleString execute(Object hostObject, int length); - @Specialization + @Specialization(limit = "3") TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @CachedLibrary(limit = "3") InteropLibrary interop) { + @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32BE, false); } @@ -420,11 +420,11 @@ abstract static class FromByteArrayUTF32FESwitchNode extends Node { abstract TruffleString execute(Object hostObject, int length); - @Specialization + @Specialization(limit = "3") TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, - @CachedLibrary(limit = "3") InteropLibrary interop) { + @CachedLibrary("hostObject") InteropLibrary interop) { byte[] input = (byte[]) getHostObject(hostObject, interop); return switchEncodingNode.execute(fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32BE, false), TruffleString.Encoding.UTF_32); } From 6a208f7d7f3ca2a0948266bace6c663653a9ed5d Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Thu, 27 Nov 2025 09:24:35 +0100 Subject: [PATCH 11/14] Resolved review comments. --- .../espresso/nodes/interop/ToReference.java | 16 ++--- .../src/org/graalvm/polyglot/Value.java | 9 ++- .../tests/interop/PolyglotBuiltinTest.java | 4 +- truffle/CHANGELOG.md | 2 +- .../api/exception/ExceptionAccessor.java | 16 +---- .../snapshot.sigtest | 4 +- .../api/interop/HeapIsolationException.java | 12 ++-- .../truffle/api/interop/InteropLibrary.java | 60 +++++++++---------- .../api/test/host/CallerSensitiveTest.java | 8 +-- .../test/host/FunctionalInterfaceTest.java | 4 +- .../api/test/host/HostAdapterTest.java | 10 ++-- .../api/test/host/HostExceptionTest.java | 26 ++++---- .../api/test/host/HostInteropErrorTest.java | 4 +- .../api/test/host/ProxyLanguageEnvTest.java | 4 +- .../api/test/host/TestMemberAccess.java | 2 +- .../test/interop/InteropAssertionsTest.java | 50 ++++++++-------- .../polyglot/ContextBuilderExtendAPITest.java | 2 +- .../api/test/polyglot/FileSystemsTest.java | 6 +- .../test/polyglot/HostClassLoadingTest.java | 4 +- .../polyglot/LanguageSPIHostInteropTest.java | 41 ++++++------- .../api/test/polyglot/ProxySPITest.java | 2 +- .../api/test/wrapper/HostEntryPoint.java | 9 +-- .../api/test/wrapper/HostGuestValue.java | 3 +- .../oracle/truffle/api/TruffleLanguage.java | 12 ++-- .../com/oracle/truffle/api/impl/Accessor.java | 2 +- .../com/oracle/truffle/host/HostFunction.java | 4 +- .../oracle/truffle/host/HostMethodScope.java | 4 +- .../com/oracle/truffle/host/HostObject.java | 23 +++---- .../oracle/truffle/host/HostToTypeNode.java | 10 ++-- .../backend/libffi/SerializeArgumentNode.java | 4 +- .../polyglot/DefaultPolyglotHostService.java | 2 +- .../truffle/polyglot/EngineAccessor.java | 46 +++++++++----- .../polyglot/OtherContextGuestObject.java | 4 +- .../polyglot/PolyglotExceptionImpl.java | 4 +- .../polyglot/PolyglotValueDispatch.java | 4 +- .../tstring/TStringBenchDummyLanguage.java | 22 +++---- 36 files changed, 218 insertions(+), 221 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java index 4f2ba659bf39..104ab9a47cac 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java @@ -656,7 +656,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.hasIterator(value)", "interop.hasMetaObject(value)", - "interop.hasHostObject(value)", + "interop.isHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -734,7 +734,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.hasIterator(value)", "interop.hasMetaObject(value)", - "interop.hasHostObject(value)", + "interop.isHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -812,7 +812,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.isIterator(value)", "interop.hasMetaObject(value)", - "interop.hasHostObject(value)", + "interop.isHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -890,7 +890,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.hasHashEntries(value)", "interop.hasMetaObject(value)", - "interop.hasHostObject(value)", + "interop.isHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -966,7 +966,7 @@ public StaticObject doForeignNull(Object value, @Specialization(guards = { "interop.hasMetaObject(value)", - "interop.hasHostObject(value)", + "interop.isHostObject(value)", "!isStaticObject(value)" }) @SuppressWarnings("truffle-static-method") @@ -1715,7 +1715,7 @@ public StaticObject doEspresso(StaticObject value, @Specialization(guards = { "!isStaticObject(value)", "!interop.isNull(value)", - "interop.hasHostObject(value)" + "interop.isHostObject(value)" }) StaticObject doForeignInterface(Object value, @Bind Node node, @@ -1780,7 +1780,7 @@ public StaticObject doEspresso(StaticObject value, @Specialization(guards = { "!isStaticObject(value)", "!interop.isNull(value)", - "interop.hasHostObject(value)" + "interop.isHostObject(value)" }) StaticObject doForeignConverter(Object value, @Bind Node node, @@ -1855,7 +1855,7 @@ public StaticObject doEspresso(StaticObject value, @Specialization(guards = { "!isStaticObject(value)", "!interop.isNull(value)", - "interop.hasHostObject(value)" + "interop.isHostObject(value)" }) StaticObject doForeignInternalConverter(Object value, @Bind Node node, diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java index 92187b7244d6..0ba50c84996a 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java @@ -1012,8 +1012,6 @@ public boolean removeMember(String identifier) { * Returns {@code true} if this value provides a {@linkplain #getStaticReceiver() static * receiver}. A static receiver represents the static or class-level members associated with * this value's type, such as static fields or methods. - *

    - * This method may only return {@code true} if {@link #hasMembers()} also returns {@code true}. * * @throws IllegalStateException if the context is already {@linkplain Context#close() closed} * @throws PolyglotException if a guest language error occurs during execution @@ -1033,14 +1031,15 @@ public boolean hasStaticReceiver() { * {@link #getMember(String)}, {@link #getMemberKeys()}, or * {@link #invokeMember(String, Object...)}. *

    - * When this value {@linkplain #hasMembers() has members}, its static receiver is also expected - * to provide (static) members and/or declared members representing the value's static context. + * The returned static receiver is always expected to provide {@link #hasMembers() members}, + * representing the receiver's static context. *

    * Examples: *

      *
    • For a Java class instance, the static receiver exposes the class's static fields and * methods.
    • - *
    • For a Python object, the static receiver exposes class-level attributes and methods.
    • + *
    • For a Python object, the static receiver exposes class-level attributes and methods, + * effectively corresponding to the members provided by the Python metaobject.
    • *
    * * @throws UnsupportedOperationException if and only if this value does not diff --git a/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java b/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java index 7918960d88b7..fc04a68bd486 100644 --- a/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java +++ b/sulong/tests/com.oracle.truffle.llvm.tests.interop/src/com/oracle/truffle/llvm/tests/interop/PolyglotBuiltinTest.java @@ -168,8 +168,8 @@ public void testHostInterop(@Inject(TestHostInteropNode.class) CallTarget testHo Object ret = testHostInterop.call(); InteropLibrary interop = InteropLibrary.getUncached(ret); - Assert.assertTrue("hasHostObject", interop.hasHostObject(ret)); - Assert.assertSame("ret", BigInteger.class, interop.getHostObject(ret)); + Assert.assertTrue("isHostObject", interop.isHostObject(ret)); + Assert.assertSame("ret", BigInteger.class, interop.asHostObject(ret)); } public static class TestEvalNoLang extends SulongTestNode { diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index c8e620597367..81eab4a50671 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -39,7 +39,6 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-70086 Deprecated `Message.resolve(Class, String)`. Use `Message.resolveExact(Class, String, Class...)` with argument types instead. This deprecation was necessary as library messages are no longer unique by message name, if the previous message was deprecated. * GR-71299 Improved the responsiveness of the Truffle compilation queue by refining the computation of the execution rate of compilation units in the queue. * Added `engine.TraversingQueueRateHalfLife` to allow fine-tuning of the compilation queue responsiveness. -* GR-71402: Added `InteropLibrary#hasStaticReceiver` and `InteropLibrary#getStaticReceiver` returning the static receiver for given object. * GR-36894: Added `DynamicObject` nodes for dealing with `DynamicObject` properties and shapes, as a more lightweight replacement for `DynamicObjectLibrary`, including: * `DynamicObject.GetNode`: gets the value of a property or a default value if absent @@ -63,6 +62,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-71870 Truffle DSL no longer supports mixed exclusive and shared inlined caches. Sharing will now be disabled if mixing was used. To resolve the new warnings it is typically necessary to use either `@Exclusive` or `@Shared` for all caches. * GR-71887: Bytecode DSL: Added a `ClearLocal` operation for fast clearing of local values. * GR-71402: Added `InteropLibrary#hasHostObject` and `InteropLibrary#getHostObject` for accessing the Java host-object representation of a Truffle guest object. Deprecated `Env#isHostObject`, `Env#isHostException`, `Env#isHostFunction`, `Env#isHostSymbol`, `Env#asHostObject`, and `Env#asHostException` in favor of the new InteropLibrary messages. +* GR-71402: Added `InteropLibrary#isHostObject` and `InteropLibrary#asHostObject` for accessing the Java host-object representation of a Truffle guest object. Deprecated `Env#isHostObject`, `Env#isHostException`, `Env#isHostFunction`, `Env#isHostSymbol`, `Env#asHostObject`, and `Env#asHostException` in favor of the new InteropLibrary messages. * GR-71402: Added `InteropLibrary#hasStaticReceiver` and `InteropLibrary#getStaticReceiver` returning the static receiver for given object. diff --git a/truffle/src/com.oracle.truffle.api.exception/src/com/oracle/truffle/api/exception/ExceptionAccessor.java b/truffle/src/com.oracle.truffle.api.exception/src/com/oracle/truffle/api/exception/ExceptionAccessor.java index 0dc3cfab9b84..1015939e4a83 100644 --- a/truffle/src/com.oracle.truffle.api.exception/src/com/oracle/truffle/api/exception/ExceptionAccessor.java +++ b/truffle/src/com.oracle.truffle.api.exception/src/com/oracle/truffle/api/exception/ExceptionAccessor.java @@ -47,8 +47,6 @@ import java.util.ListIterator; import java.util.function.Function; -import com.oracle.truffle.api.CompilerDirectives; - import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; @@ -183,9 +181,9 @@ public Object getExceptionStackTrace(Object receiver, Object polyglotContext) { private static Object[] mergeHostGuestFrames(Throwable throwable, List guestStack, boolean inHost, Object polyglotEngine) { StackTraceElement[] hostStack = null; - if (ACCESSOR.engineSupport().isHostException(throwable)) { - Throwable original = unboxHostException(throwable); - hostStack = original.getStackTrace(); + Throwable originalHostException; + if (ACCESSOR.engineSupport().isHostException(throwable) && (originalHostException = ACCESSOR.engineSupport().asHostException(throwable)) != null) { + hostStack = originalHostException.getStackTrace(); } else if (throwable instanceof AbstractTruffleException) { Throwable lazyStackTrace = ((AbstractTruffleException) throwable).getLazyStackTrace(); if (lazyStackTrace != null) { @@ -245,14 +243,6 @@ public Object apply(TruffleStackTraceElement element) { return elementsList.toArray(); } - private static Throwable unboxHostException(Throwable throwable) { - try { - return ACCESSOR.engineSupport().asHostException(throwable); - } catch (Exception e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - private static int indexOfLastGuestToHostFrame(List guestStack) { for (var iterator = guestStack.listIterator(guestStack.size()); iterator.hasPrevious();) { int index = iterator.previousIndex(); diff --git a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest index d031fd23bcea..aa126b411649 100644 --- a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest @@ -59,7 +59,6 @@ meth public boolean hasExceptionMessage(java.lang.Object) meth public boolean hasExceptionStackTrace(java.lang.Object) meth public boolean hasExecutableName(java.lang.Object) meth public boolean hasHashEntries(java.lang.Object) -meth public boolean hasHostObject(java.lang.Object) meth public boolean hasIterator(java.lang.Object) meth public boolean hasIteratorNextElement(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public boolean hasLanguage(java.lang.Object) @@ -90,6 +89,7 @@ meth public boolean isHashEntryModifiable(java.lang.Object,java.lang.Object) meth public boolean isHashEntryReadable(java.lang.Object,java.lang.Object) meth public boolean isHashEntryRemovable(java.lang.Object,java.lang.Object) meth public boolean isHashEntryWritable(java.lang.Object,java.lang.Object) +meth public boolean isHostObject(java.lang.Object) meth public boolean isIdentical(java.lang.Object,java.lang.Object,com.oracle.truffle.api.interop.InteropLibrary) meth public boolean isInstantiable(java.lang.Object) meth public boolean isIterator(java.lang.Object) @@ -131,6 +131,7 @@ meth public int identityHashCode(java.lang.Object) throws com.oracle.truffle.api meth public int readBufferInt(java.lang.Object,java.nio.ByteOrder,long) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Class> getLanguage(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="25.1") +meth public java.lang.Object asHostObject(java.lang.Object) throws com.oracle.truffle.api.interop.HeapIsolationException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getDeclaringMetaObject(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getExceptionCause(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getExceptionMessage(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException @@ -139,7 +140,6 @@ meth public java.lang.Object getExecutableName(java.lang.Object) throws com.orac meth public java.lang.Object getHashEntriesIterator(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getHashKeysIterator(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getHashValuesIterator(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException -meth public java.lang.Object getHostObject(java.lang.Object) throws com.oracle.truffle.api.interop.HeapIsolationException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getIterator(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getIteratorNextElement(java.lang.Object) throws com.oracle.truffle.api.interop.StopIterationException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getMembers(java.lang.Object,boolean) throws com.oracle.truffle.api.interop.UnsupportedMessageException diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java index f8fb9763c411..ae8a5b2588f3 100644 --- a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java @@ -46,7 +46,7 @@ * Exception thrown when a {@link TruffleObject} cannot unbox a host object because the object * resides in a foreign heap. * - * @since 26.0 + * @since 25.1 */ @SuppressWarnings("serial") public final class HeapIsolationException extends InteropException { @@ -62,7 +62,7 @@ private HeapIsolationException(Throwable cause) { /** * {@inheritDoc} * - * @since 26.0 + * @since 25.1 */ @Override public String getMessage() { @@ -72,12 +72,12 @@ public String getMessage() { /** * Creates a new {@link HeapIsolationException} indicating that a host object cannot be unboxed * because it was allocated in a foreign heap. For example, when - * {@link InteropLibrary#getHostObject(Object)} is invoked from within a polyglot isolate. + * {@link InteropLibrary#asHostObject(Object)} is invoked from within a polyglot isolate. *

    * This factory method is intended for use in {@link CompilerDirectives#inCompiledCode() * compiled code paths}. * - * @since 26.0 + * @since 25.1 */ public static HeapIsolationException create() { return new HeapIsolationException(); @@ -86,7 +86,7 @@ public static HeapIsolationException create() { /** * Creates a new {@link HeapIsolationException} indicating that a host object cannot be unboxed * because it was allocated in a foreign heap. For example, when - * {@link InteropLibrary#getHostObject(Object)} is invoked from within a polyglot isolate. + * {@link InteropLibrary#asHostObject(Object)} is invoked from within a polyglot isolate. *

    * In addition, a cause may be provided. The cause should only be set if the guest language code * caused this problem. An example for this is a language specific proxy mechanism that invokes @@ -102,7 +102,7 @@ public static HeapIsolationException create() { * This factory method is intended for use in {@link CompilerDirectives#inCompiledCode() * compiled code paths}. * - * @since 26.0 + * @since 25.1 */ public static HeapIsolationException create(Throwable cause) { return new HeapIsolationException(cause); diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java index e648870798cd..9c7f4ac2421d 100644 --- a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java @@ -968,8 +968,7 @@ public boolean hasMemberWriteSideEffects(Object receiver, String member) { * static receiver}. A static receiver represents the static or class-level members associated * with the receiver's type, such as static fields or methods. *

    - * This message may only return {@code true} if {@link #hasMembers(Object)} returns - * {@code true}. Invoking this message must not cause any observable side effects. + * Invoking this message must not cause any observable side effects. *

    * By default, this method returns {@code false}. * @@ -993,15 +992,15 @@ public boolean hasStaticReceiver(Object receiver) { * writeMember}, and {@link #invokeMember(Object, String, Object...) invokeMember} when * accessing static members. *

    + * The returned static receiver is always expected to provide {@link #hasMembers(Object) + * members}, representing the receiver's static context. + *

    * Examples: *

      *
    • In Java, the static receiver would expose static fields and methods of a class.
    • - *
    • In Python, the static receiver would expose class-level variables and methods.
    • + *
    • In Python, the static receiver would expose class-level variables and methods, + * effectively corresponding to the members provided by the Python metaobject.
    • *
    - *

    - * When the receiver {@linkplain #hasMembers(Object) has members}, the corresponding static - * receiver is also expected to have (static) members and/or declared members representing the - * receiver's static context. * * @throws UnsupportedMessageException if and only if the receiver does not * {@linkplain #hasStaticReceiver(Object) have a static receiver} @@ -3062,13 +3061,13 @@ public Object getScopeParent(Object receiver) throws UnsupportedMessageException /** * Returns {@code true} if the argument is wrapped Java host language object. This method must * not cause any observable side-effects. If this method is implemented then also - * {@link #getHostObject(Object)} must be implemented. + * {@link #asHostObject(Object)} must be implemented. * - * @see #getHostObject(Object) - * @since 26.0 + * @see #asHostObject(Object) + * @since 25.1 */ - @Abstract(ifExported = "getHostObject") - public boolean hasHostObject(Object receiver) { + @Abstract(ifExported = "asHostObject") + public boolean isHostObject(Object receiver) { return false; } @@ -3076,19 +3075,19 @@ public boolean hasHostObject(Object receiver) { * Returns the Java host object representation of the given Truffle guest object. *

    * Implementations of this method must not produce any observable side effects. If this method - * is implemented, {@link #hasHostObject(Object)} must also be implemented and return + * is implemented, {@link #isHostObject(Object)} must also be implemented and return * {@code true} for the same receiver. * - * @throws UnsupportedMessageException if {@link #hasHostObject(Object)} returns {@code false} + * @throws UnsupportedMessageException if {@link #isHostObject(Object)} returns {@code false} * for the given receiver. * @throws HeapIsolationException if the guest object represents a host object located in a * foreign heap, for example in a polyglot isolate. * - * @see #hasHostObject(Object) - * @since 26.0 + * @see #isHostObject(Object) + * @since 25.1 */ - @Abstract(ifExported = "hasHostObject") - public Object getHostObject(Object receiver) throws UnsupportedMessageException, HeapIsolationException { + @Abstract(ifExported = "isHostObject") + public Object asHostObject(Object receiver) throws UnsupportedMessageException, HeapIsolationException { throw UnsupportedMessageException.create(); } @@ -3975,7 +3974,7 @@ public Object getStaticReceiver(Object receiver) throws UnsupportedMessageExcept Object result = delegate.getStaticReceiver(receiver); assert hadStaticReceiver || isMultiThreaded(receiver) : violationInvariant(receiver); assert validInteropReturn(receiver, result); - assert verifyStaticReceiver(receiver, result); + assert UNCACHED.hasMembers(result) : violationPost(receiver, result); return result; } catch (InteropException e) { assert e instanceof UnsupportedMessageException : violationPost(receiver, e); @@ -3984,11 +3983,6 @@ public Object getStaticReceiver(Object receiver) throws UnsupportedMessageExcept } } - private static boolean verifyStaticReceiver(Object instanceReceiver, Object staticReceiver) throws UnsupportedMessageException { - assert UNCACHED.hasMembers(instanceReceiver) && UNCACHED.hasMembers(staticReceiver) : violationPost(instanceReceiver, staticReceiver); - return true; - } - @Override public boolean hasHashEntries(Object receiver) { assert preCondition(receiver); @@ -5635,15 +5629,15 @@ private boolean assertHasNoLanguageId(Object receiver) { } @Override - public boolean hasHostObject(Object receiver) { + public boolean isHostObject(Object receiver) { if (CompilerDirectives.inCompiledCode()) { - return delegate.hasHostObject(receiver); + return delegate.isHostObject(receiver); } assert preCondition(receiver); - boolean result = delegate.hasHostObject(receiver); + boolean result = delegate.isHostObject(receiver); if (result) { try { - delegate.getHostObject(receiver); + delegate.asHostObject(receiver); } catch (InteropException e) { assert e instanceof HeapIsolationException : violationInvariant(receiver); } catch (Exception e) { @@ -5657,7 +5651,7 @@ public boolean hasHostObject(Object receiver) { private boolean assertHasNoHostObject(Object receiver) { try { - delegate.getHostObject(receiver); + delegate.asHostObject(receiver); assert false : violationInvariant(receiver); } catch (UnsupportedMessageException | HeapIsolationException e) { } @@ -5684,14 +5678,14 @@ public String getLanguageId(Object receiver) throws UnsupportedMessageException } @Override - public Object getHostObject(Object receiver) throws HeapIsolationException, UnsupportedMessageException { + public Object asHostObject(Object receiver) throws HeapIsolationException, UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { - return delegate.getHostObject(receiver); + return delegate.asHostObject(receiver); } assert preCondition(receiver); - boolean wasHasHostObject = delegate.hasHostObject(receiver); + boolean wasHasHostObject = delegate.isHostObject(receiver); try { - Object result = delegate.getHostObject(receiver); + Object result = delegate.asHostObject(receiver); assert wasHasHostObject : violationInvariant(receiver); return result; } catch (UnsupportedMessageException e) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/CallerSensitiveTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/CallerSensitiveTest.java index e51716e2d95e..4f1013697d2d 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/CallerSensitiveTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/CallerSensitiveTest.java @@ -69,13 +69,13 @@ public void testLogger() throws InteropException { TruffleObject getLogger = (TruffleObject) INTEROP.readMember(loggerClass, "getLogger"); logger = (TruffleObject) INTEROP.execute(getLogger, loggerName); - assertTrue(INTEROP.hasHostObject(logger)); - assertTrue(INTEROP.getHostObject(logger) instanceof Logger); + assertTrue(INTEROP.isHostObject(logger)); + assertTrue(INTEROP.asHostObject(logger) instanceof Logger); assertEquals(loggerName, asJavaObject(Logger.class, logger).getName()); logger = (TruffleObject) INTEROP.invokeMember(loggerClass, "getLogger", loggerName); - assertTrue(INTEROP.hasHostObject(logger)); - assertTrue(INTEROP.getHostObject(logger) instanceof Logger); + assertTrue(INTEROP.isHostObject(logger)); + assertTrue(INTEROP.asHostObject(logger) instanceof Logger); assertEquals(loggerName, asJavaObject(Logger.class, logger).getName()); } } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/FunctionalInterfaceTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/FunctionalInterfaceTest.java index fb6454c70f41..268340ca3935 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/FunctionalInterfaceTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/FunctionalInterfaceTest.java @@ -110,8 +110,8 @@ public void testLegacyFunctionalInterface() throws InteropException { public void testThread() throws InteropException { TruffleObject threadClass = (TruffleObject) env.lookupHostSymbol("java.lang.Thread"); Object result = INTEROP.instantiate(threadClass, new TestExecutable()); - assertTrue(INTEROP.hasHostObject(result)); - Object thread = INTEROP.getHostObject(result); + assertTrue(INTEROP.isHostObject(result)); + Object thread = INTEROP.asHostObject(result); assertTrue(thread instanceof Thread); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java index d317d62d0603..ff635c31ab3c 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java @@ -139,7 +139,7 @@ private Object asHostType(TruffleLanguage.Env env, Class c) { @SuppressWarnings("deprecation") private static Object verifyHostAdapterClass(TruffleLanguage.Env env, Object hostAdapterClass) { - assertTrue(INTEROP.hasHostObject(hostAdapterClass)); + assertTrue(INTEROP.isHostObject(hostAdapterClass)); assertTrue(env.isHostSymbol(hostAdapterClass)); assertFalse(INTEROP.isNull(hostAdapterClass) || INTEROP.hasStaticReceiver(hostAdapterClass)); assertTrue(INTEROP.isMetaObject(hostAdapterClass)); @@ -168,7 +168,7 @@ Object createHostAdapterClassWithClassOverrides(TruffleLanguage.Env env, Class { return INTEROP.invokeMember(instance2, "abstractMethod"); - }, AbstractTruffleException.class, e -> assertTrue(e.toString(), INTEROP.hasHostObject(e) && INTEROP.isException(e))); + }, AbstractTruffleException.class, e -> assertTrue(e.toString(), INTEROP.isHostObject(e) && INTEROP.isException(e))); assertTrue(INTEROP.isMetaInstance(env.asHostSymbol(Extensible.class), instance1)); assertTrue(INTEROP.isMetaInstance(env.asHostSymbol(Extensible.class), instance2)); @@ -412,7 +412,7 @@ public void testStackedHostAdaptersWithClassOverrides() throws InteropException impl2.put("baseMethod", (ProxyExecutable) (args) -> "baseMethodImpl2"); impl2.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl2"); Object guestObject2 = env.asGuestValue(ProxyObject.fromMap(impl2)); - Object adapterClass2 = createHostAdapterClass(env, new Class[]{Interface.class, (Class) INTEROP.getHostObject(adapterClass1)}); + Object adapterClass2 = createHostAdapterClass(env, new Class[]{Interface.class, (Class) INTEROP.asHostObject(adapterClass1)}); Object instance = instantianteHostAdapter(adapterClass2, guestObject2); assertEquals("abstractMethodImpl2", INTEROP.invokeMember(instance, "abstractMethod")); @@ -449,7 +449,7 @@ public void testStackedHostAdaptersWithoutClassOverrides() throws InteropExcepti impl2.put("baseMethod", (ProxyExecutable) (args) -> "baseMethodImpl2"); impl2.put("defaultMethod", (ProxyExecutable) (args) -> "defaultMethodImpl2"); Object guestObject2 = env.asGuestValue(ProxyObject.fromMap(impl2)); - Object adapterClass2 = createHostAdapterClass(env, new Class[]{Interface.class, (Class) INTEROP.getHostObject(adapterClass1)}); + Object adapterClass2 = createHostAdapterClass(env, new Class[]{Interface.class, (Class) INTEROP.asHostObject(adapterClass1)}); Object instance = instantianteHostAdapter(adapterClass2, guestObject1, guestObject2); assertEquals("abstractMethodImpl2", INTEROP.invokeMember(instance, "abstractMethod")); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java index aee91751c076..9ef0a79a3864 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java @@ -530,7 +530,7 @@ public void testHostExceptionIsHostSymbol() { TruffleTestAssumptions.assumeWeakEncapsulation(); expectedException = RuntimeException.class; customExceptionVerifier = (t) -> { - assertFalse(INTEROP.hasHostObject(t) && !INTEROP.isNull(t) && !INTEROP.hasStaticReceiver(t)); + assertFalse(INTEROP.isHostObject(t) && !INTEROP.isNull(t) && !INTEROP.hasStaticReceiver(t)); }; Value catcher = context.eval(ProxyLanguage.ID, CATCHER); Runnable thrower = HostExceptionTest::thrower; @@ -579,7 +579,7 @@ public void testHostExceptionCause() { assertTrue("should have exception cause", INTEROP.hasExceptionCause(hostEx)); Object cause = INTEROP.getExceptionCause(hostEx); assertTrue("cause should be an exception", INTEROP.isException(cause)); - assertTrue("cause should be a host exception", INTEROP.hasHostObject(cause)); + assertTrue("cause should be a host exception", INTEROP.isHostObject(cause)); Class causeClass = NoSuchFieldException.class; assertTrue("cause should be instanceof " + causeClass.getSimpleName(), INTEROP.isMetaInstance(env.asHostSymbol(causeClass), cause)); assertFalse("cause should not have another cause", INTEROP.hasExceptionCause(cause)); @@ -630,7 +630,7 @@ public void testThrowHostExceptionObject() { assertTrue("should have exception cause", INTEROP.hasExceptionCause(hostEx)); Object cause = INTEROP.getExceptionCause(hostEx); assertTrue("cause should be an exception", INTEROP.isException(cause)); - assertTrue("cause should be a host exception", INTEROP.hasHostObject(cause)); + assertTrue("cause should be a host exception", INTEROP.isHostObject(cause)); Class causeClass = NoSuchFieldException.class; assertTrue("cause should be instanceof " + causeClass.getSimpleName(), INTEROP.isMetaInstance(env.asHostSymbol(causeClass), cause)); assertFalse("cause should not have another cause", INTEROP.hasExceptionCause(cause)); @@ -899,7 +899,7 @@ public void testGuestExceptionCaughtByHost() { hostExceptionVerifier = null; customExceptionVerifier = (guestEx) -> { - assertFalse(guestEx.toString(), INTEROP.hasHostObject(guestEx) && INTEROP.isException(guestEx)); + assertFalse(guestEx.toString(), INTEROP.isHostObject(guestEx) && INTEROP.isException(guestEx)); assertTrue(guestEx.toString(), INTEROP.isException(guestEx)); assertEquals(List.of(expectedMessage, @@ -978,7 +978,7 @@ public void testHideHostStackFrames() { hostExceptionVerifier = null; customExceptionVerifier = (guestEx) -> { - assertFalse(guestEx.toString(), INTEROP.hasHostObject(guestEx) && INTEROP.isException(guestEx)); + assertFalse(guestEx.toString(), INTEROP.isHostObject(guestEx) && INTEROP.isException(guestEx)); assertTrue(guestEx.toString(), INTEROP.isException(guestEx)); List expectedStack = List.of(expectedMessage, @@ -1220,10 +1220,10 @@ public Object execute(VirtualFrame frame) { } private void verifyHostException(Throwable ex) { - assertTrue(INTEROP.hasHostObject(ex)); + assertTrue(INTEROP.isHostObject(ex)); assertNotNull("Unexpected exception: " + ex, expectedException); - assertThat(getHostObject(ex), instanceOf(expectedException)); - assertThat(getHostObject(ex), instanceOf(expectedException)); + assertThat(asHostObject(ex), instanceOf(expectedException)); + assertThat(asHostObject(ex), instanceOf(expectedException)); try { assertTrue(InteropLibrary.getUncached().isMetaInstance(env.asHostSymbol(Throwable.class), ex)); } catch (UnsupportedMessageException e) { @@ -1231,9 +1231,9 @@ private void verifyHostException(Throwable ex) { } } - private static Object getHostObject(Object guestObject) { + private static Object asHostObject(Object guestObject) { try { - return INTEROP.getHostObject(guestObject); + return INTEROP.asHostObject(guestObject); } catch (UnsupportedMessageException | HeapIsolationException e) { throw CompilerDirectives.shouldNotReachHere(e); } @@ -1242,8 +1242,8 @@ private static Object getHostObject(Object guestObject) { @TruffleBoundary Object checkAndUnwrapException(Throwable ex) { // Avoid catching an AssertionError wrapped as a host exception. - if (INTEROP.hasHostObject(ex) && INTEROP.isException(ex)) { - Throwable t = (Throwable) getHostObject(ex); + if (INTEROP.isHostObject(ex) && INTEROP.isException(ex)) { + Throwable t = (Throwable) asHostObject(ex); if (t instanceof AssertionError) { throw (AssertionError) t; } @@ -1324,7 +1324,7 @@ public Object execute(VirtualFrame frame) { throw CompilerDirectives.shouldNotReachHere(e); } catch (Exception ex) { if (interop.isException(ex)) { - assertTrue(INTEROP.hasHostObject(ex)); + assertTrue(INTEROP.isHostObject(ex)); try { throw interop.throwException(ex); } catch (UnsupportedMessageException e) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropErrorTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropErrorTest.java index 0e4a48a883d1..f65eaa6d8430 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropErrorTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropErrorTest.java @@ -210,10 +210,10 @@ public void testClassCastExceptionInHostMethod() throws InteropException { Object foo = INTEROP.readMember(hostObj, "cce"); AbstractPolyglotTest.assertFails(() -> INTEROP.invokeMember(hostObj, "cce", 42), RuntimeException.class, (e) -> { - assertTrue(INTEROP.hasHostObject(e) && INTEROP.isException(e)); + assertTrue(INTEROP.isHostObject(e) && INTEROP.isException(e)); }); AbstractPolyglotTest.assertFails(() -> INTEROP.execute(foo, 42), RuntimeException.class, (e) -> { - assertTrue(INTEROP.hasHostObject(e) && INTEROP.isException(e)); + assertTrue(INTEROP.isHostObject(e) && INTEROP.isException(e)); }); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/ProxyLanguageEnvTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/ProxyLanguageEnvTest.java index e2fac111650c..2ce5d3c6db9d 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/ProxyLanguageEnvTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/ProxyLanguageEnvTest.java @@ -98,9 +98,9 @@ void assertThrowsExceptionWithCause(Callable callable, Class INTEROP.instantiate(clazz, "test"), IOException.class); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java index 48696ab938d9..6c9885a2941d 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java @@ -1956,73 +1956,73 @@ public void testIsInvocableMemberWithReadSideEffects() throws UnsupportedMessage } @Test - public void testHasHostObject() { - GetHostObjectTest hostObject = new GetHostObjectTest(); + public void testIsHostObject() { + AsHostObjectTest hostObject = new AsHostObjectTest(); InteropLibrary hostObjectLib = createLibrary(InteropLibrary.class, hostObject); - hostObject.hasHostObject = true; + hostObject.isHostObject = true; hostObject.hostObjectProvider = () -> { throw UnsupportedMessageException.create(); }; - assertFails(() -> hostObjectLib.hasHostObject(hostObject), AssertionError.class); + assertFails(() -> hostObjectLib.isHostObject(hostObject), AssertionError.class); hostObject.hostObjectProvider = () -> Pair.create(42, "42"); - assertTrue(hostObjectLib.hasHostObject(hostObject)); + assertTrue(hostObjectLib.isHostObject(hostObject)); hostObject.hostObjectProvider = () -> { throw HeapIsolationException.create(); }; - assertTrue(hostObjectLib.hasHostObject(hostObject)); + assertTrue(hostObjectLib.isHostObject(hostObject)); - hostObject.hasHostObject = false; + hostObject.isHostObject = false; hostObject.hostObjectProvider = null; - assertFalse(hostObjectLib.hasHostObject(hostObject)); + assertFalse(hostObjectLib.isHostObject(hostObject)); hostObject.hostObjectProvider = () -> Pair.create(42, "42"); - assertFails(() -> hostObjectLib.hasHostObject(hostObject), AssertionError.class); + assertFails(() -> hostObjectLib.isHostObject(hostObject), AssertionError.class); hostObject.hostObjectProvider = () -> { throw HeapIsolationException.create(); }; - assertFalse(hostObjectLib.hasHostObject(hostObject)); + assertFalse(hostObjectLib.isHostObject(hostObject)); } @Test - public void testGetHostObject() throws UnsupportedMessageException, HeapIsolationException { - GetHostObjectTest hostObject = new GetHostObjectTest(); + public void testAsHostObject() throws UnsupportedMessageException, HeapIsolationException { + AsHostObjectTest hostObject = new AsHostObjectTest(); InteropLibrary l = createLibrary(InteropLibrary.class, hostObject); - hostObject.hasHostObject = false; + hostObject.isHostObject = false; hostObject.hostObjectProvider = null; - assertFails(() -> l.getHostObject(hostObject), UnsupportedMessageException.class); + assertFails(() -> l.asHostObject(hostObject), UnsupportedMessageException.class); - hostObject.hasHostObject = true; + hostObject.isHostObject = true; Object value = new Object(); hostObject.hostObjectProvider = () -> value; - assertSame(value, l.getHostObject(hostObject)); + assertSame(value, l.asHostObject(hostObject)); - hostObject.hasHostObject = true; + hostObject.isHostObject = true; hostObject.hostObjectProvider = null; - assertFails(() -> l.getHostObject(hostObject), AssertionError.class); + assertFails(() -> l.asHostObject(hostObject), AssertionError.class); - hostObject.hasHostObject = false; + hostObject.isHostObject = false; hostObject.hostObjectProvider = () -> value; - assertFails(() -> l.getHostObject(hostObject), AssertionError.class); + assertFails(() -> l.asHostObject(hostObject), AssertionError.class); } @ExportLibrary(InteropLibrary.class) - static class GetHostObjectTest implements TruffleObject { + static class AsHostObjectTest implements TruffleObject { - boolean hasHostObject; + boolean isHostObject; HostObjectProvider hostObjectProvider; @ExportMessage - boolean hasHostObject() { - return hasHostObject; + boolean isHostObject() { + return isHostObject; } @ExportMessage - Object getHostObject() throws UnsupportedMessageException, HeapIsolationException { + Object asHostObject() throws UnsupportedMessageException, HeapIsolationException { if (hostObjectProvider != null) { return hostObjectProvider.call(); } else { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java index 8485fe380ac9..a9def153877b 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java @@ -485,7 +485,7 @@ static class ContextExtendTestLanguage extends AbstractExecutableTestLanguage { @TruffleBoundary protected Object execute(RootNode node, Env env, Object[] contextArguments, Object[] frameArguments) { try { - if (frameArguments.length == 1 && interop.hasHostObject(frameArguments[0])) { + if (frameArguments.length == 1 && interop.isHostObject(frameArguments[0])) { return interop.invokeMember(frameArguments[0], "getName"); } if (frameArguments.length == 1 && interop.isString(frameArguments[0]) && interop.asString(frameArguments[0]).equals("return-array")) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java index c6da53c820ac..e0daf6811004 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/FileSystemsTest.java @@ -1942,12 +1942,12 @@ protected Object execute(RootNode node, Env env, Object[] contextArguments, Obje Assert.fail("Should not reach here."); } catch (Exception e) { if (isInternal) { - Assert.assertFalse(interop.hasHostObject(e) && interop.isException(e)); + Assert.assertFalse(interop.isHostObject(e) && interop.isException(e)); Assert.assertTrue(e instanceof RuntimeException); } else { - Assert.assertTrue(interop.hasHostObject(e) && interop.isException(e)); + Assert.assertTrue(interop.isHostObject(e) && interop.isException(e)); if (weakEncapsulation) { - Assert.assertTrue(interop.getHostObject(e) instanceof NullPointerException); + Assert.assertTrue(interop.asHostObject(e) instanceof NullPointerException); } } } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostClassLoadingTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostClassLoadingTest.java index 471d9d996d6c..1ec49397bb3d 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostClassLoadingTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostClassLoadingTest.java @@ -471,7 +471,7 @@ public void testProtectionDomainJar() throws IOException, InteropException { Path jar = createJar(tempDir); languageEnv.addToHostClassPath(languageEnv.getPublicTruffleFile(jar.toString())); Object newSymbol = languageEnv.lookupHostSymbol(hostClass.getPackage().getName() + "." + TEST_REPLACE_CLASS_NAME); - Class clz = (Class) INTEROP.getHostObject(newSymbol); + Class clz = (Class) INTEROP.asHostObject(newSymbol); ProtectionDomain protectionDomain = clz.getProtectionDomain(); assertNotNull(protectionDomain); CodeSource codeSource = protectionDomain.getCodeSource(); @@ -488,7 +488,7 @@ public void testProtectionDomainFolder() throws IOException, InteropException { Path tempDir = renameHostClass(hostClass, TEST_REPLACE_CLASS_NAME); languageEnv.addToHostClassPath(languageEnv.getPublicTruffleFile(tempDir.toString())); Object newSymbol = languageEnv.lookupHostSymbol(hostClass.getPackage().getName() + "." + TEST_REPLACE_CLASS_NAME); - Class clz = (Class) INTEROP.getHostObject(newSymbol); + Class clz = (Class) INTEROP.asHostObject(newSymbol); ProtectionDomain protectionDomain = clz.getProtectionDomain(); assertNotNull(protectionDomain); CodeSource codeSource = protectionDomain.getCodeSource(); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java index ee8b7cac7b85..ef49bdf1f2a5 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPIHostInteropTest.java @@ -105,8 +105,8 @@ public static class TestClass { @Test public void conversionToClassYieldsTheClass() throws Exception { - Object javaValue = INTEROP.getHostObject(languageEnv.asGuestValue(TestClass.class)); - Object javaSymbol = INTEROP.getHostObject(languageEnv.lookupHostSymbol(TestClass.class.getName())); + Object javaValue = INTEROP.asHostObject(languageEnv.asGuestValue(TestClass.class)); + Object javaSymbol = INTEROP.asHostObject(languageEnv.lookupHostSymbol(TestClass.class.getName())); assertTrue(javaValue instanceof Class); assertSame("Both class objects are the same", javaValue, javaSymbol); @@ -121,8 +121,8 @@ public void conversionToClassNull() { @Test public void nullAsJavaObject() throws Exception { Object nullObject = languageEnv.asGuestValue(null); - assertTrue(INTEROP.hasHostObject(nullObject)); - assertNull(INTEROP.getHostObject(nullObject)); + assertTrue(INTEROP.isHostObject(nullObject)); + assertNull(INTEROP.asHostObject(nullObject)); } @SuppressWarnings("unchecked") @@ -155,7 +155,7 @@ public void invokeJavaLangObjectFields() throws InteropException { Object string = INTEROP.invokeMember(obj, "toString"); assertTrue(string instanceof String && ((String) string).startsWith(Data.class.getName() + "@")); Object clazz = INTEROP.invokeMember(obj, "getClass"); - assertTrue(clazz instanceof TruffleObject && INTEROP.getHostObject(clazz) == Data.class); + assertTrue(clazz instanceof TruffleObject && INTEROP.asHostObject(clazz) == Data.class); assertEquals(true, INTEROP.invokeMember(obj, "equals", obj)); assertTrue(INTEROP.invokeMember(obj, "hashCode") instanceof Integer); @@ -247,8 +247,8 @@ private void assertNumberMembers(Number testNumber) throws InteropException { assertInvocable(testNumber.hashCode(), guestValue, "hashCode"); assertInvocable(testNumber.toString(), guestValue, "toString"); - assertTrue(INTEROP.hasHostObject(guestValue)); - assertEquals(testNumber, INTEROP.getHostObject(guestValue)); + assertTrue(INTEROP.isHostObject(guestValue)); + assertEquals(testNumber, INTEROP.asHostObject(guestValue)); } private static void assertInvocable(Object expectedValue, Object receiver, String method, Object... args) throws InteropException { @@ -293,8 +293,8 @@ private static void assertStringMembers(String unboxValue, Object stringObject) assertInvocable(string.equals(unboxValue), stringObject, "equals", unboxValue); assertInvocable(string.hashCode(), stringObject, "hashCode"); assertInvocable(string.toString(), stringObject, "toString"); - assertTrue(INTEROP.hasHostObject(stringObject)); - assertEquals(string, INTEROP.getHostObject(stringObject)); + assertTrue(INTEROP.isHostObject(stringObject)); + assertEquals(string, INTEROP.asHostObject(stringObject)); } private static void assertBooleanMembers(Object unboxValue, Object booleanObject) throws InteropException { @@ -307,8 +307,8 @@ private static void assertBooleanMembers(Object unboxValue, Object booleanObject assertInvocable(b.hashCode(), booleanObject, "hashCode"); assertInvocable(b.toString(), booleanObject, "toString"); - assertTrue(INTEROP.hasHostObject(booleanObject)); - assertEquals(b, INTEROP.getHostObject(booleanObject)); + assertTrue(INTEROP.isHostObject(booleanObject)); + assertEquals(b, INTEROP.asHostObject(booleanObject)); } private static void assertCharacterMembers(Character unboxValue, Object charObject) throws InteropException { @@ -321,8 +321,8 @@ private static void assertCharacterMembers(Character unboxValue, Object charObje assertInvocable(b.hashCode(), charObject, "hashCode"); assertInvocable(b.toString(), charObject, "toString"); - assertTrue(INTEROP.hasHostObject(charObject)); - assertEquals(unboxValue, INTEROP.getHostObject(charObject)); + assertTrue(INTEROP.isHostObject(charObject)); + assertEquals(unboxValue, INTEROP.asHostObject(charObject)); } @Test @@ -357,9 +357,9 @@ private static void assertThrowsExceptionWithCause(Callable callable, Class INTEROP.asHostObject(exit), HeapIsolationException.class); assertTrue(languageEnv.isHostFunction(exit)); Object out = INTEROP.readMember(system, "out"); assertTrue(exit instanceof TruffleObject); - assertTrue(INTEROP.hasHostObject(out)); - assertFalse(INTEROP.hasHostObject(out) && INTEROP.isExecutable(out) && !INTEROP.hasMembers(out)); + assertTrue(INTEROP.isHostObject(out)); + assertFalse(INTEROP.isHostObject(out) && INTEROP.isExecutable(out) && !INTEROP.hasMembers(out)); assertFalse(languageEnv.isHostFunction(out)); assertFalse(languageEnv.isHostFunction(system)); - assertFalse(INTEROP.hasHostObject(system) && INTEROP.isExecutable(system) && !INTEROP.hasMembers(system)); + assertFalse(INTEROP.isHostObject(system) && INTEROP.isExecutable(system) && !INTEROP.hasMembers(system)); assertFalse(languageEnv.isHostFunction(false)); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxySPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxySPITest.java index 9cb12fd65a6a..c86d59b3f661 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxySPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ProxySPITest.java @@ -937,7 +937,7 @@ private static void assertHostError(InteropCallable r) throws InteropException { InteropLibrary interop = InteropLibrary.getUncached(); Assert.assertTrue(interop.isException(e)); Assert.assertEquals("Host Error", e.getMessage()); - Assert.assertTrue(INTEROP.getHostObject(e) instanceof TestError); + Assert.assertTrue(INTEROP.asHostObject(e) instanceof TestError); } } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEntryPoint.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEntryPoint.java index a9176d7f17be..57b04c1c35a4 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEntryPoint.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostEntryPoint.java @@ -145,7 +145,7 @@ public long remoteGetBindings(long contextId, String languageId) { return guestToHost(api.getValueReceiver(v)); } - public Object remoteMessage(long contextId, long receiverId, Message message, Object[] args) { + public Object remoteMessage(long contextId, long receiverId, Message message, Object[] args) throws InteropException { Context c = unmarshall(Context.class, contextId); c.enter(); try { @@ -156,7 +156,7 @@ public Object remoteMessage(long contextId, long receiverId, Message message, Ob try { result = lib.send(receiver, message, localValues); } catch (InteropException e) { - throw sneakyThrow(e); + throw e; } catch (AbstractTruffleException e) { // also send over stack traces and messages return new GuestExceptionPointer(guestToHost(e), e.getMessage()); @@ -169,11 +169,6 @@ public Object remoteMessage(long contextId, long receiverId, Message message, Ob } } - @SuppressWarnings("unchecked") - private static RuntimeException sneakyThrow(Throwable ex) throws T { - throw (T) ex; - } - static class GuestExceptionPointer { final long id; diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostGuestValue.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostGuestValue.java index e4ad57759bf6..16535e97395e 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostGuestValue.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/wrapper/HostGuestValue.java @@ -43,6 +43,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.interop.ExceptionType; +import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.library.ExportLibrary; @@ -81,7 +82,7 @@ final Object send(Message message, Object... args) throws Exception { } } - static Object sendImpl(HostEntryPoint hostToGuest, long contextId, long guestId, Message message, Object... args) throws AbstractTruffleException { + static Object sendImpl(HostEntryPoint hostToGuest, long contextId, long guestId, Message message, Object... args) throws InteropException, AbstractTruffleException { Object[] marshalledArgs = marshalToRemote(hostToGuest, args); Object result = hostToGuest.remoteMessage(contextId, guestId, message, marshalledArgs); return unmarshallAtHost(hostToGuest, contextId, result); diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index fae457089178..0ed61af32a17 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -2238,8 +2238,8 @@ public Object lookupHostSymbol(String symbolName) { * Truffle interop. * * @deprecated Use - * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#hasHostObject(Object) - * hosHostObject}. + * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#isHostObject(Object) + * isHostObject}. * @see #asHostObject(Object) * @since 19.0 */ @@ -2259,8 +2259,8 @@ public boolean isHostObject(Object value) { * {@link #isHostObject(Object) host object}. * * @deprecated Use - * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#getHostObject(Object) - * getHostObject}. + * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#asHostObject(Object) + * asHostObject}. * @since 19.0 */ @Deprecated(since = "25.1") @@ -2400,8 +2400,8 @@ public boolean isHostException(Throwable exception) { * @see #isHostException(Throwable) * @since 19.0 * @deprecated Use - * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#getHostObject(Object) - * getHostObject}. + * {@linkplain com.oracle.truffle.api.interop.InteropLibrary#asHostObject(Object) + * asHostObject}. */ @Deprecated(since = "25.1") @SuppressWarnings("static-method") diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java index f120ca4acac3..b2abd6ff5529 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java @@ -509,7 +509,7 @@ public abstract Thread createThread(Object polyglotLanguageContext, Runnable run public abstract boolean isHostException(Throwable exception); - public abstract Throwable asHostException(Throwable exception) throws Exception; + public abstract Throwable asHostException(Throwable exception); public abstract Object getCurrentHostContext(); diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java index c7e6948fd277..d95e42fd0b96 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostFunction.java @@ -76,13 +76,13 @@ boolean isExecutable() { @SuppressWarnings("static-method") @ExportMessage - boolean hasHostObject() { + boolean isHostObject() { return true; } @SuppressWarnings("static-method") @ExportMessage - Object getHostObject() throws HeapIsolationException { + Object asHostObject() throws HeapIsolationException { throw HeapIsolationException.create(); } diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java index 59bf5a6ed4a0..bc663efc64c3 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodScope.java @@ -65,7 +65,7 @@ final class HostMethodScope { private static final ScopedObject[] EMTPY_SCOPE_ARRAY = new ScopedObject[0]; private static final Unsafe UNSAFE = getUnsafe(); - private static final Message MESSAGE_GET_HOST_OBJECT = Message.resolveExact(InteropLibrary.class, "getHostObject", Object.class); + private static final Message MESSAGE_AS_HOST_OBJECT = Message.resolveExact(InteropLibrary.class, "asHostObject", Object.class); private ScopedObject[] scope; private int nextDynamicIndex; @@ -247,7 +247,7 @@ Object send(Message message, Object[] args, "Alternatively, use Value.pin() to prevent a scoped object from being released after the host call completed."); } Object returnValue = library.send(d, message, args); - if (message.getReturnType() == Object.class && !(d instanceof PinnedObject) && message != MESSAGE_GET_HOST_OBJECT) { + if (message.getReturnType() == Object.class && !(d instanceof PinnedObject) && message != MESSAGE_AS_HOST_OBJECT) { /* * Object return type indicates for an interop message that any interop value may be * returned. diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java index 5386d34b1254..af60af98b316 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java @@ -3312,7 +3312,7 @@ Object getMetaSimpleName() throws UnsupportedMessageException { } } - @ExportMessage(limit = "LIMIT") + @ExportMessage @TruffleBoundary boolean isMetaInstance(Object other, @Bind Node node, @@ -3321,10 +3321,10 @@ boolean isMetaInstance(Object other, Class c = asClass(); HostLanguage language = context != null ? HostLanguage.get(node) : null; InteropLibrary otherInterop = InteropLibrary.getUncached(other); - if (otherInterop.hasHostObject(other)) { + if (otherInterop.isHostObject(other)) { Object otherHostObj; try { - otherHostObj = otherInterop.getHostObject(other); + otherHostObj = otherInterop.asHostObject(other); } catch (HeapIsolationException e) { error.enter(node); throw UnsupportedMessageException.create(); @@ -3420,14 +3420,15 @@ boolean hasStaticReceiver() { } @ExportMessage - Object getStaticReceiver() throws UnsupportedMessageException { - if (hasStaticReceiver()) { - Class clz = getLookupClass(); - return new HostObject(clz, context, clz); - } else { - CompilerDirectives.transferToInterpreter(); + Object getStaticReceiver( + @Bind Node node, + @Shared("error") @Cached InlinedBranchProfile error) throws UnsupportedMessageException { + if (!hasStaticReceiver()) { + error.enter(node); throw UnsupportedMessageException.create(); } + Class clz = obj.getClass(); + return HostObject.forStaticClass(clz, context); } boolean isStaticClass() { @@ -3487,12 +3488,12 @@ static int identityHashCode(HostObject receiver) { @ExportMessage @SuppressWarnings("static-method") - boolean hasHostObject() { + boolean isHostObject() { return true; } @ExportMessage - Object getHostObject() { + Object asHostObject() { return this.obj; } diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java index 9bc421ac3684..a86295e75706 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostToTypeNode.java @@ -322,7 +322,7 @@ static boolean canConvert(Node node, Object value, Class targetType, Type gen } if (value instanceof TruffleObject) { - if (priority < HOST_PROXY && interop.hasHostObject(value)) { + if (priority < HOST_PROXY && interop.isHostObject(value)) { return false; } else { if (priority >= FUNCTION_PROXY && HostInteropReflect.isFunctionalInterface(targetType) && @@ -432,8 +432,8 @@ private static T asJavaObject(Node node, HostContext hostContext, Object val InteropLibrary interop = InteropLibrary.getFactory().getUncached(value); assert !interop.isNull(value); // already handled Object obj; - Object hostObject; - if ((hostObject = toJavaInstance(value, targetType, interop)) != null) { + Object hostObject = toJavaInstance(value, targetType, interop); + if (hostObject != null) { obj = hostObject; } else if (targetType == Object.class) { obj = convertToObject(node, hostContext, value, interop); @@ -658,9 +658,9 @@ private static T asJavaObject(Node node, HostContext hostContext, Object val } private static Object toJavaInstance(Object value, Class targetType, InteropLibrary interop) { - if (interop.hasHostObject(value)) { + if (interop.isHostObject(value)) { try { - Object hostObject = interop.getHostObject(value); + Object hostObject = interop.asHostObject(value); if (hostObject != null && targetType.isInstance(hostObject)) { return hostObject; } diff --git a/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java b/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java index 7c9ccd28cf28..a3e2ca8e7bdd 100644 --- a/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java +++ b/truffle/src/com.oracle.truffle.nfi.backend.libffi/src/com/oracle/truffle/nfi/backend/libffi/SerializeArgumentNode.java @@ -631,7 +631,7 @@ abstract static class SerializeArrayNode extends SerializeArgumentNode { final Object asHostObject(Object value, InteropLibrary interop) throws UnsupportedTypeException { try { - return interop.getHostObject(value); + return interop.asHostObject(value); } catch (UnsupportedMessageException e) { throw CompilerDirectives.shouldNotReachHere(e); } catch (HeapIsolationException e) { @@ -639,7 +639,7 @@ final Object asHostObject(Object value, InteropLibrary interop) throws Unsupport } } - @Specialization(guards = {"interop.hasHostObject(value)", "tag != null"}, limit = "3") + @Specialization(guards = {"interop.isHostObject(value)", "tag != null"}, limit = "3") void doHostObject(@SuppressWarnings("unused") Object value, NativeArgumentBuffer buffer, @CachedLibrary("value") InteropLibrary interop, @Bind("asHostObject(value, interop)") Object hostObject, diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/DefaultPolyglotHostService.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/DefaultPolyglotHostService.java index a6d6f39dd611..caa350476b47 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/DefaultPolyglotHostService.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/DefaultPolyglotHostService.java @@ -83,7 +83,7 @@ public RuntimeException hostToGuestException(AbstractHostLanguageService host, T private static boolean isHostException(Throwable throwable) { InteropLibrary interop = InteropLibrary.getUncached(throwable); - return interop.hasHostObject(throwable) && interop.isException(throwable); + return interop.isHostObject(throwable) && interop.isException(throwable); } @Override diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java index 57b1ef2cf9be..2c721d71152d 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java @@ -74,6 +74,7 @@ import java.util.logging.Level; import java.util.logging.LogRecord; +import com.oracle.truffle.api.interop.HeapIsolationException; import org.graalvm.collections.Pair; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.options.OptionKey; @@ -628,7 +629,7 @@ public boolean isMultiThreaded(Object guestObject) { } if (isPrimitive(guestObject)) { return false; - } else if (InteropLibrary.getFactory().getUncached(guestObject).hasHostObject(guestObject) || context.engine.host.isHostProxy(guestObject) || guestObject instanceof PolyglotBindings) { + } else if (InteropLibrary.getFactory().getUncached(guestObject).isHostObject(guestObject) || context.engine.host.isHostProxy(guestObject) || guestObject instanceof PolyglotBindings) { return true; } PolyglotLanguage language = findObjectLanguage(context.engine, guestObject); @@ -1259,21 +1260,28 @@ public RuntimeException wrapHostException(Node location, Object languageContext, } @Override + @TruffleBoundary public boolean isHostException(Throwable exception) { InteropLibrary interop = InteropLibrary.getUncached(exception); - return interop.hasHostObject(exception) && interop.isException(exception); + return interop.isHostObject(exception) && interop.isException(exception); } @Override - public Throwable asHostException(Throwable exception) throws Exception { + @TruffleBoundary + public Throwable asHostException(Throwable exception) { if (exception != null) { InteropLibrary interop = InteropLibrary.getUncached(exception); - boolean isHostException = interop.hasHostObject(exception) && interop.isException(exception); + boolean isHostException = interop.isHostObject(exception) && interop.isException(exception); if (isHostException) { - return (Throwable) interop.getHostObject(exception); + try { + return (Throwable) interop.asHostObject(exception); + } catch (HeapIsolationException e) { + return null; + } catch (UnsupportedMessageException e) { + // Fall through to IllegalArgumentException + } } } - CompilerDirectives.transferToInterpreterAndInvalidate(); throw new IllegalArgumentException("Provided value not a host exception."); } @@ -1541,32 +1549,40 @@ public Object getLoggerOwner(Object loggerCache) { } @Override - public Object asHostObject(Object obj) throws Exception { + @TruffleBoundary + public Object asHostObject(Object obj) { InteropLibrary interop = InteropLibrary.getUncached(obj); - if (interop.hasHostObject(obj)) { - return interop.getHostObject(obj); - } else { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException("Provided value not a host object."); + if (interop.isHostObject(obj)) { + try { + return interop.asHostObject(obj); + } catch (HeapIsolationException e) { + return null; + } catch (UnsupportedMessageException e) { + // Fall through to IllegalArgumentException + } } + throw new IllegalArgumentException("Provided value not a host object."); } @Override + @TruffleBoundary public boolean isHostFunction(Object obj) { InteropLibrary interop = InteropLibrary.getUncached(obj); - return interop.hasHostObject(obj) && interop.isExecutable(obj) && !interop.hasMembers(obj); + return interop.isHostObject(obj) && interop.isExecutable(obj) && !interop.hasMembers(obj); } @Override + @TruffleBoundary public boolean isHostObject(Object obj) { InteropLibrary interop = InteropLibrary.getUncached(obj); - return interop.hasHostObject(obj); + return interop.isHostObject(obj); } @Override + @TruffleBoundary public boolean isHostSymbol(Object obj) { InteropLibrary interop = InteropLibrary.getUncached(obj); - return interop.hasHostObject(obj) && !interop.isNull(obj) && !interop.hasStaticReceiver(obj); + return interop.isHostObject(obj) && !interop.isNull(obj) && !interop.hasStaticReceiver(obj); } @Override diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java index 1cff8c2a526c..01d0e86abe25 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/OtherContextGuestObject.java @@ -66,7 +66,7 @@ final class OtherContextGuestObject implements TruffleObject { static final Object OTHER_VALUE = new Object(); static final ReflectionLibrary OTHER_VALUE_UNCACHED = ReflectionLibrary.getFactory().getUncached(OTHER_VALUE); - private static final Message MESSAGE_GET_HOST_OBJECT = Message.resolveExact(InteropLibrary.class, "getHostObject", Object.class); + private static final Message MESSAGE_AS_HOST_OBJECT = Message.resolveExact(InteropLibrary.class, "asHostObject", Object.class); final PolyglotContextImpl receiverContext; final Object delegate; @@ -266,7 +266,7 @@ private static Object fallbackSend(Message message, Object[] args) throws Except } private static Object migrateReturn(Object arg, Message message, PolyglotContextImpl receiverContext, PolyglotContextImpl delegateContext) { - if (message == MESSAGE_GET_HOST_OBJECT) { + if (message == MESSAGE_AS_HOST_OBJECT) { return arg; } else if (arg instanceof TruffleObject) { return receiverContext.migrateValue(arg, delegateContext); diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java index 86877ff6ea84..845bbee1faa3 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java @@ -591,12 +591,12 @@ public Object apply(TruffleStackTraceElement guestFrame) { private static boolean isHostException(Throwable cause) { InteropLibrary interop = InteropLibrary.getUncached(cause); - return interop.hasHostObject(cause) && interop.isException(cause); + return interop.isHostObject(cause) && interop.isException(cause); } private static Throwable unboxHostException(Throwable cause) { try { - return (Throwable) InteropLibrary.getUncached(cause).getHostObject(cause); + return (Throwable) InteropLibrary.getUncached(cause).asHostObject(cause); } catch (Exception e) { throw CompilerDirectives.shouldNotReachHere(e); } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java index 2fcf50ce6e4b..2a7d0b7564e7 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java @@ -5634,7 +5634,7 @@ protected String getOperationName() { @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, @Bind Node node, @CachedLibrary("receiver") InteropLibrary objects) { - return objects.hasHostObject(receiver); + return objects.isHostObject(receiver); } } @@ -5660,7 +5660,7 @@ static Object doCached(PolyglotLanguageContext context, Object receiver, Object[ @Cached InlinedBranchProfile unsupported, @Cached InlinedBranchProfile isolatedHeap) { try { - return objects.getHostObject(receiver); + return objects.asHostObject(receiver); } catch (UnsupportedMessageException e) { unsupported.enter(node); return asHostObjectUnsupported(context, receiver); diff --git a/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java b/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java index f6216a4c66f7..72583e7f1eeb 100644 --- a/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java +++ b/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/tstring/TStringBenchDummyLanguage.java @@ -243,7 +243,7 @@ abstract static class RawIterateBytesNode extends Node { @Specialization(limit = "3") int bench(Object hostObject, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); int ret = 0; for (int i = 0; i < input.length; i++) { ret += Byte.toUnsignedInt(input[i]); @@ -332,7 +332,7 @@ abstract static class CalcStringAttributesUTF8Node extends Node { TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_8, false); } } @@ -345,7 +345,7 @@ abstract static class CalcStringAttributesUTF16Node extends Node { TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16, false); } } @@ -358,7 +358,7 @@ abstract static class FromByteArrayUTF16Node extends Node { TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16, true); } } @@ -371,7 +371,7 @@ abstract static class FromByteArrayUTF32Node extends Node { TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32, true); } } @@ -384,7 +384,7 @@ abstract static class FromByteArrayUTF16FENode extends Node { TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16BE, false); } } @@ -398,7 +398,7 @@ TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); return switchEncodingNode.execute(fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16BE, false), TruffleString.Encoding.UTF_16); } } @@ -411,7 +411,7 @@ abstract static class FromByteArrayUTF32FENode extends Node { TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32BE, false); } } @@ -425,7 +425,7 @@ TruffleString bench(Object hostObject, int length, @Cached TruffleString.FromByteArrayNode fromByteArrayNode, @Cached TruffleString.SwitchEncodingNode switchEncodingNode, @CachedLibrary("hostObject") InteropLibrary interop) { - byte[] input = (byte[]) getHostObject(hostObject, interop); + byte[] input = (byte[]) asHostObject(hostObject, interop); return switchEncodingNode.execute(fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32BE, false), TruffleString.Encoding.UTF_32); } } @@ -469,9 +469,9 @@ public static DummyLanguageContext get(Node node) { } } - static Object getHostObject(Object guestObject, InteropLibrary interop) { + static Object asHostObject(Object guestObject, InteropLibrary interop) { try { - return interop.getHostObject(guestObject); + return interop.asHostObject(guestObject); } catch (UnsupportedMessageException | HeapIsolationException e) { throw CompilerDirectives.shouldNotReachHere(e); } From 7edea1db0cd3d354a89ea913ecde130a37cbe3e1 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Fri, 28 Nov 2025 14:30:18 +0100 Subject: [PATCH 12/14] MetaObject's static receiver exposes the static members of the represented type rather than static members of the metaobject itself. --- .../src/org/graalvm/polyglot/Value.java | 5 ++++- .../src/com/oracle/truffle/api/interop/InteropLibrary.java | 5 ++++- .../src/com/oracle/truffle/host/HostObject.java | 7 ++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java index 0ba50c84996a..b9d27342068b 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java @@ -1025,7 +1025,10 @@ public boolean hasStaticReceiver() { /** * Returns the static receiver associated with this value. A static receiver is an object that * exposes static members, members whose values or behaviors are independent of any particular - * instance of this value. + * instance of this value. If this value is a {@link #isMetaObject() metaobject}, its static + * receiver is the same object that would be returned for an instance of the type represented by + * that metaobject, it exposes the static members of the represented type rather than static + * members of the metaobject itself. *

    * The returned static receiver can be used to access static members using * {@link #getMember(String)}, {@link #getMemberKeys()}, or diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java index 9c7f4ac2421d..92ab3ddfd26a 100644 --- a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java @@ -983,7 +983,10 @@ public boolean hasStaticReceiver(Object receiver) { /** * Returns the static receiver associated with the given receiver. A static receiver is an * object that exposes static members, members whose values or behaviors are independent of any - * particular instance of the receiver. + * particular instance of the receiver. If the {@code receiver} is a + * {@link #isMetaObject(Object) metaobject}, its static receiver is the same object that would + * be returned for an instance of the type represented by that metaobject, it exposes the static + * members of the represented type rather than static members of the metaobject itself. *

    * The static receiver typically serves as an artificial or meta-level object that provides * access to instance-independent members declared by the receiver's type or meta object. The diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java index af60af98b316..7cfee4973d42 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java @@ -3427,7 +3427,12 @@ Object getStaticReceiver( error.enter(node); throw UnsupportedMessageException.create(); } - Class clz = obj.getClass(); + Class clz; + if (isClass()) { + clz = (Class) obj; + } else { + clz = obj.getClass(); + } return HostObject.forStaticClass(clz, context); } From 8d96e5bca28e3c00ca540ad33a2e76575c4339f7 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Wed, 3 Dec 2025 15:31:54 +0100 Subject: [PATCH 13/14] Rename getStaticReceiver to getStaticScope. GetStaticScope is supported only on meta object. --- sdk/CHANGELOG.md | 2 +- sdk/src/org.graalvm.polyglot/snapshot.sigtest | 4 +- .../src/org/graalvm/polyglot/Value.java | 48 +-- .../polyglot/impl/AbstractPolyglotImpl.java | 4 +- truffle/CHANGELOG.md | 2 +- .../snapshot.sigtest | 2 +- .../oracle/truffle/api/debug/DebugValue.java | 8 +- .../snapshot.sigtest | 4 +- .../truffle/api/interop/InteropLibrary.java | 125 +++++--- .../api/test/host/HostAdapterTest.java | 3 +- .../api/test/host/HostExceptionTest.java | 2 +- .../test/interop/InteropAssertionsTest.java | 280 ++++++++++++++++-- .../api/test/polyglot/HostAccessTest.java | 24 +- .../api/test/polyglot/ValueAPITest.java | 28 +- .../polyglot/ValueHostConversionTest.java | 9 +- .../test/polyglot/ValueHostInteropTest.java | 2 +- .../oracle/truffle/api/TruffleLanguage.java | 3 +- .../com/oracle/truffle/host/HostObject.java | 22 +- .../truffle/polyglot/EngineAccessor.java | 2 +- .../polyglot/PolyglotValueDispatch.java | 46 +-- .../oracle/truffle/tck/tests/ValueAssert.java | 34 ++- .../interop/HostProxyBenchmarkLanguage.java | 12 +- 22 files changed, 469 insertions(+), 197 deletions(-) diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index bea04cdc4fbf..8893f2590384 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -12,7 +12,7 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f * GR-69590: Closing a garbage-collected engine or context now logs only the first failure by default. To log all failures, use `engine.CloseOnGCFailureAction.PrintAll`. * GR-35913: Updated the Javadoc of `Value#asHostObject()`, `Value#asNativePointer()`, and `Value#asProxyObject()` to clarify that these methods throw a `ClassCastException` rather than an `UnsupportedOperationException` when the value is not of the expected type. * GR-35913: `Value#asHostObject()` throws `UnsupportedOperationException` if object is allocated in a foreign heap. -* GR-71402: Added `Value#hasStaticReceiver` and `Value#getStaticReceiver` returning the Value's static receiver. +* GR-71402: Added `Value#hasStaticScope` and `Value#getStaticScope` returning the static scope representing static or class-level members associated with the meta object. ## Version 25.0.0 * GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens. diff --git a/sdk/src/org.graalvm.polyglot/snapshot.sigtest b/sdk/src/org.graalvm.polyglot/snapshot.sigtest index 63728c8e9ec8..97405138c477 100644 --- a/sdk/src/org.graalvm.polyglot/snapshot.sigtest +++ b/sdk/src/org.graalvm.polyglot/snapshot.sigtest @@ -598,7 +598,7 @@ meth public boolean hasIteratorNextElement() meth public boolean hasMember(java.lang.String) meth public boolean hasMembers() meth public boolean hasMetaParents() -meth public boolean hasStaticReceiver() +meth public boolean hasStaticScope() meth public boolean isBoolean() meth public boolean isBufferWritable() meth public boolean isDate() @@ -660,7 +660,7 @@ meth public org.graalvm.polyglot.Value getIteratorNextElement() meth public org.graalvm.polyglot.Value getMember(java.lang.String) meth public org.graalvm.polyglot.Value getMetaObject() meth public org.graalvm.polyglot.Value getMetaParents() -meth public org.graalvm.polyglot.Value getStaticReceiver() +meth public org.graalvm.polyglot.Value getStaticScope() meth public short asShort() meth public short readBufferShort(java.nio.ByteOrder,long) meth public static org.graalvm.polyglot.Value asValue(java.lang.Object) diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java index b9d27342068b..6c01da02c2f1 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Value.java @@ -1009,52 +1009,52 @@ public boolean removeMember(String identifier) { } /** - * Returns {@code true} if this value provides a {@linkplain #getStaticReceiver() static - * receiver}. A static receiver represents the static or class-level members associated with - * this value's type, such as static fields or methods. + * Returns {@code true} if this value is a {@linkplain #isMetaObject() meta object} that + * provides a {@linkplain #getStaticScope() static scope}. A static scope represents the static + * or class level members associated with the type described by this meta object, such as static + * fields or methods. * * @throws IllegalStateException if the context is already {@linkplain Context#close() closed} * @throws PolyglotException if a guest language error occurs during execution - * @see #getStaticReceiver() + * @see #isMetaObject() + * @see #getStaticScope() * @since 25.1 */ - public boolean hasStaticReceiver() { - return dispatch.hasStaticReceiver(this.context, receiver); + public boolean hasStaticScope() { + return dispatch.hasStaticScope(this.context, receiver); } /** - * Returns the static receiver associated with this value. A static receiver is an object that - * exposes static members, members whose values or behaviors are independent of any particular - * instance of this value. If this value is a {@link #isMetaObject() metaobject}, its static - * receiver is the same object that would be returned for an instance of the type represented by - * that metaobject, it exposes the static members of the represented type rather than static - * members of the metaobject itself. + * Returns the static scope associated with this value. This value must be a + * {@linkplain #isMetaObject() meta-object}. A static scope is an object that exposes static + * members, members whose values or behavior are independent of any particular instance. *

    - * The returned static receiver can be used to access static members using + * The returned static scope can be used to access static members using * {@link #getMember(String)}, {@link #getMemberKeys()}, or * {@link #invokeMember(String, Object...)}. *

    - * The returned static receiver is always expected to provide {@link #hasMembers() members}, - * representing the receiver's static context. + * The returned static scope is always expected to provide {@link #hasMembers() members}, + * representing the static context. *

    * Examples: + *

    *
      - *
    • For a Java class instance, the static receiver exposes the class's static fields and - * methods.
    • - *
    • For a Python object, the static receiver exposes class-level attributes and methods, - * effectively corresponding to the members provided by the Python metaobject.
    • + *
    • In Java, the static scope exposes static fields and methods of a class.
    • + *
    • In Python, the static scope exposes class-level attributes and methods, effectively + * corresponding to the members provided by the Python metaobject.
    • *
    * * @throws UnsupportedOperationException if and only if this value does not - * {@linkplain #hasStaticReceiver() have a static receiver} + * {@linkplain #hasStaticScope() have a static scope} * @throws IllegalStateException if the context is already {@linkplain Context#close() closed} * @throws PolyglotException if a guest language error occurs during execution - * @see #hasStaticReceiver() - * @see #getMemberKeys() + * @see #hasStaticScope() + * @see #isMetaObject() + * @see #hasMembers() * @since 25.1 */ - public Value getStaticReceiver() { - return (Value) dispatch.getStaticReceiver(this.context, receiver); + public Value getStaticScope() { + return (Value) dispatch.getStaticScope(this.context, receiver); } // executable diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java index b88952664bdd..a45167d48e4f 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java @@ -1137,11 +1137,11 @@ public Set getMemberKeys(Object context, Object receiver) { public abstract boolean removeMember(Object context, Object receiver, String key); - public boolean hasStaticReceiver(Object context, Object receiver) { + public boolean hasStaticScope(Object context, Object receiver) { return false; } - public abstract Object getStaticReceiver(Object context, Object receiver); + public abstract Object getStaticScope(Object context, Object receiver); public boolean canExecute(Object context, Object receiver) { return false; diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 81eab4a50671..1f7a09d7e1a5 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -63,7 +63,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-71887: Bytecode DSL: Added a `ClearLocal` operation for fast clearing of local values. * GR-71402: Added `InteropLibrary#hasHostObject` and `InteropLibrary#getHostObject` for accessing the Java host-object representation of a Truffle guest object. Deprecated `Env#isHostObject`, `Env#isHostException`, `Env#isHostFunction`, `Env#isHostSymbol`, `Env#asHostObject`, and `Env#asHostException` in favor of the new InteropLibrary messages. * GR-71402: Added `InteropLibrary#isHostObject` and `InteropLibrary#asHostObject` for accessing the Java host-object representation of a Truffle guest object. Deprecated `Env#isHostObject`, `Env#isHostException`, `Env#isHostFunction`, `Env#isHostSymbol`, `Env#asHostObject`, and `Env#asHostException` in favor of the new InteropLibrary messages. -* GR-71402: Added `InteropLibrary#hasStaticReceiver` and `InteropLibrary#getStaticReceiver` returning the static receiver for given object. +* GR-71402: Added `InteropLibrary#hasStaticScope` and `InteropLibrary#getStaticScope` returning the static scope representing static or class-level members associated with the given meta object. ## Version 25.0 diff --git a/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest index efa85f2aec6d..2d8deea2e80d 100644 --- a/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest @@ -212,7 +212,7 @@ meth public final boolean isNull() meth public final com.oracle.truffle.api.debug.DebugValue asInLanguage(com.oracle.truffle.api.nodes.LanguageInfo) meth public final com.oracle.truffle.api.debug.DebugValue getMetaObject() meth public final com.oracle.truffle.api.debug.DebugValue getProperty(java.lang.String) -meth public final com.oracle.truffle.api.debug.DebugValue getStaticReceiver() +meth public final com.oracle.truffle.api.debug.DebugValue getStaticScope() meth public final com.oracle.truffle.api.nodes.LanguageInfo getOriginalLanguage() meth public final com.oracle.truffle.api.source.SourceSection getSourceLocation() meth public final java.lang.String asString() diff --git a/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugValue.java b/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugValue.java index b24248072bc6..c45ddc03079c 100644 --- a/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugValue.java +++ b/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugValue.java @@ -1033,19 +1033,19 @@ public void accept(Breakpoint b) { /** * Returns a value that provides static members whose value is independent on a specific - * instance. Returns {@code null} when no static receiver is available. + * instance. Returns {@code null} when no static scope is available. * * @throws DebugException when guest language code throws an exception * @since 25.1 */ - public final DebugValue getStaticReceiver() { + public final DebugValue getStaticScope() { if (!isReadable()) { return null; } Object view = getLanguageView(); try { - if (INTEROP.hasStaticReceiver(view)) { - return new HeapValue(getSession(), resolveLanguage(), null, INTEROP.getStaticReceiver(view)); + if (INTEROP.hasStaticScope(view)) { + return new HeapValue(getSession(), resolveLanguage(), null, INTEROP.getStaticScope(view)); } } catch (ThreadDeath td) { throw td; diff --git a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest index aa126b411649..3d983db041a8 100644 --- a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest @@ -71,7 +71,7 @@ meth public boolean hasMetaObject(java.lang.Object) meth public boolean hasMetaParents(java.lang.Object) meth public boolean hasScopeParent(java.lang.Object) meth public boolean hasSourceLocation(java.lang.Object) -meth public boolean hasStaticReceiver(java.lang.Object) +meth public boolean hasStaticScope(java.lang.Object) meth public boolean isArrayElementInsertable(java.lang.Object,long) meth public boolean isArrayElementModifiable(java.lang.Object,long) meth public boolean isArrayElementReadable(java.lang.Object,long) @@ -148,7 +148,7 @@ meth public java.lang.Object getMetaParents(java.lang.Object) throws com.oracle. meth public java.lang.Object getMetaQualifiedName(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getMetaSimpleName(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object getScopeParent(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException -meth public java.lang.Object getStaticReceiver(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public java.lang.Object getStaticScope(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object readArrayElement(java.lang.Object,long) throws com.oracle.truffle.api.interop.InvalidArrayIndexException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object readHashValue(java.lang.Object,java.lang.Object) throws com.oracle.truffle.api.interop.UnknownKeyException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public java.lang.Object readHashValueOrDefault(java.lang.Object,java.lang.Object,java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java index 92ab3ddfd26a..02ba11d48bbe 100644 --- a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java @@ -141,7 +141,7 @@ *
  • {@link #isInstantiable(Object) instantiable} *
  • {@link #isPointer(Object) pointer} *
  • {@link #hasMembers(Object) members} - *
  • {@link #hasStaticReceiver(Object receiver) static receiver} + *
  • {@link #hasStaticScope(Object receiver) static scope} *
  • {@link #hasHashEntries(Object) hash entries} *
  • {@link #hasArrayElements(Object) array elements} *
  • {@link #hasBufferElements(Object) buffer elements} @@ -964,54 +964,59 @@ public boolean hasMemberWriteSideEffects(Object receiver, String member) { } /** - * Returns {@code true} if the given receiver provides a {@linkplain #getStaticReceiver(Object) - * static receiver}. A static receiver represents the static or class-level members associated - * with the receiver's type, such as static fields or methods. + * Returns {@code true} if the given receiver is a {@linkplain #isMetaObject(Object) meta + * object} that provides a {@linkplain #getStaticScope(Object) static scope}. A static scope + * represents the static or class-level members associated with the receiver's type, such as + * static fields or methods. *

    * Invoking this message must not cause any observable side effects. *

    + * Only {@link #isMetaObject(Object) meta objects} are permitted to expose a static scope, for + * all non-meta objects this method must return {@code false}. + *

    * By default, this method returns {@code false}. * - * @see #getStaticReceiver(Object) + * @see #getStaticScope(Object) + * @see #isMetaObject(Object) * @since 25.1 */ - @Abstract(ifExported = {"getStaticReceiver"}) - public boolean hasStaticReceiver(Object receiver) { + @Abstract(ifExported = {"getStaticScope"}) + public boolean hasStaticScope(Object receiver) { return false; } /** - * Returns the static receiver associated with the given receiver. A static receiver is an - * object that exposes static members, members whose values or behaviors are independent of any - * particular instance of the receiver. If the {@code receiver} is a - * {@link #isMetaObject(Object) metaobject}, its static receiver is the same object that would - * be returned for an instance of the type represented by that metaobject, it exposes the static - * members of the represented type rather than static members of the metaobject itself. + * Returns the static scope associated with the given receiver. The receiver must be a + * {@linkplain #isMetaObject(Object) meta object}. A static scope is an object that exposes + * static members, members whose values or behaviors are independent of any particular instance + * of the receiver. *

    - * The static receiver typically serves as an artificial or meta-level object that provides - * access to instance-independent members declared by the receiver's type or meta object. The - * returned object can be used as the receiver for interop messages such as - * {@link #readMember(Object, String) readMember}, {@link #writeMember(Object, String, Object) - * writeMember}, and {@link #invokeMember(Object, String, Object...) invokeMember} when - * accessing static members. + * The static scope typically serves as an artificial or meta-level object that provides access + * to instance-independent members declared by the meta object. The returned object can be used + * as the receiver for interop messages such as {@link #readMember(Object, String) readMember}, + * {@link #writeMember(Object, String, Object) writeMember}, and + * {@link #invokeMember(Object, String, Object...) invokeMember} when accessing static members. *

    - * The returned static receiver is always expected to provide {@link #hasMembers(Object) - * members}, representing the receiver's static context. + * The returned static scope is always expected to be a {@link #isScope(Object) scope} and + * provide {@link #hasMembers(Object) members}, representing the receiver's static context. *

    * Examples: *

      - *
    • In Java, the static receiver would expose static fields and methods of a class.
    • - *
    • In Python, the static receiver would expose class-level variables and methods, - * effectively corresponding to the members provided by the Python metaobject.
    • + *
    • In Java, the static scope exposes static fields and methods of a class.
    • + *
    • In Python, the static scope exposes class-level attributes and methods, effectively + * corresponding to the members provided by the Python metaobject.
    • *
    * * @throws UnsupportedMessageException if and only if the receiver does not - * {@linkplain #hasStaticReceiver(Object) have a static receiver} - * @see #hasStaticReceiver(Object) + * {@linkplain #hasStaticScope(Object) have a static scope} + * @see #hasStaticScope(Object) + * @see #isMetaObject(Object) + * @see #isScope(Object) + * @see #hasMembers(Object) * @since 25.1 */ - @Abstract(ifExported = {"hasStaticReceiver"}) - public Object getStaticReceiver(Object receiver) throws UnsupportedMessageException { + @Abstract(ifExported = {"hasStaticScope"}) + public Object getStaticScope(Object receiver) throws UnsupportedMessageException { throw UnsupportedMessageException.create(); } @@ -2683,7 +2688,7 @@ public final Object toDisplayString(Object receiver) { * * @since 20.1 */ - @Abstract(ifExported = {"getMetaQualifiedName", "getMetaSimpleName", "isMetaInstance"}) + @Abstract(ifExported = {"getMetaQualifiedName", "getMetaSimpleName", "isMetaInstance", "hasStaticScope"}) public boolean isMetaObject(Object receiver) { return false; } @@ -2994,7 +2999,7 @@ public int identityHashCode(Object receiver) throws UnsupportedMessageException * Returns true if the value represents a scope object, else false. * The scope object contains variables as {@link #getMembers(Object) members} and has a * {@link InteropLibrary#toDisplayString(Object, boolean) scope display name}. It needs to be - * associated with a {@link #getLanguage(Object) language}. The scope may return a + * associated with a {@link #getLanguageId(Object) language}. The scope may return a * {@link InteropLibrary#getSourceLocation(Object) source location} that indicates the range of * the scope in the source code. The scope may have {@link #hasScopeParent(Object) parent * scopes}. @@ -3015,7 +3020,7 @@ public int identityHashCode(Object receiver) throws UnsupportedMessageException * also {@link #hasMembers(Object)} and {@link #toDisplayString(Object, boolean)} must be * implemented and {@link #hasSourceLocation(Object)} is recommended. * - * @see #getLanguage(Object) + * @see #getLanguageId(Object) * @see #getMembers(Object) * @see #hasScopeParent(Object) * @since 20.3 @@ -3959,24 +3964,54 @@ public boolean isMemberInternal(Object receiver, String identifier) { } @Override - public boolean hasStaticReceiver(Object receiver) { + public boolean hasStaticScope(Object receiver) { + if (CompilerDirectives.inCompiledCode()) { + return delegate.hasStaticScope(receiver); + } assert preCondition(receiver); - boolean result = delegate.hasStaticReceiver(receiver); + boolean result = delegate.hasStaticScope(receiver); + if (result) { + assert UNCACHED.isMetaObject(receiver) : violationPost(receiver, result); + assert assertHasStaticScope(receiver); + } else { + assert assertHasNoStaticScope(receiver); + } assert validProtocolReturn(receiver, result); return result; } + private boolean assertHasStaticScope(Object receiver) { + try { + delegate.getStaticScope(receiver); + } catch (InteropException e) { + assert false : violationInvariant(receiver); + } + return true; + } + + private boolean assertHasNoStaticScope(Object receiver) { + try { + delegate.getStaticScope(receiver); + assert false : violationInvariant(receiver); + } catch (UnsupportedMessageException e) { + // Falls to return true + } + return true; + } + @Override - public Object getStaticReceiver(Object receiver) throws UnsupportedMessageException { + public Object getStaticScope(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { - return delegate.getStaticReceiver(receiver); + return delegate.getStaticScope(receiver); } assert preCondition(receiver); - boolean hadStaticReceiver = delegate.hasStaticReceiver(receiver); + boolean hadStaticReceiver = delegate.hasStaticScope(receiver); try { - Object result = delegate.getStaticReceiver(receiver); + Object result = delegate.getStaticScope(receiver); assert hadStaticReceiver || isMultiThreaded(receiver) : violationInvariant(receiver); assert validInteropReturn(receiver, result); + assert UNCACHED.isMetaObject(receiver) : violationPost(receiver, result); + assert UNCACHED.isScope(result) : violationPost(receiver, result); assert UNCACHED.hasMembers(result) : violationPost(receiver, result); return result; } catch (InteropException e) { @@ -5639,12 +5674,7 @@ public boolean isHostObject(Object receiver) { assert preCondition(receiver); boolean result = delegate.isHostObject(receiver); if (result) { - try { - delegate.asHostObject(receiver); - } catch (InteropException e) { - assert e instanceof HeapIsolationException : violationInvariant(receiver); - } catch (Exception e) { - } + assert assertHasHostObject(receiver); } else { assert assertHasNoHostObject(receiver); } @@ -5652,6 +5682,15 @@ public boolean isHostObject(Object receiver) { return result; } + private boolean assertHasHostObject(Object receiver) { + try { + delegate.asHostObject(receiver); + } catch (InteropException e) { + assert e instanceof HeapIsolationException : violationInvariant(receiver); + } + return true; + } + private boolean assertHasNoHostObject(Object receiver) { try { delegate.asHostObject(receiver); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java index ff635c31ab3c..c67b85bbe0a8 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostAdapterTest.java @@ -43,7 +43,6 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -141,7 +140,7 @@ private Object asHostType(TruffleLanguage.Env env, Class c) { private static Object verifyHostAdapterClass(TruffleLanguage.Env env, Object hostAdapterClass) { assertTrue(INTEROP.isHostObject(hostAdapterClass)); assertTrue(env.isHostSymbol(hostAdapterClass)); - assertFalse(INTEROP.isNull(hostAdapterClass) || INTEROP.hasStaticReceiver(hostAdapterClass)); + assertTrue(INTEROP.isScope(hostAdapterClass)); assertTrue(INTEROP.isMetaObject(hostAdapterClass)); assertTrue(INTEROP.isInstantiable(hostAdapterClass)); return hostAdapterClass; diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java index 9ef0a79a3864..1bae8fb3db44 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostExceptionTest.java @@ -530,7 +530,7 @@ public void testHostExceptionIsHostSymbol() { TruffleTestAssumptions.assumeWeakEncapsulation(); expectedException = RuntimeException.class; customExceptionVerifier = (t) -> { - assertFalse(INTEROP.isHostObject(t) && !INTEROP.isNull(t) && !INTEROP.hasStaticReceiver(t)); + assertFalse(INTEROP.isHostObject(t) && INTEROP.isScope(t)); }; Value catcher = context.eval(ProxyLanguage.ID, CATCHER); Runnable thrower = HostExceptionTest::thrower; diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java index 6c9885a2941d..8dd37bbd7584 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropAssertionsTest.java @@ -795,40 +795,40 @@ String getLanguageId() { Object toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) { return "ScopeCached[" + id + "]"; } + } - @ExportLibrary(InteropLibrary.class) - static final class ScopeMembers implements TruffleObject { + @ExportLibrary(InteropLibrary.class) + static final class ScopeMembers implements TruffleObject { - private final long len; + private final long len; - private ScopeMembers(long len) { - this.len = len; - } + private ScopeMembers(long len) { + this.len = len; + } - @ExportMessage - @SuppressWarnings("static-method") - boolean hasArrayElements() { - return true; - } + @ExportMessage + @SuppressWarnings("static-method") + boolean hasArrayElements() { + return true; + } - @ExportMessage - Object readArrayElement(long index) throws InvalidArrayIndexException { - if (0 <= index && index < len) { - return Long.toString(len - index); - } else { - throw InvalidArrayIndexException.create(index); - } + @ExportMessage + Object readArrayElement(long index) throws InvalidArrayIndexException { + if (0 <= index && index < len) { + return Long.toString(len - index); + } else { + throw InvalidArrayIndexException.create(index); } + } - @ExportMessage - long getArraySize() { - return len; - } + @ExportMessage + long getArraySize() { + return len; + } - @ExportMessage - boolean isArrayElementReadable(long index) { - return 0 <= index && index < len; - } + @ExportMessage + boolean isArrayElementReadable(long index) { + return 0 <= index && index < len; } } @@ -2038,6 +2038,234 @@ interface HostObjectProvider extends Callable { Object call() throws UnsupportedMessageException, HeapIsolationException; } + @Test + public void testHasStaticScope() { + GetStaticScopeTest receiver = new GetStaticScopeTest(); + InteropLibrary hasStaticScopeLib = createLibrary(InteropLibrary.class, receiver); + + receiver.isMetaObject = true; + receiver.hasStaticScope = true; + receiver.staticScopeProvider = () -> new StaticScope(true, true); + assertTrue(hasStaticScopeLib.hasStaticScope(receiver)); + + receiver.isMetaObject = true; + receiver.hasStaticScope = false; + receiver.staticScopeProvider = () -> { + throw UnsupportedMessageException.create(); + }; + assertFalse(hasStaticScopeLib.hasStaticScope(receiver)); + + receiver.isMetaObject = false; + receiver.hasStaticScope = false; + receiver.staticScopeProvider = () -> { + throw UnsupportedMessageException.create(); + }; + assertFalse(hasStaticScopeLib.hasStaticScope(receiver)); + + receiver.isMetaObject = false; + receiver.hasStaticScope = true; + receiver.staticScopeProvider = () -> new StaticScope(true, true); + assertFails(() -> hasStaticScopeLib.hasStaticScope(receiver), AssertionError.class); + + receiver.isMetaObject = true; + receiver.hasStaticScope = false; + receiver.staticScopeProvider = () -> new StaticScope(true, true); + assertFails(() -> hasStaticScopeLib.hasStaticScope(receiver), AssertionError.class); + + receiver.isMetaObject = true; + receiver.hasStaticScope = true; + receiver.staticScopeProvider = () -> { + throw UnsupportedMessageException.create(); + }; + assertFails(() -> hasStaticScopeLib.hasStaticScope(receiver), AssertionError.class); + } + + @Test + public void testGetStaticScope() throws Exception { + setupEnv(Context.create()); // we need no multi threaded context. + GetStaticScopeTest receiver = new GetStaticScopeTest(); + InteropLibrary getStaticScopeLib = createLibrary(InteropLibrary.class, receiver); + + receiver.isMetaObject = true; + receiver.hasStaticScope = true; + receiver.staticScopeProvider = () -> new StaticScope(true, true); + assertNotNull(getStaticScopeLib.getStaticScope(receiver)); + + receiver.isMetaObject = true; + receiver.hasStaticScope = false; + receiver.staticScopeProvider = () -> { + throw UnsupportedMessageException.create(); + }; + assertFails(() -> getStaticScopeLib.getStaticScope(receiver), UnsupportedMessageException.class); + + receiver.isMetaObject = false; + receiver.hasStaticScope = false; + receiver.staticScopeProvider = () -> { + throw UnsupportedMessageException.create(); + }; + assertFails(() -> getStaticScopeLib.getStaticScope(receiver), UnsupportedMessageException.class); + + receiver.isMetaObject = false; + receiver.hasStaticScope = true; + receiver.staticScopeProvider = () -> new StaticScope(true, true); + assertFails(() -> getStaticScopeLib.getStaticScope(receiver), AssertionError.class); + + receiver.isMetaObject = true; + receiver.hasStaticScope = false; + receiver.staticScopeProvider = () -> new StaticScope(true, true); + assertFails(() -> getStaticScopeLib.getStaticScope(receiver), AssertionError.class); + + receiver.isMetaObject = true; + receiver.hasStaticScope = true; + receiver.staticScopeProvider = () -> { + throw UnsupportedMessageException.create(); + }; + assertFails(() -> getStaticScopeLib.getStaticScope(receiver), AssertionError.class); + + receiver.isMetaObject = true; + receiver.hasStaticScope = true; + receiver.staticScopeProvider = () -> new StaticScope(false, true); + assertFails(() -> getStaticScopeLib.getStaticScope(receiver), AssertionError.class); + + receiver.isMetaObject = true; + receiver.hasStaticScope = true; + receiver.staticScopeProvider = () -> new StaticScope(true, false); + assertFails(() -> getStaticScopeLib.getStaticScope(receiver), AssertionError.class); + } + + @ExportLibrary(InteropLibrary.class) + static final class GetStaticScopeTest implements TruffleObject { + + boolean hasStaticScope; + boolean isMetaObject; + StaticScopeProvider staticScopeProvider; + + @ExportMessage + boolean isMetaObject() { + return isMetaObject; + } + + @ExportMessage + Object getMetaQualifiedName() throws UnsupportedMessageException { + if (isMetaObject) { + return GetStaticScopeTest.class.getName(); + } else { + throw UnsupportedMessageException.create(); + } + } + + @ExportMessage + Object getMetaSimpleName() throws UnsupportedMessageException { + if (isMetaObject) { + return GetStaticScopeTest.class.getSimpleName(); + } else { + throw UnsupportedMessageException.create(); + } + } + + @ExportMessage + @SuppressWarnings("unused") + boolean isMetaInstance(Object instance) throws UnsupportedMessageException { + if (isMetaObject) { + return false; + } else { + throw UnsupportedMessageException.create(); + } + } + + @ExportMessage + boolean hasStaticScope() { + return hasStaticScope; + } + + @ExportMessage + Object getStaticScope() throws UnsupportedMessageException { + return staticScopeProvider.call(); + } + + /* + * Needed for multi-threaded check. + */ + @ExportMessage + @SuppressWarnings("static-method") + boolean hasLanguageId() { + return true; + } + + /* + * Needed for multi-threaded check. + */ + @ExportMessage + @SuppressWarnings("static-method") + String getLanguageId() { + return ProxyLanguage.ID; + } + + /* + * Needed for multi-threaded check. + */ + @ExportMessage + @SuppressWarnings({"static-method", "unused"}) + Object toDisplayString(boolean allowSideEffects) { + return GetStaticScopeTest.class.getSimpleName(); + } + } + + @FunctionalInterface + interface StaticScopeProvider extends Callable { + + @Override + Object call() throws UnsupportedMessageException; + } + + @ExportLibrary(InteropLibrary.class) + static final class StaticScope implements TruffleObject { + + private final boolean isScope; + private final boolean hasMembers; + + StaticScope(boolean isScope, boolean hasMembers) { + this.isScope = isScope; + this.hasMembers = hasMembers; + } + + @ExportMessage + boolean isScope() { + return isScope; + } + + @ExportMessage + boolean hasLanguageId() { + return isScope(); + } + + @ExportMessage + String getLanguageId() throws UnsupportedMessageException { + if (isScope()) { + return ProxyLanguage.ID; + } else { + throw UnsupportedMessageException.create(); + } + } + + @ExportMessage + @SuppressWarnings({"static-method", "unused"}) + String toDisplayString(boolean sideEffects) { + return StaticScope.class.getSimpleName(); + } + + @ExportMessage + boolean hasMembers() { + return hasMembers; + } + + @ExportMessage + @SuppressWarnings({"static-method", "unused"}) + Object getMembers(boolean includeInternal) { + return new ScopeMembers(0); + } + } + @SuppressWarnings("static-method") @ExportLibrary(InteropLibrary.class) static class IsInvocableUnknown implements TruffleObject { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostAccessTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostAccessTest.java index 52e36a8cfba3..8b379ad2add1 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostAccessTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostAccessTest.java @@ -378,7 +378,7 @@ public void testArrayAccessEnabled() { } catch (UnsupportedOperationException e) { } assertEquals(2 /* arr.length and arr.clone(). */, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT); } @Test @@ -397,7 +397,7 @@ private static void assertArrayAccessDisabled(Context context) { int[] array = new int[]{1, 2, 3}; Value value = context.asValue(array); assertSame(array, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); } @Test @@ -423,7 +423,7 @@ private static void assertBufferAccessEnabled(Context context) { assertEquals(42, value.readBufferByte(0)); value.writeBufferByte(1, (byte) 24); assertEquals(24, value.readBufferByte(1)); - ValueAssert.assertValue(value, false, Trait.BUFFER_ELEMENTS, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.BUFFER_ELEMENTS, Trait.MEMBERS, Trait.HOST_OBJECT); } @Test @@ -432,7 +432,7 @@ public void testBufferAccessDisabled() { ByteBuffer buffer = ByteBuffer.allocate(2); Value value = context.asValue(buffer); assertSame(buffer, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); } /* @@ -482,7 +482,7 @@ public void testListAccessEnabled() { assertEquals(0, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.ARRAY_ELEMENTS, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT); assertArrayAccessDisabled(context); } @@ -508,7 +508,7 @@ public void testIterableAccessEnabled() { assertFalse(iterator.hasIteratorNextElement()); assertSame(iterable, value.asHostObject()); assertEquals(0, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.ITERABLE, Trait.MEMBERS, Trait.HOST_OBJECT); assertListAccessDisabled(context, true); assertArrayAccessDisabled(context); } @@ -533,7 +533,7 @@ public void testIteratorAccessEnabled() { assertFalse(value.hasIteratorNextElement()); assertSame(iterator, value.asHostObject()); assertEquals(0, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.ITERATOR, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.ITERATOR, Trait.MEMBERS, Trait.HOST_OBJECT); assertIterableAccessDisabled(context); assertListAccessDisabled(context, false); assertArrayAccessDisabled(context); @@ -565,7 +565,7 @@ public void testMapAccessEnabled() { assertEquals(1, entry.getArrayElement(0).asInt()); assertEquals(Integer.toBinaryString(1), entry.getArrayElement(1).asString()); assertEquals(0, value.getMemberKeys().size()); - ValueAssert.assertValue(value, false, Trait.HASH, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.HASH, Trait.MEMBERS, Trait.HOST_OBJECT); assertArrayAccessDisabled(context); } @@ -591,7 +591,7 @@ private static void assertListAccessDisabled(Context context, boolean iterableAc List array = new ArrayList<>(Arrays.asList(1, 2, 3)); Value value = context.asValue(array); assertSame(array, value.asHostObject()); - List expectedTypes = new ArrayList<>(Arrays.asList(Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER)); + List expectedTypes = new ArrayList<>(Arrays.asList(Trait.MEMBERS, Trait.HOST_OBJECT)); if (iterableAccess) { expectedTypes.add(Trait.ITERABLE); } @@ -602,21 +602,21 @@ private static void assertIterableAccessDisabled(Context context) { Iterable iterable = new IterableImpl<>(1, 2, 3); Value value = context.asValue(iterable); assertSame(iterable, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); } private static void assertIteratorAccessDisabled(Context context) { Iterator iterator = new IteratorImpl<>(1, 2, 3); Value value = context.asValue(iterator); assertSame(iterator, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); } private static void assertMapAccessDisabled(Context context) { Map map = Collections.singletonMap(1, "string"); Value value = context.asValue(map); assertSame(map, value.asHostObject()); - ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.STATIC_RECEIVER); + ValueAssert.assertValue(value, false, Trait.MEMBERS, Trait.HOST_OBJECT); } public static final class TargetClass1 { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java index 39a89f80eec1..ffda6dc6d114 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java @@ -40,7 +40,6 @@ */ package com.oracle.truffle.api.test.polyglot; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STATIC_RECEIVER; import static com.oracle.truffle.tck.tests.ValueAssert.assertValue; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.ARRAY_ELEMENTS; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN; @@ -246,14 +245,14 @@ public void testString() { @Test public void testDatesTimesZonesAndDuration() { - assertValueInContexts(context.asValue(LocalDate.now()), HOST_OBJECT, MEMBERS, DATE, EXECUTABLE, STATIC_RECEIVER); - assertValueInContexts(context.asValue(LocalTime.now()), HOST_OBJECT, MEMBERS, TIME, EXECUTABLE, STATIC_RECEIVER); - assertValueInContexts(context.asValue(LocalDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, EXECUTABLE, STATIC_RECEIVER); - assertValueInContexts(context.asValue(Instant.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, EXECUTABLE, STATIC_RECEIVER); - assertValueInContexts(context.asValue(ZonedDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, STATIC_RECEIVER); - assertValueInContexts(context.asValue(ZoneId.of("UTC")), HOST_OBJECT, MEMBERS, TIMEZONE, STATIC_RECEIVER); - assertValueInContexts(context.asValue(Date.from(Instant.now())), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, STATIC_RECEIVER); - assertValueInContexts(context.asValue(Duration.ofMillis(100)), HOST_OBJECT, MEMBERS, DURATION, STATIC_RECEIVER); + assertValueInContexts(context.asValue(LocalDate.now()), HOST_OBJECT, MEMBERS, DATE, EXECUTABLE); + assertValueInContexts(context.asValue(LocalTime.now()), HOST_OBJECT, MEMBERS, TIME, EXECUTABLE); + assertValueInContexts(context.asValue(LocalDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, EXECUTABLE); + assertValueInContexts(context.asValue(Instant.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE, EXECUTABLE); + assertValueInContexts(context.asValue(ZonedDateTime.now()), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE); + assertValueInContexts(context.asValue(ZoneId.of("UTC")), HOST_OBJECT, MEMBERS, TIMEZONE); + assertValueInContexts(context.asValue(Date.from(Instant.now())), HOST_OBJECT, MEMBERS, DATE, TIME, TIMEZONE); + assertValueInContexts(context.asValue(Duration.ofMillis(100)), HOST_OBJECT, MEMBERS, DURATION); assertValueInContexts(context.asValue(ProxyDate.from(LocalDate.now())), DATE, PROXY_OBJECT); assertValueInContexts(context.asValue(ProxyTime.from(LocalTime.now())), TIME, PROXY_OBJECT); @@ -388,7 +387,6 @@ public void testHostObject() { List expectedTraits = new ArrayList<>(); expectedTraits.add(MEMBERS); expectedTraits.add(HOST_OBJECT); - expectedTraits.add(STATIC_RECEIVER); if (value.getClass() == BigInteger.class) { expectedTraits.add(NUMBER); @@ -453,7 +451,7 @@ public void testHostObject() { @Test public void testArrays() { for (Object array : ARRAYS) { - assertValueInContexts(context.asValue(array), ARRAY_ELEMENTS, ITERABLE, HOST_OBJECT, MEMBERS, STATIC_RECEIVER); + assertValueInContexts(context.asValue(array), ARRAY_ELEMENTS, ITERABLE, HOST_OBJECT, MEMBERS); } } @@ -517,8 +515,8 @@ public void testBuffers() { for (final ByteBuffer buffer : testBuffers) { final Value value = context.asValue(buffer); - assertValueInContexts(value, BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS, STATIC_RECEIVER); - assertValueInContexts(createDelegateInteropWrapper(context, value), BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS, STATIC_RECEIVER); + assertValueInContexts(value, BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS); + assertValueInContexts(createDelegateInteropWrapper(context, value), BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS); } } @@ -2328,8 +2326,8 @@ private static void expandObjectVariants(Context sourceContext, List obj @Test public void testHostException() { Value exceptionValue = context.asValue(new RuntimeException("expected")); - assertValueInContexts(exceptionValue, HOST_OBJECT, MEMBERS, EXCEPTION, STATIC_RECEIVER); - assertValueInContexts(createDelegateInteropWrapper(context, exceptionValue), HOST_OBJECT, MEMBERS, EXCEPTION, STATIC_RECEIVER); + assertValueInContexts(exceptionValue, HOST_OBJECT, MEMBERS, EXCEPTION); + assertValueInContexts(createDelegateInteropWrapper(context, exceptionValue), HOST_OBJECT, MEMBERS, EXCEPTION); try { exceptionValue.throwException(); fail("should have thrown"); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java index a50a18bc1e8c..920b7a8840a7 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java @@ -40,7 +40,6 @@ */ package com.oracle.truffle.api.test.polyglot; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STATIC_RECEIVER; import static com.oracle.truffle.tck.tests.ValueAssert.assertUnsupported; import static com.oracle.truffle.tck.tests.ValueAssert.assertValue; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN; @@ -163,7 +162,7 @@ public void testHostObjectIdentityRestore() { assertTrue(value.isHostObject()); assertSame(obj, context.asValue(value.as(Object.class)).asHostObject()); - assertUnsupported(value, HOST_OBJECT, MEMBERS, STATIC_RECEIVER); + assertUnsupported(value, HOST_OBJECT, MEMBERS); } /** @@ -259,7 +258,7 @@ public void testClassProperties() { assertTrue(newInstance.getMetaObject().newInstance().asHostObject() instanceof JavaRecord); assertSame(JavaRecord.class, newInstance.getMetaObject().asHostObject()); - assertValue(recordClass, Trait.INSTANTIABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.META, STATIC_RECEIVER); + assertValue(recordClass, Trait.INSTANTIABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.META); } @Test @@ -317,8 +316,8 @@ public void testObjectProperties() { assertTrue(record.hasMember("wait")); assertTrue(record.hasMember("notifyAll")); - assertValue(record, Trait.MEMBERS, Trait.HOST_OBJECT, STATIC_RECEIVER); - assertValue(record.getMetaObject(), Trait.INSTANTIABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.META, STATIC_RECEIVER); + assertValue(record, Trait.MEMBERS, Trait.HOST_OBJECT); + assertValue(record.getMetaObject(), Trait.INSTANTIABLE, Trait.MEMBERS, Trait.HOST_OBJECT, Trait.META); } @Test diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java index f387e6ec342f..7caf6be29e32 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java @@ -358,7 +358,7 @@ public void accessAllPublicPropertiesDirectly() { assertEquals("One field x", "x", propertyNames[0]); assertEquals("One method to access x", "readX", propertyNames[1]); - Value staticPojo = pojo.getStaticReceiver(); + Value staticPojo = pojo.getMetaObject().getStaticScope(); assertTrue(staticPojo.hasMembers()); propertyNames = staticPojo.getMemberKeys().toArray(); assertEquals("One static field, one method and class", 3, propertyNames.length); diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index 0ed61af32a17..a539aed8bc00 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -2419,8 +2419,7 @@ public Throwable asHostException(Throwable exception) { * * @see #lookupHostSymbol(String) * @since 19.0 - * @deprecated Use - * {@code interopLibrary.isHostObject(obj) && !interopLibrary.isNull(obj) && !interopLibrary.hasStaticReceiver(obj)}. + * @deprecated Use {@code interopLibrary.isHostObject(obj) && interopLibrary.isScope(obj)}. */ @Deprecated(since = "25.1") @SuppressWarnings("static-method") diff --git a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java index 7cfee4973d42..70a742ec33af 100644 --- a/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java +++ b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostObject.java @@ -3277,12 +3277,16 @@ Object getMetaObject() throws UnsupportedMessageException { } } - @SuppressWarnings("static-method") @ExportMessage boolean isMetaObject() { return isClass(); } + @ExportMessage + boolean isScope() { + return isStaticClass(); + } + @ExportMessage Object getMetaQualifiedName() throws UnsupportedMessageException { if (isClass()) { @@ -3415,25 +3419,19 @@ Object readArrayElement(long idx, } @ExportMessage - boolean hasStaticReceiver() { - return obj != null && !isStaticClass(); + boolean hasStaticScope() { + return isMetaObject(); } @ExportMessage - Object getStaticReceiver( + Object getStaticScope( @Bind Node node, @Shared("error") @Cached InlinedBranchProfile error) throws UnsupportedMessageException { - if (!hasStaticReceiver()) { + if (!hasStaticScope()) { error.enter(node); throw UnsupportedMessageException.create(); } - Class clz; - if (isClass()) { - clz = (Class) obj; - } else { - clz = obj.getClass(); - } - return HostObject.forStaticClass(clz, context); + return HostObject.forStaticClass((Class) obj, context); } boolean isStaticClass() { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java index 2c721d71152d..1248951f1c77 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/EngineAccessor.java @@ -1582,7 +1582,7 @@ public boolean isHostObject(Object obj) { @TruffleBoundary public boolean isHostSymbol(Object obj) { InteropLibrary interop = InteropLibrary.getUncached(obj); - return interop.isHostObject(obj) && !interop.isNull(obj) && !interop.hasStaticReceiver(obj); + return interop.isHostObject(obj) && interop.isScope(obj); } @Override diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java index 2a7d0b7564e7..032763b917d1 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValueDispatch.java @@ -120,7 +120,7 @@ import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetMemberNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetMetaQualifiedNameNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetMetaSimpleNameNodeGen; -import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetStaticReceiverNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.GetStaticScopeNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasArrayElementsNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasBufferElementsNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasHashEntriesNodeGen; @@ -129,7 +129,7 @@ import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasIteratorNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasMemberNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasMembersNodeGen; -import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasStaticReceiverNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.HasStaticScopeNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.InvokeNoArgsNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.InvokeNodeGen; import com.oracle.truffle.polyglot.PolyglotValueDispatchFactory.InteropValueFactory.IsBufferWritableNodeGen; @@ -591,11 +591,11 @@ static RuntimeException removeMemberUnsupported(PolyglotLanguageContext context, } @Override - public Object getStaticReceiver(Object languageContext, Object receiver) { + public Object getStaticScope(Object languageContext, Object receiver) { PolyglotLanguageContext context = (PolyglotLanguageContext) languageContext; Object prev = hostEnter(context); try { - return getStaticReceiverUnsupported(context, receiver); + return getStaticScopeUnsupported(context, receiver); } catch (Throwable e) { throw guestToHostException(context, e, true); } finally { @@ -604,8 +604,8 @@ public Object getStaticReceiver(Object languageContext, Object receiver) { } @TruffleBoundary - static Object getStaticReceiverUnsupported(PolyglotLanguageContext context, Object receiver) { - throw unsupported(context, receiver, "getStaticReceiver(Object)", "hasStaticReceiver"); + static Object getStaticScopeUnsupported(PolyglotLanguageContext context, Object receiver) { + throw unsupported(context, receiver, "getStaticScope(Object)", "hasStaticScope"); } @Override @@ -2320,8 +2320,8 @@ static final class InteropValue extends PolyglotValueDispatch { final CallTarget asHostObject; final CallTarget asClassLiteral; final CallTarget asTypeLiteral; - final CallTarget hasStaticReceiver; - final CallTarget getStaticReceiver; + final CallTarget hasStaticScope; + final CallTarget getStaticScope; final Class receiverType; @@ -2404,8 +2404,8 @@ static final class InteropValue extends PolyglotValueDispatch { this.getHashValuesIterator = createTarget(GetHashValuesIteratorNodeGen.create(this)); this.isHostObject = createTarget(IsHostObjectNodeGen.create(this)); this.asHostObject = createTarget(AsHostObjectNodeGen.create(this)); - this.hasStaticReceiver = createTarget(HasStaticReceiverNodeGen.create(this)); - this.getStaticReceiver = createTarget(GetStaticReceiverNodeGen.create(this)); + this.hasStaticScope = createTarget(HasStaticScopeNodeGen.create(this)); + this.getStaticScope = createTarget(GetStaticScopeNodeGen.create(this)); } @SuppressWarnings("unchecked") @@ -2571,13 +2571,13 @@ public Set getMemberKeys(Object languageContext, Object receiver) { } @Override - public boolean hasStaticReceiver(Object languageContext, Object receiver) { - return (boolean) RUNTIME.callProfiled(this.hasStaticReceiver, languageContext, receiver); + public boolean hasStaticScope(Object languageContext, Object receiver) { + return (boolean) RUNTIME.callProfiled(this.hasStaticScope, languageContext, receiver); } @Override - public Object getStaticReceiver(Object languageContext, Object receiver) { - return RUNTIME.callProfiled(this.getStaticReceiver, languageContext, receiver); + public Object getStaticScope(Object languageContext, Object receiver) { + return RUNTIME.callProfiled(this.getStaticScope, languageContext, receiver); } @Override @@ -5677,9 +5677,9 @@ static RuntimeException asHostObjectIsolatedHeap(PolyglotLanguageContext context } } - abstract static class HasStaticReceiverNode extends InteropNode { + abstract static class HasStaticScopeNode extends InteropNode { - protected HasStaticReceiverNode(InteropValue interop) { + protected HasStaticScopeNode(InteropValue interop) { super(interop); } @@ -5690,19 +5690,19 @@ protected Class[] getArgumentTypes() { @Override protected String getOperationName() { - return "hasStaticReceiver"; + return "hasStaticScope"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // @CachedLibrary("receiver") InteropLibrary objects) { - return objects.hasStaticReceiver(receiver); + return objects.hasStaticScope(receiver); } } - abstract static class GetStaticReceiverNode extends InteropNode { + abstract static class GetStaticScopeNode extends InteropNode { - protected GetStaticReceiverNode(InteropValue interop) { + protected GetStaticScopeNode(InteropValue interop) { super(interop); } @@ -5713,7 +5713,7 @@ protected Class[] getArgumentTypes() { @Override protected String getOperationName() { - return "getStaticReceiver"; + return "getStaticScope"; } @Specialization(limit = "CACHE_LIMIT") @@ -5723,10 +5723,10 @@ static Object doCached(PolyglotLanguageContext context, Object receiver, Object[ @Cached ToHostValueNode toHost, @Cached InlinedBranchProfile unsupported) { try { - return toHost.execute(node, context, objects.getStaticReceiver(receiver)); + return toHost.execute(node, context, objects.getStaticScope(receiver)); } catch (UnsupportedMessageException e) { unsupported.enter(node); - return getStaticReceiverUnsupported(context, receiver); + return getStaticScopeUnsupported(context, receiver); } } } diff --git a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java index 5e4701810b06..4240f2c80bf4 100644 --- a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java +++ b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/ValueAssert.java @@ -58,7 +58,6 @@ import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NULL; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NUMBER; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.PROXY_OBJECT; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STATIC_RECEIVER; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STRING; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIME; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIMEZONE; @@ -288,10 +287,6 @@ public static void assertUnsupported(Value value, Trait... supported) { } } - break; - case STATIC_RECEIVER: - assertFalse(value.hasStaticReceiver()); - assertFails(value::getStaticReceiver, UnsupportedOperationException.class); break; case EXECUTABLE: assertFalse(value.toString(), value.canExecute()); @@ -464,6 +459,8 @@ public static void assertUnsupported(Value value, Trait... supported) { assertFails(() -> value.isMetaInstance(""), UnsupportedOperationException.class); assertFalse(value.hasMetaParents()); assertFails(() -> value.getMetaParents(), UnsupportedOperationException.class); + assertFalse(value.hasStaticScope()); + assertFails(() -> value.getStaticScope(), UnsupportedOperationException.class); break; case ITERABLE: assertFalse(value.hasIterator()); @@ -667,10 +664,6 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces assertEquals(value.toString(), value.as(Map.class).toString()); } break; - case STATIC_RECEIVER: - assertTrue(msg, value.hasStaticReceiver()); - assertNotNull(value.getStaticReceiver()); - break; case NATIVE: assertTrue(msg, value.isNativePointer()); value.asNativePointer(); @@ -757,6 +750,23 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces // caught expected exception } } + if (value.hasStaticScope()) { + Value staticScope = value.getStaticScope(); + assertTrue(staticScope.hasMembers()); + for (String key : staticScope.getMemberKeys()) { + Value staticMember = staticScope.getMember(key); + assertValueImpl(staticMember, depth + 1, hasHostAccess, detectSupportedTypes(staticMember)); + } + } else { + try { + value.getStaticScope(); + fail("should have thrown"); + } catch (PolyglotException expected) { + throw new AssertionError(expected); + } catch (UnsupportedOperationException expected) { + // caught expected exception + } + } break; case ITERABLE: assertTrue(msg, value.hasIterator()); @@ -1169,9 +1179,6 @@ private static Trait[] detectSupportedTypes(Value value) { if (value.hasMembers()) { valueTypes.add(MEMBERS); } - if (value.hasStaticReceiver()) { - valueTypes.add(STATIC_RECEIVER); - } if (value.hasArrayElements()) { valueTypes.add(ARRAY_ELEMENTS); } @@ -1242,8 +1249,7 @@ public enum Trait { META, ITERABLE, ITERATOR, - HASH, - STATIC_RECEIVER + HASH } } diff --git a/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/interop/HostProxyBenchmarkLanguage.java b/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/interop/HostProxyBenchmarkLanguage.java index 84375abc2e7f..9e301b7318da 100644 --- a/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/interop/HostProxyBenchmarkLanguage.java +++ b/truffle/src/org.graalvm.truffle.benchmark/src/org/graalvm/truffle/benchmark/interop/HostProxyBenchmarkLanguage.java @@ -50,6 +50,7 @@ import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.interop.ArityException; +import com.oracle.truffle.api.interop.HeapIsolationException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedMessageException; @@ -109,11 +110,16 @@ Object execute(Object[] args, @Bind Node node) throws ArityException, Unsupporte Object receiver = args[0]; String messageName = InteropLibrary.getUncached().asString(args[1]); Object arg1 = args[2]; - if (!env.isHostObject(arg1)) { + InteropLibrary hostObjects = InteropLibrary.getUncached(arg1); + if (!hostObjects.isHostObject(arg1)) { throw new InteropBenchmarkException("Invalid arguments. Interop message name and arguments with receiver as host Object[]."); } - Object[] hostArguments = (Object[]) env.asHostObject(arg1); - return new ExecuteInteropExecutable(receiver, messageName, hostArguments); + try { + Object[] hostArguments = (Object[]) hostObjects.asHostObject(arg1); + return new ExecuteInteropExecutable(receiver, messageName, hostArguments); + } catch (HeapIsolationException e) { + throw new InteropBenchmarkException("Invalid arguments. Host object cannot be unboxed because it was allocated in an isolated heap."); + } } } From 46bde32bb9f7f26d9facf316e8fb0582b19f37b6 Mon Sep 17 00:00:00 2001 From: Tomas Zezula Date: Thu, 4 Dec 2025 15:22:04 +0100 Subject: [PATCH 14/14] Removed toDisplayString and hasLanguageId requirements for isScope. --- truffle/CHANGELOG.md | 2 +- .../truffle/api/interop/InteropLibrary.java | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 1f7a09d7e1a5..9396ea965328 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -61,7 +61,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-71088 Added `CompilerDirectives.EarlyEscapeAnalysis` annotation that runs partial escape analysis early before partial evaluation enabling partial-evaluation-constant scalar replacements. * GR-71870 Truffle DSL no longer supports mixed exclusive and shared inlined caches. Sharing will now be disabled if mixing was used. To resolve the new warnings it is typically necessary to use either `@Exclusive` or `@Shared` for all caches. * GR-71887: Bytecode DSL: Added a `ClearLocal` operation for fast clearing of local values. -* GR-71402: Added `InteropLibrary#hasHostObject` and `InteropLibrary#getHostObject` for accessing the Java host-object representation of a Truffle guest object. Deprecated `Env#isHostObject`, `Env#isHostException`, `Env#isHostFunction`, `Env#isHostSymbol`, `Env#asHostObject`, and `Env#asHostException` in favor of the new InteropLibrary messages. +* GR-71088 Added `CompilerDirectives.EarlyEscapeAnalysis` annotation that runs partial escape analysis early before partial evaluation enabling partial-evaluation-constant scalar replacements. * GR-71402: Added `InteropLibrary#isHostObject` and `InteropLibrary#asHostObject` for accessing the Java host-object representation of a Truffle guest object. Deprecated `Env#isHostObject`, `Env#isHostException`, `Env#isHostFunction`, `Env#isHostSymbol`, `Env#asHostObject`, and `Env#asHostException` in favor of the new InteropLibrary messages. * GR-71402: Added `InteropLibrary#hasStaticScope` and `InteropLibrary#getStaticScope` returning the static scope representing static or class-level members associated with the given meta object. diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java index 02ba11d48bbe..13de7a460e82 100644 --- a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InteropLibrary.java @@ -2518,7 +2518,7 @@ public Class> getLanguage(Object receiver) throws U * @see #toDisplayString(Object) * @since 25.1 */ - @Abstract(ifExported = {"getLanguageId"}, ifExportedAsWarning = {"isScope", "hasLanguage"}, replacementOf = "hasLanguage(Object)", replacementMethod = "hasLanguageLegacy") + @Abstract(ifExported = {"getLanguageId"}, ifExportedAsWarning = {"hasLanguage"}, replacementOf = "hasLanguage(Object)", replacementMethod = "hasLanguageLegacy") public boolean hasLanguageId(Object receiver) { if (!hasLanguage(receiver)) { return false; @@ -2649,7 +2649,7 @@ public Object getMetaObject(Object receiver) throws UnsupportedMessageException * @see TruffleLanguage#getLanguageView(Object, Object) * @since 20.1 */ - @Abstract(ifExported = {"hasLanguageId", "getLanguageId", "hasLanguage", "getLanguage", "isScope"}) + @Abstract(ifExported = {"hasLanguageId", "getLanguageId", "hasLanguage", "getLanguage"}) @TruffleBoundary public Object toDisplayString(Object receiver, boolean allowSideEffects) { if (allowSideEffects) { @@ -2997,12 +2997,10 @@ public int identityHashCode(Object receiver) throws UnsupportedMessageException /** * Returns true if the value represents a scope object, else false. - * The scope object contains variables as {@link #getMembers(Object) members} and has a - * {@link InteropLibrary#toDisplayString(Object, boolean) scope display name}. It needs to be - * associated with a {@link #getLanguageId(Object) language}. The scope may return a - * {@link InteropLibrary#getSourceLocation(Object) source location} that indicates the range of - * the scope in the source code. The scope may have {@link #hasScopeParent(Object) parent - * scopes}. + * The scope object contains variables as {@link #getMembers(Object) members}. The scope may + * return a {@link InteropLibrary#getSourceLocation(Object) source location} that indicates the + * range of the scope in the source code. The scope may have {@link #hasScopeParent(Object) + * parent scopes}. *

    * The {@link #getMembers(Object) members} of a scope represent all visible flattened variables, * including all parent scopes, if any. The variables of the current scope must be listed first @@ -3017,10 +3015,9 @@ public int identityHashCode(Object receiver) throws UnsupportedMessageException * member elements providing the same {@link #asString(Object) name}. *

    * This method must not cause any observable side-effects. If this method is implemented then - * also {@link #hasMembers(Object)} and {@link #toDisplayString(Object, boolean)} must be - * implemented and {@link #hasSourceLocation(Object)} is recommended. + * also {@link #hasMembers(Object)} must be implemented and {@link #hasSourceLocation(Object)} + * is recommended. * - * @see #getLanguageId(Object) * @see #getMembers(Object) * @see #hasScopeParent(Object) * @since 20.3