diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/PolyglotHostObjectPartialEvaluationTest.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/PolyglotHostObjectPartialEvaluationTest.java new file mode 100644 index 000000000000..eba5f4cffe44 --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/PolyglotHostObjectPartialEvaluationTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.truffle.test; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidBufferOffsetException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.polyglot.ProxyLanguage; +import com.oracle.truffle.api.test.polyglot.ValueAPITest; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.HostAccess; +import org.junit.Before; +import org.junit.Test; + +import java.nio.Buffer; + +public class PolyglotHostObjectPartialEvaluationTest extends PartialEvaluationTest { + + @Before + public void setup() { + final HostAccess hostAccess = HostAccess.newBuilder().allowBufferAccess(true).build(); + final Context context = Context.newBuilder().allowExperimentalOptions(true).option("engine.CompileImmediately", "true").allowHostAccess(hostAccess).build(); + setupContext(context); + } + + public static Object constantTrue() { + return true; + } + + public static Object constantFalse() { + return false; + } + + @Test + public void hasBufferElements() { + for (final Buffer buffer : ValueAPITest.makeTestBuffers()) { + getContext().initialize(ProxyLanguage.ID); + final Object bufferHostObject = ProxyLanguage.getCurrentContext().getEnv().asGuestValue(buffer); + final RootNode node = new RootNode(null) { + @Child InteropLibrary interop = InteropLibrary.getFactory().createDispatched(1); + + @Override + public Object execute(VirtualFrame frame) { + return interop.hasBufferElements(bufferHostObject); + } + }; + assertPartialEvalEquals("constantTrue", node); + } + } + + @Test + public void isBufferWritable() { + for (final Buffer buffer : ValueAPITest.makeTestBuffers()) { + getContext().initialize(ProxyLanguage.ID); + final Object bufferHostObject = ProxyLanguage.getCurrentContext().getEnv().asGuestValue(buffer); + final RootNode node = new RootNode(null) { + @Child InteropLibrary interop = InteropLibrary.getFactory().createDispatched(1); + + @Override + public Object execute(VirtualFrame frame) { + try { + return interop.isBufferWritable(bufferHostObject); + } catch (UnsupportedMessageException e) { + throw new RuntimeException(e); + } + } + }; + assertPartialEvalEquals(buffer.isReadOnly() ? "constantFalse" : "constantTrue", node); + } + } + + @Test + public void getBufferSize() { + for (final Buffer buffer : ValueAPITest.makeTestBuffers()) { + getContext().initialize(ProxyLanguage.ID); + final Object bufferHostObject = ProxyLanguage.getCurrentContext().getEnv().asGuestValue(buffer); + final RootNode node = new RootNode(null) { + @Child InteropLibrary interop = InteropLibrary.getFactory().createDispatched(1); + + @Override + public Object execute(VirtualFrame frame) { + try { + return interop.getBufferSize(bufferHostObject); + } catch (UnsupportedMessageException e) { + throw new RuntimeException(e); + } + } + }; + assertPartialEvalNoInvokes(node); + } + } + + @Test + public void readBufferByte() { + for (final Buffer buffer : ValueAPITest.makeTestBuffers()) { + getContext().initialize(ProxyLanguage.ID); + final Object bufferHostObject = ProxyLanguage.getCurrentContext().getEnv().asGuestValue(buffer); + final RootNode node = new RootNode(null) { + @Child InteropLibrary interop = InteropLibrary.getFactory().createDispatched(1); + + @Override + public Object execute(VirtualFrame frame) { + try { + return interop.readBufferByte(bufferHostObject, 0); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw new RuntimeException(e); + } + } + }; + assertPartialEvalNoInvokes(node); + } + } + + @Test + public void writeBufferByte() { + for (final Buffer buffer : ValueAPITest.makeTestBuffers()) { + if (buffer.isReadOnly()) { + continue; + } + getContext().initialize(ProxyLanguage.ID); + final Object bufferHostObject = ProxyLanguage.getCurrentContext().getEnv().asGuestValue(buffer); + final RootNode node = new RootNode(null) { + @Child InteropLibrary interop = InteropLibrary.getFactory().createDispatched(1); + + @Override + public Object execute(VirtualFrame frame) { + try { + interop.writeBufferByte(bufferHostObject, 0, (byte) 42); + } catch (UnsupportedMessageException | InvalidBufferOffsetException e) { + throw new RuntimeException(e); + } + return null; + } + }; + assertPartialEvalNoInvokes(node); + } + } + +} diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/nodes/RootTestNode.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/nodes/RootTestNode.java index 670a80e4a2e0..2d3b2065278b 100644 --- a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/nodes/RootTestNode.java +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/nodes/RootTestNode.java @@ -38,6 +38,10 @@ public class RootTestNode extends RootNode { @Child private AbstractTestNode node; + public RootTestNode(String name, AbstractTestNode node) { + this(new FrameDescriptor(), name, node, false, false); + } + public RootTestNode(FrameDescriptor descriptor, String name, AbstractTestNode node) { this(descriptor, name, node, false, false); } diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 61beb2fdc9ee..afdcee6acc74 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -2,6 +2,14 @@ This changelog summarizes major changes between GraalVM SDK versions. The main focus is on APIs exported by GraalVM SDK. +## Version 21.1.0 +* Added new methods in `Value` for interacting with buffer-like objects: + * Added `Value.hasBufferElements()` that returns `true` if this object supports buffer messages. + * Added `Value.isBufferWritable()` that returns `true` if this object supports writing buffer elements. + * Added `Value.getBufferSize()` to return the size of this buffer. + * Added `Value.readBufferByte(long)`, `Value.readBufferShort(ByteOrder, long)`, `Value.readBufferInt(ByteOrder, long)`, `Value.readBufferLong(ByteOrder, long)`, `Value.readBufferFloat(ByteOrder, long)` and `Value.readBufferDouble(ByteOrder, long)` to read a primitive from this buffer at the given index. + * Added `Value.writeBufferByte(long, byte)`, `Value.writeBufferShort(ByteOrder, long, short)`, `Value.writeBufferInt(ByteOrder, long, int)`, `Value.writeBufferLong(ByteOrder, long, long)`, `Value.writeBufferFloat(ByteOrder, long, float)` and `Value.writeBufferDouble(ByteOrder, long, double)` to write a primitive in this buffer at the given index (supported only if `Value.isBufferWritable()` returns `true`). + ## Version 21.0.0 * Added support for explicitly selecting a host method overload using the signature in the form of comma-separated fully qualified parameter type names enclosed by parentheses (e.g. `methodName(f.q.TypeName,java.lang.String,int,int[])`). * Deprecated host method selection by JNI mangled signature, replaced by the aforementioned new form. Scheduled for removal in 21.2. @@ -41,7 +49,6 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f * The result of `Value.getMetaObject()` will now return always [meta-objects](Value.isMetaObject). It is recommended but not required to change uses of meta-objects to use `Value.getMetaQualifiedName()` instead of `Value.toString()` to return a type name. * Added [Context.Builder.hostClassLoader](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Context.Builder.html#hostClassLoader-java.lang.ClassLoader-) to allow an embedder to specify a context ClassLoader for code execution. - ## Version 20.0.0 * The deprecated `graalvm.home` and `graalvm.version` system properties have been removed, use the [HomeFinder](https://www.graalvm.org/sdk/javadoc/org/graalvm/home/HomeFinder.html) instead. * Added `EventContext.createError` which allows to introduce guest application errors in execution listeners/nodes. @@ -50,7 +57,6 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f * The algorithm used to generate a unique [URI](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Source.html#getURI--) for a `Source` built without an `URI` was changed to SHA-256. * All Truffle Graal runtime options (-Dgraal.) will be deprecated with 20.1. The Truffle runtime options are no longer specified as Graal options (-Dgraal.). The Graal options must be replaced by corresponding engine options specified using [polyglot API](https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Engine.Builder.html#option-java.lang.String-java.lang.String-). The `TRUFFLE_STRICT_OPTION_DEPRECATION` environment variable can be used to detect usages of deprecated Graal options. When the `TRUFFLE_STRICT_OPTION_DEPRECATION` is set to `true` and the deprecated Graal option is used the engine throws a `PolyglotException` listing the used deprecated options and corresponding replacements. - ## Version 19.3.0 * The default temporary directory can be configured by [FileSystem](http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/io/FileSystem.html#getTempDirectory--). * Added `org.graalvm.polyglot.ResourceLimits` that allows to specify context specific time and statement count execution limits. diff --git a/sdk/src/org.graalvm.polyglot/snapshot.sigtest b/sdk/src/org.graalvm.polyglot/snapshot.sigtest index 5f8f74155282..07230a28a701 100644 --- a/sdk/src/org.graalvm.polyglot/snapshot.sigtest +++ b/sdk/src/org.graalvm.polyglot/snapshot.sigtest @@ -232,7 +232,7 @@ meth public java.lang.String toString() meth public static org.graalvm.polyglot.HostAccess$Builder newBuilder() meth public static org.graalvm.polyglot.HostAccess$Builder newBuilder(org.graalvm.polyglot.HostAccess) supr java.lang.Object -hfds EMPTY,accessAnnotations,allowAllClassImplementations,allowAllInterfaceImplementations,allowArrayAccess,allowListAccess,allowPublic,excludeTypes,impl,implementableAnnotations,implementableTypes,members,name,targetMappings +hfds EMPTY,accessAnnotations,allowAllClassImplementations,allowAllInterfaceImplementations,allowArrayAccess,allowBufferAccess,allowListAccess,allowPublic,excludeTypes,impl,implementableAnnotations,implementableTypes,members,name,targetMappings CLSS public final org.graalvm.polyglot.HostAccess$Builder outer org.graalvm.polyglot.HostAccess @@ -245,6 +245,7 @@ meth public org.graalvm.polyglot.HostAccess$Builder allowAccessAnnotatedBy(java. meth public org.graalvm.polyglot.HostAccess$Builder allowAllClassImplementations(boolean) meth public org.graalvm.polyglot.HostAccess$Builder allowAllImplementations(boolean) meth public org.graalvm.polyglot.HostAccess$Builder allowArrayAccess(boolean) +meth public org.graalvm.polyglot.HostAccess$Builder allowBufferAccess(boolean) meth public org.graalvm.polyglot.HostAccess$Builder allowImplementations(java.lang.Class) meth public org.graalvm.polyglot.HostAccess$Builder allowImplementationsAnnotatedBy(java.lang.Class) meth public org.graalvm.polyglot.HostAccess$Builder allowListAccess(boolean) @@ -252,7 +253,7 @@ meth public org.graalvm.polyglot.HostAccess$Builder allowPublicAccess(boolean) meth public org.graalvm.polyglot.HostAccess$Builder denyAccess(java.lang.Class) meth public org.graalvm.polyglot.HostAccess$Builder denyAccess(java.lang.Class,boolean) supr java.lang.Object -hfds accessAnnotations,allowAllClassImplementations,allowAllImplementations,allowArrayAccess,allowListAccess,allowPublic,excludeTypes,implementableTypes,implementationAnnotations,members,name,targetMappings +hfds accessAnnotations,allowAllClassImplementations,allowAllImplementations,allowArrayAccess,allowBufferAccess,allowListAccess,allowPublic,excludeTypes,implementableTypes,implementationAnnotations,members,name,targetMappings CLSS public abstract interface static !annotation org.graalvm.polyglot.HostAccess$Export outer org.graalvm.polyglot.HostAccess @@ -488,9 +489,11 @@ meth public boolean fitsInInt() meth public boolean fitsInLong() meth public boolean fitsInShort() meth public boolean hasArrayElements() +meth public boolean hasBufferElements() meth public boolean hasMember(java.lang.String) meth public boolean hasMembers() meth public boolean isBoolean() +meth public boolean isBufferWritable() meth public boolean isDate() meth public boolean isDuration() meth public boolean isException() @@ -508,10 +511,14 @@ meth public boolean isTimeZone() meth public boolean removeArrayElement(long) meth public boolean removeMember(java.lang.String) meth public byte asByte() +meth public byte readBufferByte(long) meth public double asDouble() +meth public double readBufferDouble(java.nio.ByteOrder,long) meth public float asFloat() +meth public float readBufferFloat(java.nio.ByteOrder,long) meth public int asInt() meth public int hashCode() +meth public int readBufferInt(java.nio.ByteOrder,long) meth public java.lang.RuntimeException throwException() meth public java.lang.String asString() meth public java.lang.String getMetaQualifiedName() @@ -526,15 +533,24 @@ meth public java.util.Set getMemberKeys() meth public long asLong() meth public long asNativePointer() meth public long getArraySize() +meth public long getBufferSize() +meth public long readBufferLong(java.nio.ByteOrder,long) meth public org.graalvm.polyglot.Context getContext() meth public org.graalvm.polyglot.SourceSection getSourceLocation() meth public org.graalvm.polyglot.Value getArrayElement(long) meth public org.graalvm.polyglot.Value getMember(java.lang.String) meth public org.graalvm.polyglot.Value getMetaObject() meth public short asShort() +meth public short readBufferShort(java.nio.ByteOrder,long) meth public static org.graalvm.polyglot.Value asValue(java.lang.Object) meth public void putMember(java.lang.String,java.lang.Object) meth public void setArrayElement(long,java.lang.Object) +meth public void writeBufferByte(long,byte) +meth public void writeBufferDouble(java.nio.ByteOrder,long,double) +meth public void writeBufferFloat(java.nio.ByteOrder,long,float) +meth public void writeBufferInt(java.nio.ByteOrder,long,int) +meth public void writeBufferLong(java.nio.ByteOrder,long,long) +meth public void writeBufferShort(java.nio.ByteOrder,long,short) supr java.lang.Object hfds impl,receiver diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java index f8575f998228..711804b24f68 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java @@ -40,6 +40,26 @@ */ package org.graalvm.polyglot; +import org.graalvm.collections.UnmodifiableEconomicSet; +import org.graalvm.home.HomeFinder; +import org.graalvm.nativeimage.ImageInfo; +import org.graalvm.options.OptionDescriptor; +import org.graalvm.options.OptionDescriptors; +import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence; +import org.graalvm.polyglot.PolyglotException.StackFrame; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractContextImpl; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractEngineImpl; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractExceptionImpl; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractInstrumentImpl; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractLanguageImpl; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractStackFrameImpl; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl; +import org.graalvm.polyglot.io.ByteSequence; +import org.graalvm.polyglot.io.FileSystem; +import org.graalvm.polyglot.io.MessageTransport; +import org.graalvm.polyglot.management.ExecutionEvent; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -68,26 +88,6 @@ import java.util.logging.Handler; import java.util.logging.Level; -import org.graalvm.collections.UnmodifiableEconomicSet; -import org.graalvm.home.HomeFinder; -import org.graalvm.nativeimage.ImageInfo; -import org.graalvm.options.OptionDescriptor; -import org.graalvm.options.OptionDescriptors; -import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence; -import org.graalvm.polyglot.PolyglotException.StackFrame; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractContextImpl; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractEngineImpl; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractExceptionImpl; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractInstrumentImpl; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractLanguageImpl; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractStackFrameImpl; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl; -import org.graalvm.polyglot.io.ByteSequence; -import org.graalvm.polyglot.io.FileSystem; -import org.graalvm.polyglot.io.MessageTransport; -import org.graalvm.polyglot.management.ExecutionEvent; - /** * An execution engine for Graal {@linkplain Language guest languages} that allows to inspect the * the installed {@link #getLanguages() guest languages}, {@link #getInstruments() instruments} and @@ -674,6 +674,11 @@ public boolean isListAccessible(HostAccess access) { return access.allowListAccess; } + @Override + public boolean isBufferAccessible(HostAccess access) { + return access.allowBufferAccess; + } + @Override public Object getHostAccessImpl(HostAccess conf) { return conf.impl; diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/HostAccess.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/HostAccess.java index eaa6d6e7efa5..0f71d14b7e86 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/HostAccess.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/HostAccess.java @@ -40,6 +40,11 @@ */ package org.graalvm.polyglot; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.EconomicSet; +import org.graalvm.collections.Equivalence; +import org.graalvm.collections.MapCursor; + import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -56,11 +61,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.graalvm.collections.EconomicMap; -import org.graalvm.collections.EconomicSet; -import org.graalvm.collections.Equivalence; -import org.graalvm.collections.MapCursor; - /** * Represents the host access policy of a polyglot context. The host access policy specifies which * methods and fields are accessible to the guest application, whenever Java host objects are @@ -95,9 +95,10 @@ public final class HostAccess { private final boolean allowAllClassImplementations; final boolean allowArrayAccess; final boolean allowListAccess; + final boolean allowBufferAccess; volatile Object impl; - private static final HostAccess EMPTY = new HostAccess(null, null, null, null, null, null, null, false, false, false, false, false); + private static final HostAccess EMPTY = new HostAccess(null, null, null, null, null, null, null, false, false, false, false, false, false); /** * Predefined host access policy that allows access to public host methods or fields that were @@ -147,7 +148,7 @@ public final class HostAccess { allowPublicAccess(true).// allowAllImplementations(true).// allowAllClassImplementations(true).// - allowArrayAccess(true).allowListAccess(true).// + allowArrayAccess(true).allowListAccess(true).allowBufferAccess(true).// name("HostAccess.ALL").build(); /** @@ -167,7 +168,7 @@ public final class HostAccess { EconomicSet> implementableAnnotations, EconomicSet> implementableTypes, List targetMappings, String name, - boolean allowPublic, boolean allowAllImplementations, boolean allowAllClassImplementations, boolean allowArrayAccess, boolean allowListAccess) { + boolean allowPublic, boolean allowAllImplementations, boolean allowAllClassImplementations, boolean allowArrayAccess, boolean allowListAccess, boolean allowBufferAccess) { // create defensive copies this.accessAnnotations = copySet(annotations, Equivalence.IDENTITY); this.excludeTypes = copyMap(excludeTypes, Equivalence.IDENTITY); @@ -181,6 +182,7 @@ public final class HostAccess { this.allowAllClassImplementations = allowAllClassImplementations; this.allowArrayAccess = allowArrayAccess; this.allowListAccess = allowListAccess; + this.allowBufferAccess = allowBufferAccess; } /** @@ -546,8 +548,9 @@ public final class Builder { private EconomicSet members; private List targetMappings; private boolean allowPublic; - private boolean allowListAccess; private boolean allowArrayAccess; + private boolean allowListAccess; + private boolean allowBufferAccess; private boolean allowAllImplementations; private boolean allowAllClassImplementations; private String name; @@ -762,6 +765,19 @@ public Builder allowListAccess(boolean listAccess) { return this; } + /** + * Allows the guest application to access {@link java.nio.ByteBuffer}s as values with + * {@link Value#hasBufferElements() buffer elements}. By default no buffer access is + * allowed. + * + * @see Value#hasBufferElements() + * @since 21.1 + */ + public Builder allowBufferAccess(boolean bufferAccess) { + this.allowBufferAccess = bufferAccess; + return this; + } + /** * Adds a custom source to target type mapping for Java host calls, host field assignments * and {@link Value#as(Class) explicit value conversions}. Method is equivalent to calling @@ -926,7 +942,7 @@ HostAccess.Builder name(String givenName) { */ public HostAccess build() { return new HostAccess(accessAnnotations, excludeTypes, members, implementationAnnotations, implementableTypes, targetMappings, name, allowPublic, - allowAllImplementations, allowAllClassImplementations, allowArrayAccess, allowListAccess); + allowAllImplementations, allowAllClassImplementations, allowArrayAccess, allowListAccess, allowBufferAccess); } } 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 43b7b355a545..305743f8f6ec 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 @@ -40,8 +40,13 @@ */ package org.graalvm.polyglot; +import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl; +import org.graalvm.polyglot.proxy.Proxy; + import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.ByteOrder; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; @@ -56,10 +61,6 @@ import java.util.Set; import java.util.function.Function; -import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl; -import org.graalvm.polyglot.proxy.Proxy; - /** * Represents a polyglot value that can be accessed using a set of language agnostic operations. * Polyglot values represent values from {@link #isHostObject() host} or guest language. Polyglot @@ -106,6 +107,8 @@ * for executable elements are functions, methods, closures or promises. *
  • {@link #canInstantiate() Instantiable}: This value can be {@link #newInstance(Object...) * instantiated}. For example, Java classes are instantiable. + *
  • {@link #hasBufferElements() Buffer Elements}: This value may contain buffer elements. The + * buffer indices always start with 0, also if the language uses a different style. * *

    * In addition to the language agnostic types, the language specific type can be accessed using @@ -322,6 +325,369 @@ public long getArraySize() { return impl.getArraySize(receiver); } + // region Buffer Methods + + /** + * Returns {@code true} if the receiver may have buffer elements. In this case, the buffer size + * can be queried using {@link #getBufferSize()} and elements can be read using + * {@link #readBufferByte(long)}, {@link #readBufferShort(ByteOrder, long)}, + * {@link #readBufferInt(ByteOrder, long)}, {@link #readBufferLong(ByteOrder, long)}, + * {@link #readBufferFloat(ByteOrder, long)} and {@link #readBufferDouble(ByteOrder, long)}. If + * {@link #isBufferWritable()} returns {@code true}, then buffer elements can also be written + * using {@link #writeBufferByte(long, byte)}, + * {@link #writeBufferShort(ByteOrder, long, short)}, + * {@link #writeBufferInt(ByteOrder, long, int)}, + * {@link #writeBufferLong(ByteOrder, long, long)}, + * {@link #writeBufferFloat(ByteOrder, long, float)} and + * {@link #writeBufferDouble(ByteOrder, long, double)}. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @see #hasBufferElements() + * @since 21.1 + */ + public boolean hasBufferElements() { + return impl.hasBufferElements(receiver); + } + + /** + * Returns true if the receiver object is a modifiable buffer. In this case, elements can be + * written using {@link #writeBufferByte(long, byte)}, + * {@link #writeBufferShort(ByteOrder, long, short)}, + * {@link #writeBufferInt(ByteOrder, long, int)}, + * {@link #writeBufferLong(ByteOrder, long, long)}, + * {@link #writeBufferFloat(ByteOrder, long, float)} and + * {@link #writeBufferDouble(ByteOrder, long, double)}. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements}. + * @since 21.1 + */ + public boolean isBufferWritable() throws UnsupportedOperationException { + return impl.isBufferWritable(receiver); + } + + /** + * Returns the buffer size in bytes for values with buffer elements. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements}. + * @since 21.1 + */ + public long getBufferSize() throws UnsupportedOperationException { + return impl.getBufferSize(receiver); + } + + /** + * Reads the byte at the given byte offset from the start of the buffer. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @param byteOffset the offset, in bytes, from the start of the buffer at which the byte will + * be read. + * @return the byte at the given byte offset from the start of the buffer. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()}. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public byte readBufferByte(long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return impl.readBufferByte(receiver, byteOffset); + } + + /** + * Writes the given byte at the given byte offset from the start of the buffer. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + * + * @param byteOffset the offset, in bytes, from the start of the buffer at which the byte will + * be written. + * @param value the byte value to be written. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()}. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements} or is not {@link #isBufferWritable() modifiable}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public void writeBufferByte(long byteOffset, byte value) throws UnsupportedOperationException, IndexOutOfBoundsException { + impl.writeBufferByte(receiver, byteOffset, value); + } + + /** + * Reads the short at the given byte offset from the start of the buffer in the given byte + * order. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @param order the order in which to read the individual bytes of the short. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the short + * will be read. + * @return the short at the given byte offset from the start of the buffer. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 1. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public short readBufferShort(ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return impl.readBufferShort(receiver, order, byteOffset); + } + + /** + * Writes the given short in the given byte order at the given byte offset from the start of the + * buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + * + * @param order the order in which to write the individual bytes of the short. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the short + * will be written. + * @param value the short value to be written. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 1. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements} or is not {@link #isBufferWritable() modifiable}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public void writeBufferShort(ByteOrder order, long byteOffset, short value) throws UnsupportedOperationException, IndexOutOfBoundsException { + impl.writeBufferShort(receiver, order, byteOffset, value); + } + + /** + * Reads the int at the given byte offset from the start of the buffer in the given byte order. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @param order the order in which to read the individual bytes of the int. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the int will + * be read. + * @return the int at the given byte offset from the start of the buffer. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 3. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public int readBufferInt(ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return impl.readBufferInt(receiver, order, byteOffset); + } + + /** + * Writes the given int in the given byte order at the given byte offset from the start of the + * buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + * + * @param order the order in which to write the individual bytes of the int. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the int will + * be written. + * @param value the int value to be written. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 3. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements} or is not {@link #isBufferWritable() modifiable}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public void writeBufferInt(ByteOrder order, long byteOffset, int value) throws UnsupportedOperationException, IndexOutOfBoundsException { + impl.writeBufferInt(receiver, order, byteOffset, value); + } + + /** + * Reads the long at the given byte offset from the start of the buffer in the given byte order. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @param order the order in which to read the individual bytes of the long. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the int will + * be read. + * @return the int at the given byte offset from the start of the buffer. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 7. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public long readBufferLong(ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return impl.readBufferLong(receiver, order, byteOffset); + } + + /** + * Writes the given long in the given byte order at the given byte offset from the start of the + * buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + * + * @param order the order in which to write the individual bytes of the long. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the int will + * be written. + * @param value the int value to be written. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 7. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements} or is not {@link #isBufferWritable() modifiable}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public void writeBufferLong(ByteOrder order, long byteOffset, long value) throws UnsupportedOperationException, IndexOutOfBoundsException { + impl.writeBufferLong(receiver, order, byteOffset, value); + } + + /** + * Reads the float at the given byte offset from the start of the buffer in the given byte + * order. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @param order the order in which to read the individual bytes of the float. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the float + * will be read. + * @return the float at the given byte offset from the start of the buffer. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 3. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public float readBufferFloat(ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return impl.readBufferFloat(receiver, order, byteOffset); + } + + /** + * Writes the given float in the given byte order at the given byte offset from the start of the + * buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + * + * @param order the order in which to read the individual bytes of the float. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the float + * will be written. + * @param value the float value to be written. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 3. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements} or is not {@link #isBufferWritable() modifiable}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public void writeBufferFloat(ByteOrder order, long byteOffset, float value) throws UnsupportedOperationException, IndexOutOfBoundsException { + impl.writeBufferFloat(receiver, order, byteOffset, value); + } + + /** + * Reads the double at the given byte offset from the start of the buffer in the given byte + * order. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + *

    + * Invoking this method does not cause any observable side-effects. + * + * @param order the order in which to write the individual bytes of the double. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the double + * will be read. + * @return the double at the given byte offset from the start of the buffer. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 7. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public double readBufferDouble(ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return impl.readBufferDouble(receiver, order, byteOffset); + } + + /** + * Writes the given double in the given byte order at the given byte offset from the start of + * the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this method is not + * thread-safe. + * + * @param order the order in which to write the individual bytes of the double. + * @param byteOffset the offset, in bytes, from the start of the buffer from which the double + * will be written. + * @param value the double value to be written. + * @throws IndexOutOfBoundsException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize()} - 7. + * @throws UnsupportedOperationException if the value does not have {@link #hasBufferElements + * buffer elements} or is not {@link #isBufferWritable() modifiable}. + * @throws IllegalStateException if the context is already closed. + * @throws PolyglotException if a guest language error occurred during execution. + * @since 21.1 + */ + public void writeBufferDouble(ByteOrder order, long byteOffset, double value) throws UnsupportedOperationException, IndexOutOfBoundsException { + impl.writeBufferDouble(receiver, order, byteOffset, value); + } + + // endregion + /** * Returns true if this value generally supports containing members. To check * whether a value has no members use 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 590696809b02..3f6ee5cc85fb 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 @@ -40,6 +40,30 @@ */ package org.graalvm.polyglot.impl; +import org.graalvm.collections.UnmodifiableEconomicSet; +import org.graalvm.options.OptionDescriptors; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Engine; +import org.graalvm.polyglot.EnvironmentAccess; +import org.graalvm.polyglot.HostAccess; +import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence; +import org.graalvm.polyglot.Instrument; +import org.graalvm.polyglot.Language; +import org.graalvm.polyglot.PolyglotAccess; +import org.graalvm.polyglot.PolyglotException; +import org.graalvm.polyglot.PolyglotException.StackFrame; +import org.graalvm.polyglot.ResourceLimitEvent; +import org.graalvm.polyglot.ResourceLimits; +import org.graalvm.polyglot.Source; +import org.graalvm.polyglot.SourceSection; +import org.graalvm.polyglot.TypeLiteral; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.io.ByteSequence; +import org.graalvm.polyglot.io.FileSystem; +import org.graalvm.polyglot.io.MessageTransport; +import org.graalvm.polyglot.io.ProcessHandler; +import org.graalvm.polyglot.management.ExecutionEvent; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -50,6 +74,7 @@ import java.lang.reflect.AnnotatedElement; import java.net.URI; import java.net.URL; +import java.nio.ByteOrder; import java.nio.charset.Charset; import java.time.Duration; import java.time.Instant; @@ -66,30 +91,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.graalvm.collections.UnmodifiableEconomicSet; -import org.graalvm.options.OptionDescriptors; -import org.graalvm.polyglot.Context; -import org.graalvm.polyglot.Engine; -import org.graalvm.polyglot.EnvironmentAccess; -import org.graalvm.polyglot.HostAccess; -import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence; -import org.graalvm.polyglot.Instrument; -import org.graalvm.polyglot.Language; -import org.graalvm.polyglot.PolyglotAccess; -import org.graalvm.polyglot.PolyglotException; -import org.graalvm.polyglot.PolyglotException.StackFrame; -import org.graalvm.polyglot.ResourceLimitEvent; -import org.graalvm.polyglot.ResourceLimits; -import org.graalvm.polyglot.Source; -import org.graalvm.polyglot.SourceSection; -import org.graalvm.polyglot.TypeLiteral; -import org.graalvm.polyglot.Value; -import org.graalvm.polyglot.io.ByteSequence; -import org.graalvm.polyglot.io.FileSystem; -import org.graalvm.polyglot.io.MessageTransport; -import org.graalvm.polyglot.io.ProcessHandler; -import org.graalvm.polyglot.management.ExecutionEvent; - @SuppressWarnings("unused") public abstract class AbstractPolyglotImpl { @@ -179,6 +180,8 @@ protected APIAccess() { public abstract boolean isListAccessible(HostAccess access); + public abstract boolean isBufferAccessible(HostAccess access); + public abstract Object getHostAccessImpl(HostAccess conf); public abstract void setHostAccessImpl(HostAccess conf, Object impl); @@ -583,6 +586,42 @@ public boolean hasArrayElements(Object receiver) { public abstract long getArraySize(Object receiver); + // region Buffer Methods + + public boolean hasBufferElements(Object receiver) { + return false; + } + + public abstract boolean isBufferWritable(Object receiver); + + public abstract long getBufferSize(Object receiver); + + public abstract byte readBufferByte(Object receiver, long byteOffset); + + public abstract void writeBufferByte(Object receiver, long byteOffset, byte value); + + public abstract short readBufferShort(Object receiver, ByteOrder order, long byteOffset); + + public abstract void writeBufferShort(Object receiver, ByteOrder order, long byteOffset, short value); + + public abstract int readBufferInt(Object receiver, ByteOrder order, long byteOffset); + + public abstract void writeBufferInt(Object receiver, ByteOrder order, long byteOffset, int value); + + public abstract long readBufferLong(Object receiver, ByteOrder order, long byteOffset); + + public abstract void writeBufferLong(Object receiver, ByteOrder order, long byteOffset, long value); + + public abstract float readBufferFloat(Object receiver, ByteOrder order, long byteOffset); + + public abstract void writeBufferFloat(Object receiver, ByteOrder order, long byteOffset, float value); + + public abstract double readBufferDouble(Object receiver, ByteOrder order, long byteOffset); + + public abstract void writeBufferDouble(Object receiver, ByteOrder order, long byteOffset, double value); + + // endregion + public boolean hasMembers(Object receiver) { return false; } diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java index fce5bde6d0cc..9d2c53963fa4 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java @@ -24,6 +24,29 @@ */ package com.oracle.svm.driver; +import com.oracle.graal.pointsto.api.PointstoOptions; +import com.oracle.svm.core.FallbackExecutor; +import com.oracle.svm.core.FallbackExecutor.Options; +import com.oracle.svm.core.OS; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.configure.ConfigurationFiles; +import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.util.ClasspathUtils; +import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.driver.MacroOption.EnabledOption; +import com.oracle.svm.driver.MacroOption.MacroOptionKind; +import com.oracle.svm.driver.MacroOption.Registry; +import com.oracle.svm.hosted.AbstractNativeImageClassLoaderSupport; +import com.oracle.svm.hosted.NativeImageGeneratorRunner; +import com.oracle.svm.hosted.NativeImageSystemClassLoader; +import com.oracle.svm.util.ModuleSupport; +import org.graalvm.compiler.options.OptionKey; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.ProcessProperties; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -70,30 +93,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.graalvm.compiler.options.OptionKey; -import org.graalvm.compiler.serviceprovider.JavaVersionUtil; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.ProcessProperties; - -import com.oracle.graal.pointsto.api.PointstoOptions; -import com.oracle.svm.core.FallbackExecutor; -import com.oracle.svm.core.FallbackExecutor.Options; -import com.oracle.svm.core.OS; -import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.configure.ConfigurationFiles; -import com.oracle.svm.core.option.SubstrateOptionsParser; -import com.oracle.svm.core.util.ClasspathUtils; -import com.oracle.svm.core.util.UserError; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.driver.MacroOption.EnabledOption; -import com.oracle.svm.driver.MacroOption.MacroOptionKind; -import com.oracle.svm.driver.MacroOption.Registry; -import com.oracle.svm.hosted.AbstractNativeImageClassLoaderSupport; -import com.oracle.svm.hosted.NativeImageGeneratorRunner; -import com.oracle.svm.hosted.NativeImageSystemClassLoader; -import com.oracle.svm.util.ModuleSupport; - public class NativeImage { private static final String DEFAULT_GENERATOR_CLASS_NAME = "com.oracle.svm.hosted.NativeImageGeneratorRunner"; @@ -201,7 +200,7 @@ private static String oR(OptionKey option) { private static final String pKeyNativeImageArgs = "NativeImageArgs"; - private final LinkedHashSet imageBuilderArgs = new LinkedHashSet<>(); + private final ArrayList imageBuilderArgs = new ArrayList<>(); private final LinkedHashSet imageBuilderClasspath = new LinkedHashSet<>(); private final LinkedHashSet imageBuilderBootClasspath = new LinkedHashSet<>(); private final LinkedHashSet imageIncludeBuiltinModules = new LinkedHashSet<>(); @@ -209,7 +208,7 @@ private static String oR(OptionKey option) { private final LinkedHashSet imageClasspath = new LinkedHashSet<>(); private final LinkedHashSet imageProvidedClasspath = new LinkedHashSet<>(); private final ArrayList customJavaArgs = new ArrayList<>(); - private final LinkedHashSet customImageBuilderArgs = new LinkedHashSet<>(); + private final ArrayList customImageBuilderArgs = new ArrayList<>(); private final LinkedHashSet customImageClasspath = new LinkedHashSet<>(); private final ArrayList> optionHandlers = new ArrayList<>(); @@ -1297,7 +1296,7 @@ private void addTargetArguments() { private String imageName; private Path imagePath; - protected static List createImageBuilderArgs(LinkedHashSet imageArgs, LinkedHashSet imagecp) { + protected static List createImageBuilderArgs(ArrayList imageArgs, LinkedHashSet imagecp) { List result = new ArrayList<>(); result.add(SubstrateOptions.IMAGE_CLASSPATH_PREFIX); result.add(imagecp.stream().map(ClasspathUtils::classpathToString).collect(Collectors.joining(File.pathSeparator))); @@ -1326,7 +1325,7 @@ public void run() { } } - protected int buildImage(List javaArgs, LinkedHashSet bcp, LinkedHashSet cp, LinkedHashSet imageArgs, LinkedHashSet imagecp) { + protected int buildImage(List javaArgs, LinkedHashSet bcp, LinkedHashSet cp, ArrayList imageArgs, LinkedHashSet imagecp) { /* Construct ProcessBuilder command from final arguments */ List command = new ArrayList<>(); command.add(canonicalize(config.getJavaExecutable()).toString()); @@ -1513,7 +1512,6 @@ List apply(boolean strict) { void addPlainImageBuilderArg(String plainArg) { assert plainArg.startsWith(NativeImage.oH) || plainArg.startsWith(NativeImage.oR); - imageBuilderArgs.remove(plainArg); imageBuilderArgs.add(plainArg); } diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImageServer.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImageServer.java index 9b39b4d3ecfc..3b1b913ff5b8 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImageServer.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImageServer.java @@ -24,6 +24,15 @@ */ package com.oracle.svm.driver; +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.util.ClasspathUtils; +import com.oracle.svm.hosted.server.NativeImageBuildClient; +import com.oracle.svm.hosted.server.NativeImageBuildServer; +import com.oracle.svm.hosted.server.SubstrateServerMessage.ServerCommand; +import org.graalvm.nativeimage.ProcessProperties; +import org.graalvm.word.WordFactory; + import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; @@ -56,16 +65,6 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import org.graalvm.nativeimage.ProcessProperties; -import org.graalvm.word.WordFactory; - -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.option.SubstrateOptionsParser; -import com.oracle.svm.core.util.ClasspathUtils; -import com.oracle.svm.hosted.server.NativeImageBuildClient; -import com.oracle.svm.hosted.server.NativeImageBuildServer; -import com.oracle.svm.hosted.server.SubstrateServerMessage.ServerCommand; - final class NativeImageServer extends NativeImage { private static final String serverDirPrefix = "server-id-"; @@ -171,7 +170,7 @@ private int sendRequest(Consumer out, Consumer err, ServerComman return exitCode; } - int sendBuildRequest(LinkedHashSet imageCP, LinkedHashSet imageArgs) { + int sendBuildRequest(LinkedHashSet imageCP, ArrayList imageArgs) { int[] requestStatus = {1}; withLockDirFileChannel(serverDir, lockFileChannel -> { boolean abortedOnce = false; @@ -738,7 +737,7 @@ private void killServer() { } @Override - protected int buildImage(List javaArgs, LinkedHashSet bcp, LinkedHashSet cp, LinkedHashSet imageArgs, LinkedHashSet imagecp) { + protected int buildImage(List javaArgs, LinkedHashSet bcp, LinkedHashSet cp, ArrayList imageArgs, LinkedHashSet imagecp) { boolean printFlags = imageArgs.stream().anyMatch(arg -> arg.contains(enablePrintFlags) || arg.contains(enablePrintFlagsWithExtraHelp)); if (useServer && !printFlags && !useDebugAttach()) { AbortBuildSignalHandler signalHandler = new AbortBuildSignalHandler(); diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 25321a2e7592..87ae5c458f7c 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -12,6 +12,12 @@ This changelog summarizes major changes between Truffle versions relevant to lan * Added `@GenerateAOT` to support preparation for AOT specializing nodes. Read the (AOT tutorial)[https://github.com/oracle/graal/blob/master/truffle/docs/AOT.md] to get started with Truffle and AOT compilation. * Profiles now can be disabled using `Profile.disable()` and reset using `Profile.reset()`. * Added `--engine.CompileAOTOnCreate` option to trigger AOT compilation on call target create. +* Added new messages to `InteropLibrary` for interacting with buffer-like objects: + * Added `hasBufferElements(Object)` that returns `true` if this object supports buffer messages. + * Added `isBufferWritable(Object)` that returns `true` if this object supports writing buffer elements. + * Added `getBufferSize(Object)` to return the size of this buffer. + * Added `readBufferByte(Object, long)`, `readBufferShort(Object, ByteOrder, long)`, `readBufferInt(Object, ByteOrder, long)`, `readBufferLong(Object, ByteOrder, long)`, `readBufferFloat(Object, ByteOrder, long)` and `readBufferDouble(Object, ByteOrder, long)` to read a primitive from this buffer at the given index. + * Added `writeBufferByte(Object, long, byte)`, `writeBufferShort(Object, ByteOrder, long, short)`, `writeBufferInt(Object, ByteOrder, long, int)`, `writeBufferLong(Object, ByteOrder, long, long)`, `writeBufferFloat(Object, ByteOrder, long, float)` and `writeBufferDouble(Object, ByteOrder, long, double)` to write a primitive in this buffer at the given index (supported only if `isBufferWritable(Object)` returns `true`). ## Version 21.0.0 * If an `AbstractTruffleException` is thrown from the `ContextLocalFactory`, `ContextThreadLocalFactory` or event listener, which is called during the context enter, the excepion interop messages are executed without a context being entered. The event listeners called during the context enter are: diff --git a/truffle/mx.truffle/macro-truffle.properties b/truffle/mx.truffle/macro-truffle.properties index 81bdd7598f34..0c4eae8e808d 100644 --- a/truffle/mx.truffle/macro-truffle.properties +++ b/truffle/mx.truffle/macro-truffle.properties @@ -3,7 +3,7 @@ ImageBuilderBootClasspath8 = ${.}/../../../truffle/truffle-api.jar ImageIncludeBuiltinModules = org.graalvm.truffle Args = -H:Features=com.oracle.svm.truffle.TruffleFeature,org.graalvm.home.HomeFinderFeature \ - -H:MaxRuntimeCompileMethods=1700 \ + -H:MaxRuntimeCompileMethods=2000 \ --initialize-at-build-time=org.graalvm.launcher,com.oracle.truffle JavaArgs = -Dtruffle.TruffleRuntime=com.oracle.svm.truffle.api.SubstrateTruffleRuntime \ -Dgraalvm.ForcePolyglotInvalid=false diff --git a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest index 9d2d3759873e..bb512c5d96d9 100644 --- a/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest +++ b/truffle/src/com.oracle.truffle.api.interop/snapshot.sigtest @@ -43,6 +43,7 @@ meth public boolean fitsInInt(java.lang.Object) meth public boolean fitsInLong(java.lang.Object) meth public boolean fitsInShort(java.lang.Object) meth public boolean hasArrayElements(java.lang.Object) +meth public boolean hasBufferElements(java.lang.Object) meth public boolean hasDeclaringMetaObject(java.lang.Object) meth public boolean hasExceptionCause(java.lang.Object) meth public boolean hasExceptionMessage(java.lang.Object) @@ -60,6 +61,7 @@ meth public boolean isArrayElementModifiable(java.lang.Object,long) meth public boolean isArrayElementReadable(java.lang.Object,long) meth public boolean isArrayElementRemovable(java.lang.Object,long) meth public boolean isBoolean(java.lang.Object) +meth public boolean isBufferWritable(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public boolean isDate(java.lang.Object) meth public boolean isDuration(java.lang.Object) meth public boolean isException(java.lang.Object) @@ -83,9 +85,11 @@ meth public boolean isString(java.lang.Object) meth public boolean isTime(java.lang.Object) meth public boolean isTimeZone(java.lang.Object) meth public byte asByte(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public byte readBufferByte(java.lang.Object,long) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public com.oracle.truffle.api.interop.ExceptionType getExceptionType(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public com.oracle.truffle.api.source.SourceSection getSourceLocation(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public double asDouble(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public double readBufferDouble(java.lang.Object,java.nio.ByteOrder,long) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public final boolean hasIdentity(java.lang.Object) meth public final boolean isArrayElementExisting(java.lang.Object,long) meth public final boolean isArrayElementWritable(java.lang.Object,long) @@ -95,9 +99,11 @@ meth public final boolean isMemberWritable(java.lang.Object,java.lang.String) meth public final java.lang.Object getMembers(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public final java.lang.Object toDisplayString(java.lang.Object) meth public float asFloat(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public float readBufferFloat(java.lang.Object,java.nio.ByteOrder,long) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public int asInt(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public int getExceptionExitStatus(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public int identityHashCode(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +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 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 @@ -122,7 +128,10 @@ meth public java.time.ZoneId asTimeZone(java.lang.Object) throws com.oracle.truf meth public long asLong(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public long asPointer(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException meth public long getArraySize(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public long getBufferSize(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public long readBufferLong(java.lang.Object,java.nio.ByteOrder,long) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public short asShort(java.lang.Object) throws com.oracle.truffle.api.interop.UnsupportedMessageException +meth public short readBufferShort(java.lang.Object,java.nio.ByteOrder,long) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public static com.oracle.truffle.api.interop.InteropLibrary getUncached() meth public static com.oracle.truffle.api.interop.InteropLibrary getUncached(java.lang.Object) meth public static com.oracle.truffle.api.library.LibraryFactory getFactory() @@ -130,6 +139,12 @@ meth public void removeArrayElement(java.lang.Object,long) throws com.oracle.tru meth public void removeMember(java.lang.Object,java.lang.String) throws com.oracle.truffle.api.interop.UnknownIdentifierException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public void toNative(java.lang.Object) meth public void writeArrayElement(java.lang.Object,long,java.lang.Object) throws com.oracle.truffle.api.interop.InvalidArrayIndexException,com.oracle.truffle.api.interop.UnsupportedMessageException,com.oracle.truffle.api.interop.UnsupportedTypeException +meth public void writeBufferByte(java.lang.Object,long,byte) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException +meth public void writeBufferDouble(java.lang.Object,java.nio.ByteOrder,long,double) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException +meth public void writeBufferFloat(java.lang.Object,java.nio.ByteOrder,long,float) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException +meth public void writeBufferInt(java.lang.Object,java.nio.ByteOrder,long,int) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException +meth public void writeBufferLong(java.lang.Object,java.nio.ByteOrder,long,long) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException +meth public void writeBufferShort(java.lang.Object,java.nio.ByteOrder,long,short) throws com.oracle.truffle.api.interop.InvalidBufferOffsetException,com.oracle.truffle.api.interop.UnsupportedMessageException meth public void writeMember(java.lang.Object,java.lang.String,java.lang.Object) throws com.oracle.truffle.api.interop.UnknownIdentifierException,com.oracle.truffle.api.interop.UnsupportedMessageException,com.oracle.truffle.api.interop.UnsupportedTypeException supr com.oracle.truffle.api.library.Library hfds FACTORY,UNCACHED @@ -143,6 +158,15 @@ meth public static com.oracle.truffle.api.interop.InvalidArrayIndexException cre supr com.oracle.truffle.api.interop.InteropException hfds invalidIndex,serialVersionUID +CLSS public final com.oracle.truffle.api.interop.InvalidBufferOffsetException +meth public java.lang.String getMessage() +meth public long getByteOffset() +meth public long getLength() +meth public static com.oracle.truffle.api.interop.InvalidBufferOffsetException create(long,long) +meth public static com.oracle.truffle.api.interop.InvalidBufferOffsetException create(long,long,java.lang.Throwable) +supr com.oracle.truffle.api.interop.InteropException +hfds index,length,serialVersionUID + CLSS public abstract com.oracle.truffle.api.interop.NodeLibrary cons protected init() meth public boolean hasReceiverMember(java.lang.Object,com.oracle.truffle.api.frame.Frame) 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 d6f654ff7b6c..cfed0fd542ee 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 @@ -40,31 +40,12 @@ */ package com.oracle.truffle.api.interop; -import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; -import static com.oracle.truffle.api.interop.AssertUtils.assertString; -import static com.oracle.truffle.api.interop.AssertUtils.preCondition; -import static com.oracle.truffle.api.interop.AssertUtils.validArgument; -import static com.oracle.truffle.api.interop.AssertUtils.validArguments; -import static com.oracle.truffle.api.interop.AssertUtils.validNonInteropArgument; -import static com.oracle.truffle.api.interop.AssertUtils.validReturn; -import static com.oracle.truffle.api.interop.AssertUtils.validScope; -import static com.oracle.truffle.api.interop.AssertUtils.violationInvariant; -import static com.oracle.truffle.api.interop.AssertUtils.violationPost; - -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.zone.ZoneRules; - import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.TruffleLanguage.Registration; +import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.impl.Accessor.EngineSupport; @@ -83,6 +64,26 @@ import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.utilities.TriState; +import java.nio.ByteOrder; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.zone.ZoneRules; + +import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; +import static com.oracle.truffle.api.interop.AssertUtils.assertString; +import static com.oracle.truffle.api.interop.AssertUtils.preCondition; +import static com.oracle.truffle.api.interop.AssertUtils.validArgument; +import static com.oracle.truffle.api.interop.AssertUtils.validArguments; +import static com.oracle.truffle.api.interop.AssertUtils.validNonInteropArgument; +import static com.oracle.truffle.api.interop.AssertUtils.validReturn; +import static com.oracle.truffle.api.interop.AssertUtils.validScope; +import static com.oracle.truffle.api.interop.AssertUtils.violationInvariant; +import static com.oracle.truffle.api.interop.AssertUtils.violationPost; + /** * Represents the library that specifies the interoperability message protocol between Truffle * languages, tools and embedders. Every method represents one message specified by the protocol. In @@ -133,6 +134,7 @@ *

  • {@link #isPointer(Object) pointer} *
  • {@link #hasMembers(Object) members} *
  • {@link #hasArrayElements(Object) array elements} + *
  • {@link #hasBufferElements(Object) buffer elements} *
  • {@link #hasLanguage(Object) language} *
  • {@link #hasMetaObject(Object) associated metaobject} *
  • {@link #hasDeclaringMetaObject(Object) declaring meta object} @@ -143,8 +145,7 @@ *
  • {@link #hasExceptionMessage(Object) exception message} *
  • {@link #hasExceptionCause(Object) exception cause} *
  • {@link #hasExceptionStackTrace(Object) exception stack trace} - *
      - *

      + *

    *

    Naive and aware dates and times

    *

    * If a date or time value has a {@link #isTimeZone(Object) timezone} then it is called aware @@ -623,8 +624,8 @@ public Object getMembers(Object receiver, boolean includeInternal) throws Unsupp } /** - * Short-cut for {@link #getMembers(Object) getMembers(receiver, false)}. Invoking this message - * does not cause any observable side-effects. + * Short-cut for {@link #getMembers(Object, boolean) getMembers(receiver, false)}. Invoking this + * message does not cause any observable side-effects. * * @throws UnsupportedMessageException if and only if the receiver has no * {@link #hasMembers(Object) members}. @@ -707,7 +708,7 @@ public boolean isMemberInsertable(Object receiver, String member) { * {@link #isMemberModifiable(Object, String) modifiable}, or not existing and * {@link #isMemberInsertable(Object, String) insertable}. * - * This method must have not observable side-effects other than the changed member unless + * This method must have no observable side-effects other than the changed member unless * {@link #hasMemberWriteSideEffects(Object, String) side-effects} are allowed. * * @throws UnsupportedMessageException when the receiver does not support writing at all, e.g. @@ -1034,6 +1035,331 @@ public final boolean isArrayElementExisting(Object receiver, long index) { return isArrayElementModifiable(receiver, index) || isArrayElementReadable(receiver, index) || isArrayElementRemovable(receiver, index); } + // region Buffer Messages + + /** + * Returns {@code true} if the receiver may have buffer elements. + *

    + * If this message returns {@code true}, then {@link #getBufferSize(Object)}, + * {@link #readBufferByte(Object, long)}, {@link #readBufferShort(Object, ByteOrder, long)}, + * {@link #readBufferInt(Object, ByteOrder, long)}, + * {@link #readBufferLong(Object, ByteOrder, long)}, + * {@link #readBufferFloat(Object, ByteOrder, long)} and + * {@link #readBufferDouble(Object, ByteOrder, long)} must not throw + * {@link UnsupportedMessageException}. + *

    + * Invoking this message does not cause any observable side-effects. + *

    + * By default, it returns {@code false}. + * + * @since 21.1 + */ + @Abstract(ifExported = {"getBufferSize", "isBufferWritable", "readBufferByte", "readBufferShort", "readBufferInt", "readBufferLong", "readBufferFloat", "readBufferDouble", "writeBufferByte", + "writeBufferShort", "writeBufferInt", "writeBufferLong", "writeBufferFloat", "writeBufferDouble"}) + public boolean hasBufferElements(Object receiver) { + return false; + } + + /** + * Returns {@code true} if the receiver is a modifiable buffer. + *

    + * If this message returns {@code true}, then {@link #getBufferSize(Object)}, + * {@link #writeBufferByte(Object, long, byte)}, + * {@link #writeBufferShort(Object, ByteOrder, long, short)}, + * {@link #writeBufferInt(Object, ByteOrder, long, int)}, + * {@link #writeBufferLong(Object, ByteOrder, long, long)}, + * {@link #writeBufferFloat(Object, ByteOrder, long, float)} and + * {@link #writeBufferDouble(Object, ByteOrder, long, double)} must not throw + * {@link UnsupportedMessageException}. + *

    + * Invoking this message does not cause any observable side-effects. + *

    + * By default, it returns {@code false} if {@link #hasBufferElements(Object)} return + * {@code true}, and throws {@link UnsupportedMessageException} otherwise. + * + * @throws UnsupportedMessageException if and only if {@link #hasBufferElements(Object)} returns + * {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"writeBufferByte", "writeBufferShort", "writeBufferInt", "writeBufferLong", "writeBufferFloat", "writeBufferDouble"}) + public boolean isBufferWritable(Object receiver) throws UnsupportedMessageException { + if (hasBufferElements(receiver)) { + return false; + } else { + throw UnsupportedMessageException.create(); + } + } + + /** + * Returns the buffer size of the receiver, in bytes. + *

    + * Invoking this message does not cause any observable side-effects. + * + * @throws UnsupportedMessageException if and only if {@link #hasBufferElements(Object)} returns + * {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"hasBufferElements"}) + public long getBufferSize(Object receiver) throws UnsupportedMessageException { + throw UnsupportedMessageException.create(); + } + + /** + * Reads the byte from the receiver object at the given byte offset from the start of the + * buffer. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + *

    + * Invoking this message does not cause any observable side-effects. + * + * @return the byte at the given index + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} + * @throws UnsupportedMessageException if and only if either {@link #hasBufferElements(Object)} + * returns {@code false} returns {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"hasBufferElements"}) + public byte readBufferByte(Object receiver, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Writes the given byte from the receiver object at the given byte offset from the start of the + * buffer. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + * + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} + * @throws UnsupportedMessageException if and only if either {@link #hasBufferElements(Object)} + * or {@link #isBufferWritable} returns {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"isBufferWritable"}) + public void writeBufferByte(Object receiver, long byteOffset, byte value) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Reads the short from the receiver object in the given byte order at the given byte offset + * from the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + *

    + * Invoking this message does not cause any observable side-effects. + * + * @return the short at the given byte offset from the start of the buffer + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 1 + * @throws UnsupportedMessageException if and only if {@link #hasBufferElements(Object)} returns + * {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"hasBufferElements"}) + public short readBufferShort(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Writes the given short from the receiver object in the given byte order at the given byte + * offset from the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + * + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 1 + * @throws UnsupportedMessageException if and only if either {@link #hasBufferElements(Object)} + * or {@link #isBufferWritable} returns {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"isBufferWritable"}) + public void writeBufferShort(Object receiver, ByteOrder order, long byteOffset, short value) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Reads the int from the receiver object in the given byte order at the given byte offset from + * the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + *

    + * Invoking this message does not cause any observable side-effects. + * + * @return the int at the given byte offset from the start of the buffer + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 3 + * @throws UnsupportedMessageException if and only if {@link #hasBufferElements(Object)} returns + * {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"hasBufferElements"}) + public int readBufferInt(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Writes the given int from the receiver object in the given byte order at the given byte + * offset from the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + * + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 3 + * @throws UnsupportedMessageException if and only if either {@link #hasBufferElements(Object)} + * or {@link #isBufferWritable} returns {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"isBufferWritable"}) + public void writeBufferInt(Object receiver, ByteOrder order, long byteOffset, int value) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Reads the long from the receiver object in the given byte order at the given byte offset from + * the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + *

    + * Invoking this message does not cause any observable side-effects. + * + * @return the int at the given byte offset from the start of the buffer + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 7 + * @throws UnsupportedMessageException if and only if {@link #hasBufferElements(Object)} returns + * {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"hasBufferElements"}) + public long readBufferLong(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Writes the given long from the receiver object in the given byte order at the given byte + * offset from the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + * + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 7 + * @throws UnsupportedMessageException if and only if either {@link #hasBufferElements(Object)} + * or {@link #isBufferWritable} returns {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"isBufferWritable"}) + public void writeBufferLong(Object receiver, ByteOrder order, long byteOffset, long value) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Reads the float from the receiver object in the given byte order at the given byte offset + * from the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + *

    + * Invoking this message does not cause any observable side-effects. + * + * @return the float at the given byte offset from the start of the buffer + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 3 + * @throws UnsupportedMessageException if and only if {@link #hasBufferElements(Object)} returns + * {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"hasBufferElements"}) + public float readBufferFloat(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Writes the given float from the receiver object in the given byte order at the given byte + * offset from the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + * + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 3 + * @throws UnsupportedMessageException if and only if either {@link #hasBufferElements(Object)} + * or {@link #isBufferWritable} returns {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"isBufferWritable"}) + public void writeBufferFloat(Object receiver, ByteOrder order, long byteOffset, float value) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Reads the double from the receiver object in the given byte order at the given byte offset + * from the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + *

    + * Invoking this message does not cause any observable side-effects. + * + * @return the double at the given byte offset from the start of the buffer + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 7 + * @throws UnsupportedMessageException if and only if {@link #hasBufferElements(Object)} returns + * {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"hasBufferElements"}) + public double readBufferDouble(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + /** + * Writes the given double from the receiver object in the given byte order at the given byte + * offset from the start of the buffer. + *

    + * Unaligned accesses are supported. + *

    + * The access is not guaranteed to be atomic. Therefore, this message is not + * thread-safe. + * + * @throws InvalidBufferOffsetException if and only if + * byteOffset < 0 || byteOffset >= {@link #getBufferSize(Object)} - 7 + * @throws UnsupportedMessageException if and only if either {@link #hasBufferElements(Object)} + * or {@link #isBufferWritable} returns {@code false} + * @since 21.1 + */ + @Abstract(ifExported = {"isBufferWritable"}) + public void writeBufferDouble(Object receiver, ByteOrder order, long byteOffset, double value) throws UnsupportedMessageException, InvalidBufferOffsetException { + throw UnsupportedMessageException.create(); + } + + // endregion + /** * Returns true if the receiver value represents a native pointer. Native pointers * are represented as 64 bit pointers. Invoking this message does not cause any observable @@ -1749,7 +2075,8 @@ private static Env getLegacyEnv(Object receiver, boolean nullForhost) { /** * Converts the receiver to a human readable {@link #isString(Object) string} of the language. - * Short-cut for {@link #toDisplayString(Object) toDisplayString}(true). + * Short-cut for + * {@link #toDisplayString(Object, boolean) toDisplayString(Object, true)}. * * @see #toDisplayString(Object, boolean) * @since 20.1 @@ -2957,6 +3284,234 @@ public boolean isArrayElementRemovable(Object receiver, long identifier) { return result; } + // region Buffer Messages + + @Override + public boolean hasBufferElements(Object receiver) { + assert preCondition(receiver); + return delegate.hasBufferElements(receiver); + } + + @Override + public boolean isBufferWritable(Object receiver) throws UnsupportedMessageException { + assert preCondition(receiver); + try { + final boolean result = delegate.isBufferWritable(receiver); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver); + return result; + } catch (InteropException e) { + assert e instanceof UnsupportedMessageException : violationPost(receiver, e); + throw e; + } + } + + @Override + public long getBufferSize(Object receiver) throws UnsupportedMessageException { + assert preCondition(receiver); + try { + final long result = delegate.getBufferSize(receiver); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver); + return result; + } catch (InteropException e) { + assert e instanceof UnsupportedMessageException : violationPost(receiver, e); + throw e; + } + } + + @Override + public byte readBufferByte(Object receiver, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + final byte result = delegate.readBufferByte(receiver, byteOffset); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + return result; + } catch (UnsupportedMessageException e) { + assert !delegate.hasBufferElements(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public void writeBufferByte(Object receiver, long byteOffset, byte value) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + delegate.writeBufferByte(receiver, byteOffset, value); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + assert delegate.isBufferWritable(receiver) : violationInvariant(receiver, byteOffset); + } catch (UnsupportedMessageException e) { + assert !delegate.isBufferWritable(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public short readBufferShort(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + final short result = delegate.readBufferShort(receiver, order, byteOffset); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + return result; + } catch (UnsupportedMessageException e) { + assert !delegate.hasBufferElements(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public void writeBufferShort(Object receiver, ByteOrder order, long byteOffset, short value) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + delegate.writeBufferShort(receiver, order, byteOffset, value); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + assert delegate.isBufferWritable(receiver) : violationInvariant(receiver, byteOffset); + } catch (UnsupportedMessageException e) { + assert !delegate.isBufferWritable(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public int readBufferInt(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + final int result = delegate.readBufferInt(receiver, order, byteOffset); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + return result; + } catch (UnsupportedMessageException e) { + assert !delegate.hasBufferElements(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public void writeBufferInt(Object receiver, ByteOrder order, long byteOffset, int value) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + delegate.writeBufferInt(receiver, order, byteOffset, value); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + assert delegate.isBufferWritable(receiver) : violationInvariant(receiver, byteOffset); + } catch (UnsupportedMessageException e) { + assert !delegate.isBufferWritable(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public long readBufferLong(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + final long result = delegate.readBufferLong(receiver, order, byteOffset); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + return result; + } catch (UnsupportedMessageException e) { + assert !delegate.hasBufferElements(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public void writeBufferLong(Object receiver, ByteOrder order, long byteOffset, long value) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + delegate.writeBufferLong(receiver, order, byteOffset, value); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + assert delegate.isBufferWritable(receiver) : violationInvariant(receiver, byteOffset); + } catch (UnsupportedMessageException e) { + assert !delegate.isBufferWritable(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public float readBufferFloat(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + final float result = delegate.readBufferFloat(receiver, order, byteOffset); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + return result; + } catch (UnsupportedMessageException e) { + assert !delegate.hasBufferElements(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public void writeBufferFloat(Object receiver, ByteOrder order, long byteOffset, float value) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + delegate.writeBufferFloat(receiver, order, byteOffset, value); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + assert delegate.isBufferWritable(receiver) : violationInvariant(receiver, byteOffset); + } catch (UnsupportedMessageException e) { + assert !delegate.isBufferWritable(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public double readBufferDouble(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + final double result = delegate.readBufferDouble(receiver, order, byteOffset); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + return result; + } catch (UnsupportedMessageException e) { + assert !delegate.hasBufferElements(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + @Override + public void writeBufferDouble(Object receiver, ByteOrder order, long byteOffset, double value) throws UnsupportedMessageException, InvalidBufferOffsetException { + assert preCondition(receiver); + try { + delegate.writeBufferDouble(receiver, order, byteOffset, value); + assert delegate.hasBufferElements(receiver) : violationInvariant(receiver, byteOffset); + assert delegate.isBufferWritable(receiver) : violationInvariant(receiver, byteOffset); + } catch (UnsupportedMessageException e) { + assert !delegate.isBufferWritable(receiver) : violationPost(receiver, e); + throw e; + } catch (InteropException e) { + assert e instanceof InvalidBufferOffsetException : violationPost(receiver, e); + throw e; + } + } + + // endregion + @Override public boolean isPointer(Object receiver) { assert preCondition(receiver); diff --git a/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InvalidBufferOffsetException.java b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InvalidBufferOffsetException.java new file mode 100644 index 000000000000..8d6396b8312b --- /dev/null +++ b/truffle/src/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/InvalidBufferOffsetException.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2020, 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; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; + +/** + * An exception thrown if a buffer access if out of bounds. Interop exceptions are supposed to be + * caught and converted into a guest language error by the caller. + * + * @see #getByteOffset() + * @see #getLength() + * @see InteropLibrary + * @since 21.1 + */ +public final class InvalidBufferOffsetException extends InteropException { + + private static final long serialVersionUID = 1857745390734085837L; + + private final long byteOffset; + private final long length; + + private InvalidBufferOffsetException(long byteOffset, long length) { + super(null); + this.byteOffset = byteOffset; + this.length = length; + } + + private InvalidBufferOffsetException(long byteOffset, long length, Throwable cause) { + super(null, cause); + this.byteOffset = byteOffset; + this.length = length; + } + + /** + * Returns the start byte offset of the invalid access from the start of the buffer. + * + * @since 21.1 + */ + public long getByteOffset() { + return byteOffset; + } + + /** + * Returns the length of the accessed memory region in bytes starting from {@link #getByteOffset + * the start byte offset}. + * + * @since 21.1 + */ + public long getLength() { + return length; + } + + /** + * {@inheritDoc} + * + * @since 21.1 + */ + @Override + @TruffleBoundary + public String getMessage() { + return "Invalid buffer access of length " + length + " at byteOffset " + byteOffset + "."; + } + + /** + * Creates an {@link InvalidBufferOffsetException} to indicate that a buffer access is invalid. + *

    + * This method is designed to be used in {@link CompilerDirectives#inCompiledCode() compiled} + * code paths. + * + * @param byteOffset the start byteOffset of the invalid access + * @param length the length of the accessed memory region in bytes starting from + * {@code byteOffset} + * @since 21.1 + */ + public static InvalidBufferOffsetException create(long byteOffset, long length) { + return new InvalidBufferOffsetException(byteOffset, length); + } + + /** + * Creates an {@link InvalidBufferOffsetException} to indicate that a buffer access is invalid. + *

    + * 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.TruffleException} in addition to {@link Throwable} otherwise an + * {@link IllegalArgumentException} is thrown. + *

    + * This method is designed to be used in {@link CompilerDirectives#inCompiledCode() compiled} + * code paths. + * + * @param byteOffset the start byteOffset of the invalid access + * @param length the length of the accessed memory region in bytes starting from + * {@code byteOffset}. + * @param cause the guest language exception that caused the error. + * @since 21.1 + */ + public static InvalidBufferOffsetException create(long byteOffset, long length, Throwable cause) { + return new InvalidBufferOffsetException(byteOffset, length, cause); + } + +} diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropDefaultsTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropDefaultsTest.java index e6542b1b01a7..da43a829056d 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropDefaultsTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropDefaultsTest.java @@ -42,16 +42,6 @@ import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.interop.ExceptionType; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.concurrent.atomic.AtomicBoolean; - -import org.graalvm.polyglot.Context; -import org.junit.Test; - import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; @@ -60,6 +50,15 @@ import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.test.polyglot.ProxyLanguage; +import org.graalvm.polyglot.Context; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; @SuppressWarnings("deprecation") public class InteropDefaultsTest extends InteropLibraryBaseTest { @@ -83,6 +82,7 @@ private void assertBoolean(Object v, boolean expected) throws UnsupportedMessage assertNotNull(v); assertNoObject(v); assertNoArray(v); + assertNoBuffer(v); assertNoString(v); assertNoNumber(v); assertNoNative(v); @@ -229,6 +229,7 @@ private void assertString(Object v, String expectedString) throws UnsupportedMes assertNotNull(v); assertNoObject(v); assertNoArray(v); + assertNoBuffer(v); // assert string assertNoNumber(v); assertNoNative(v); @@ -297,6 +298,7 @@ private void assertNumber(Object v, boolean supportsByte, boolean supportsShort, assertNotNull(v); assertNoObject(v); assertNoArray(v); + assertNoBuffer(v); assertNoString(v); // assert number assertNoNative(v); @@ -406,6 +408,7 @@ protected String toString(LanguageContext c, Object value) { assertNotNull(v1); assertNoObject(v1); assertNoArray(v1); + assertNoBuffer(v1); assertNoString(v1); assertNoNumber(v1); assertNoNative(v1); @@ -434,6 +437,7 @@ protected String toString(LanguageContext c, Object value) { assertNotNull(v2); assertNoObject(v2); assertNoArray(v2); + assertNoBuffer(v2); assertNoString(v2); assertNoNumber(v2); assertNoNative(v2); @@ -454,6 +458,7 @@ private void assertNoTypes(Object v) { assertNotNull(v); assertNoObject(v); assertNoArray(v); + assertNoBuffer(v); assertNoString(v); assertNoNumber(v); assertNoNative(v); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropLibraryBaseTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropLibraryBaseTest.java index d7fca320b5d8..61a2dbde42d2 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropLibraryBaseTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/interop/InteropLibraryBaseTest.java @@ -40,21 +40,21 @@ */ package com.oracle.truffle.api.test.interop; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Arrays; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.test.AbstractParametrizedLibraryTest; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.Arrays; +import java.util.List; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; @RunWith(Parameterized.class) public abstract class InteropLibraryBaseTest extends AbstractParametrizedLibraryTest { @@ -82,6 +82,25 @@ protected final void assertNoArray(Object value) { assertUnsupported(() -> lib.writeArrayElement(value, 0, "")); } + protected final void assertNoBuffer(Object value) { + InteropLibrary lib = createLibrary(InteropLibrary.class, value); + assertFalse(lib.hasBufferElements(value)); + assertUnsupported(() -> lib.isBufferWritable(value)); + assertUnsupported(() -> lib.getBufferSize(value)); + assertUnsupported(() -> lib.readBufferByte(value, 0)); + assertUnsupported(() -> lib.writeBufferByte(value, 0, (byte) 0)); + assertUnsupported(() -> lib.readBufferShort(value, LITTLE_ENDIAN, 0)); + assertUnsupported(() -> lib.writeBufferShort(value, LITTLE_ENDIAN, 0, (short) 0)); + assertUnsupported(() -> lib.readBufferInt(value, LITTLE_ENDIAN, 0)); + assertUnsupported(() -> lib.writeBufferInt(value, LITTLE_ENDIAN, 0, 0)); + assertUnsupported(() -> lib.readBufferLong(value, LITTLE_ENDIAN, 0)); + assertUnsupported(() -> lib.writeBufferLong(value, LITTLE_ENDIAN, 0, 0)); + assertUnsupported(() -> lib.readBufferFloat(value, LITTLE_ENDIAN, 0)); + assertUnsupported(() -> lib.writeBufferFloat(value, LITTLE_ENDIAN, 0, 0)); + assertUnsupported(() -> lib.readBufferDouble(value, LITTLE_ENDIAN, 0)); + assertUnsupported(() -> lib.writeBufferDouble(value, LITTLE_ENDIAN, 0, 0)); + } + protected final void assertNoNative(Object value) { InteropLibrary lib = createLibrary(InteropLibrary.class, value); assertFalse(lib.isPointer(value)); 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 9613cdd74512..aea5dadcaa86 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAPITest.java @@ -40,9 +40,74 @@ */ package com.oracle.truffle.api.test.polyglot; -import static com.oracle.truffle.tck.tests.ValueAssert.assertValue; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.exception.AbstractTruffleException; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.tck.tests.ValueAssert; +import com.oracle.truffle.tck.tests.ValueAssert.Trait; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.HostAccess; +import org.graalvm.polyglot.HostAccess.Implementable; +import org.graalvm.polyglot.PolyglotException; +import org.graalvm.polyglot.TypeLiteral; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyDate; +import org.graalvm.polyglot.proxy.ProxyDuration; +import org.graalvm.polyglot.proxy.ProxyExecutable; +import org.graalvm.polyglot.proxy.ProxyInstant; +import org.graalvm.polyglot.proxy.ProxyInstantiable; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.graalvm.polyglot.proxy.ProxyTime; +import org.graalvm.polyglot.proxy.ProxyTimeZone; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.ComparisonFailure; +import org.junit.Test; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + import static com.oracle.truffle.tck.tests.ValueAssert.Trait.ARRAY_ELEMENTS; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BUFFER_ELEMENTS; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.DATE; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.DURATION; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.EXCEPTION; @@ -57,6 +122,7 @@ import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STRING; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIME; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIMEZONE; +import static com.oracle.truffle.tck.tests.ValueAssert.assertValue; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -69,70 +135,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import org.graalvm.polyglot.Context; -import org.graalvm.polyglot.HostAccess; -import org.graalvm.polyglot.HostAccess.Implementable; -import org.graalvm.polyglot.PolyglotException; -import org.graalvm.polyglot.TypeLiteral; -import org.graalvm.polyglot.Value; -import org.graalvm.polyglot.proxy.ProxyArray; -import org.graalvm.polyglot.proxy.ProxyDate; -import org.graalvm.polyglot.proxy.ProxyDuration; -import org.graalvm.polyglot.proxy.ProxyExecutable; -import org.graalvm.polyglot.proxy.ProxyInstant; -import org.graalvm.polyglot.proxy.ProxyInstantiable; -import org.graalvm.polyglot.proxy.ProxyObject; -import org.graalvm.polyglot.proxy.ProxyTime; -import org.graalvm.polyglot.proxy.ProxyTimeZone; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.ComparisonFailure; -import org.junit.Test; - -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.exception.AbstractTruffleException; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.InvalidArrayIndexException; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.interop.UnknownIdentifierException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.library.ExportLibrary; -import com.oracle.truffle.api.library.ExportMessage; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.profiles.BranchProfile; -import com.oracle.truffle.tck.tests.ValueAssert; -import com.oracle.truffle.tck.tests.ValueAssert.Trait; - public class ValueAPITest { private Context context; @@ -266,11 +268,15 @@ public void testNull() { public Object apply(Object t) { return t; } - }, new Supplier() { + }, + new Supplier() { public String get() { return "foobar"; } - }, BigDecimal.class, Class.class, Proxy.newProxyInstance(ValueAPITest.class.getClassLoader(), new Class[]{ProxyInterface.class}, new InvocationHandler() { + }, + BigDecimal.class, + Class.class, + Proxy.newProxyInstance(ValueAPITest.class.getClassLoader(), new Class[]{ProxyInterface.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { switch (method.getName()) { case "foobar": @@ -285,7 +291,8 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl throw new UnsupportedOperationException(method.getName()); } } - })}; + }), + ByteBuffer.wrap(new byte[0])}; @Test public void testHostObject() { @@ -305,6 +312,10 @@ public void testHostObject() { expectedTraits.add(ARRAY_ELEMENTS); } + if (value instanceof ByteBuffer) { + expectedTraits.add(BUFFER_ELEMENTS); + } + if (value instanceof Class && value != Class.class) { expectedTraits.add(INSTANTIABLE); } @@ -360,6 +371,247 @@ public void testListRemove() { assertEquals(3, vlist.getArraySize()); } + // region Buffer tests + + /** + * Returns an array of test {@link ByteBuffer}s with different implementation. + *

    + * All test buffers contains bytes [1, 2, 3, 4, 5, 6, 7, 8]. + */ + public static ByteBuffer[] makeTestBuffers() { + final List list = new ArrayList<>(); + + // Heap buffers + list.add(fillTestBuffer(ByteBuffer.allocate(8))); // HeapByteBuffer + list.add(fillTestBuffer(ByteBuffer.allocate(8)).order(ByteOrder.BIG_ENDIAN)); // HeapByteBuffer + list.add(fillTestBuffer(ByteBuffer.allocate(8)).order(ByteOrder.LITTLE_ENDIAN)); // HeapByteBuffer + list.add(fillTestBuffer(ByteBuffer.wrap(new byte[8]))); // HeapByteBuffer + list.add(fillTestBuffer(sliceTestBuffer(ByteBuffer.allocate(10)))); // HeapByteBuffer + list.add(fillTestBuffer(ByteBuffer.allocate(8)).asReadOnlyBuffer()); // HeapByteBufferR + + // Direct buffers + list.add(fillTestBuffer(ByteBuffer.allocateDirect(8))); // DirectHeapBuffer + list.add(fillTestBuffer(ByteBuffer.allocateDirect(8)).order(ByteOrder.BIG_ENDIAN)); // DirectHeapBuffer + list.add(fillTestBuffer(ByteBuffer.allocateDirect(8)).order(ByteOrder.LITTLE_ENDIAN)); // DirectHeapBuffer + list.add(fillTestBuffer(sliceTestBuffer(ByteBuffer.allocateDirect(10)))); // DirectHeapBuffer + list.add(fillTestBuffer(ByteBuffer.allocateDirect(8)).asReadOnlyBuffer()); // DirectHeapBufferR + + return list.toArray(new ByteBuffer[0]); + } + + private static final ByteBuffer[] BUFFERS = makeTestBuffers(); + + private static ByteBuffer fillTestBuffer(ByteBuffer buffer) { + return buffer.put(0, (byte) 1).put(1, (byte) 2).put(2, (byte) 3).put(3, (byte) 4).put(4, (byte) 5).put(5, (byte) 6).put(6, (byte) 7).put(7, (byte) 8); + } + + private static ByteBuffer sliceTestBuffer(ByteBuffer buffer) { + buffer.position(1); // not chainable (returns Buffer) + buffer.limit(9); // not chainable (returns Buffer) + return buffer.slice(); + } + + @Test + public void testBuffers() { + for (final ByteBuffer buffer : BUFFERS) { + final Value value = context.asValue(buffer); + assertValue(value, BUFFER_ELEMENTS, HOST_OBJECT, MEMBERS); + } + } + + @Test + public void testBuffersModifiable() { + for (final ByteBuffer buffer : makeTestBuffers()) { + Assert.assertEquals(!buffer.isReadOnly(), context.asValue(buffer).isBufferWritable()); + } + } + + @Test + public void testBuffersSize() { + for (final ByteBuffer buffer : makeTestBuffers()) { + Assert.assertEquals(8, context.asValue(buffer).getBufferSize()); + } + } + + @Test + public void testBuffersRead() { + for (final ByteBuffer buffer : makeTestBuffers()) { + final Value value = context.asValue(buffer); + final ByteOrder order = buffer.order(); + for (byte i = 0; i < value.getBufferSize(); ++i) { + Assert.assertEquals(buffer.get(i), value.readBufferByte(i)); + Assert.assertEquals("Side effect: readBufferByte should not modify wrapped buffer's order", order, buffer.order()); + } + for (byte i = 0; i < value.getBufferSize() - 1; ++i) { + Assert.assertEquals(buffer.getShort(i), value.readBufferShort(order, i)); + Assert.assertEquals("Side effect: readBufferShort should not modify wrapped buffer's order", order, buffer.order()); + } + for (byte i = 0; i < value.getBufferSize() - 3; ++i) { + Assert.assertEquals(buffer.getInt(i), value.readBufferInt(order, i)); + Assert.assertEquals("Side effect: readBufferInt should not modify wrapped buffer's order", order, buffer.order()); + Assert.assertEquals(buffer.getFloat(i), value.readBufferFloat(order, i), 0); + Assert.assertEquals("Side effect: readBufferFloat should not modify wrapped buffer's order", order, buffer.order()); + } + for (byte i = 0; i < value.getBufferSize() - 7; ++i) { + Assert.assertEquals(buffer.getLong(i), value.readBufferLong(order, i)); + Assert.assertEquals("Side effect: readBufferLong should not modify wrapped buffer's order", order, buffer.order()); + Assert.assertEquals(buffer.getDouble(i), value.readBufferDouble(order, i), 0); + Assert.assertEquals("Side effect: readBufferDouble should not modify wrapped buffer's order", order, buffer.order()); + } + } + } + + @Test + public void testBuffersWrite() { + for (final ByteBuffer buffer : makeTestBuffers()) { + final Value value = context.asValue(buffer); + final ByteOrder order = buffer.order(); + if (value.isBufferWritable()) { + for (int i = 0; i < value.getBufferSize(); ++i) { + value.writeBufferByte(i, Byte.MAX_VALUE); + Assert.assertEquals(Byte.MAX_VALUE, buffer.get(i)); + Assert.assertEquals("Side effect: writeBufferByte should not modify wrapped buffer's order", order, buffer.order()); + } + } + } + for (final ByteBuffer buffer : makeTestBuffers()) { + final Value value = context.asValue(buffer); + final ByteOrder order = buffer.order(); + if (value.isBufferWritable()) { + for (int i = 0; i < value.getBufferSize() - 1; ++i) { + value.writeBufferShort(order, i, Short.MAX_VALUE); + Assert.assertEquals(Short.MAX_VALUE, buffer.getShort(i)); + } + } + } + for (final ByteBuffer buffer : makeTestBuffers()) { + final Value value = context.asValue(buffer); + final ByteOrder order = buffer.order(); + if (value.isBufferWritable()) { + for (int i = 0; i < value.getBufferSize() - 3; ++i) { + value.writeBufferInt(order, i, Integer.MAX_VALUE); + Assert.assertEquals(Integer.MAX_VALUE, buffer.getInt(i)); + } + } + } + for (final ByteBuffer buffer : makeTestBuffers()) { + final Value value = context.asValue(buffer); + final ByteOrder order = buffer.order(); + if (value.isBufferWritable()) { + for (int i = 0; i < value.getBufferSize() - 3; ++i) { + value.writeBufferFloat(order, i, Float.MAX_VALUE); + Assert.assertEquals(Float.MAX_VALUE, buffer.getFloat(i), 0); + } + } + } + for (final ByteBuffer buffer : makeTestBuffers()) { + final Value value = context.asValue(buffer); + final ByteOrder order = buffer.order(); + if (value.isBufferWritable()) { + for (int i = 0; i < value.getBufferSize() - 7; ++i) { + value.writeBufferLong(order, i, Long.MAX_VALUE); + Assert.assertEquals(Long.MAX_VALUE, buffer.getLong(i)); + } + } + } + for (final ByteBuffer buffer : makeTestBuffers()) { + final Value value = context.asValue(buffer); + final ByteOrder order = buffer.order(); + if (value.isBufferWritable()) { + for (int i = 0; i < value.getBufferSize() - 7; ++i) { + value.writeBufferDouble(order, i, Double.MAX_VALUE); + Assert.assertEquals(Double.MAX_VALUE, buffer.getDouble(i), 0); + } + } + } + + } + + @Test + public void testBuffersErrors() { + for (final ByteBuffer buffer : BUFFERS) { + final Value value = context.asValue(buffer); + final String className = buffer.getClass().getName(); + final ByteOrder order = buffer.order(); + if (buffer.isReadOnly()) { + assertFails(() -> value.writeBufferByte(0, Byte.MAX_VALUE), UnsupportedOperationException.class, + "Unsupported operation Value.writeBufferByte() for '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + + "). You can ensure that the operation is supported using Value.isBufferWritable()."); + assertFails(() -> value.writeBufferShort(order, 0, Short.MAX_VALUE), UnsupportedOperationException.class, + "Unsupported operation Value.writeBufferShort() for '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + + "). You can ensure that the operation is supported using Value.isBufferWritable()."); + assertFails(() -> value.writeBufferInt(order, 0, Integer.MAX_VALUE), UnsupportedOperationException.class, + "Unsupported operation Value.writeBufferInt() for '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + + "). You can ensure that the operation is supported using Value.isBufferWritable()."); + assertFails(() -> value.writeBufferLong(order, 0, Long.MAX_VALUE), UnsupportedOperationException.class, + "Unsupported operation Value.writeBufferLong() for '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + + "). You can ensure that the operation is supported using Value.isBufferWritable()."); + assertFails(() -> value.writeBufferFloat(order, 0, Float.MAX_VALUE), UnsupportedOperationException.class, + "Unsupported operation Value.writeBufferFloat() for '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + + "). You can ensure that the operation is supported using Value.isBufferWritable()."); + assertFails(() -> value.writeBufferDouble(order, 0, Double.MAX_VALUE), UnsupportedOperationException.class, + "Unsupported operation Value.writeBufferDouble() for '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + + "). You can ensure that the operation is supported using Value.isBufferWritable()."); + } else { + assertFails(() -> value.readBufferByte(-1), IndexOutOfBoundsException.class, + "Invalid buffer access of length 1 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.readBufferByte(value.getBufferSize()), IndexOutOfBoundsException.class, + "Invalid buffer access of length 1 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferByte(-1, Byte.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 1 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferByte(value.getBufferSize(), Byte.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 1 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + + assertFails(() -> value.readBufferShort(order, -1), IndexOutOfBoundsException.class, + "Invalid buffer access of length 2 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.readBufferShort(order, value.getBufferSize()), IndexOutOfBoundsException.class, + "Invalid buffer access of length 2 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferShort(order, -1, Short.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 2 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferShort(order, value.getBufferSize(), Short.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 2 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + + assertFails(() -> value.readBufferInt(order, -1), IndexOutOfBoundsException.class, + "Invalid buffer access of length 4 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.readBufferInt(order, value.getBufferSize()), IndexOutOfBoundsException.class, + "Invalid buffer access of length 4 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferInt(order, -1, Integer.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 4 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferInt(order, value.getBufferSize(), Integer.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 4 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + + assertFails(() -> value.readBufferLong(order, -1), IndexOutOfBoundsException.class, + "Invalid buffer access of length 8 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.readBufferLong(order, value.getBufferSize()), IndexOutOfBoundsException.class, + "Invalid buffer access of length 8 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferLong(order, -1, Long.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 8 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferLong(order, value.getBufferSize(), Long.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 8 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + + assertFails(() -> value.readBufferFloat(order, -1), IndexOutOfBoundsException.class, + "Invalid buffer access of length 4 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.readBufferFloat(order, value.getBufferSize()), IndexOutOfBoundsException.class, + "Invalid buffer access of length 4 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferFloat(order, -1, Float.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 4 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferFloat(order, value.getBufferSize(), Float.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 4 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + + assertFails(() -> value.readBufferDouble(order, -1), IndexOutOfBoundsException.class, + "Invalid buffer access of length 8 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.readBufferDouble(order, value.getBufferSize()), IndexOutOfBoundsException.class, + "Invalid buffer access of length 8 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferDouble(order, -1, Double.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 8 at byte offset -1 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + assertFails(() -> value.writeBufferDouble(order, value.getBufferSize(), Double.MAX_VALUE), IndexOutOfBoundsException.class, + "Invalid buffer access of length 8 at byte offset 8 for buffer '" + className + "[pos=0 lim=8 cap=8]'(language: Java, type: " + className + ")."); + } + } + } + + // endregion + @Test public void testComplexGenericCoercion() { TypeLiteral>>> literal = new TypeLiteral>>>() { @@ -1755,6 +2007,7 @@ public void testHostObjectsAndPrimitivesSharable() { sharableObjects.addAll(Arrays.asList(BOOLEANS)); sharableObjects.addAll(Arrays.asList(STRINGS)); sharableObjects.addAll(Arrays.asList(ARRAYS)); + sharableObjects.addAll(Arrays.asList(BUFFERS)); expandObjectVariants(context1, sharableObjects); for (Object object : sharableObjects) { @@ -2053,6 +2306,20 @@ public void testBadNarrowingConversions() { Value array = context.asValue(new int[]{1, 2, 3}); AbstractPolyglotTest.assertFails(() -> array.getArrayElement(index), ArrayIndexOutOfBoundsException.class); AbstractPolyglotTest.assertFails(() -> array.setArrayElement(index, 42), ArrayIndexOutOfBoundsException.class); + + final Value buffer = context.asValue(ByteBuffer.wrap(new byte[]{1, 2, 3})); + AbstractPolyglotTest.assertFails(() -> buffer.readBufferByte(index), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.writeBufferByte(index, (byte) 42), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.readBufferShort(ByteOrder.LITTLE_ENDIAN, index), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.writeBufferShort(ByteOrder.LITTLE_ENDIAN, index, (short) 42), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.readBufferInt(ByteOrder.LITTLE_ENDIAN, index), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.writeBufferInt(ByteOrder.LITTLE_ENDIAN, index, 2), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.readBufferLong(ByteOrder.LITTLE_ENDIAN, index), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.writeBufferLong(ByteOrder.LITTLE_ENDIAN, index, 42), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.readBufferFloat(ByteOrder.LITTLE_ENDIAN, index), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.writeBufferFloat(ByteOrder.LITTLE_ENDIAN, index, 42), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.readBufferDouble(ByteOrder.LITTLE_ENDIAN, index), IndexOutOfBoundsException.class); + AbstractPolyglotTest.assertFails(() -> buffer.writeBufferDouble(ByteOrder.LITTLE_ENDIAN, index, 42), IndexOutOfBoundsException.class); } } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java index adae49823397..7de8f2974f45 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostConversionTest.java @@ -40,26 +40,31 @@ */ package com.oracle.truffle.api.test.polyglot; -import static com.oracle.truffle.tck.tests.ValueAssert.assertUnsupported; -import static com.oracle.truffle.tck.tests.ValueAssert.assertValue; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.HOST_OBJECT; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.MEMBERS; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NULL; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NUMBER; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.PROXY_OBJECT; -import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STRING; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleOptions; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.examples.TargetMappings; +import com.oracle.truffle.tck.tests.ValueAssert.Trait; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.HostAccess; +import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence; +import org.graalvm.polyglot.PolyglotException; +import org.graalvm.polyglot.PolyglotException.StackFrame; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.Proxy; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -67,25 +72,20 @@ import java.util.function.Function; import java.util.function.Supplier; -import org.graalvm.polyglot.Context; -import org.graalvm.polyglot.HostAccess; -import org.graalvm.polyglot.HostAccess.TargetMappingPrecedence; -import org.graalvm.polyglot.PolyglotException; -import org.graalvm.polyglot.PolyglotException.StackFrame; -import org.graalvm.polyglot.Value; -import org.graalvm.polyglot.proxy.Proxy; -import org.junit.Assert; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; - -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleOptions; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.test.examples.TargetMappings; -import com.oracle.truffle.tck.tests.ValueAssert.Trait; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.HOST_OBJECT; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.MEMBERS; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NULL; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NUMBER; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.PROXY_OBJECT; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.STRING; +import static com.oracle.truffle.tck.tests.ValueAssert.assertUnsupported; +import static com.oracle.truffle.tck.tests.ValueAssert.assertValue; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; /** * Tests class for {@link Context#asValue(Object)}. @@ -182,6 +182,8 @@ public void testBasicExamples() { assertTrue(context.asValue(new String[0]).hasArrayElements()); assertTrue(context.asValue(new ArrayList<>()).isHostObject()); assertTrue(context.asValue(new ArrayList<>()).hasArrayElements()); + assertTrue(context.asValue(ByteBuffer.allocate(4)).isHostObject()); + assertTrue(context.asValue(ByteBuffer.allocate(4)).hasBufferElements()); } @Test diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/ByteArraySupport.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/ByteArraySupport.java index b919111b8586..002d47407663 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/ByteArraySupport.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/ByteArraySupport.java @@ -46,6 +46,16 @@ * from {@link #littleEndian()} or {@link #bigEndian()} respectively to access byte arrays in * little-endian or big-endian order. * + *

    Thread safety

    + *

    + * The methods of this class are not safe for use by multiple concurrent threads. If a byte + * array is to be used by more than one thread then access to the byte array should be controlled by + * appropriate synchronization. + * + *

    Alignment

    + *

    + * Unaligned accesses are allowed. + * * @since 20.3 */ public abstract class ByteArraySupport { @@ -102,157 +112,157 @@ public static ByteArraySupport bigEndian() { * interpreter mode) to use this method to check for them than catching * {@link IndexOutOfBoundsException}s. * - * @param buffer The byte array - * @param startIndex The start index of the access - * @param length The number of bytes accessed + * @param buffer the byte array + * @param startByteOffset the start byte offset of the access + * @param length the number of bytes accessed * @return True if if the access is in bounds, false otherwise * @since 20.3 */ - public final boolean inBounds(byte[] buffer, int startIndex, int length) { - return length >= 1 && startIndex >= 0 && startIndex <= buffer.length - length; + public final boolean inBounds(byte[] buffer, int startByteOffset, int length) { + return length >= 1 && startByteOffset >= 0 && startByteOffset <= buffer.length - length; } /** - * Reads the byte at the given index. + * Reads the byte at the given byte offset from the start of the buffer. * - * @param buffer The byte array to read from - * @param index The index from which the byte will be read - * @return The byte at the given index - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size + * @param buffer the byte array to read from + * @param byteOffset the byte offset at which the byte will be read + * @return the byte at the given byte offset from the start of the buffer + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length} * @since 20.3 */ - public abstract byte getByte(byte[] buffer, int index) throws IndexOutOfBoundsException; + public abstract byte getByte(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException; /** - * Writes the given byte at the given index. + * Writes the given byte at the given byte offset from the start of the buffer. * - * @param buffer The byte array to write in - * @param index The index at which the byte will be written - * @param value The byte value to be written - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size + * @param buffer the byte array to write in + * @param byteOffset the byte offset at which the byte will be written + * @param value the byte value to be written + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length} * @since 20.3 */ - public abstract void putByte(byte[] buffer, int index, byte value) throws IndexOutOfBoundsException; + public abstract void putByte(byte[] buffer, int byteOffset, byte value) throws IndexOutOfBoundsException; /** - * Reads the short at the given index. + * Reads the short at the given byte offset from the start of the buffer. * - * @param buffer The byte array to read from - * @param index The index from which the short will be read - * @return The short at the given index - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus one + * @param buffer the byte array to read from + * @param byteOffset the byte offset from which the short will be read + * @return the short at the given byte offset from the start of the buffer + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 1} * @since 20.3 */ - public abstract short getShort(byte[] buffer, int index) throws IndexOutOfBoundsException; + public abstract short getShort(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException; /** - * Writes the given short at the given index. + * Writes the given short at the given byte offset from the start of the buffer. * - * @param buffer The byte array to write in - * @param index The index at which the short will be written - * @param value The short value to be written - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus one + * @param buffer the byte array to write in + * @param byteOffset the byte offset from which the short will be written + * @param value the short value to be written + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 1} * @since 20.3 */ - public abstract void putShort(byte[] buffer, int index, short value) throws IndexOutOfBoundsException; + public abstract void putShort(byte[] buffer, int byteOffset, short value) throws IndexOutOfBoundsException; /** - * Reads the int at the given index. + * Reads the int at the given byte offset from the start of the buffer. * - * @param buffer The byte array to read from - * @param index The index from which the int will be read - * @return The int at the given index - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus three + * @param buffer the byte array to read from + * @param byteOffset the byte offset from which the int will be read + * @return the int at the given byte offset from the start of the buffer + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 3} * @since 20.3 */ - public abstract int getInt(byte[] buffer, int index) throws IndexOutOfBoundsException; + public abstract int getInt(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException; /** - * Writes the given int at the given index. + * Writes the given int at the given byte offset from the start of the buffer. * - * @param buffer The byte array to write in - * @param index The index at which the int will be written - * @param value The int value to be written - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus three + * @param buffer the byte array to write in + * @param byteOffset the byte offset from which the int will be written + * @param value the int value to be written + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 3} * @since 20.3 */ - public abstract void putInt(byte[] buffer, int index, int value) throws IndexOutOfBoundsException; + public abstract void putInt(byte[] buffer, int byteOffset, int value) throws IndexOutOfBoundsException; /** - * Reads the int at the given index. + * Reads the long at the given byte offset from the start of the buffer. * - * @param buffer The byte array to read from - * @param index The index from which the int will be read - * @return The int at the given index - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus seven + * @param buffer the byte array to read from + * @param byteOffset the byte offset from which the int will be read + * @return the int at the given byte offset from the start of the buffer + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 7} * @since 20.3 */ - public abstract long getLong(byte[] buffer, int index) throws IndexOutOfBoundsException; + public abstract long getLong(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException; /** - * Writes the given int at the given index. + * Writes the given long at the given byte offset from the start of the buffer. * - * @param buffer The byte array to write in - * @param index The index at which the int will be written - * @param value The int value to be written - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus seven + * @param buffer the byte array to write in + * @param byteOffset the byte offset from which the int will be written + * @param value the int value to be written + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 7} * @since 20.3 */ - public abstract void putLong(byte[] buffer, int index, long value) throws IndexOutOfBoundsException; + public abstract void putLong(byte[] buffer, int byteOffset, long value) throws IndexOutOfBoundsException; /** - * Reads the float at the given index. + * Reads the float at the given byte offset from the start of the buffer. * - * @param buffer The byte array to read from - * @param index The index from which the float will be read - * @return The float at the given index - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus three + * @param buffer the byte array to read from + * @param byteOffset the byte offset from which the float will be read + * @return the float at the given byte offset from the start of the buffer + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 3} * @since 20.3 */ - public abstract float getFloat(byte[] buffer, int index) throws IndexOutOfBoundsException; + public abstract float getFloat(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException; /** - * Writes the given float at the given index. + * Writes the given float at the given byte offset from the start of the buffer. * - * @param buffer The byte array to write in - * @param index The index at which the float will be written - * @param value The float value to be written - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus three + * @param buffer the byte array to write in + * @param byteOffset the byte offset from which the float will be written + * @param value the float value to be written + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 3} * @since 20.3 */ - public abstract void putFloat(byte[] buffer, int index, float value) throws IndexOutOfBoundsException; + public abstract void putFloat(byte[] buffer, int byteOffset, float value) throws IndexOutOfBoundsException; /** - * Reads the double at the given index. + * Reads the double at the given byte offset from the start of the buffer. * - * @param buffer The byte array to read from - * @param index The index from which the double will be read - * @return The double at the given index - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus seven + * @param buffer the byte array to read from + * @param byteOffset the byte offset from which the double will be read + * @return the double at the given byte offset from the start of the buffer + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 7} * @since 20.3 */ - public abstract double getDouble(byte[] buffer, int index) throws IndexOutOfBoundsException; + public abstract double getDouble(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException; /** - * Writes the given double at the given index. + * Writes the given double at the given byte offset from the start of the buffer. * - * @param buffer The byte array to write in - * @param index The index at which the double will be written - * @param value The double value to be written - * @throws IndexOutOfBoundsException If index is negative or not smaller than the - * buffer's size minus seven + * @param buffer the byte array to write in + * @param byteOffset the byte offset from which the double will be written + * @param value the double value to be written + * @throws IndexOutOfBoundsException if and only if + * {@code byteOffset < 0 || byteOffset >= buffer.length - 7} * @since 20.3 */ - public abstract void putDouble(byte[] buffer, int index, double value) throws IndexOutOfBoundsException; + public abstract void putDouble(byte[] buffer, int byteOffset, double value) throws IndexOutOfBoundsException; } diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/CheckedByteArraySupport.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/CheckedByteArraySupport.java index 965ccb882032..17b2224d9558 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/CheckedByteArraySupport.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/CheckedByteArraySupport.java @@ -54,81 +54,81 @@ final class CheckedByteArraySupport extends ByteArraySupport { this.access = access; } - private void checkBounds(byte[] buffer, int startIndex, int length) { - if (!inBounds(buffer, startIndex, length)) { + private void checkBounds(byte[] buffer, int startByteOffset, int length) { + if (!inBounds(buffer, startByteOffset, length)) { throw new ByteArrayOutOfBoundsException(); } } @Override - public byte getByte(byte[] buffer, int index) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Byte.BYTES); - return access.getByte(buffer, index); + public byte getByte(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Byte.BYTES); + return access.getByte(buffer, byteOffset); } @Override - public void putByte(byte[] buffer, int index, byte value) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Byte.BYTES); - access.putByte(buffer, index, value); + public void putByte(byte[] buffer, int byteOffset, byte value) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Byte.BYTES); + access.putByte(buffer, byteOffset, value); } @Override - public short getShort(byte[] buffer, int index) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Short.BYTES); - return access.getShort(buffer, index); + public short getShort(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Short.BYTES); + return access.getShort(buffer, byteOffset); } @Override - public void putShort(byte[] buffer, int index, short value) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Short.BYTES); - access.putShort(buffer, index, value); + public void putShort(byte[] buffer, int byteOffset, short value) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Short.BYTES); + access.putShort(buffer, byteOffset, value); } @Override - public int getInt(byte[] buffer, int index) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Integer.BYTES); - return access.getInt(buffer, index); + public int getInt(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Integer.BYTES); + return access.getInt(buffer, byteOffset); } @Override - public void putInt(byte[] buffer, int index, int value) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Integer.BYTES); - access.putInt(buffer, index, value); + public void putInt(byte[] buffer, int byteOffset, int value) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Integer.BYTES); + access.putInt(buffer, byteOffset, value); } @Override - public long getLong(byte[] buffer, int index) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Long.BYTES); - return access.getLong(buffer, index); + public long getLong(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Long.BYTES); + return access.getLong(buffer, byteOffset); } @Override - public void putLong(byte[] buffer, int index, long value) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Long.BYTES); - access.putLong(buffer, index, value); + public void putLong(byte[] buffer, int byteOffset, long value) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Long.BYTES); + access.putLong(buffer, byteOffset, value); } @Override - public float getFloat(byte[] buffer, int index) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Float.BYTES); - return access.getFloat(buffer, index); + public float getFloat(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Float.BYTES); + return access.getFloat(buffer, byteOffset); } @Override - public void putFloat(byte[] buffer, int index, float value) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Float.BYTES); - access.putFloat(buffer, index, value); + public void putFloat(byte[] buffer, int byteOffset, float value) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Float.BYTES); + access.putFloat(buffer, byteOffset, value); } @Override - public double getDouble(byte[] buffer, int index) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Double.BYTES); - return access.getDouble(buffer, index); + public double getDouble(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Double.BYTES); + return access.getDouble(buffer, byteOffset); } @Override - public void putDouble(byte[] buffer, int index, double value) throws IndexOutOfBoundsException { - checkBounds(buffer, index, Double.BYTES); - access.putDouble(buffer, index, value); + public void putDouble(byte[] buffer, int byteOffset, double value) throws IndexOutOfBoundsException { + checkBounds(buffer, byteOffset, Double.BYTES); + access.putDouble(buffer, byteOffset, value); } } diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/ReversedByteArraySupport.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/ReversedByteArraySupport.java index 5df5f813097d..e509cfb3a353 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/ReversedByteArraySupport.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/ReversedByteArraySupport.java @@ -54,62 +54,62 @@ final class ReversedByteArraySupport extends ByteArraySupport { } @Override - public byte getByte(byte[] buffer, int index) throws IndexOutOfBoundsException { - return access.getByte(buffer, index); + public byte getByte(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return access.getByte(buffer, byteOffset); } @Override - public void putByte(byte[] buffer, int index, byte value) throws IndexOutOfBoundsException { - access.putByte(buffer, index, value); + public void putByte(byte[] buffer, int byteOffset, byte value) throws IndexOutOfBoundsException { + access.putByte(buffer, byteOffset, value); } @Override - public short getShort(byte[] buffer, int index) throws IndexOutOfBoundsException { - return Short.reverseBytes(access.getShort(buffer, index)); + public short getShort(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return Short.reverseBytes(access.getShort(buffer, byteOffset)); } @Override - public void putShort(byte[] buffer, int index, short value) throws IndexOutOfBoundsException { - access.putShort(buffer, index, Short.reverseBytes(value)); + public void putShort(byte[] buffer, int byteOffset, short value) throws IndexOutOfBoundsException { + access.putShort(buffer, byteOffset, Short.reverseBytes(value)); } @Override - public int getInt(byte[] buffer, int index) throws IndexOutOfBoundsException { - return Integer.reverseBytes(access.getInt(buffer, index)); + public int getInt(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return Integer.reverseBytes(access.getInt(buffer, byteOffset)); } @Override - public void putInt(byte[] buffer, int index, int value) throws IndexOutOfBoundsException { - access.putInt(buffer, index, Integer.reverseBytes(value)); + public void putInt(byte[] buffer, int byteOffset, int value) throws IndexOutOfBoundsException { + access.putInt(buffer, byteOffset, Integer.reverseBytes(value)); } @Override - public long getLong(byte[] buffer, int index) throws IndexOutOfBoundsException { - return Long.reverseBytes(access.getLong(buffer, index)); + public long getLong(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return Long.reverseBytes(access.getLong(buffer, byteOffset)); } @Override - public void putLong(byte[] buffer, int index, long value) throws IndexOutOfBoundsException { - access.putLong(buffer, index, Long.reverseBytes(value)); + public void putLong(byte[] buffer, int byteOffset, long value) throws IndexOutOfBoundsException { + access.putLong(buffer, byteOffset, Long.reverseBytes(value)); } @Override - public float getFloat(byte[] buffer, int index) throws IndexOutOfBoundsException { - return Float.intBitsToFloat(Integer.reverseBytes(access.getInt(buffer, index))); + public float getFloat(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return Float.intBitsToFloat(Integer.reverseBytes(access.getInt(buffer, byteOffset))); } @Override - public void putFloat(byte[] buffer, int index, float value) throws IndexOutOfBoundsException { - access.putInt(buffer, index, Integer.reverseBytes(Float.floatToIntBits(value))); + public void putFloat(byte[] buffer, int byteOffset, float value) throws IndexOutOfBoundsException { + access.putInt(buffer, byteOffset, Integer.reverseBytes(Float.floatToIntBits(value))); } @Override - public double getDouble(byte[] buffer, int index) throws IndexOutOfBoundsException { - return Double.longBitsToDouble(Long.reverseBytes(access.getLong(buffer, index))); + public double getDouble(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return Double.longBitsToDouble(Long.reverseBytes(access.getLong(buffer, byteOffset))); } @Override - public void putDouble(byte[] buffer, int index, double value) throws IndexOutOfBoundsException { - access.putLong(buffer, index, Long.reverseBytes(Double.doubleToLongBits(value))); + public void putDouble(byte[] buffer, int byteOffset, double value) throws IndexOutOfBoundsException { + access.putLong(buffer, byteOffset, Long.reverseBytes(Double.doubleToLongBits(value))); } } diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/SimpleByteArraySupport.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/SimpleByteArraySupport.java index de7d88f6c24c..ef603e0def7c 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/SimpleByteArraySupport.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/SimpleByteArraySupport.java @@ -42,91 +42,91 @@ package com.oracle.truffle.api.memory; /** - * Implementation of {@link ByteArraySupport} by indexing individual bytes. + * Implementation of {@link ByteArraySupport} by byteOffseting individual bytes. *

    * Bytes ordering is big-endian. */ @SuppressWarnings("PointlessArithmeticExpression") final class SimpleByteArraySupport extends ByteArraySupport { @Override - public byte getByte(byte[] buffer, int index) throws IndexOutOfBoundsException { - return buffer[index]; + public byte getByte(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return buffer[byteOffset]; } @Override - public void putByte(byte[] buffer, int index, byte value) throws IndexOutOfBoundsException { - buffer[index] = value; + public void putByte(byte[] buffer, int byteOffset, byte value) throws IndexOutOfBoundsException { + buffer[byteOffset] = value; } @Override - public short getShort(byte[] buffer, int index) throws IndexOutOfBoundsException { - return (short) (((buffer[index] & 0xFF) << Byte.SIZE) | - (buffer[index + 1] & 0xFF)); + public short getShort(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return (short) (((buffer[byteOffset] & 0xFF) << Byte.SIZE) | + (buffer[byteOffset + 1] & 0xFF)); } @Override - public void putShort(byte[] buffer, int index, short value) throws IndexOutOfBoundsException { - buffer[index + 0] = (byte) (value >> Byte.SIZE); - buffer[index + 1] = (byte) (value); + public void putShort(byte[] buffer, int byteOffset, short value) throws IndexOutOfBoundsException { + buffer[byteOffset + 0] = (byte) (value >> Byte.SIZE); + buffer[byteOffset + 1] = (byte) (value); } @Override - public int getInt(byte[] buffer, int index) throws IndexOutOfBoundsException { - return ((buffer[index + 0] & 0xFF) << Byte.SIZE * 3) | - ((buffer[index + 1] & 0xFF) << Byte.SIZE * 2) | - ((buffer[index + 2] & 0xFF) << Byte.SIZE) | - ((buffer[index + 3] & 0xFF)); + public int getInt(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return ((buffer[byteOffset + 0] & 0xFF) << Byte.SIZE * 3) | + ((buffer[byteOffset + 1] & 0xFF) << Byte.SIZE * 2) | + ((buffer[byteOffset + 2] & 0xFF) << Byte.SIZE) | + ((buffer[byteOffset + 3] & 0xFF)); } @Override - public void putInt(byte[] buffer, int index, int value) throws IndexOutOfBoundsException { - buffer[index + 0] = (byte) (value >> Byte.SIZE * 3); - buffer[index + 1] = (byte) (value >> Byte.SIZE * 2); - buffer[index + 2] = (byte) (value >> Byte.SIZE); - buffer[index + 3] = (byte) (value); + public void putInt(byte[] buffer, int byteOffset, int value) throws IndexOutOfBoundsException { + buffer[byteOffset + 0] = (byte) (value >> Byte.SIZE * 3); + buffer[byteOffset + 1] = (byte) (value >> Byte.SIZE * 2); + buffer[byteOffset + 2] = (byte) (value >> Byte.SIZE); + buffer[byteOffset + 3] = (byte) (value); } @Override - public long getLong(byte[] buffer, int index) throws IndexOutOfBoundsException { - return ((buffer[index + 0] & 0xFFL) << (Byte.SIZE * 7)) | - ((buffer[index + 1] & 0xFFL) << (Byte.SIZE * 6)) | - ((buffer[index + 2] & 0xFFL) << (Byte.SIZE * 5)) | - ((buffer[index + 3] & 0xFFL) << (Byte.SIZE * 4)) | - ((buffer[index + 4] & 0xFFL) << (Byte.SIZE * 3)) | - ((buffer[index + 5] & 0xFFL) << (Byte.SIZE * 2)) | - ((buffer[index + 6] & 0xFFL) << (Byte.SIZE)) | - ((buffer[index + 7] & 0xFFL)); + public long getLong(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return ((buffer[byteOffset + 0] & 0xFFL) << (Byte.SIZE * 7)) | + ((buffer[byteOffset + 1] & 0xFFL) << (Byte.SIZE * 6)) | + ((buffer[byteOffset + 2] & 0xFFL) << (Byte.SIZE * 5)) | + ((buffer[byteOffset + 3] & 0xFFL) << (Byte.SIZE * 4)) | + ((buffer[byteOffset + 4] & 0xFFL) << (Byte.SIZE * 3)) | + ((buffer[byteOffset + 5] & 0xFFL) << (Byte.SIZE * 2)) | + ((buffer[byteOffset + 6] & 0xFFL) << (Byte.SIZE)) | + ((buffer[byteOffset + 7] & 0xFFL)); } @Override - public void putLong(byte[] buffer, int index, long value) throws IndexOutOfBoundsException { - buffer[index + 0] = (byte) (value >> (Byte.SIZE * 7)); - buffer[index + 1] = (byte) (value >> (Byte.SIZE * 6)); - buffer[index + 2] = (byte) (value >> (Byte.SIZE * 5)); - buffer[index + 3] = (byte) (value >> (Byte.SIZE * 4)); - buffer[index + 4] = (byte) (value >> (Byte.SIZE * 3)); - buffer[index + 5] = (byte) (value >> (Byte.SIZE * 2)); - buffer[index + 6] = (byte) (value >> (Byte.SIZE)); - buffer[index + 7] = (byte) (value); + public void putLong(byte[] buffer, int byteOffset, long value) throws IndexOutOfBoundsException { + buffer[byteOffset + 0] = (byte) (value >> (Byte.SIZE * 7)); + buffer[byteOffset + 1] = (byte) (value >> (Byte.SIZE * 6)); + buffer[byteOffset + 2] = (byte) (value >> (Byte.SIZE * 5)); + buffer[byteOffset + 3] = (byte) (value >> (Byte.SIZE * 4)); + buffer[byteOffset + 4] = (byte) (value >> (Byte.SIZE * 3)); + buffer[byteOffset + 5] = (byte) (value >> (Byte.SIZE * 2)); + buffer[byteOffset + 6] = (byte) (value >> (Byte.SIZE)); + buffer[byteOffset + 7] = (byte) (value); } @Override - public float getFloat(byte[] buffer, int index) throws IndexOutOfBoundsException { - return Float.intBitsToFloat(getInt(buffer, index)); + public float getFloat(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return Float.intBitsToFloat(getInt(buffer, byteOffset)); } @Override - public void putFloat(byte[] buffer, int index, float value) throws IndexOutOfBoundsException { - putInt(buffer, index, Float.floatToRawIntBits(value)); + public void putFloat(byte[] buffer, int byteOffset, float value) throws IndexOutOfBoundsException { + putInt(buffer, byteOffset, Float.floatToRawIntBits(value)); } @Override - public double getDouble(byte[] buffer, int index) throws IndexOutOfBoundsException { - return Double.longBitsToDouble(getLong(buffer, index)); + public double getDouble(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return Double.longBitsToDouble(getLong(buffer, byteOffset)); } @Override - public void putDouble(byte[] buffer, int index, double value) throws IndexOutOfBoundsException { - putLong(buffer, index, Double.doubleToRawLongBits(value)); + public void putDouble(byte[] buffer, int byteOffset, double value) throws IndexOutOfBoundsException { + putLong(buffer, byteOffset, Double.doubleToRawLongBits(value)); } } diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/UnsafeByteArraySupport.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/UnsafeByteArraySupport.java index 0e0315609d1f..60efae3b55ca 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/UnsafeByteArraySupport.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/memory/UnsafeByteArraySupport.java @@ -69,63 +69,63 @@ public Unsafe run() { }); @Override - public byte getByte(byte[] buffer, int index) throws IndexOutOfBoundsException { - return UNSAFE.getByte(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index)); + public byte getByte(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return UNSAFE.getByte(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset)); } @Override - public void putByte(byte[] buffer, int index, byte value) throws IndexOutOfBoundsException { - UNSAFE.putByte(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index), value); + public void putByte(byte[] buffer, int byteOffset, byte value) throws IndexOutOfBoundsException { + UNSAFE.putByte(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset), value); } @Override - public short getShort(byte[] buffer, int index) throws IndexOutOfBoundsException { - return UNSAFE.getShort(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index)); + public short getShort(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return UNSAFE.getShort(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset)); } @Override - public void putShort(byte[] buffer, int index, short value) throws IndexOutOfBoundsException { - UNSAFE.putShort(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index), value); + public void putShort(byte[] buffer, int byteOffset, short value) throws IndexOutOfBoundsException { + UNSAFE.putShort(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset), value); } @Override - public int getInt(byte[] buffer, int index) throws IndexOutOfBoundsException { - return UNSAFE.getInt(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index)); + public int getInt(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return UNSAFE.getInt(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset)); } @Override - public void putInt(byte[] buffer, int index, int value) throws IndexOutOfBoundsException { - UNSAFE.putInt(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index), value); + public void putInt(byte[] buffer, int byteOffset, int value) throws IndexOutOfBoundsException { + UNSAFE.putInt(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset), value); } @Override - public long getLong(byte[] buffer, int index) throws IndexOutOfBoundsException { - return UNSAFE.getLong(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index)); + public long getLong(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return UNSAFE.getLong(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset)); } @Override - public void putLong(byte[] buffer, int index, long value) throws IndexOutOfBoundsException { - UNSAFE.putLong(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index), value); + public void putLong(byte[] buffer, int byteOffset, long value) throws IndexOutOfBoundsException { + UNSAFE.putLong(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset), value); } @Override - public float getFloat(byte[] buffer, int index) throws IndexOutOfBoundsException { - return UNSAFE.getFloat(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index)); + public float getFloat(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return UNSAFE.getFloat(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset)); } @Override - public void putFloat(byte[] buffer, int index, float value) throws IndexOutOfBoundsException { - UNSAFE.putFloat(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index), value); + public void putFloat(byte[] buffer, int byteOffset, float value) throws IndexOutOfBoundsException { + UNSAFE.putFloat(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset), value); } @Override - public double getDouble(byte[] buffer, int index) throws IndexOutOfBoundsException { - return UNSAFE.getDouble(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index)); + public double getDouble(byte[] buffer, int byteOffset) throws IndexOutOfBoundsException { + return UNSAFE.getDouble(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset)); } @Override - public void putDouble(byte[] buffer, int index, double value) throws IndexOutOfBoundsException { - UNSAFE.putDouble(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(index), value); + public void putDouble(byte[] buffer, int byteOffset, double value) throws IndexOutOfBoundsException { + UNSAFE.putDouble(buffer, Unsafe.ARRAY_BYTE_BASE_OFFSET + Integer.toUnsignedLong(byteOffset), value); } } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostClassCache.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostClassCache.java index b71cfabcea1e..d1406b3b3b07 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostClassCache.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostClassCache.java @@ -40,6 +40,12 @@ */ package com.oracle.truffle.polyglot; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleOptions; +import org.graalvm.polyglot.HostAccess; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.APIAccess; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; @@ -51,13 +57,6 @@ import java.util.Map; import java.util.Map.Entry; -import org.graalvm.polyglot.HostAccess; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.APIAccess; - -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.TruffleOptions; - final class HostClassCache { static final PolyglotTargetMapping[] EMPTY_MAPPINGS = new PolyglotTargetMapping[0]; @@ -66,6 +65,7 @@ final class HostClassCache { final HostAccess hostAccess; private final boolean arrayAccess; private final boolean listAccess; + private final boolean bufferAccess; private final Map, Object> targetMappings; private final Object unnamedModule; @@ -73,6 +73,7 @@ private HostClassCache(AbstractPolyglotImpl.APIAccess apiAccess, HostAccess conf this.hostAccess = conf; this.arrayAccess = apiAccess.isArrayAccessible(hostAccess); this.listAccess = apiAccess.isListAccessible(hostAccess); + this.bufferAccess = apiAccess.isBufferAccessible(hostAccess); this.apiAccess = apiAccess; this.targetMappings = groupMappings(apiAccess, conf); this.unnamedModule = EngineAccessor.JDKSERVICES.getUnnamedModule(classLoader); @@ -224,8 +225,11 @@ boolean isListAccess() { return listAccess; } + boolean isBufferAccess() { + return bufferAccess; + } + boolean allowsImplementation(Class type) { return apiAccess.allowsImplementation(hostAccess, type); } - } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostObject.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostObject.java index 9fe58ce62009..50fd3777b4a7 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostObject.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostObject.java @@ -40,18 +40,6 @@ */ package com.oracle.truffle.polyglot; -import java.lang.reflect.Array; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.Date; -import java.util.List; -import java.util.Objects; - import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -66,6 +54,7 @@ import com.oracle.truffle.api.interop.ExceptionType; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.InvalidBufferOffsetException; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnknownIdentifierException; import com.oracle.truffle.api.interop.UnsupportedMessageException; @@ -78,12 +67,33 @@ import com.oracle.truffle.api.utilities.TriState; import com.oracle.truffle.polyglot.PolyglotLanguageContext.ToGuestValueNode; +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ReadOnlyBufferException; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import java.util.List; +import java.util.Objects; + @ExportLibrary(InteropLibrary.class) @SuppressWarnings("unused") final class HostObject implements TruffleObject { static final int LIMIT = 5; + // PE-friendly ByteBuffer's implementations: + private static final Class HEAP_BYTE_BUFFER_CLASS = ByteBuffer.allocate(0).getClass(); + private static final Class HEAP_BYTE_BUFFER_R_CLASS = ByteBuffer.allocate(0).asReadOnlyBuffer().getClass(); + private static final Class DIRECT_BYTE_BUFFER_CLASS = ByteBuffer.allocateDirect(0).getClass(); + private static final Class DIRECT_BYTE_BUFFER_R_CLASS = ByteBuffer.allocateDirect(0).asReadOnlyBuffer().getClass(); + private static final ZoneId UTC = ZoneId.of("UTC"); static final HostObject NULL = new HostObject(null, null, null); @@ -659,6 +669,437 @@ long getArraySize(@Shared("isArray") @Cached IsArrayNode isArray, throw UnsupportedMessageException.create(); } + // region Buffer Messages + + @ExportMessage + boolean hasBufferElements(@Shared("isBuffer") @Cached IsBufferNode isBuffer) { + return isBuffer.execute(this); + } + + @ExportMessage + boolean isBufferWritable(@Shared("isBuffer") @Cached IsBufferNode isBuffer, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException { + if (isBuffer.execute(this)) { + final ByteBuffer buffer = (ByteBuffer) obj; + return isPEFriendlyBuffer(buffer) ? !buffer.isReadOnly() : isBufferWritableBoundary(buffer); + } + error.enter(); + throw UnsupportedMessageException.create(); + } + + @TruffleBoundary + private static boolean isBufferWritableBoundary(ByteBuffer buffer) { + return !buffer.isReadOnly(); + } + + @ExportMessage + long getBufferSize(@Shared("isBuffer") @Cached IsBufferNode isBuffer, @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException { + if (isBuffer.execute(this)) { + final ByteBuffer buffer = (ByteBuffer) obj; + return isPEFriendlyBuffer(buffer) ? buffer.limit() : getBufferSizeBoundary(buffer); + } + error.enter(); + throw UnsupportedMessageException.create(); + } + + @TruffleBoundary + private static long getBufferSizeBoundary(ByteBuffer buffer) { + return buffer.limit(); + } + + private static boolean isPEFriendlyBuffer(ByteBuffer buffer) { + final Class clazz = buffer.getClass(); + final boolean result = CompilerDirectives.isPartialEvaluationConstant(clazz) && + clazz == HEAP_BYTE_BUFFER_CLASS || clazz == HEAP_BYTE_BUFFER_R_CLASS || + clazz == DIRECT_BYTE_BUFFER_CLASS || clazz == DIRECT_BYTE_BUFFER_R_CLASS; + assert result : "Unexpected Buffer subclass"; + return result; + } + + @ExportMessage + public byte readBufferByte(long index, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Byte.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + return isPEFriendlyBuffer(buffer) ? buffer.get((int) index) : getBufferByteBoundary(buffer, (int) index); + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Byte.BYTES); + } + } + + @TruffleBoundary + private static byte getBufferByteBoundary(ByteBuffer buffer, int index) { + return buffer.get(index); + } + + @ExportMessage + public void writeBufferByte(long index, byte value, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException, UnsupportedMessageException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Byte.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + if (isPEFriendlyBuffer(buffer)) { + buffer.put((int) index, value); + } else { + putBufferByteBoundary(buffer, (int) index, value); + } + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Byte.BYTES); + } catch (ReadOnlyBufferException e) { + throw UnsupportedMessageException.create(); + } + } + + @TruffleBoundary + private static void putBufferByteBoundary(ByteBuffer buffer, int index, byte value) { + buffer.put(index, value); + } + + @ExportMessage + public short readBufferShort(ByteOrder order, long index, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Short.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + final short result = isPEFriendlyBuffer(buffer) ? buffer.getShort((int) index) : getBufferShortBoundary(buffer, (int) index); + buffer.order(originalOrder); + return result; + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Short.BYTES); + } + } + + @TruffleBoundary + private static short getBufferShortBoundary(ByteBuffer buffer, int index) { + return buffer.getShort(index); + } + + @ExportMessage + public void writeBufferShort(ByteOrder order, long index, short value, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException, UnsupportedMessageException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Short.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + if (isPEFriendlyBuffer(buffer)) { + buffer.putShort((int) index, value); + } else { + putBufferShortBoundary(buffer, (int) index, value); + } + buffer.order(originalOrder); + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Short.BYTES); + } catch (ReadOnlyBufferException e) { + throw UnsupportedMessageException.create(); + } + } + + @TruffleBoundary + private static void putBufferShortBoundary(ByteBuffer buffer, int index, short value) { + buffer.putShort(index, value); + } + + @ExportMessage + public int readBufferInt(ByteOrder order, long index, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Integer.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + final int result = isPEFriendlyBuffer(buffer) ? buffer.getInt((int) index) : getBufferIntBoundary(buffer, (int) index); + buffer.order(originalOrder); + return result; + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Integer.BYTES); + } + } + + @TruffleBoundary + private static int getBufferIntBoundary(ByteBuffer buffer, int index) { + return buffer.getInt(index); + } + + @ExportMessage + public void writeBufferInt(ByteOrder order, long index, int value, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException, UnsupportedMessageException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Integer.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + if (isPEFriendlyBuffer(buffer)) { + buffer.putInt((int) index, value); + } else { + putBufferIntBoundary(buffer, (int) index, value); + } + buffer.order(originalOrder); + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Integer.BYTES); + } catch (ReadOnlyBufferException e) { + throw UnsupportedMessageException.create(); + } + } + + @TruffleBoundary + private static void putBufferIntBoundary(ByteBuffer buffer, int index, int value) { + buffer.putInt(index, value); + } + + @ExportMessage + public long readBufferLong(ByteOrder order, long index, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Long.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + final long result = isPEFriendlyBuffer(buffer) ? buffer.getLong((int) index) : getBufferLongBoundary(buffer, (int) index); + buffer.order(originalOrder); + return result; + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Long.BYTES); + } + } + + @TruffleBoundary + private static long getBufferLongBoundary(ByteBuffer buffer, int index) { + return buffer.getLong(index); + } + + @ExportMessage + public void writeBufferLong(ByteOrder order, long index, long value, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException, UnsupportedMessageException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Long.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + if (isPEFriendlyBuffer(buffer)) { + buffer.putLong((int) index, value); + } else { + putBufferLongBoundary(buffer, (int) index, value); + } + buffer.order(originalOrder); + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Long.BYTES); + } catch (ReadOnlyBufferException e) { + throw UnsupportedMessageException.create(); + } + } + + @TruffleBoundary + private static void putBufferLongBoundary(ByteBuffer buffer, int index, long value) { + buffer.putLong(index, value); + } + + @ExportMessage + public float readBufferFloat(ByteOrder order, long index, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Float.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + final float result = isPEFriendlyBuffer(buffer) ? buffer.getFloat((int) index) : getBufferFloatBoundary(buffer, (int) index); + buffer.order(originalOrder); + return result; + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Float.BYTES); + } + } + + @CompilerDirectives.TruffleBoundary + private static float getBufferFloatBoundary(ByteBuffer buffer, int index) { + return buffer.getFloat(index); + } + + @ExportMessage + public void writeBufferFloat(ByteOrder order, long index, float value, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException, UnsupportedMessageException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Float.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + if (isPEFriendlyBuffer(buffer)) { + buffer.putFloat((int) index, value); + } else { + putBufferFloatBoundary(buffer, (int) index, value); + } + buffer.order(originalOrder); + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Float.BYTES); + } catch (ReadOnlyBufferException e) { + throw UnsupportedMessageException.create(); + } + } + + @CompilerDirectives.TruffleBoundary + private static void putBufferFloatBoundary(ByteBuffer buffer, int index, float value) { + buffer.putFloat(index, value); + } + + @ExportMessage + public double readBufferDouble(ByteOrder order, long index, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws UnsupportedMessageException, InvalidBufferOffsetException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Double.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + final double result = isPEFriendlyBuffer(buffer) ? buffer.getDouble((int) index) : getBufferDoubleBoundary(buffer, (int) index); + buffer.order(originalOrder); + return result; + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Double.BYTES); + } + } + + @CompilerDirectives.TruffleBoundary + private static double getBufferDoubleBoundary(ByteBuffer buffer, int index) { + return buffer.getDouble(index); + } + + @ExportMessage + public void writeBufferDouble(ByteOrder order, long index, double value, + @Shared("isBuffer") @Cached IsBufferNode isBuffer, + @Shared("error") @Cached BranchProfile error) throws InvalidBufferOffsetException, UnsupportedMessageException { + if (!isBuffer.execute(this)) { + error.enter(); + throw UnsupportedMessageException.create(); + } + if (index < 0 || Integer.MAX_VALUE < index) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Double.BYTES); + } + try { + final ByteBuffer buffer = (ByteBuffer) obj; + final ByteOrder originalOrder = buffer.order(); + buffer.order(order); + if (isPEFriendlyBuffer(buffer)) { + buffer.putDouble((int) index, value); + } else { + putBufferDoubleBoundary(buffer, (int) index, value); + } + buffer.order(originalOrder); + } catch (IndexOutOfBoundsException e) { + error.enter(); + throw InvalidBufferOffsetException.create(index, Double.BYTES); + } catch (ReadOnlyBufferException e) { + throw UnsupportedMessageException.create(); + } + } + + @CompilerDirectives.TruffleBoundary + private static void putBufferDoubleBoundary(ByteBuffer buffer, int index, double value) { + buffer.putDouble(index, value); + } + + // endregion + @TruffleBoundary(allowInlining = true) int getListSize() { return ((List) obj).size(); @@ -1652,4 +2093,19 @@ public boolean doDefault(HostObject receiver, } } + + @GenerateUncached + abstract static class IsBufferNode extends Node { + + public abstract boolean execute(HostObject receiver); + + @Specialization + public boolean doDefault(HostObject receiver, + @Cached(value = "receiver.getHostClassCache().isBufferAccess()", allowUncached = true) boolean isBufferAccess) { + assert receiver.getHostClassCache().isBufferAccess() == isBufferAccess; + return isBufferAccess && receiver.obj != null && ByteBuffer.class.isAssignableFrom(receiver.obj.getClass()); + } + + } + } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineException.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineException.java index a557b175727f..d71f7be01964 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineException.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineException.java @@ -40,10 +40,9 @@ */ package com.oracle.truffle.polyglot; -import org.graalvm.polyglot.Context; - import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage.Env; +import org.graalvm.polyglot.Context; /** * Represents an expected user exception caused by the polyglot engine. It is wrapped such that it @@ -156,4 +155,8 @@ static PolyglotEngineException arrayIndexOutOfBounds(String message) { return new PolyglotEngineException(new ArrayIndexOutOfBoundsException(message)); } + static PolyglotEngineException bufferIndexOutOfBounds(String message) { + return new PolyglotEngineException(new IndexOutOfBoundsException(message)); + } + } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValue.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValue.java index ab20d79f187e..76b5556b790b 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValue.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotValue.java @@ -40,29 +40,6 @@ */ package com.oracle.truffle.polyglot; -import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; -import static com.oracle.truffle.polyglot.EngineAccessor.RUNTIME; - -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneId; -import java.util.AbstractSet; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Set; - -import org.graalvm.polyglot.Context; -import org.graalvm.polyglot.SourceSection; -import org.graalvm.polyglot.TypeLiteral; -import org.graalvm.polyglot.Value; -import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl; - import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Truffle; @@ -71,6 +48,7 @@ import com.oracle.truffle.api.interop.ArityException; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.InvalidBufferOffsetException; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnknownIdentifierException; import com.oracle.truffle.api.interop.UnsupportedMessageException; @@ -91,13 +69,16 @@ import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.CanInvokeNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetArrayElementNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetArraySizeNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetBufferSizeNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetMemberKeysNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetMemberNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetMetaQualifiedNameNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.GetMetaSimpleNameNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.HasArrayElementsNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.HasBufferElementsNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.HasMemberNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.HasMembersNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsBufferWritableNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsDateNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsDurationNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsExceptionNodeGen; @@ -109,10 +90,41 @@ import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.IsTimeZoneNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.NewInstanceNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.PutMemberNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.ReadBufferFloatNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.ReadBufferIntNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.RemoveArrayElementNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.RemoveMemberNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.SetArrayElementNodeGen; import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.ThrowExceptionNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.WriteBufferByteNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.WriteBufferDoubleNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.WriteBufferFloatNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.WriteBufferIntNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.WriteBufferLongNodeGen; +import com.oracle.truffle.polyglot.PolyglotValueFactory.InteropCodeCacheFactory.WriteBufferShortNodeGen; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.SourceSection; +import org.graalvm.polyglot.TypeLiteral; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl; + +import java.nio.ByteOrder; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; + +import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; +import static com.oracle.truffle.polyglot.EngineAccessor.RUNTIME; abstract class PolyglotValue extends AbstractValueImpl { @@ -155,7 +167,7 @@ public Value getArrayElement(Object receiver, long index) { } @TruffleBoundary - static final Value getArrayElementUnsupported(PolyglotLanguageContext context, Object receiver) { + static Value getArrayElementUnsupported(PolyglotLanguageContext context, Object receiver) { throw unsupported(context, receiver, "getArrayElement(long)", "hasArrayElements()"); } @@ -210,6 +222,254 @@ static long getArraySizeUnsupported(PolyglotLanguageContext context, Object rece throw unsupported(context, receiver, "getArraySize()", "hasArrayElements()"); } + // region Buffer Methods + + @Override + public boolean isBufferWritable(Object receiver) throws UnsupportedOperationException { + final Object prev = hostEnter(languageContext); + try { + throw isBufferWritableUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException isBufferWritableUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "isBufferWritable()", "hasBufferElements()"); + } + + @Override + public long getBufferSize(Object receiver) throws UnsupportedOperationException { + final Object prev = hostEnter(languageContext); + try { + throw getBufferSizeUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException getBufferSizeUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "getBufferSize()", "hasBufferElements()"); + } + + @Override + public byte readBufferByte(Object receiver, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw readBufferByteUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException readBufferByteUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "readBufferByte()", "hasBufferElements()"); + } + + @Override + public void writeBufferByte(Object receiver, long byteOffset, byte value) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw writeBufferByteUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException writeBufferByteUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "writeBufferByte()", "hasBufferElements()"); + } + + @Override + public short readBufferShort(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw readBufferShortUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException readBufferShortUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "readBufferShort()", "hasBufferElements()"); + } + + @Override + public void writeBufferShort(Object receiver, ByteOrder order, long byteOffset, short value) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw writeBufferShortUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException writeBufferShortUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "writeBufferShort()", "hasBufferElements()"); + } + + @Override + public int readBufferInt(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw readBufferIntUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException readBufferIntUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "readBufferInt()", "hasBufferElements()"); + } + + @Override + public void writeBufferInt(Object receiver, ByteOrder order, long byteOffset, int value) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw writeBufferIntUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException writeBufferIntUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "writeBufferInt()", "hasBufferElements()"); + } + + @Override + public long readBufferLong(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw readBufferLongUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException readBufferLongUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "readBufferLong()", "hasBufferElements()"); + } + + @Override + public void writeBufferLong(Object receiver, ByteOrder order, long byteOffset, long value) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw writeBufferLongUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException writeBufferLongUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "writeBufferLong()", "hasBufferElements()"); + } + + @Override + public float readBufferFloat(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw readBufferFloatUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException readBufferFloatUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "readBufferFloat()", "hasBufferElements()"); + } + + @Override + public void writeBufferFloat(Object receiver, ByteOrder order, long byteOffset, float value) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw writeBufferFloatUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException writeBufferFloatUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "writeBufferFloat()", "hasBufferElements()"); + } + + @Override + public double readBufferDouble(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw readBufferDoubleUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException readBufferDoubleUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "readBufferDouble()", "hasBufferElements()"); + } + + @Override + public void writeBufferDouble(Object receiver, ByteOrder order, long byteOffset, double value) throws UnsupportedOperationException, IndexOutOfBoundsException { + final Object prev = hostEnter(languageContext); + try { + throw writeBufferDoubleUnsupported(languageContext, receiver); + } catch (Throwable e) { + throw PolyglotImpl.guestToHostException(languageContext, e, true); + } finally { + hostLeave(languageContext, prev); + } + } + + @TruffleBoundary + static RuntimeException writeBufferDoubleUnsupported(PolyglotLanguageContext context, Object receiver) { + return unsupported(context, receiver, "writeBufferDouble()", "hasBufferElements()"); + } + + @TruffleBoundary + protected static RuntimeException invalidBufferIndex(PolyglotLanguageContext context, Object receiver, long byteOffset, long size) { + final String message = String.format("Invalid buffer access of length %d at byte offset %d for buffer %s.", size, byteOffset, getValueInfo(context, receiver)); + throw PolyglotEngineException.bufferIndexOutOfBounds(message); + } + + // endregion + @Override public Value getMember(Object receiver, String key) { Object prev = hostEnter(languageContext); @@ -652,7 +912,7 @@ public final Value getMetaObject(Object receiver) { try { return getMetaObjectImpl(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, prev); } @@ -711,7 +971,7 @@ protected static RuntimeException unsupported(PolyglotLanguageContext languageCo polyglotMessage = String.format("Unsupported operation %s.%s for %s.", Value.class.getSimpleName(), message, getValueInfo(languageContext, receiver)); } - throw PolyglotEngineException.unsupported(polyglotMessage); + return PolyglotEngineException.unsupported(polyglotMessage); } private static final int CHARACTER_LIMIT = 140; @@ -726,7 +986,6 @@ static String getValueInfo(PolyglotLanguageContext languageContext, Object recei assert false : "receiver should never be null"; return "null"; } - assert languageContext == null || !languageContext.context.engine.needsEnter(languageContext.context); PolyglotContextImpl context = languageContext.context; PolyglotLanguage displayLanguage = EngineAccessor.EngineImpl.findObjectLanguage(context.engine, receiver); Object view; @@ -891,7 +1150,7 @@ public final String toString(Object receiver) { try { return toStringImpl(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, prev); } @@ -1037,6 +1296,21 @@ static class InteropCodeCache { final CallTarget setArrayElement; final CallTarget removeArrayElement; final CallTarget getArraySize; + final CallTarget hasBufferElements; + final CallTarget isBufferWritable; + final CallTarget getBufferSize; + final CallTarget readBufferByte; + final CallTarget writeBufferByte; + final CallTarget readBufferShort; + final CallTarget writeBufferShort; + final CallTarget readBufferInt; + final CallTarget writeBufferInt; + final CallTarget readBufferLong; + final CallTarget writeBufferLong; + final CallTarget readBufferFloat; + final CallTarget writeBufferFloat; + final CallTarget readBufferDouble; + final CallTarget writeBufferDouble; final CallTarget hasMembers; final CallTarget hasMember; final CallTarget getMember; @@ -1091,6 +1365,21 @@ static class InteropCodeCache { this.setArrayElement = createTarget(SetArrayElementNodeGen.create(this)); this.removeArrayElement = createTarget(RemoveArrayElementNodeGen.create(this)); this.getArraySize = createTarget(GetArraySizeNodeGen.create(this)); + this.hasBufferElements = createTarget(HasBufferElementsNodeGen.create(this)); + this.isBufferWritable = createTarget(IsBufferWritableNodeGen.create(this)); + this.getBufferSize = createTarget(GetBufferSizeNodeGen.create(this)); + this.readBufferByte = createTarget(PolyglotValueFactory.InteropCodeCacheFactory.ReadBufferByteNodeGen.create(this)); + this.writeBufferByte = createTarget(WriteBufferByteNodeGen.create(this)); + this.readBufferShort = createTarget(PolyglotValueFactory.InteropCodeCacheFactory.ReadBufferShortNodeGen.create(this)); + this.writeBufferShort = createTarget(WriteBufferShortNodeGen.create(this)); + this.readBufferInt = createTarget(ReadBufferIntNodeGen.create(this)); + this.writeBufferInt = createTarget(WriteBufferIntNodeGen.create(this)); + this.readBufferLong = createTarget(PolyglotValueFactory.InteropCodeCacheFactory.ReadBufferLongNodeGen.create(this)); + this.writeBufferLong = createTarget(WriteBufferLongNodeGen.create(this)); + this.readBufferFloat = createTarget(ReadBufferFloatNodeGen.create(this)); + this.writeBufferFloat = createTarget(WriteBufferFloatNodeGen.create(this)); + this.readBufferDouble = createTarget(PolyglotValueFactory.InteropCodeCacheFactory.ReadBufferDoubleNodeGen.create(this)); + this.writeBufferDouble = createTarget(WriteBufferDoubleNodeGen.create(this)); this.hasMember = createTarget(HasMemberNodeGen.create(this)); this.getMember = createTarget(GetMemberNodeGen.create(this)); this.putMember = createTarget(PutMemberNodeGen.create(this)); @@ -1339,352 +1628,906 @@ static Object doCached(PolyglotLanguageContext context, Object receiver, Object[ @CachedLibrary("receiver") InteropLibrary objects, @Cached BranchProfile unsupported) { try { - return objects.asDuration(receiver); + return objects.asDuration(receiver); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + if (objects.isNull(receiver)) { + return null; + } else { + throw cannotConvert(context, receiver, null, "asDuration()", "isDuration()", "Value does not contain duration information."); + } + } + } + } + + abstract static class AsInstantNode extends InteropNode { + + protected AsInstantNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "getInstant"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary objects, + @Cached BranchProfile unsupported) { + try { + return objects.asInstant(receiver); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + if (objects.isNull(receiver)) { + return null; + } else { + throw cannotConvert(context, receiver, null, "asInstant()", "hasInstant()", "Value does not contain instant information."); + } + } + } + } + + private static class AsClassLiteralNode extends InteropNode { + + @Child ToHostNode toHost = ToHostNodeGen.create(); + + protected AsClassLiteralNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Class.class}; + } + + @Override + protected String getOperationName() { + return "as"; + } + + @Override + protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) { + return toHost.execute(receiver, (Class) args[ARGUMENT_OFFSET], null, context, true); + } + + } + + private static class AsTypeLiteralNode extends InteropNode { + + @Child ToHostNode toHost = ToHostNodeGen.create(); + + protected AsTypeLiteralNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, TypeLiteral.class}; + } + + @Override + protected String getOperationName() { + return "as"; + } + + @Override + protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) { + TypeLiteral typeLiteral = (TypeLiteral) args[ARGUMENT_OFFSET]; + return toHost.execute(receiver, typeLiteral.getRawType(), typeLiteral.getType(), context, true); + } + + } + + abstract static class IsNativePointerNode extends InteropNode { + + protected IsNativePointerNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "isNativePointer"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary natives) { + return natives.isPointer(receiver); + } + + } + + abstract static class AsNativePointerNode extends InteropNode { + + protected AsNativePointerNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "asNativePointer"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary natives, + @Cached BranchProfile unsupported) { + try { + return natives.asPointer(receiver); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + throw cannotConvert(context, receiver, long.class, "asNativePointer()", "isNativeObject()", "Value cannot be converted to a native pointer."); + } + } + + } + + abstract static class HasArrayElementsNode extends InteropNode { + + protected HasArrayElementsNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "hasArrayElements"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary arrays) { + return arrays.hasArrayElements(receiver); + } + + } + + abstract static class GetMemberKeysNode extends InteropNode { + + protected GetMemberKeysNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "getMemberKeys"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary objects, + @Cached("createToHost()") ToHostValueNode toHost, + @Cached BranchProfile unsupported) { + try { + return toHost.execute(context, objects.getMembers(receiver)); + } catch (UnsupportedMessageException e) { + return null; + } + } + } + + abstract static class GetArrayElementNode extends InteropNode { + + protected GetArrayElementNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class}; + } + + @Override + protected String getOperationName() { + return "getArrayElement"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary arrays, + @Cached("createToHost()") ToHostValueNode toHost, + @Cached BranchProfile unsupported, + @Cached BranchProfile unknown) { + long index = (long) args[ARGUMENT_OFFSET]; + try { + return toHost.execute(context, arrays.readArrayElement(receiver, index)); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + return getArrayElementUnsupported(context, receiver); + } catch (InvalidArrayIndexException e) { + unknown.enter(); + throw invalidArrayIndex(context, receiver, index); + } + } + } + + abstract static class SetArrayElementNode extends InteropNode { + protected SetArrayElementNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class, null}; + } + + @Override + protected String getOperationName() { + return "setArrayElement"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary arrays, + @Cached ToGuestValueNode toGuestValue, + @Cached BranchProfile unsupported, + @Cached BranchProfile invalidIndex, + @Cached BranchProfile invalidValue) { + long index = (long) args[ARGUMENT_OFFSET]; + Object value = toGuestValue.execute(context, args[ARGUMENT_OFFSET + 1]); + try { + arrays.writeArrayElement(receiver, index, value); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + setArrayElementUnsupported(context, receiver); + } catch (UnsupportedTypeException e) { + invalidValue.enter(); + throw invalidArrayValue(context, receiver, index, value); + } catch (InvalidArrayIndexException e) { + invalidIndex.enter(); + throw invalidArrayIndex(context, receiver, index); + } + return null; + } + } + + abstract static class RemoveArrayElementNode extends InteropNode { + + protected RemoveArrayElementNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class}; + } + + @Override + protected String getOperationName() { + return "removeArrayElement"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary arrays, + @Cached BranchProfile unsupported, + @Cached BranchProfile invalidIndex) { + long index = (long) args[ARGUMENT_OFFSET]; + Object value; + try { + arrays.removeArrayElement(receiver, index); + value = Boolean.TRUE; + } catch (UnsupportedMessageException e) { + unsupported.enter(); + throw removeArrayElementUnsupported(context, receiver); + } catch (InvalidArrayIndexException e) { + invalidIndex.enter(); + throw invalidArrayIndex(context, receiver, index); + } + return value; + } + + } + + abstract static class GetArraySizeNode extends InteropNode { + + protected GetArraySizeNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "getArraySize"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary arrays, + @Cached BranchProfile unsupported) { + try { + return arrays.getArraySize(receiver); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + return getArraySizeUnsupported(context, receiver); + } + } + + } + + // region Buffer nodes + + abstract static class HasBufferElementsNode extends InteropNode { + + protected HasBufferElementsNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "hasBufferElements"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary buffers) { + return buffers.hasBufferElements(receiver); + } + + } + + abstract static class IsBufferWritableNode extends InteropNode { + + protected IsBufferWritableNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "isBufferWritable"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached BranchProfile unsupported) { + try { + return buffers.isBufferWritable(receiver); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + throw getBufferSizeUnsupported(context, receiver); + } + } + + } + + abstract static class GetBufferSizeNode extends InteropNode { + + protected GetBufferSizeNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + } + + @Override + protected String getOperationName() { + return "getBufferSize"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached BranchProfile unsupported) { + try { + return buffers.getBufferSize(receiver); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + throw getBufferSizeUnsupported(context, receiver); + } + } + + } + + abstract static class ReadBufferByteNode extends InteropNode { + + protected ReadBufferByteNode(InteropCodeCache interop) { + super(interop); + } + + @Override + protected Class[] getArgumentTypes() { + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class}; + } + + @Override + protected String getOperationName() { + return "readBufferByte"; + } + + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached("createToHost()") ToHostValueNode toHost, + @Cached BranchProfile unsupported, + @Cached BranchProfile unknown) { + final long byteOffset = (long) args[ARGUMENT_OFFSET]; + try { + return buffers.readBufferByte(receiver, byteOffset); } catch (UnsupportedMessageException e) { unsupported.enter(); - if (objects.isNull(receiver)) { - return null; - } else { - throw cannotConvert(context, receiver, null, "asDuration()", "isDuration()", "Value does not contain duration information."); - } + throw readBufferByteUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + unknown.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); } } - } - abstract static class AsInstantNode extends InteropNode { + } - protected AsInstantNode(InteropCodeCache interop) { + abstract static class WriteBufferByteNode extends InteropNode { + protected WriteBufferByteNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class, Byte.class}; } @Override protected String getOperationName() { - return "getInstant"; + return "writeBufferByte"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary objects, - @Cached BranchProfile unsupported) { + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached BranchProfile unsupported, + @Cached BranchProfile invalidIndex, + @Cached BranchProfile invalidValue) { + final long byteOffset = (long) args[ARGUMENT_OFFSET]; + final byte value = (byte) args[ARGUMENT_OFFSET + 1]; try { - return objects.asInstant(receiver); + buffers.writeBufferByte(receiver, byteOffset, value); } catch (UnsupportedMessageException e) { unsupported.enter(); - if (objects.isNull(receiver)) { - return null; - } else { - throw cannotConvert(context, receiver, null, "asInstant()", "hasInstant()", "Value does not contain instant information."); + if (buffers.hasBufferElements(receiver)) { + throw unsupported(context, receiver, "writeBufferByte()", "isBufferWritable()"); } + throw writeBufferByteUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + invalidIndex.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); } + return null; } - } - private static class AsClassLiteralNode extends InteropNode { + } - @Child ToHostNode toHost = ToHostNodeGen.create(); + abstract static class ReadBufferShortNode extends InteropNode { - protected AsClassLiteralNode(InteropCodeCache interop) { + protected ReadBufferShortNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Class.class}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class}; } @Override protected String getOperationName() { - return "as"; + return "readBufferShort"; } - @Override - protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) { - return toHost.execute(receiver, (Class) args[ARGUMENT_OFFSET], null, context, true); + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached("createToHost()") ToHostValueNode toHost, + @Cached BranchProfile unsupported, + @Cached BranchProfile unknown) { + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; + try { + return buffers.readBufferShort(receiver, order, byteOffset); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + throw readBufferShortUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + unknown.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); + } } } - private static class AsTypeLiteralNode extends InteropNode { - - @Child ToHostNode toHost = ToHostNodeGen.create(); - - protected AsTypeLiteralNode(InteropCodeCache interop) { + abstract static class WriteBufferShortNode extends InteropNode { + protected WriteBufferShortNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, TypeLiteral.class}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class, Short.class}; } @Override protected String getOperationName() { - return "as"; + return "writeBufferShort"; } - @Override - protected Object executeImpl(PolyglotLanguageContext context, Object receiver, Object[] args) { - TypeLiteral typeLiteral = (TypeLiteral) args[ARGUMENT_OFFSET]; - return toHost.execute(receiver, typeLiteral.getRawType(), typeLiteral.getType(), context, true); + @Specialization(limit = "CACHE_LIMIT") + static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached BranchProfile unsupported, + @Cached BranchProfile invalidIndex, + @Cached BranchProfile invalidValue) { + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; + final short value = (short) args[ARGUMENT_OFFSET + 2]; + try { + buffers.writeBufferShort(receiver, order, byteOffset, value); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + if (buffers.hasBufferElements(receiver)) { + throw unsupported(context, receiver, "writeBufferShort()", "isBufferWritable()"); + } + throw writeBufferShortUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + invalidIndex.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); + } + return null; } } - abstract static class IsNativePointerNode extends InteropNode { + abstract static class ReadBufferIntNode extends InteropNode { - protected IsNativePointerNode(InteropCodeCache interop) { + protected ReadBufferIntNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class}; } @Override protected String getOperationName() { - return "isNativePointer"; + return "readBufferInt"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary natives) { - return natives.isPointer(receiver); + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached("createToHost()") ToHostValueNode toHost, + @Cached BranchProfile unsupported, + @Cached BranchProfile unknown) { + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; + try { + return buffers.readBufferInt(receiver, order, byteOffset); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + throw readBufferIntUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + unknown.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); + } } } - abstract static class AsNativePointerNode extends InteropNode { - - protected AsNativePointerNode(InteropCodeCache interop) { + abstract static class WriteBufferIntNode extends InteropNode { + protected WriteBufferIntNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class, Integer.class}; } @Override protected String getOperationName() { - return "asNativePointer"; + return "writeBufferInt"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary natives, - @Cached BranchProfile unsupported) { + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached BranchProfile unsupported, + @Cached BranchProfile invalidIndex, + @Cached BranchProfile invalidValue) { + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; + final int value = (int) args[ARGUMENT_OFFSET + 2]; try { - return natives.asPointer(receiver); + buffers.writeBufferInt(receiver, order, byteOffset, value); } catch (UnsupportedMessageException e) { unsupported.enter(); - throw cannotConvert(context, receiver, long.class, "asNativePointer()", "isNativeObject()", "Value cannot be converted to a native pointer."); + if (buffers.hasBufferElements(receiver)) { + throw unsupported(context, receiver, "writeBufferInt()", "isBufferWritable()"); + } + throw writeBufferIntUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + invalidIndex.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); } + return null; } } - abstract static class HasArrayElementsNode extends InteropNode { + abstract static class ReadBufferLongNode extends InteropNode { - protected HasArrayElementsNode(InteropCodeCache interop) { + protected ReadBufferLongNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class}; } @Override protected String getOperationName() { - return "hasArrayElements"; + return "readBufferLong"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary arrays) { - return arrays.hasArrayElements(receiver); + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached("createToHost()") ToHostValueNode toHost, + @Cached BranchProfile unsupported, + @Cached BranchProfile unknown) { + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; + try { + return buffers.readBufferLong(receiver, order, byteOffset); + } catch (UnsupportedMessageException e) { + unsupported.enter(); + throw readBufferLongUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + unknown.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); + } } } - abstract static class GetMemberKeysNode extends InteropNode { - - protected GetMemberKeysNode(InteropCodeCache interop) { + abstract static class WriteBufferLongNode extends InteropNode { + protected WriteBufferLongNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class, Long.class}; } @Override protected String getOperationName() { - return "getMemberKeys"; + return "writeBufferLong"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary objects, - @Cached("createToHost()") ToHostValueNode toHost, - @Cached BranchProfile unsupported) { + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached BranchProfile unsupported, + @Cached BranchProfile invalidIndex, + @Cached BranchProfile invalidValue) { + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; + final long value = (long) args[ARGUMENT_OFFSET + 2]; try { - return toHost.execute(context, objects.getMembers(receiver)); + buffers.writeBufferLong(receiver, order, byteOffset, value); } catch (UnsupportedMessageException e) { - return null; + unsupported.enter(); + if (buffers.hasBufferElements(receiver)) { + throw unsupported(context, receiver, "writeBufferLong()", "isBufferWritable()"); + } + throw writeBufferLongUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + invalidIndex.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); } + return null; } + } - abstract static class GetArrayElementNode extends InteropNode { + abstract static class ReadBufferFloatNode extends InteropNode { - protected GetArrayElementNode(InteropCodeCache interop) { + protected ReadBufferFloatNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class}; } @Override protected String getOperationName() { - return "getArrayElement"; + return "readBufferFloat"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary arrays, + @CachedLibrary("receiver") InteropLibrary buffers, @Cached("createToHost()") ToHostValueNode toHost, @Cached BranchProfile unsupported, @Cached BranchProfile unknown) { - long index = (long) args[ARGUMENT_OFFSET]; + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; try { - return toHost.execute(context, arrays.readArrayElement(receiver, index)); + return buffers.readBufferFloat(receiver, order, byteOffset); } catch (UnsupportedMessageException e) { unsupported.enter(); - return getArrayElementUnsupported(context, receiver); - } catch (InvalidArrayIndexException e) { + throw readBufferFloatUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { unknown.enter(); - throw invalidArrayIndex(context, receiver, index); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); } } + } - abstract static class SetArrayElementNode extends InteropNode { - protected SetArrayElementNode(InteropCodeCache interop) { + abstract static class WriteBufferFloatNode extends InteropNode { + protected WriteBufferFloatNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class, null}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class, Float.class}; } @Override protected String getOperationName() { - return "setArrayElement"; + return "writeBufferFloat"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary arrays, - @Cached ToGuestValueNode toGuestValue, + @CachedLibrary("receiver") InteropLibrary buffers, @Cached BranchProfile unsupported, @Cached BranchProfile invalidIndex, @Cached BranchProfile invalidValue) { - long index = (long) args[ARGUMENT_OFFSET]; - Object value = toGuestValue.execute(context, args[ARGUMENT_OFFSET + 1]); + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; + final float value = (float) args[ARGUMENT_OFFSET + 2]; try { - arrays.writeArrayElement(receiver, index, value); + buffers.writeBufferFloat(receiver, order, byteOffset, value); } catch (UnsupportedMessageException e) { unsupported.enter(); - setArrayElementUnsupported(context, receiver); - } catch (UnsupportedTypeException e) { - invalidValue.enter(); - throw invalidArrayValue(context, receiver, index, value); - } catch (InvalidArrayIndexException e) { + if (buffers.hasBufferElements(receiver)) { + throw unsupported(context, receiver, "writeBufferFloat()", "isBufferWritable()"); + } + throw writeBufferFloatUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { invalidIndex.enter(); - throw invalidArrayIndex(context, receiver, index); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); } return null; } + } - abstract static class RemoveArrayElementNode extends InteropNode { + abstract static class ReadBufferDoubleNode extends InteropNode { - protected RemoveArrayElementNode(InteropCodeCache interop) { + protected ReadBufferDoubleNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, Long.class}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class}; } @Override protected String getOperationName() { - return "removeArrayElement"; + return "readBufferDouble"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary arrays, + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached("createToHost()") ToHostValueNode toHost, @Cached BranchProfile unsupported, - @Cached BranchProfile invalidIndex) { - long index = (long) args[ARGUMENT_OFFSET]; - Object value; + @Cached BranchProfile unknown) { + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; try { - arrays.removeArrayElement(receiver, index); - value = Boolean.TRUE; + return buffers.readBufferDouble(receiver, order, byteOffset); } catch (UnsupportedMessageException e) { unsupported.enter(); - throw removeArrayElementUnsupported(context, receiver); - } catch (InvalidArrayIndexException e) { - invalidIndex.enter(); - throw invalidArrayIndex(context, receiver, index); + throw readBufferDoubleUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + unknown.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); } - return value; } } - abstract static class GetArraySizeNode extends InteropNode { - - protected GetArraySizeNode(InteropCodeCache interop) { + abstract static class WriteBufferDoubleNode extends InteropNode { + protected WriteBufferDoubleNode(InteropCodeCache interop) { super(interop); } @Override protected Class[] getArgumentTypes() { - return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType}; + return new Class[]{PolyglotLanguageContext.class, polyglot.receiverType, ByteOrder.class, Long.class, Double.class}; } @Override protected String getOperationName() { - return "getArraySize"; + return "writeBufferDouble"; } @Specialization(limit = "CACHE_LIMIT") static Object doCached(PolyglotLanguageContext context, Object receiver, Object[] args, // - @CachedLibrary("receiver") InteropLibrary arrays, - @Cached BranchProfile unsupported) { + @CachedLibrary("receiver") InteropLibrary buffers, + @Cached BranchProfile unsupported, + @Cached BranchProfile invalidIndex, + @Cached BranchProfile invalidValue) { + final ByteOrder order = (ByteOrder) args[ARGUMENT_OFFSET]; + final long byteOffset = (long) args[ARGUMENT_OFFSET + 1]; + final double value = (double) args[ARGUMENT_OFFSET + 2]; try { - return arrays.getArraySize(receiver); + buffers.writeBufferDouble(receiver, order, byteOffset, value); } catch (UnsupportedMessageException e) { unsupported.enter(); - return getArraySizeUnsupported(context, receiver); + if (buffers.hasBufferElements(receiver)) { + throw unsupported(context, receiver, "writeBufferDouble()", "isBufferWritable()"); + } + throw writeBufferDoubleUnsupported(context, receiver); + } catch (InvalidBufferOffsetException e) { + invalidIndex.enter(); + throw invalidBufferIndex(context, receiver, e.getByteOffset(), e.getLength()); } + return null; } } + // endregion + abstract static class GetMemberNode extends InteropNode { protected GetMemberNode(InteropCodeCache interop) { @@ -2735,6 +3578,85 @@ public long getArraySize(Object receiver) { return (long) RUNTIME.callProfiled(cache.getArraySize, languageContext, receiver); } + // region Buffer Methods + + @Override + public boolean hasBufferElements(Object receiver) { + return (boolean) RUNTIME.callProfiled(cache.hasBufferElements, languageContext, receiver); + } + + @Override + public boolean isBufferWritable(Object receiver) { + return (boolean) RUNTIME.callProfiled(cache.isBufferWritable, languageContext, receiver); + } + + @Override + public long getBufferSize(Object receiver) throws UnsupportedOperationException { + return (long) RUNTIME.callProfiled(cache.getBufferSize, languageContext, receiver); + } + + @Override + public byte readBufferByte(Object receiver, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return (byte) RUNTIME.callProfiled(cache.readBufferByte, languageContext, receiver, byteOffset); + } + + @Override + public void writeBufferByte(Object receiver, long byteOffset, byte value) throws UnsupportedOperationException, IndexOutOfBoundsException { + RUNTIME.callProfiled(cache.writeBufferByte, languageContext, receiver, byteOffset, value); + } + + @Override + public short readBufferShort(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return (short) RUNTIME.callProfiled(cache.readBufferShort, languageContext, receiver, order, byteOffset); + } + + @Override + public void writeBufferShort(Object receiver, ByteOrder order, long byteOffset, short value) throws UnsupportedOperationException, IndexOutOfBoundsException { + RUNTIME.callProfiled(cache.writeBufferShort, languageContext, receiver, order, byteOffset, value); + } + + @Override + public int readBufferInt(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return (int) RUNTIME.callProfiled(cache.readBufferInt, languageContext, receiver, order, byteOffset); + } + + @Override + public void writeBufferInt(Object receiver, ByteOrder order, long byteOffset, int value) throws UnsupportedOperationException, IndexOutOfBoundsException { + RUNTIME.callProfiled(cache.writeBufferInt, languageContext, receiver, order, byteOffset, value); + } + + @Override + public long readBufferLong(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return (long) RUNTIME.callProfiled(cache.readBufferLong, languageContext, receiver, order, byteOffset); + } + + @Override + public void writeBufferLong(Object receiver, ByteOrder order, long byteOffset, long value) throws UnsupportedOperationException, IndexOutOfBoundsException { + RUNTIME.callProfiled(cache.writeBufferLong, languageContext, receiver, order, byteOffset, value); + } + + @Override + public float readBufferFloat(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return (float) RUNTIME.callProfiled(cache.readBufferFloat, languageContext, receiver, order, byteOffset); + } + + @Override + public void writeBufferFloat(Object receiver, ByteOrder order, long byteOffset, float value) throws UnsupportedOperationException, IndexOutOfBoundsException { + RUNTIME.callProfiled(cache.writeBufferFloat, languageContext, receiver, order, byteOffset, value); + } + + @Override + public double readBufferDouble(Object receiver, ByteOrder order, long byteOffset) throws UnsupportedOperationException, IndexOutOfBoundsException { + return (double) RUNTIME.callProfiled(cache.readBufferDouble, languageContext, receiver, order, byteOffset); + } + + @Override + public void writeBufferDouble(Object receiver, ByteOrder order, long byteOffset, double value) throws UnsupportedOperationException, IndexOutOfBoundsException { + RUNTIME.callProfiled(cache.writeBufferDouble, languageContext, receiver, order, byteOffset, value); + } + + // endregion + @Override public boolean hasMembers(Object receiver) { return (boolean) RUNTIME.callProfiled(cache.hasMembers, languageContext, receiver); @@ -2920,7 +3842,7 @@ public boolean isNumber(Object receiver) { try { return UNCACHED_INTEROP.isNumber(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -2932,7 +3854,7 @@ public boolean fitsInByte(Object receiver) { try { return UNCACHED_INTEROP.fitsInByte(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -2948,7 +3870,7 @@ public byte asByte(Object receiver) { return asByteUnsupported(receiver); } } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -2960,7 +3882,7 @@ public boolean isString(Object receiver) { try { return UNCACHED_INTEROP.isString(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -2979,7 +3901,7 @@ public String asString(Object receiver) { return asStringUnsupported(receiver); } } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -2991,7 +3913,7 @@ public boolean fitsInInt(Object receiver) { try { return UNCACHED_INTEROP.fitsInInt(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3007,7 +3929,7 @@ public int asInt(Object receiver) { return asIntUnsupported(receiver); } } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3019,7 +3941,7 @@ public boolean isBoolean(Object receiver) { try { return InteropLibrary.getFactory().getUncached().isBoolean(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3035,7 +3957,7 @@ public boolean asBoolean(Object receiver) { return asBooleanUnsupported(receiver); } } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3047,7 +3969,7 @@ public boolean fitsInFloat(Object receiver) { try { return InteropLibrary.getFactory().getUncached().fitsInFloat(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3063,7 +3985,7 @@ public float asFloat(Object receiver) { return asFloatUnsupported(receiver); } } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3075,7 +3997,7 @@ public boolean fitsInDouble(Object receiver) { try { return UNCACHED_INTEROP.fitsInDouble(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3091,7 +4013,7 @@ public double asDouble(Object receiver) { return asDoubleUnsupported(receiver); } } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3103,7 +4025,7 @@ public boolean fitsInLong(Object receiver) { try { return UNCACHED_INTEROP.fitsInLong(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3119,7 +4041,7 @@ public long asLong(Object receiver) { return asLongUnsupported(receiver); } } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3131,7 +4053,7 @@ public boolean fitsInShort(Object receiver) { try { return UNCACHED_INTEROP.fitsInShort(receiver); } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } @@ -3147,7 +4069,7 @@ public short asShort(Object receiver) { return asShortUnsupported(receiver); } } catch (Throwable e) { - throw PolyglotImpl.guestToHostException((languageContext), e, true); + throw PolyglotImpl.guestToHostException(languageContext, e, true); } finally { hostLeave(languageContext, c); } diff --git a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/JavaHostLanguageProvider.java b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/JavaHostLanguageProvider.java index b84d9a3c7ac9..28a2ee5f3416 100644 --- a/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/JavaHostLanguageProvider.java +++ b/truffle/src/com.oracle.truffle.tck.tests/src/com/oracle/truffle/tck/tests/JavaHostLanguageProvider.java @@ -40,6 +40,21 @@ */ package com.oracle.truffle.tck.tests; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Source; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.ProxyArray; +import org.graalvm.polyglot.proxy.ProxyDate; +import org.graalvm.polyglot.proxy.ProxyDuration; +import org.graalvm.polyglot.proxy.ProxyExecutable; +import org.graalvm.polyglot.proxy.ProxyObject; +import org.graalvm.polyglot.proxy.ProxyTime; +import org.graalvm.polyglot.proxy.ProxyTimeZone; +import org.graalvm.polyglot.tck.LanguageProvider; +import org.graalvm.polyglot.tck.Snippet; +import org.graalvm.polyglot.tck.TypeDescriptor; + +import java.nio.ByteBuffer; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; @@ -59,20 +74,6 @@ import java.util.function.Function; import java.util.function.Supplier; -import org.graalvm.polyglot.Context; -import org.graalvm.polyglot.Source; -import org.graalvm.polyglot.Value; -import org.graalvm.polyglot.proxy.ProxyArray; -import org.graalvm.polyglot.proxy.ProxyDate; -import org.graalvm.polyglot.proxy.ProxyDuration; -import org.graalvm.polyglot.proxy.ProxyExecutable; -import org.graalvm.polyglot.proxy.ProxyObject; -import org.graalvm.polyglot.proxy.ProxyTime; -import org.graalvm.polyglot.proxy.ProxyTimeZone; -import org.graalvm.polyglot.tck.LanguageProvider; -import org.graalvm.polyglot.tck.Snippet; -import org.graalvm.polyglot.tck.TypeDescriptor; - public final class JavaHostLanguageProvider implements LanguageProvider { private static final String ID = "java-host"; @@ -134,6 +135,7 @@ public Collection createValueConstructors(final Context conte for (Primitive primitive : primitives.values()) { result.add(createPrimitive(context, primitive)); } + // Arrays result.add(Snippet.newBuilder("Array", export(context, new ValueSupplier<>(new int[]{1, 2})), TypeDescriptor.array(TypeDescriptor.NUMBER)).build()); @@ -145,6 +147,10 @@ public Collection createValueConstructors(final Context conte result.add(createProxyArray(context, primitive)); } + // Buffers + result.add(Snippet.newBuilder("HeapByteBuffer", export(context, new ValueSupplier<>(ByteBuffer.wrap(new byte[]{1, 2, 3}))), TypeDescriptor.OBJECT).build()); + result.add(Snippet.newBuilder("HeapByteBufferR", export(context, new ValueSupplier<>(ByteBuffer.wrap(new byte[]{1, 2, 3}).asReadOnlyBuffer())), TypeDescriptor.OBJECT).build()); + // Object Proxies result.add(Snippet.newBuilder("Proxy", export(context, new ValueSupplier<>(ProxyObject.fromMap(Collections.emptyMap()))), TypeDescriptor.OBJECT).build()); final Map props = new HashMap<>(); 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 d5bbd1fc46c8..47135c19e9d5 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 @@ -40,8 +40,40 @@ */ package com.oracle.truffle.tck.tests; +import org.graalvm.polyglot.HostAccess.Implementable; +import org.graalvm.polyglot.PolyglotException; +import org.graalvm.polyglot.TypeLiteral; +import org.graalvm.polyglot.Value; +import org.graalvm.polyglot.proxy.Proxy; + +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.function.Function; + import static com.oracle.truffle.tck.tests.ValueAssert.Trait.ARRAY_ELEMENTS; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BOOLEAN; +import static com.oracle.truffle.tck.tests.ValueAssert.Trait.BUFFER_ELEMENTS; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.DATE; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.DURATION; import static com.oracle.truffle.tck.tests.ValueAssert.Trait.EXCEPTION; @@ -66,35 +98,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.lang.reflect.Modifier; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.function.Function; - -import org.graalvm.polyglot.HostAccess.Implementable; -import org.graalvm.polyglot.PolyglotException; -import org.graalvm.polyglot.TypeLiteral; -import org.graalvm.polyglot.Value; -import org.graalvm.polyglot.proxy.Proxy; - public class ValueAssert { private static final TypeLiteral> OBJECT_LIST = new TypeLiteral>() { @@ -332,6 +335,31 @@ public static void assertUnsupported(Value value, Trait... supported) { assertNull(value.as(Object[].class)); } break; + case BUFFER_ELEMENTS: + assertFalse(value.hasBufferElements()); + assertFails(() -> value.isBufferWritable(), UnsupportedOperationException.class); + assertFails(() -> value.getBufferSize(), UnsupportedOperationException.class); + assertFails(() -> value.readBufferByte(0), UnsupportedOperationException.class); + assertFails(() -> value.writeBufferByte(0, (byte) 0), UnsupportedOperationException.class); + assertFails(() -> value.readBufferShort(ByteOrder.LITTLE_ENDIAN, 0), UnsupportedOperationException.class); + assertFails(() -> value.writeBufferShort(ByteOrder.LITTLE_ENDIAN, 0, (short) 0), UnsupportedOperationException.class); + assertFails(() -> value.readBufferInt(ByteOrder.LITTLE_ENDIAN, 0), UnsupportedOperationException.class); + assertFails(() -> value.writeBufferInt(ByteOrder.LITTLE_ENDIAN, 0, 0), UnsupportedOperationException.class); + assertFails(() -> value.readBufferLong(ByteOrder.LITTLE_ENDIAN, 0), UnsupportedOperationException.class); + assertFails(() -> value.writeBufferLong(ByteOrder.LITTLE_ENDIAN, 0, 0L), UnsupportedOperationException.class); + assertFails(() -> value.readBufferFloat(ByteOrder.LITTLE_ENDIAN, 0), UnsupportedOperationException.class); + assertFails(() -> value.writeBufferFloat(ByteOrder.LITTLE_ENDIAN, 0, 0f), UnsupportedOperationException.class); + assertFails(() -> value.readBufferDouble(ByteOrder.LITTLE_ENDIAN, 0), UnsupportedOperationException.class); + assertFails(() -> value.writeBufferDouble(ByteOrder.LITTLE_ENDIAN, 0, 0.0), UnsupportedOperationException.class); + + if (!value.isNull()) { + if ((!value.isHostObject() || (!(value.asHostObject() instanceof ByteBuffer)))) { + assertFails(() -> value.as(ByteBuffer.class), ClassCastException.class); + } + } else { + assertNull(value.as(ByteBuffer.class)); + } + break; case HOST_OBJECT: assertFalse(value.isHostObject()); assertFails(() -> value.asHostObject(), ClassCastException.class); @@ -471,6 +499,10 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces assertTrue(msg, value.hasArrayElements()); assertValueArrayElements(value, depth, hasHostAccess); break; + case BUFFER_ELEMENTS: + assertTrue(msg, value.hasBufferElements()); + assertValueBufferElements(value); + break; case EXECUTABLE: assertTrue(msg, value.canExecute()); assertFunctionalInterfaceMapping(value); @@ -493,13 +525,18 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces if (isStaticClass) { assertClassMembers(value, (Class) hostObject, true); } else { - if (hasHostAccess) { - assertClassMembers(value, Class.class, false); - assertTrue(value.hasMember("static")); - } + assertClassMembers(value, Class.class, false); + assertTrue(value.hasMember("static")); } } else { - assertClassMembers(value, hostObject.getClass(), false); + // 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; + } + } } } assertEquals(Value.asValue(hostObject), value); @@ -518,35 +555,39 @@ private static void assertValueImpl(Value value, int depth, boolean hasHostAcces case MEMBERS: assertTrue(msg, value.hasMembers()); - Map expectedValues = new HashMap<>(); for (String key : value.getMemberKeys()) { Value child = value.getMember(key); - expectedValues.put(key, child.as(Object.class)); if (!isSameHostObject(value, child)) { assertValueImpl(child, depth + 1, hasHostAccess, detectSupportedTypes(child)); } } - if (value.isHostObject() && value.asHostObject() instanceof Map) { - expectedValues = value.asHostObject(); + if (value.isNull()) { + assertNull(value.as(STRING_OBJECT_MAP)); + } else if (value.isHostObject() && value.asHostObject() instanceof Map) { + Map expectedValues = value.asHostObject(); + assertEquals(value.as(OBJECT_OBJECT_MAP), expectedValues); } else { - if (value.isNull()) { - assertNull(value.as(STRING_OBJECT_MAP)); - } else { - Map stringMap = value.as(STRING_OBJECT_MAP); - assertTrue(expectedValues.equals(expectedValues)); - assertTrue(stringMap.equals(stringMap)); - assertFalse(value.as(STRING_OBJECT_MAP).equals(expectedValues)); - assertTrue(value.as(STRING_OBJECT_MAP).equals(value.as(STRING_OBJECT_MAP))); - Set keySet = value.as(Map.class).keySet(); - assertEquals(value.getMemberKeys(), keySet); - for (String key : keySet) { - assertTrue(value.hasMember(key)); - } - assertNotNull(value.as(STRING_OBJECT_MAP).hashCode()); - assertNotNull(value.as(STRING_OBJECT_MAP).toString()); - assertEquals(value.toString(), value.as(Map.class).toString()); + Map expectedValues = new HashMap<>(); + for (String key : value.getMemberKeys()) { + Value child = value.getMember(key); + expectedValues.put(key, child.as(Object.class)); + } + + Map stringMap = value.as(STRING_OBJECT_MAP); + assertEquals("PolyglotMap should be equal with itself", stringMap, stringMap); + assertEquals("Two PolyglotMaps wrapping the same host object should be equal", value.as(STRING_OBJECT_MAP), value.as(STRING_OBJECT_MAP)); + assertNotEquals("A PolyglotMap should not be equal with a Map", value.as(STRING_OBJECT_MAP), expectedValues); + + Set keySet = value.as(Map.class).keySet(); + assertEquals(value.getMemberKeys(), keySet); + + for (String key : keySet) { + assertTrue(value.hasMember(key)); } + + assertNotNull(value.as(STRING_OBJECT_MAP).toString()); + assertEquals(value.toString(), value.as(Map.class).toString()); } break; case NATIVE: @@ -675,6 +716,54 @@ private static void assertValueArrayElements(Value value, int depth, boolean has assertCollectionEqualValues(receivedObjectsLongMap.values(), objectMap4.values()); } + private static void assertValueBufferElements(Value value) { + assertTrue(value.hasBufferElements()); + final boolean isWritable = value.isBufferWritable(); + + for (long i = 0L; i < value.getBufferSize(); i++) { + final byte result = value.readBufferByte(i); + if (isWritable) { + // Write the same value in order not to change buffer's content. + value.writeBufferByte(i, result); + } + } + + for (long i = 0L; i < value.getBufferSize() - 1; i += 2) { + final short result = value.readBufferShort(ByteOrder.LITTLE_ENDIAN, i); + if (isWritable) { + value.writeBufferShort(ByteOrder.LITTLE_ENDIAN, i, result); + } + } + + for (long i = 0L; i < value.getBufferSize() - 3; i += 4) { + final int result = value.readBufferInt(ByteOrder.LITTLE_ENDIAN, i); + if (isWritable) { + value.writeBufferInt(ByteOrder.LITTLE_ENDIAN, i, result); + } + } + + for (long i = 0L; i < value.getBufferSize() - 7; i += 8) { + final long result = value.readBufferLong(ByteOrder.LITTLE_ENDIAN, i); + if (isWritable) { + value.writeBufferLong(ByteOrder.LITTLE_ENDIAN, i, result); + } + } + + for (long i = 0L; i < value.getBufferSize() - 3; i += 4) { + final float result = value.readBufferFloat(ByteOrder.LITTLE_ENDIAN, i); + if (isWritable) { + value.writeBufferFloat(ByteOrder.LITTLE_ENDIAN, i, result); + } + } + + for (long i = 0L; i < value.getBufferSize() - 7; i += 8) { + final double result = value.readBufferDouble(ByteOrder.LITTLE_ENDIAN, i); + if (isWritable) { + value.writeBufferDouble(ByteOrder.LITTLE_ENDIAN, i, result); + } + } + } + private static void assertCollectionEqualValues(Collection expected, Collection actual) { assertEquals(expected.size(), actual.size()); Iterator expectedi = expected.iterator(); @@ -903,6 +992,9 @@ private static Trait[] detectSupportedTypes(Value value) { if (value.hasArrayElements()) { valueTypes.add(ARRAY_ELEMENTS); } + if (value.hasBufferElements()) { + valueTypes.add(BUFFER_ELEMENTS); + } if (value.canInstantiate()) { valueTypes.add(INSTANTIABLE); } @@ -949,6 +1041,7 @@ public enum Trait { INSTANTIABLE, MEMBERS, ARRAY_ELEMENTS, + BUFFER_ELEMENTS, DATE, TIME, TIMEZONE,