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..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)", - "isHostObject(getContext(), 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)", - "isHostObject(getContext(), 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)", - "isHostObject(getContext(), 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)", - "isHostObject(getContext(), value)", + "interop.isHostObject(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.isHostObject(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.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)", - "isHostObject(getContext(), 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)", - "isHostObject(getContext(), value)" + "interop.isHostObject(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/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 9e48c728de14..8893f2590384 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -10,6 +10,9 @@ 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. +* 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 1e48653b9786..97405138c477 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 hasStaticScope() 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 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 8c2257677b82..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 @@ -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 @@ -1008,6 +1008,55 @@ public boolean removeMember(String identifier) { } } + /** + * 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 #isMetaObject() + * @see #getStaticScope() + * @since 25.1 + */ + public boolean hasStaticScope() { + return dispatch.hasStaticScope(this.context, receiver); + } + + /** + * 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 scope can be used to access static members using + * {@link #getMember(String)}, {@link #getMemberKeys()}, or + * {@link #invokeMember(String, Object...)}. + *

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

+ * Examples: + *

+ * + * + * @throws UnsupportedOperationException if and only if this value does not + * {@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 #hasStaticScope() + * @see #isMetaObject() + * @see #hasMembers() + * @since 25.1 + */ + public Value getStaticScope() { + return (Value) dispatch.getStaticScope(this.context, receiver); + } + // executable /** @@ -1554,7 +1603,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,7 +1635,9 @@ public boolean isHostObject() { /** * Returns the original Java host language object. * - * @throws UnsupportedOperationException if {@link #isHostObject()} is false. + * @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 @@ -1620,7 +1671,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/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..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 @@ -1034,32 +1034,16 @@ 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 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); @@ -1153,6 +1137,12 @@ public Set getMemberKeys(Object context, Object receiver) { public abstract boolean removeMember(Object context, Object receiver, String key); + public boolean hasStaticScope(Object context, Object receiver) { + return false; + } + + public abstract Object getStaticScope(Object context, Object receiver); + public boolean canExecute(Object context, Object receiver) { return false; } 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..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 @@ -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. * @@ -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("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 52d4864af6d0..9396ea965328 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -61,6 +61,10 @@ 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-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. + ## 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.debug/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.debug/snapshot.sigtest index 05960677b690..2d8deea2e80d 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 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 f5f4579225be..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 @@ -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 scope is available. + * + * @throws DebugException when guest language code throws an exception + * @since 25.1 + */ + public final DebugValue getStaticScope() { + if (!isReadable()) { + return null; + } + Object view = getLanguageView(); + try { + if (INTEROP.hasStaticScope(view)) { + return new HeapValue(getSession(), resolveLanguage(), null, INTEROP.getStaticScope(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.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..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 org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractHostLanguageService; - import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; @@ -183,10 +181,9 @@ 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); - 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) { diff --git a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest index eb45f1b7459f..3d983db041a8 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() @@ -65,6 +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 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) @@ -82,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) @@ -123,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,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 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/HeapIsolationException.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/HeapIsolationException.java new file mode 100644 index 000000000000..ae8a5b2588f3 --- /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 25.1 + */ +@SuppressWarnings("serial") +public final class HeapIsolationException extends InteropException { + + private HeapIsolationException() { + super(null); + } + + private HeapIsolationException(Throwable cause) { + super(null, cause); + } + + /** + * {@inheritDoc} + * + * @since 25.1 + */ + @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#asHostObject(Object)} is invoked from within a polyglot isolate. + *

+ * This factory method is intended for use in {@link CompilerDirectives#inCompiledCode() + * compiled code paths}. + * + * @since 25.1 + */ + 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#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 + * 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 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 9d3fe707d143..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 @@ -141,6 +141,7 @@ *

  • {@link #isInstantiable(Object) instantiable} *
  • {@link #isPointer(Object) pointer} *
  • {@link #hasMembers(Object) members} + *
  • {@link #hasStaticScope(Object receiver) static scope} *
  • {@link #hasHashEntries(Object) hash entries} *
  • {@link #hasArrayElements(Object) array elements} *
  • {@link #hasBufferElements(Object) buffer elements} @@ -962,6 +963,63 @@ public boolean hasMemberWriteSideEffects(Object receiver, String member) { return false; } + /** + * 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 #getStaticScope(Object) + * @see #isMetaObject(Object) + * @since 25.1 + */ + @Abstract(ifExported = {"getStaticScope"}) + public boolean hasStaticScope(Object receiver) { + return false; + } + + /** + * 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 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 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 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 #hasStaticScope(Object) have a static scope} + * @see #hasStaticScope(Object) + * @see #isMetaObject(Object) + * @see #isScope(Object) + * @see #hasMembers(Object) + * @since 25.1 + */ + @Abstract(ifExported = {"hasStaticScope"}) + public Object getStaticScope(Object receiver) throws UnsupportedMessageException { + throw UnsupportedMessageException.create(); + } + // Hashes /** * Returns {@code true} if the receiver may have hash entries. Therefore, at least one of @@ -2460,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; @@ -2591,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) { @@ -2630,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; } @@ -2939,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 #getLanguage(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 @@ -2959,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 #getLanguage(Object) * @see #getMembers(Object) * @see #hasScopeParent(Object) * @since 20.3 @@ -3008,6 +3063,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 #asHostObject(Object)} must be implemented. + * + * @see #asHostObject(Object) + * @since 25.1 + */ + @Abstract(ifExported = "asHostObject") + public boolean isHostObject(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 #isHostObject(Object)} must also be implemented and return + * {@code true} for the same receiver. + * + * @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 #isHostObject(Object) + * @since 25.1 + */ + @Abstract(ifExported = "isHostObject") + public Object asHostObject(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)}. @@ -3872,6 +3960,64 @@ public boolean isMemberInternal(Object receiver, String identifier) { return result; } + @Override + public boolean hasStaticScope(Object receiver) { + if (CompilerDirectives.inCompiledCode()) { + return delegate.hasStaticScope(receiver); + } + assert preCondition(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 getStaticScope(Object receiver) throws UnsupportedMessageException { + if (CompilerDirectives.inCompiledCode()) { + return delegate.getStaticScope(receiver); + } + assert preCondition(receiver); + boolean hadStaticReceiver = delegate.hasStaticScope(receiver); + try { + 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) { + assert e instanceof UnsupportedMessageException : violationPost(receiver, e); + assert isMultiThreaded(receiver) || !hadStaticReceiver : violationInvariant(receiver); + throw e; + } + } + @Override public boolean hasHashEntries(Object receiver) { assert preCondition(receiver); @@ -5517,6 +5663,40 @@ private boolean assertHasNoLanguageId(Object receiver) { return true; } + @Override + public boolean isHostObject(Object receiver) { + if (CompilerDirectives.inCompiledCode()) { + return delegate.isHostObject(receiver); + } + assert preCondition(receiver); + boolean result = delegate.isHostObject(receiver); + if (result) { + assert assertHasHostObject(receiver); + } else { + assert assertHasNoHostObject(receiver); + } + assert validProtocolReturn(receiver, result); + 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); + assert false : violationInvariant(receiver); + } catch (UnsupportedMessageException | HeapIsolationException e) { + } + return true; + } + @Override public String getLanguageId(Object receiver) throws UnsupportedMessageException { if (CompilerDirectives.inCompiledCode()) { @@ -5535,6 +5715,26 @@ public String getLanguageId(Object receiver) throws UnsupportedMessageException throw e; } } + + @Override + public Object asHostObject(Object receiver) throws HeapIsolationException, UnsupportedMessageException { + if (CompilerDirectives.inCompiledCode()) { + return delegate.asHostObject(receiver); + } + assert preCondition(receiver); + boolean wasHasHostObject = delegate.isHostObject(receiver); + try { + Object result = delegate.asHostObject(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..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(env.isHostObject(logger)); - assertTrue(env.asHostObject(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(env.isHostObject(logger)); - assertTrue(env.asHostObject(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 1e1cbb03eea0..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(env.isHostObject(result)); - Object thread = env.asHostObject(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 e3930077df03..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 @@ -136,9 +136,11 @@ private Object asHostType(TruffleLanguage.Env env, Class c) { } } + @SuppressWarnings("deprecation") private static Object verifyHostAdapterClass(TruffleLanguage.Env env, Object hostAdapterClass) { - assertTrue(env.isHostObject(hostAdapterClass)); + assertTrue(INTEROP.isHostObject(hostAdapterClass)); assertTrue(env.isHostSymbol(hostAdapterClass)); + assertTrue(INTEROP.isScope(hostAdapterClass)); assertTrue(INTEROP.isMetaObject(hostAdapterClass)); assertTrue(INTEROP.isInstantiable(hostAdapterClass)); return hostAdapterClass; @@ -162,10 +164,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 +188,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.isHostObject(e) && INTEROP.isException(e))); assertTrue(INTEROP.isMetaInstance(env.asHostSymbol(Extensible.class), instance1)); assertTrue(INTEROP.isMetaInstance(env.asHostSymbol(Extensible.class), instance2)); @@ -213,7 +215,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 +247,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 +349,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 +377,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 +400,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 +411,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.asHostObject(adapterClass1)}); + Object instance = instantianteHostAdapter(adapterClass2, guestObject2); assertEquals("abstractMethodImpl2", INTEROP.invokeMember(instance, "abstractMethod")); assertEquals("baseMethodImpl2", INTEROP.invokeMember(instance, "baseMethod")); @@ -435,7 +437,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 +448,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.asHostObject(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..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 @@ -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); @@ -528,7 +530,7 @@ public void testHostExceptionIsHostSymbol() { TruffleTestAssumptions.assumeWeakEncapsulation(); expectedException = RuntimeException.class; customExceptionVerifier = (t) -> { - assertFalse(env.isHostSymbol(t)); + assertFalse(INTEROP.isHostObject(t) && INTEROP.isScope(t)); }; Value catcher = context.eval(ProxyLanguage.ID, CATCHER); Runnable thrower = HostExceptionTest::thrower; @@ -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.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)); @@ -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.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)); @@ -897,7 +899,7 @@ public void testGuestExceptionCaughtByHost() { hostExceptionVerifier = null; customExceptionVerifier = (guestEx) -> { - assertFalse(guestEx.toString(), env.isHostException(guestEx)); + assertFalse(guestEx.toString(), INTEROP.isHostObject(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.isHostObject(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.isHostObject(ex)); assertNotNull("Unexpected exception: " + ex, expectedException); - assertThat(env.asHostObject(ex), instanceOf(expectedException)); - assertThat(LanguageContext.get(null).getEnv().asHostException(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) { @@ -1229,11 +1231,19 @@ private void verifyHostException(Throwable ex) { } } + private static Object asHostObject(Object guestObject) { + try { + return INTEROP.asHostObject(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.isHostObject(ex) && INTEROP.isException(ex)) { + Throwable t = (Throwable) asHostObject(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.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 55789c44973f..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(env.isHostException(e)); + assertTrue(INTEROP.isHostObject(e) && INTEROP.isException(e)); }); AbstractPolyglotTest.assertFails(() -> INTEROP.execute(foo, 42), RuntimeException.class, (e) -> { - assertTrue(env.isHostException(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 9e56dd0a48fc..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 @@ -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.isHostObject(cause) && interopLibrary.isException(cause)) { + try { + cause = (Throwable) interopLibrary.asHostObject(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..16ad4ea5a6ee 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.asHostObject(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..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 @@ -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; @@ -793,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; } } @@ -1953,6 +1955,317 @@ public void testIsInvocableMemberWithReadSideEffects() throws UnsupportedMessage assertEquals(42, memberLib.invokeMember(obj, memberName)); } + @Test + public void testIsHostObject() { + AsHostObjectTest hostObject = new AsHostObjectTest(); + InteropLibrary hostObjectLib = createLibrary(InteropLibrary.class, hostObject); + + hostObject.isHostObject = true; + hostObject.hostObjectProvider = () -> { + throw UnsupportedMessageException.create(); + }; + assertFails(() -> hostObjectLib.isHostObject(hostObject), AssertionError.class); + + hostObject.hostObjectProvider = () -> Pair.create(42, "42"); + assertTrue(hostObjectLib.isHostObject(hostObject)); + + hostObject.hostObjectProvider = () -> { + throw HeapIsolationException.create(); + }; + assertTrue(hostObjectLib.isHostObject(hostObject)); + + hostObject.isHostObject = false; + hostObject.hostObjectProvider = null; + assertFalse(hostObjectLib.isHostObject(hostObject)); + + hostObject.hostObjectProvider = () -> Pair.create(42, "42"); + assertFails(() -> hostObjectLib.isHostObject(hostObject), AssertionError.class); + + hostObject.hostObjectProvider = () -> { + throw HeapIsolationException.create(); + }; + assertFalse(hostObjectLib.isHostObject(hostObject)); + } + + @Test + public void testAsHostObject() throws UnsupportedMessageException, HeapIsolationException { + AsHostObjectTest hostObject = new AsHostObjectTest(); + InteropLibrary l = createLibrary(InteropLibrary.class, hostObject); + + hostObject.isHostObject = false; + hostObject.hostObjectProvider = null; + assertFails(() -> l.asHostObject(hostObject), UnsupportedMessageException.class); + + hostObject.isHostObject = true; + Object value = new Object(); + hostObject.hostObjectProvider = () -> value; + assertSame(value, l.asHostObject(hostObject)); + + hostObject.isHostObject = true; + hostObject.hostObjectProvider = null; + assertFails(() -> l.asHostObject(hostObject), AssertionError.class); + + hostObject.isHostObject = false; + hostObject.hostObjectProvider = () -> value; + assertFails(() -> l.asHostObject(hostObject), AssertionError.class); + } + + @ExportLibrary(InteropLibrary.class) + static class AsHostObjectTest implements TruffleObject { + + boolean isHostObject; + HostObjectProvider hostObjectProvider; + + @ExportMessage + boolean isHostObject() { + return isHostObject; + } + + @ExportMessage + Object asHostObject() throws UnsupportedMessageException, HeapIsolationException { + if (hostObjectProvider != null) { + return hostObjectProvider.call(); + } else { + throw UnsupportedMessageException.create(); + } + } + } + + @FunctionalInterface + interface HostObjectProvider extends Callable { + + @Override + 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/ContextBuilderExtendAPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextBuilderExtendAPITest.java index b6f2c049a109..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 && env.isHostObject(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 e9b9cf88b4a9..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(env.isHostException(e)); + Assert.assertFalse(interop.isHostObject(e) && interop.isException(e)); Assert.assertTrue(e instanceof RuntimeException); } else { - Assert.assertTrue(env.isHostException(e)); + Assert.assertTrue(interop.isHostObject(e) && interop.isException(e)); if (weakEncapsulation) { - Assert.assertTrue(env.asHostException(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 5477f9b5843e..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 @@ -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.asHostObject(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.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 5704eac6b9ce..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 @@ -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.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); @@ -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.isHostObject(nullObject)); + assertNull(INTEROP.asHostObject(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.asHostObject(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.isHostObject(guestValue)); + assertEquals(testNumber, INTEROP.asHostObject(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.isHostObject(stringObject)); + assertEquals(string, INTEROP.asHostObject(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.isHostObject(booleanObject)); + assertEquals(b, INTEROP.asHostObject(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.isHostObject(charObject)); + assertEquals(unboxValue, INTEROP.asHostObject(charObject)); } @Test @@ -350,30 +352,39 @@ 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.isHostObject(e) && INTEROP.isException(e)); + try { + assertSame(exception, INTEROP.asHostObject(e).getClass()); + } catch (UnsupportedMessageException | HeapIsolationException ex) { + throw CompilerDirectives.shouldNotReachHere(ex); + } } } @Test + @SuppressWarnings("deprecation") 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)); + assertTrue(INTEROP.isHostObject(exit)); + assertTrue(INTEROP.isHostObject(exit) && INTEROP.isExecutable(exit) && !INTEROP.hasMembers(exit)); + assertFails(() -> INTEROP.asHostObject(exit), HeapIsolationException.class); assertTrue(languageEnv.isHostFunction(exit)); Object out = INTEROP.readMember(system, "out"); assertTrue(exit instanceof TruffleObject); - assertTrue(languageEnv.isHostObject(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.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 5babfbb62b88..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 @@ -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.asHostObject(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/polyglot/ValueHostInteropTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java index b6a2b1ac1d53..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,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.getMetaObject().getStaticScope(); + 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.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..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 @@ -103,26 +103,11 @@ public T toHostType(Object hostNode, Object targetNode, Object hostContext, throw new UnsupportedOperationException(); } - @Override - 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,26 +118,6 @@ 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; - } - @Override public Object createHostAdapter(Object hostContextObject, Object[] types, Object classOverrides) { return null; @@ -168,11 +133,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..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 @@ -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; @@ -144,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 { @@ -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 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 { 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/snapshot.sigtest b/truffle/src/com.oracle.truffle.api/snapshot.sigtest index 3f2c84e0f650..42de8dbb4c2f 100644 --- a/truffle/src/com.oracle.truffle.api/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api/snapshot.sigtest @@ -578,10 +578,14 @@ 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) + 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() @@ -616,6 +620,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 +648,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 f4ed255ad5d1..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 @@ -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#isHostObject(Object) + * isHostObject}. * @see #asHostObject(Object) * @since 19.0 */ + @Deprecated(since = "25.1") @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#asHostObject(Object) + * asHostObject}. * @since 19.0 */ + @Deprecated(since = "25.1") 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); } @@ -2321,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); } @@ -2355,7 +2366,7 @@ 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)}. * @@ -2364,11 +2375,14 @@ public Object findMetaObject(Object value) { * otherwise * @see #asHostException(Throwable) * @since 19.0 + * @deprecated Use + * {@code interopLibrary.isHostObject(obj) && interopLibrary.isException(obj)}. */ + @Deprecated(since = "25.1") @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); } @@ -2376,7 +2390,7 @@ 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)}. * @@ -2385,11 +2399,15 @@ public boolean isHostException(Throwable 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#asHostObject(Object) + * asHostObject}. */ + @Deprecated(since = "25.1") @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); } @@ -2401,11 +2419,13 @@ public Throwable asHostException(Throwable exception) { * * @see #lookupHostSymbol(String) * @since 19.0 + * @deprecated Use {@code interopLibrary.isHostObject(obj) && interopLibrary.isScope(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); } @@ -2568,10 +2588,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 @@ -2729,7 +2748,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. * @@ -3048,13 +3067,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) { @@ -3075,13 +3094,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) { @@ -3136,11 +3155,10 @@ 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) { @@ -3161,11 +3179,10 @@ 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) { @@ -3580,7 +3597,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 */ @@ -3622,7 +3638,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 */ @@ -3700,7 +3715,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) { @@ -4051,7 +4065,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 @@ -4338,15 +4352,14 @@ public enum ContextPolicy { /** * Mode of exit operation. * - * @since 22.0 * @see #exitContext(Object, ExitMode, int) + * @since 22.0 */ 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/TruffleStackTrace.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java index 07d0e4cd9cb7..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 @@ -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,7 @@ public static TruffleStackTrace fillIn(Throwable throwable) { } private static boolean isHostException(Throwable throwable) { - Object polyglotEngine = LanguageAccessor.ENGINE.getCurrentPolyglotEngine(); - if (polyglotEngine == null) { - return false; - } - AbstractHostLanguageService hostService = LanguageAccessor.ENGINE.getHostService(polyglotEngine); - // hostService is null during context pre-initialization - return hostService != null && hostService.isHostException(throwable); + return LanguageAccessor.ENGINE.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..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 @@ -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); public abstract Object getCurrentHostContext(); @@ -567,13 +567,13 @@ 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); + 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/HostAccessor.java b/truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostAccessor.java index 67fe38282720..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,12 +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) { - return HostObject.valueOf(null, hostValue); + 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 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 3df874363797..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 @@ -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 isHostObject() { + return true; } @SuppressWarnings("static-method") @ExportMessage - boolean isExecutable() { - return true; + Object asHostObject() throws HeapIsolationException { + throw HeapIsolationException.create(); } @ExportMessage @@ -145,5 +150,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..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 @@ -161,33 +161,11 @@ 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 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,35 +177,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); } - @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(); @@ -261,7 +215,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) { @@ -271,7 +224,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 @@ -283,15 +235,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..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,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_AS_HOST_OBJECT = Message.resolveExact(InteropLibrary.class, "asHostObject", Object.class); 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_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 20d5c320bdba..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 @@ -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); @@ -3304,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()) { @@ -3343,13 +3320,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.isHostObject(other)) { + Object otherHostObj; + try { + otherHostObj = otherInterop.asHostObject(other); + } catch (HeapIsolationException e) { + error.enter(node); + throw UnsupportedMessageException.create(); + } if (otherHostObj == null) { return false; } else { @@ -3435,6 +3418,22 @@ Object readArrayElement(long idx, } } + @ExportMessage + boolean hasStaticScope() { + return isMetaObject(); + } + + @ExportMessage + Object getStaticScope( + @Bind Node node, + @Shared("error") @Cached InlinedBranchProfile error) throws UnsupportedMessageException { + if (!hasStaticScope()) { + error.enter(node); + throw UnsupportedMessageException.create(); + } + return HostObject.forStaticClass((Class) obj, context); + } + boolean isStaticClass() { return extraInfo instanceof Class; } @@ -3490,6 +3489,17 @@ static int identityHashCode(HostObject receiver) { return System.identityHashCode(receiver.obj); } + @ExportMessage + @SuppressWarnings("static-method") + boolean isHostObject() { + return true; + } + + @ExportMessage + Object asHostObject() { + 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..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 @@ -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; } @@ -317,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.isHostObject(value)) { return false; } else { if (priority >= FUNCTION_PROXY && HostInteropReflect.isFunctionalInterface(targetType) && @@ -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 = toJavaInstance(value, targetType, interop); + if (hostObject != 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.isHostObject(value)) { + try { + Object hostObject = interop.asHostObject(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..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 @@ -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,20 @@ abstract static class SerializeArrayNode extends SerializeArgumentNode { } } - final boolean isHostObject(Object value) { - return LibFFIContext.get(this).env.isHostObject(value); - } - - final Object asHostObject(Object value) { - return LibFFIContext.get(this).env.asHostObject(value); + final Object asHostObject(Object value, InteropLibrary interop) throws UnsupportedTypeException { + try { + return interop.asHostObject(value); + } catch (UnsupportedMessageException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } catch (HeapIsolationException e) { + throw UnsupportedTypeException.create(new Object[]{value}); + } } - @Specialization(guards = {"isHostObject(value)", "tag != null"}) + @Specialization(guards = {"interop.isHostObject(value)", "tag != null"}, limit = "3") void doHostObject(@SuppressWarnings("unused") Object value, NativeArgumentBuffer buffer, - @Bind("asHostObject(value)") Object hostObject, + @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/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..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 @@ -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.isHostObject(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..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 @@ -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 (context.engine.host.isHostValue(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,24 +1260,29 @@ 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); + @TruffleBoundary + public boolean isHostException(Throwable exception) { + InteropLibrary interop = InteropLibrary.getUncached(exception); + return interop.isHostObject(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."); + @TruffleBoundary + public Throwable asHostException(Throwable exception) { + if (exception != null) { + InteropLibrary interop = InteropLibrary.getUncached(exception); + boolean isHostException = interop.isHostObject(exception) && interop.isException(exception); + if (isHostException) { + try { + return (Throwable) interop.asHostObject(exception); + } catch (HeapIsolationException e) { + return null; + } catch (UnsupportedMessageException e) { + // Fall through to IllegalArgumentException + } + } } - return host; + throw new IllegalArgumentException("Provided value not a host exception."); } @Override @@ -1543,40 +1549,40 @@ 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); + @TruffleBoundary + public Object asHostObject(Object obj) { + InteropLibrary interop = InteropLibrary.getUncached(obj); + 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 - 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); + @TruffleBoundary + public boolean isHostFunction(Object obj) { + InteropLibrary interop = InteropLibrary.getUncached(obj); + return interop.isHostObject(obj) && interop.isExecutable(obj) && !interop.hasMembers(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); + @TruffleBoundary + public boolean isHostObject(Object obj) { + InteropLibrary interop = InteropLibrary.getUncached(obj); + return interop.isHostObject(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); + @TruffleBoundary + public boolean isHostSymbol(Object obj) { + InteropLibrary interop = InteropLibrary.getUncached(obj); + return interop.isHostObject(obj) && interop.isScope(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..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,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_AS_HOST_OBJECT = Message.resolveExact(InteropLibrary.class, "asHostObject", Object.class); 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_AS_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..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 @@ -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,17 @@ 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) { + InteropLibrary interop = InteropLibrary.getUncached(cause); + return interop.isHostObject(cause) && interop.isException(cause); + } + + private static Throwable unboxHostException(Throwable cause) { + try { + return (Throwable) InteropLibrary.getUncached(cause).asHostObject(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/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()); 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..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 @@ -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; @@ -118,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.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; @@ -126,12 +129,14 @@ 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.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; 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; @@ -585,6 +590,24 @@ static RuntimeException removeMemberUnsupported(PolyglotLanguageContext context, throw unsupported(context, receiver, "removeMember(String, Object)", null); } + @Override + public Object getStaticScope(Object languageContext, Object receiver) { + PolyglotLanguageContext context = (PolyglotLanguageContext) languageContext; + Object prev = hostEnter(context); + try { + return getStaticScopeUnsupported(context, receiver); + } catch (Throwable e) { + throw guestToHostException(context, e, true); + } finally { + hostLeave(context, prev); + } + } + + @TruffleBoundary + static Object getStaticScopeUnsupported(PolyglotLanguageContext context, Object receiver) { + throw unsupported(context, receiver, "getStaticScope(Object)", "hasStaticScope"); + } + @Override public Object execute(Object languageContext, Object receiver, Object[] arguments) { PolyglotLanguageContext context = (PolyglotLanguageContext) languageContext; @@ -2293,9 +2316,13 @@ 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; + final CallTarget hasStaticScope; + final CallTarget getStaticScope; + final Class receiverType; InteropValue(PolyglotImpl polyglot, PolyglotLanguageInstance languageInstance, Object receiverObject, Class receiverType) { @@ -2375,7 +2402,10 @@ 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)); + this.hasStaticScope = createTarget(HasStaticScopeNodeGen.create(this)); + this.getStaticScope = createTarget(GetStaticScopeNodeGen.create(this)); } @SuppressWarnings("unchecked") @@ -2540,6 +2570,16 @@ public Set getMemberKeys(Object languageContext, Object receiver) { return new MemberSet(this.getEngine().getAPIAccess(), languageContext, receiver, keys); } + @Override + public boolean hasStaticScope(Object languageContext, Object receiver) { + return (boolean) RUNTIME.callProfiled(this.hasStaticScope, languageContext, receiver); + } + + @Override + public Object getStaticScope(Object languageContext, Object receiver) { + return RUNTIME.callProfiled(this.getStaticScope, languageContext, receiver); + } + @Override public long asNativePointer(Object languageContext, Object receiver) { return (long) RUNTIME.callProfiled(this.asNativePointer, languageContext, receiver); @@ -2592,15 +2632,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 +2663,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 +5615,120 @@ 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.isHostObject(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.asHostObject(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.unsupported(polyglotMessage); + } + } + + abstract static class HasStaticScopeNode extends InteropNode { + + protected HasStaticScopeNode(InteropValue interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "hasStaticScope"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary objects) { + return objects.hasStaticScope(receiver); + } + } + + abstract static class GetStaticScopeNode extends InteropNode { + + protected GetStaticScopeNode(InteropValue interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "getStaticScope"; + } + + @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.getStaticScope(receiver)); + } catch (UnsupportedMessageException e) { + unsupported.enter(node); + 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 4302fa7d90d9..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 @@ -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(), ClassCastException.class); + assertFails(() -> value.asHostObject(), ClassCastException.class, UnsupportedOperationException.class); break; case PROXY_OBJECT: assertFalse(value.isProxyObject()); @@ -459,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()); @@ -479,7 +481,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 +492,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 +582,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 +637,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()) { @@ -734,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()); @@ -756,7 +789,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 +810,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)); 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."); + } } } 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..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 @@ -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; @@ -236,9 +241,9 @@ 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); + @Specialization(limit = "3") + int bench(Object hostObject, @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(hostObject, interop); int ret = 0; for (int i = 0; i < input.length; i++) { ret += Byte.toUnsignedInt(input[i]); @@ -323,10 +328,11 @@ 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) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_8, false); } } @@ -335,10 +341,11 @@ 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) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16, false); } } @@ -347,10 +354,11 @@ 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) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16, true); } } @@ -359,10 +367,11 @@ 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) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32, true); } } @@ -371,10 +380,11 @@ 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) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16BE, false); } } @@ -383,11 +393,12 @@ 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) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(hostObject, interop); return switchEncodingNode.execute(fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_16BE, false), TruffleString.Encoding.UTF_16); } } @@ -396,10 +407,11 @@ 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) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(hostObject, interop); return fromByteArrayNode.execute(input, 0, length, TruffleString.Encoding.UTF_32BE, false); } } @@ -408,11 +420,12 @@ 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) { - byte[] input = (byte[]) DummyLanguageContext.get(this).getEnv().asHostObject(hostObject); + @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @CachedLibrary("hostObject") InteropLibrary interop) { + byte[] input = (byte[]) asHostObject(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 asHostObject(Object guestObject, InteropLibrary interop) { + try { + return interop.asHostObject(guestObject); + } catch (UnsupportedMessageException | HeapIsolationException e) { + throw CompilerDirectives.shouldNotReachHere(e); + } + } }