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 ab6b1e86e4df..fa6aafcbcd39 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 @@ -3477,7 +3477,7 @@ public Object getMembers(Object receiver, boolean internal) throws UnsupportedMe assert validInteropReturn(receiver, result); assert validProtocolArgument(receiver, internal); assert isMultiThreaded(receiver) || assertMemberKeys(receiver, result, internal); - assert !delegate.hasScopeParent(receiver) || assertScopeMembers(receiver, result, delegate.getMembers(delegate.getScopeParent(receiver), internal)); + assert !delegate.hasScopeParent(receiver) || assertScopeMembers(receiver, result, getUncached().getMembers(delegate.getScopeParent(receiver), internal)); assert validInteropReturn(receiver, result); return result; } catch (InteropException e) { 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 2df4286b086f..34287f2fce40 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 @@ -48,6 +48,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.Callable; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -55,7 +56,9 @@ import org.graalvm.polyglot.Context; import org.junit.Test; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.ExceptionType; @@ -589,6 +592,134 @@ public void testIsSame() { assertTrue(l0.isIdentical(v0, v1, l1)); } + @Test + public void testValidScopeUsage() throws Exception { + ScopeCached sc = new ScopeCached(5); + InteropLibrary iop = createLibrary(InteropLibrary.class, sc); + assertTrue(iop.hasMembers(sc)); + Object members = iop.getMembers(sc); + assertNotNull(members); + assertTrue(iop.hasScopeParent(sc)); + Object scParent = iop.getScopeParent(sc); + assertNotNull(scParent); + if (run == TestRun.CACHED) { + checkInvalidUsage(() -> iop.hasMembers(scParent)); + checkInvalidUsage(() -> iop.getMembers(scParent)); + checkInvalidUsage(() -> iop.hasScopeParent(scParent)); + checkInvalidUsage(() -> iop.getScopeParent(scParent)); + } + } + + private static void checkInvalidUsage(Callable call) throws Exception { + boolean invalidUsage = false; + try { + call.call(); + } catch (AssertionError err) { + assertTrue(err.getMessage(), err.getMessage().startsWith("Invalid library usage")); + invalidUsage = true; + } + assertTrue(invalidUsage); + } + + @ExportLibrary(InteropLibrary.class) + static class ScopeCached implements TruffleObject { + + final long id; + + ScopeCached(long id) { + this.id = id; + } + + @ExportMessage + boolean accepts(@Cached(value = "this.id") long cachedId) { + return this.id == cachedId; + } + + @ExportMessage + @SuppressWarnings("static-method") + boolean isScope() { + return true; + } + + @ExportMessage + boolean hasScopeParent() { + return this.id > 0; + } + + @ExportMessage + @TruffleBoundary + Object getScopeParent() throws UnsupportedMessageException { + if (this.id > 0) { + return new ScopeCached(id - 1); + } else { + throw UnsupportedMessageException.create(); + } + } + + @ExportMessage + @SuppressWarnings("static-method") + boolean hasMembers() { + return true; + } + + @ExportMessage + Object getMembers(@SuppressWarnings("unused") boolean includeInternal) { + return new ScopeMembers(id); + } + + @ExportMessage + @SuppressWarnings("static-method") + boolean hasLanguage() { + return true; + } + + @ExportMessage + @SuppressWarnings("static-method") + Class> getLanguage() { + return ProxyLanguage.class; + } + + @ExportMessage + Object toDisplayString(@SuppressWarnings("unused") boolean allowSideEffects) { + return "ScopeCached[" + id + "]"; + } + + @ExportLibrary(InteropLibrary.class) + static final class ScopeMembers implements TruffleObject { + + private final long len; + + private ScopeMembers(long len) { + this.len = len; + } + + @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 + long getArraySize() { + return len; + } + + @ExportMessage + boolean isArrayElementReadable(long index) { + return 0 <= index && index < len; + } + } + } + static class Members implements TruffleObject { } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITest.java index 2731b634c8c3..cee0ca3020f5 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITest.java @@ -57,6 +57,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -1892,7 +1893,7 @@ Object readArrayElement(long index) throws InvalidArrayIndexException { @ExportLibrary(InteropLibrary.class) static final class TestScope implements TruffleObject { - final Map values = new HashMap<>(); + final Map values = new LinkedHashMap<>(); TestScope parentScope; boolean modifiable; boolean insertable; @@ -2160,9 +2161,9 @@ private void testBindingsWithMultipleScopes(TestScope[] scopes) { // Expected as the merged scope does not contain parent scopes. } // Correct the scope hierarchy: - scopes[0].values.put("foo", "val"); scopes[0].values.put("bar", "val"); - scopes[1].values.put("bar", "val"); + scopes[0].values.put("foo", "val"); + scopes[3].values.clear(); assertValue(bindings, ValueAssert.Trait.MEMBERS); c.close();