Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.oracle.truffle.tck.tests.ValueAssert Maven / Gradle / Ivy
/*
* Copyright (c) 2018, 2022, 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.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;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.EXECUTABLE;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.HASH;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.HOST_OBJECT;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.INSTANTIABLE;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.ITERABLE;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.ITERATOR;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.MEMBERS;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.META;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.NATIVE;
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.Trait.TIME;
import static com.oracle.truffle.tck.tests.ValueAssert.Trait.TIMEZONE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class ValueAssert {
private static final TypeLiteral> OBJECT_LIST = new TypeLiteral<>() {
};
private static final TypeLiteral> OBJECT_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> STRING_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> LONG_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> INTEGER_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> SHORT_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> BYTE_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> NUMBER_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> FLOAT_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> DOUBLE_OBJECT_MAP = new TypeLiteral<>() {
};
private static final TypeLiteral> FUNCTION = new TypeLiteral<>() {
};
private static final TypeLiteral> OBJECT_ITERABLE = new TypeLiteral<>() {
};
private static final TypeLiteral> OBJECT_ITERATOR = new TypeLiteral<>() {
};
public static void assertValue(Value value) {
assertValue(value, detectSupportedTypes(value));
}
public static void assertValue(Value value, Trait... expectedTypes) {
try {
assertValueImpl(value, 0, true, expectedTypes);
} catch (AssertionError e) {
e.addSuppressed(new AssertionError(String.format("assertValue: %s traits: %s", value, Arrays.asList(expectedTypes))));
throw e;
}
}
public static void assertValue(Value value, boolean hasHostAccess, Trait... expectedTypes) {
try {
assertValueImpl(value, 0, hasHostAccess, expectedTypes);
} catch (AssertionError e) {
e.addSuppressed(new AssertionError(String.format("assertValue: %s traits: %s", value, Arrays.asList(expectedTypes))));
throw e;
}
}
public static void assertValueFast(Value value) {
try {
/*
* The idea is that type detection triggers all type checks which triggers basic
* assertions.
*/
detectSupportedTypes(value);
} catch (AssertionError e) {
e.addSuppressed(new AssertionError(String.format("assertValue: %s", value)));
throw e;
}
}
public static void assertUnsupported(Value value, Trait... supported) {
Set supportedSet = new HashSet<>(Arrays.asList(supported));
for (Trait unsupportedType : Trait.values()) {
if (supportedSet.contains(unsupportedType)) {
continue;
}
switch (unsupportedType) {
case NUMBER:
assertFalse(value.isNumber());
assertFalse(value.fitsInByte());
assertFalse(value.fitsInShort());
assertFalse(value.fitsInInt());
assertFalse(value.fitsInLong());
assertFalse(value.fitsInFloat());
assertFalse(value.fitsInDouble());
if (value.isNull()) {
assertNull(value.as(Number.class));
assertNull(value.as(Byte.class));
assertNull(value.as(Short.class));
assertNull(value.as(Integer.class));
assertNull(value.as(Long.class));
assertNull(value.as(Float.class));
assertFails(() -> value.as(byte.class), NullPointerException.class);
assertFails(() -> value.as(short.class), NullPointerException.class);
assertFails(() -> value.as(int.class), NullPointerException.class);
assertFails(() -> value.as(long.class), NullPointerException.class);
assertFails(() -> value.as(float.class), NullPointerException.class);
assertFails(() -> value.asByte(), NullPointerException.class);
assertFails(() -> value.asShort(), NullPointerException.class);
assertFails(() -> value.asInt(), NullPointerException.class);
assertFails(() -> value.asLong(), NullPointerException.class);
assertFails(() -> value.asFloat(), NullPointerException.class);
assertFails(() -> value.asDouble(), NullPointerException.class);
} else {
if (value.isHostObject() && value.asHostObject() instanceof Number) {
assertSame(value.asHostObject(), value.as(Number.class));
} else {
assertFails(() -> value.as(Number.class), ClassCastException.class);
}
assertFails(() -> value.as(Byte.class), ClassCastException.class);
assertFails(() -> value.as(Short.class), ClassCastException.class);
assertFails(() -> value.as(Integer.class), ClassCastException.class);
assertFails(() -> value.as(Long.class), ClassCastException.class);
assertFails(() -> value.as(Float.class), ClassCastException.class);
assertFails(() -> value.as(byte.class), ClassCastException.class);
assertFails(() -> value.as(short.class), ClassCastException.class);
assertFails(() -> value.as(int.class), ClassCastException.class);
assertFails(() -> value.as(long.class), ClassCastException.class);
assertFails(() -> value.as(float.class), ClassCastException.class);
assertFails(() -> value.asByte(), ClassCastException.class);
assertFails(() -> value.asShort(), ClassCastException.class);
assertFails(() -> value.asInt(), ClassCastException.class);
assertFails(() -> value.asLong(), ClassCastException.class);
assertFails(() -> value.asFloat(), ClassCastException.class);
assertFails(() -> value.asDouble(), ClassCastException.class);
}
break;
case BOOLEAN:
assertFalse(value.isBoolean());
if (value.isNull()) {
assertFails(() -> value.asBoolean(), NullPointerException.class);
assertFails(() -> value.as(boolean.class), NullPointerException.class);
assertNull(value.as(Boolean.class));
} else {
assertFails(() -> value.asBoolean(), ClassCastException.class);
assertFails(() -> value.as(boolean.class), ClassCastException.class);
assertFails(() -> value.as(Boolean.class), ClassCastException.class);
}
break;
case STRING:
assertFalse(value.isString());
if (value.isNull()) {
assertNull(value.as(String.class));
assertNull(value.as(Character.class));
assertNull(value.asString());
} else {
assertFails(() -> value.asString(), ClassCastException.class);
assertFails(() -> value.as(String.class), ClassCastException.class);
if (value.fitsInInt() && value.asInt() >= 0 && value.asInt() < 65536) {
assertEquals((Character) (char) value.asInt(), value.as(Character.class));
assertEquals((Character) (char) value.asInt(), value.as(char.class));
} else {
assertFails(() -> value.as(Character.class), ClassCastException.class);
assertFails(() -> value.as(char.class), ClassCastException.class);
}
}
break;
case MEMBERS:
assertFalse(value.hasMembers());
assertFalse(value.hasMember("asdf"));
assertFails(() -> value.getMember("asdf"), UnsupportedOperationException.class);
assertFails(() -> value.putMember("", ""), UnsupportedOperationException.class);
assertFails(() -> value.removeMember(""), UnsupportedOperationException.class);
assertFails(() -> value.invokeMember(""), UnsupportedOperationException.class);
assertTrue(value.getMemberKeys().isEmpty());
if (value.isNull()) {
assertNull(value.as(Map.class));
} else {
if ((!value.isHostObject() || (!(value.asHostObject() instanceof Map))) && !value.hasHashEntries()) {
assertFails(() -> value.as(Map.class), ClassCastException.class);
}
}
break;
case EXECUTABLE:
assertFalse(value.toString(), value.canExecute());
assertFails(() -> value.execute(), UnsupportedOperationException.class);
if (value.isNull()) {
assertNull(value.as(Function.class));
assertNull(value.as(IsFunctionalInterfaceVarArgs.class));
} else if (!value.canInstantiate()) {
/*
* Proxy mapping fails in AOT mode if not configured. That is fine.
*/
if (value.hasMembers()) {
assertFails(() -> value.as(FUNCTION).apply(null), UnsupportedOperationException.class);
assertFails(() -> value.as(IsFunctionalInterfaceVarArgs.class).foobarbaz(123), UnsupportedOperationException.class);
} else if (!value.isHostObject() || (!(value.asHostObject() instanceof Function))) {
assertFails(() -> value.as(FUNCTION), ClassCastException.class);
assertFails(() -> value.as(IsFunctionalInterfaceVarArgs.class), ClassCastException.class);
}
}
break;
case INSTANTIABLE:
assertFalse(value.canInstantiate());
assertFails(() -> value.newInstance(), UnsupportedOperationException.class);
if (value.isNull()) {
assertNull(value.as(Function.class));
assertNull(value.as(IsFunctionalInterfaceVarArgs.class));
} else if (!value.canExecute()) {
/*
* Proxy mapping fails in AOT mode if not configured. That is fine.
*/
if (value.hasMembers()) {
assertFails(() -> value.as(FUNCTION).apply(null), UnsupportedOperationException.class);
assertFails(() -> value.as(IsFunctionalInterfaceVarArgs.class).foobarbaz(123), UnsupportedOperationException.class);
} else if (!value.isHostObject() || (!(value.asHostObject() instanceof Function))) {
assertFails(() -> value.as(FUNCTION), ClassCastException.class);
assertFails(() -> value.as(IsFunctionalInterfaceVarArgs.class), ClassCastException.class);
}
}
break;
case NULL:
assertFalse(value.isNull());
break;
case ARRAY_ELEMENTS:
assertFalse(value.hasArrayElements());
assertFails(() -> value.getArrayElement(0), UnsupportedOperationException.class);
assertFails(() -> value.setArrayElement(0, null), UnsupportedOperationException.class);
assertFails(() -> value.getArraySize(), UnsupportedOperationException.class);
if (!value.isNull()) {
if ((!value.isHostObject() || (!(value.asHostObject() instanceof List) && !(value.asHostObject() instanceof Object[])))) {
assertFails(() -> value.as(List.class), ClassCastException.class);
assertFails(() -> value.as(Object[].class), ClassCastException.class);
}
} else {
assertNull(value.as(List.class));
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);
break;
case PROXY_OBJECT:
assertFalse(value.isProxyObject());
assertFails(() -> value.asProxyObject(), ClassCastException.class);
break;
case NATIVE:
assertFalse(value.isNativePointer());
assertFails(() -> value.asNativePointer(), ClassCastException.class);
break;
case DATE:
assertFalse(value.isDate());
if (value.isNull()) {
assertNull(value.asDate());
assertNull(value.asInstant());
assertNull(value.as(LocalDateTime.class));
assertNull(value.as(LocalDate.class));
assertNull(value.as(ZonedDateTime.class));
assertNull(value.as(Date.class));
} else {
assertFails(() -> value.asDate(), ClassCastException.class);
assertFails(() -> value.asInstant(), ClassCastException.class);
assertFails(() -> value.as(LocalDateTime.class), ClassCastException.class);
assertFails(() -> value.as(LocalDate.class), ClassCastException.class);
assertFails(() -> value.as(ZonedDateTime.class), ClassCastException.class);
assertFails(() -> value.as(Date.class), ClassCastException.class);
}
break;
case TIME:
assertFalse(value.isTime());
if (value.isNull()) {
assertNull(value.asTime());
assertNull(value.asInstant());
assertNull(value.as(LocalDateTime.class));
assertNull(value.as(LocalTime.class));
assertNull(value.as(ZonedDateTime.class));
assertNull(value.as(Date.class));
} else {
assertFails(() -> value.asTime(), ClassCastException.class);
assertFails(() -> value.asInstant(), ClassCastException.class);
assertFails(() -> value.as(LocalDateTime.class), ClassCastException.class);
assertFails(() -> value.as(LocalTime.class), ClassCastException.class);
assertFails(() -> value.as(ZonedDateTime.class), ClassCastException.class);
assertFails(() -> value.as(Date.class), ClassCastException.class);
}
break;
case TIMEZONE:
assertFalse(value.isTimeZone());
if (value.isNull()) {
assertNull(value.asTimeZone());
assertNull(value.asInstant());
assertNull(value.as(ZoneId.class));
assertNull(value.as(ZoneOffset.class));
assertNull(value.as(ZonedDateTime.class));
assertNull(value.as(Date.class));
} else {
assertFails(() -> value.asTimeZone(), ClassCastException.class);
assertFails(() -> value.asInstant(), ClassCastException.class);
assertFails(() -> value.as(ZoneId.class), ClassCastException.class);
assertFails(() -> value.as(ZoneOffset.class), ClassCastException.class);
assertFails(() -> value.as(ZonedDateTime.class), ClassCastException.class);
assertFails(() -> value.as(Date.class), ClassCastException.class);
}
break;
case DURATION:
assertFalse(value.isDuration());
if (value.isNull()) {
assertNull(value.asDuration());
assertNull(value.as(Duration.class));
} else {
assertFails(() -> value.asDuration(), ClassCastException.class);
assertFails(() -> value.as(Duration.class), ClassCastException.class);
}
break;
case EXCEPTION:
assertFalse(value.isException());
assertFails(() -> value.throwException(), UnsupportedOperationException.class);
break;
case META:
assertFalse(value.isMetaObject());
assertFails(() -> value.getMetaQualifiedName(), UnsupportedOperationException.class);
assertFails(() -> value.getMetaSimpleName(), UnsupportedOperationException.class);
assertFails(() -> value.isMetaInstance(""), UnsupportedOperationException.class);
assertFalse(value.hasMetaParents());
assertFails(() -> value.getMetaParents(), UnsupportedOperationException.class);
break;
case ITERABLE:
assertFalse(value.hasIterator());
assertFails(() -> value.getIterator(), UnsupportedOperationException.class);
break;
case ITERATOR:
assertFalse(value.isIterator());
assertFails(() -> value.hasIteratorNextElement(), UnsupportedOperationException.class);
assertFails(() -> value.getIteratorNextElement(), UnsupportedOperationException.class);
break;
case HASH:
assertFalse(value.hasHashEntries());
assertFalse(value.hasHashEntry("asdf"));
assertFails(() -> value.getHashValue("asdf"), UnsupportedOperationException.class);
assertFails(() -> value.putHashEntry("", ""), UnsupportedOperationException.class);
assertFails(() -> value.removeHashEntry(""), UnsupportedOperationException.class);
assertFails(() -> value.getHashEntriesIterator(), UnsupportedOperationException.class);
if (value.isNull()) {
assertNull(value.as(Map.class));
} else {
if ((!value.isHostObject() || (!(value.asHostObject() instanceof Map))) && !value.hasMembers()) {
assertFails(() -> value.as(Map.class), ClassCastException.class);
}
}
break;
default:
throw new AssertionError();
}
}
}
@SuppressWarnings("unchecked")
private static void assertValueImpl(Value value, int depth, boolean hasHostAccess, Trait... expectedTypes) {
if (depth > 1) {
// stop at a certain recursion depth for recursive data structures
return;
}
assertNotNull(value.toString());
Value metaObject = value.getMetaObject();
if (metaObject != null && depth == 0) { // meta-object may be null
assertValueImpl(metaObject, depth + 1, hasHostAccess, detectSupportedTypes(metaObject));
assertNotNull(metaObject.toString());
}
assertSame(value, value.as(Value.class));
for (Trait supportedType : expectedTypes) {
String msg = "expected " + supportedType.name() + " but was " + value.toString();
switch (supportedType) {
case NULL:
assertTrue(msg, value.isNull());
break;
case BOOLEAN:
assertTrue(msg, value.isBoolean());
boolean booleanValue = value.asBoolean();
assertEquals(booleanValue, value.as(Boolean.class));
assertEquals(booleanValue, value.as(boolean.class));
break;
case STRING:
assertTrue(msg, value.isString());
String stringValue = value.asString();
assertEquals(stringValue, value.as(String.class));
if (stringValue.length() == 1) {
assertEquals(stringValue.charAt(0), (char) value.as(Character.class));
assertEquals(stringValue.charAt(0), (char) value.as(char.class));
}
break;
case NUMBER:
assertValueNumber(value);
break;
case ARRAY_ELEMENTS:
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);
break;
case INSTANTIABLE:
assertTrue(msg, value.canInstantiate());
value.as(Function.class);
// otherwise its ambiguous with the executable semantics.
if (!value.canExecute()) {
assertFunctionalInterfaceMapping(value);
}
break;
case HOST_OBJECT:
assertTrue(msg, value.isHostObject());
Object hostObject = value.asHostObject();
assertFalse(hostObject instanceof Proxy);
boolean isStaticClass = false;
if (hasHostAccess && hostObject != null && value.hasMembers() && !java.lang.reflect.Proxy.isProxyClass(hostObject.getClass())) {
if (hostObject instanceof Class) {
isStaticClass = value.hasMember("class");
if (isStaticClass) {
assertClassMembers(value, (Class>) hostObject, true);
} else {
assertClassMembers(value, Class.class, false);
assertTrue(value.hasMember("static"));
}
} else {
// Asserts that value exposes the same members as the host object's
// class first public inclusive ancestor.
for (Class> clazz = hostObject.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
if (Modifier.isPublic(clazz.getModifiers())) {
assertClassMembers(value, clazz, false);
break;
}
}
}
}
if (isStaticClass) {
assertNotEquals(Value.asValue(hostObject), value);
} else {
assertEquals(Value.asValue(hostObject), value);
}
assertEquals(Value.asValue(hostObject).hashCode(), value.hashCode());
break;
case PROXY_OBJECT:
assertTrue(msg, value.isProxyObject());
Object proxyObject = value.asProxyObject();
assertTrue(proxyObject instanceof Proxy);
assertEquals(Value.asValue(proxyObject), value);
assertEquals(Value.asValue(proxyObject).hashCode(), value.hashCode());
break;
case MEMBERS:
assertTrue(msg, value.hasMembers());
for (String key : value.getMemberKeys()) {
Value child = value.getMember(key);
if (child == null) {
assertTrue("A non-readable member must at least be modifiable, removable, or invocable", value.hasMember(key));
} else if (!isSameHostObject(value, child)) {
assertValueImpl(child, depth + 1, hasHostAccess, detectSupportedTypes(child));
}
}
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.hasHashEntries()) {
assertHashKeys(value);
} else {
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:
assertTrue(msg, value.isNativePointer());
value.asNativePointer();
break;
case DATE:
assertFalse(value.isNull());
assertTrue(value.isDate());
assertNotNull(value.asDate());
assertNotNull(value.as(LocalDate.class));
if (value.isTime()) {
assertEquals(value.as(LocalDateTime.class).toLocalDate(), value.as(LocalDate.class));
assertEquals(value.as(LocalDateTime.class).toLocalTime(), value.as(LocalTime.class));
if (value.isTimeZone()) {
assertNotNull(value.asInstant());
assertEquals(value.as(ZonedDateTime.class).toLocalDate(), value.as(LocalDate.class));
assertNotNull(value.as(Instant.class));
assertNotNull(value.as(Date.class));
}
}
break;
case TIME:
assertTrue(value.isTime());
assertNotNull(value.asTime());
assertNotNull(value.as(LocalTime.class));
if (value.isDate()) {
// asserted by DATE trait
} else {
if (value.isTimeZone()) {
// invalid combination
assertFails(() -> value.asTime(), AssertionError.class);
assertFails(() -> value.asTimeZone(), AssertionError.class);
}
}
break;
case TIMEZONE:
assertTrue(value.isTimeZone());
assertNotNull(value.asTimeZone());
assertNotNull(value.as(ZoneId.class));
break;
case DURATION:
assertTrue(value.isDuration());
assertNotNull(value.asDuration());
assertNotNull(value.as(Duration.class));
break;
case EXCEPTION:
assertTrue(value.isException());
try {
value.throwException();
fail("should have thrown");
} catch (PolyglotException expected) {
// caught expected exception
} catch (UnsupportedOperationException unsupported) {
throw new AssertionError(unsupported);
}
break;
case META:
assertTrue(value.isMetaObject());
assertNotNull(value.getMetaQualifiedName());
assertNotNull(value.getMetaSimpleName());
value.isMetaInstance("");
if (value.hasMetaParents()) {
try {
Value metaParents = value.getMetaParents();
assertTrue(metaParents.hasArrayElements());
long size = metaParents.getArraySize();
for (long i = 0; i < size; i++) {
Value metaParent = metaParents.getArrayElement(i);
assertValueImpl(metaParent, depth + 1, hasHostAccess, detectSupportedTypes(metaParent));
}
} catch (PolyglotException notExpected) {
throw new AssertionError(notExpected);
} catch (UnsupportedOperationException expected) {
// caught expected exception
}
} else {
try {
value.getMetaParents();
fail("should have thrown");
} catch (PolyglotException expected) {
throw new AssertionError(expected);
} catch (UnsupportedOperationException expected) {
// caught expected exception
}
}
break;
case ITERABLE:
assertTrue(msg, value.hasIterator());
assertValueIterable(value, depth, hasHostAccess);
break;
case ITERATOR:
assertTrue(msg, value.isIterator());
value.hasIteratorNextElement();
break;
case HASH:
assertTrue(msg, value.hasHashEntries());
assertValueHash(value, depth, hasHostAccess);
break;
default:
throw new AssertionError();
}
}
assertUnsupported(value, expectedTypes);
}
private static boolean isSameHostObject(Value a, Value b) {
return a.isHostObject() && b.isHostObject() && a.asHostObject() == b.asHostObject();
}
@SuppressWarnings("unchecked")
private static void assertValueArrayElements(Value value, int depth, boolean hasHostAccess) {
assertTrue(value.hasArrayElements());
List receivedObjects = new ArrayList<>();
Map receivedObjectsLongMap = new HashMap<>();
Map receivedObjectsIntMap = new HashMap<>();
for (long i = 0L; i < value.getArraySize(); i++) {
Value arrayElement = value.getArrayElement(i);
receivedObjects.add(arrayElement.as(Object.class));
receivedObjectsLongMap.put(i, arrayElement.as(Object.class));
receivedObjectsIntMap.put((int) i, arrayElement.as(Object.class));
assertValueImpl(arrayElement, depth + 1, hasHostAccess, detectSupportedTypes(arrayElement));
}
List objectList1 = value.as(OBJECT_LIST);
List objectList2 = Arrays.asList(value.as(Object[].class));
if (!value.isHostObject() || !(value.asHostObject() instanceof List>)) {
assertFalse(objectList1.equals(objectList2));
}
assertTrue(objectList1.equals(objectList1));
assertTrue(value.as(OBJECT_LIST).equals(value.as(OBJECT_LIST)));
assertNotEquals(0, objectList1.hashCode());
assertNotNull(objectList1.toString());
assertCollectionEqualValues(receivedObjects, objectList1);
assertCollectionEqualValues(receivedObjects, objectList2);
if (value.hasHashEntries()) {
assertHashKeys(value);
} else {
if (value.hasMembers()) {
Map objectMap1 = value.as(OBJECT_OBJECT_MAP);
assertTrue(objectMap1.keySet().equals(value.getMemberKeys()));
} else {
assertFails(() -> value.as(OBJECT_OBJECT_MAP), ClassCastException.class);
}
Map objectMap2 = value.as(LONG_OBJECT_MAP);
Map objectMap3 = value.as(INTEGER_OBJECT_MAP);
Map objectMap4 = value.as(NUMBER_OBJECT_MAP);
assertFails(() -> value.as(SHORT_OBJECT_MAP), ClassCastException.class);
assertFails(() -> value.as(BYTE_OBJECT_MAP), ClassCastException.class);
assertFails(() -> value.as(FLOAT_OBJECT_MAP), ClassCastException.class);
assertFails(() -> value.as(DOUBLE_OBJECT_MAP), ClassCastException.class);
assertCollectionEqualValues(receivedObjectsLongMap.values(), objectMap2.values());
assertCollectionEqualValues(receivedObjectsIntMap.values(), objectMap3.values());
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 extends Object> expected, Collection extends Object> actual) {
assertEquals(expected.size(), actual.size());
Iterator extends Object> expectedi = expected.iterator();
Iterator extends Object> actuali = actual.iterator();
while (expectedi.hasNext()) {
assertEqualValues(expectedi.next(), actuali.next());
}
}
private static void assertEqualValues(Object expected, Object actual) {
Value v0 = Value.asValue(expected);
Value v1 = Value.asValue(actual);
List expectTraits = Arrays.asList(detectSupportedTypes(v0));
List actualTraits = Arrays.asList(detectSupportedTypes(v1));
assertEquals(expectTraits, actualTraits);
for (Trait trait : actualTraits) {
switch (trait) {
case NUMBER:
if (v0.fitsInLong()) {
assertEquals(v0.asLong(), v1.asLong());
} else {
assertFalse(v1.fitsInLong());
}
if (v0.fitsInDouble()) {
assertEquals(Double.doubleToLongBits(v0.asDouble()), Double.doubleToLongBits(v1.asDouble()));
} else {
assertFalse(v1.fitsInDouble());
}
break;
case STRING:
assertEquals(v0.asString(), v1.asString());
break;
case BOOLEAN:
assertEquals(v0.asBoolean(), v1.asBoolean());
break;
case HOST_OBJECT:
assertEquals(v0, v1);
break;
case PROXY_OBJECT:
assertEquals(v0, v1);
break;
case DURATION:
assertEquals(v0.asDuration(), v1.asDuration());
break;
case DATE:
assertEquals(v0.asDate(), v1.asDate());
break;
case TIME:
assertEquals(v0.asTime(), v1.asTime());
break;
case TIMEZONE:
assertEquals(v0.asTimeZone(), v1.asTimeZone());
break;
default:
break;
}
}
}
private static void assertValueIterable(Value value, int depth, boolean hasHostAccess) {
assertTrue(value.hasIterator());
List receivedObjects = new ArrayList<>();
Value iterator = value.getIterator();
while (iterator.hasIteratorNextElement()) {
Value element = iterator.getIteratorNextElement();
receivedObjects.add(element.as(Object.class));
assertValueImpl(element, depth + 1, hasHostAccess, detectSupportedTypes(element));
}
Iterable objectIterable = value.as(OBJECT_ITERABLE);
assertTrue(objectIterable.equals(objectIterable));
assertTrue(value.as(OBJECT_ITERABLE).equals(value.as(OBJECT_ITERABLE)));
assertNotEquals(0, objectIterable.hashCode());
assertNotNull(objectIterable.toString());
Iterator receivedIterator = receivedObjects.iterator();
Iterator objectIterator1 = objectIterable.iterator();
Iterator objectIterator2 = value.getIterator().as(OBJECT_ITERATOR);
while (objectIterator1.hasNext() && objectIterator2.hasNext() && receivedIterator.hasNext()) {
Object expected = receivedIterator.next();
assertEqualValues(expected, objectIterator1.next());
assertEqualValues(expected, objectIterator2.next());
}
assertFalse(objectIterator1.hasNext() || objectIterator2.hasNext() || receivedIterator.hasNext());
}
private static void assertValueHash(Value value, int depth, boolean hasHostAccess) {
assertTrue(value.hasHashEntries());
Map receivedObjects = new HashMap<>();
Value iterator = value.getHashEntriesIterator();
while (iterator.hasIteratorNextElement()) {
Value element = iterator.getIteratorNextElement();
assertTrue(element.hasArrayElements());
receivedObjects.put(element.getArrayElement(0).as(Object.class), element.getArrayElement(1).as(Object.class));
assertValueImpl(element, depth + 1, hasHostAccess, detectSupportedTypes(element));
}
Map objectMap = value.as(OBJECT_OBJECT_MAP);
assertTrue(objectMap.equals(objectMap));
assertTrue(value.as(OBJECT_OBJECT_MAP).equals(value.as(OBJECT_OBJECT_MAP)));
assertNotNull(objectMap.toString());
Iterator> receivedIterator = receivedObjects.entrySet().iterator();
Iterator> objectIterator1 = objectMap.entrySet().iterator();
while (objectIterator1.hasNext() && receivedIterator.hasNext()) {
Map.Entry expected = receivedIterator.next();
Map.Entry actual = objectIterator1.next();
assertEqualValues(expected.getKey(), actual.getKey());
assertEqualValues(expected.getValue(), actual.getValue());
}
assertFalse(objectIterator1.hasNext() || receivedIterator.hasNext());
}
private static void assertHashKeys(Value value) {
Set hashKeys = new HashSet<>();
for (Value iterator = value.getHashKeysIterator(); iterator.hasIteratorNextElement();) {
hashKeys.add(iterator.getIteratorNextElement().as(Object.class));
}
Map hashMap = value.as(OBJECT_OBJECT_MAP);
assertTrue(hashMap.keySet().equals(hashKeys));
}
@SafeVarargs
private static void assertFails(Runnable runnable, Class extends Throwable>... exceptionType) {
assertFails(() -> {
runnable.run();
return null;
}, exceptionType);
}
@SafeVarargs
private static void assertFails(Callable> callable, Class extends Throwable>... exceptionTypes) {
try {
callable.call();
} catch (Throwable t) {
boolean found = false;
for (Class extends Throwable> exceptionType : exceptionTypes) {
if (exceptionType.isInstance(t)) {
found = true;
}
}
if (!found) {
Set names = new LinkedHashSet<>();
for (Class extends Throwable> exceptionType : exceptionTypes) {
names.add(exceptionType.getName());
}
throw new AssertionError("expected instanceof " + names + " was " + t.getClass().getName(), t);
}
return;
}
Set names = new LinkedHashSet<>();
for (Class extends Throwable> exceptionType : exceptionTypes) {
names.add(exceptionType.getName());
}
fail("expected " + names + " but no exception was thrown");
}
private static void assertValueNumber(Value value) {
assertTrue(value.isNumber());
if (value.fitsInByte()) {
value.asByte();
} else {
assertFails(() -> value.asByte(), ClassCastException.class);
}
if (value.fitsInShort()) {
short shortValue = value.asShort();
assertEquals((byte) shortValue == shortValue, value.fitsInByte());
} else {
assertFails(() -> value.asShort(), ClassCastException.class);
}
if (value.fitsInInt()) {
int intValue = value.asInt();
assertEquals((byte) intValue == intValue, value.fitsInByte());
assertEquals((short) intValue == intValue, value.fitsInShort());
} else {
assertFails(() -> value.asInt(), ClassCastException.class);
}
if (value.fitsInLong()) {
long longValue = value.asLong();
assertEquals((byte) longValue == longValue, value.fitsInByte());
assertEquals((short) longValue == longValue, value.fitsInShort());
assertEquals((int) longValue == longValue, value.fitsInInt());
} else {
assertFails(() -> value.asLong(), ClassCastException.class);
}
if (value.fitsInFloat()) {
value.asFloat();
} else {
assertFails(() -> value.asFloat(), ClassCastException.class);
}
if (value.fitsInDouble()) {
value.asDouble();
} else {
assertFails(() -> value.asDouble(), ClassCastException.class);
}
}
private static void assertClassMembers(Value value, Class> expectedClass, boolean staticMembers) {
for (java.lang.reflect.Method m : expectedClass.getMethods()) {
if (Modifier.isPublic(m.getModifiers()) && Modifier.isStatic(m.getModifiers()) == staticMembers) {
assertTrue(m.getName(), value.hasMember(m.getName()));
}
}
}
@Implementable
public interface EmptyInterface {
}
@Implementable
public interface NonFunctionalInterface {
void foobarbaz();
// rare name that has no conflicts
void oldmacdonaldhadafarm();
}
@FunctionalInterface
public interface IsFunctionalInterfaceVarArgs {
Object foobarbaz(Object... args);
}
@SuppressWarnings({"unchecked", "cast"})
private static void assertFunctionalInterfaceMapping(Value value) {
if (value.isHostObject()) {
assertNotNull(value.as(Function.class));
} else {
assertNotNull((Function) value.as(Object.class));
}
assertNotNull(value.as(Function.class));
assertNotNull(value.as(IsFunctionalInterfaceVarArgs.class));
// mapping an empty interface works with any value.
if (value.isNull()) {
assertNull(value.as(EmptyInterface.class));
} else {
if (value.hasMembers()) {
assertTrue(value.as(EmptyInterface.class) instanceof EmptyInterface);
} else {
assertFails(() -> value.as(EmptyInterface.class), ClassCastException.class);
}
}
Function f = (Function) value.as(Function.class);
assertEquals(f, f);
assertEquals(value.as(Function.class), value.as(Function.class));
assertNotEquals(value.as(Function.class), (Function) (e) -> e);
assertNotNull(value.as(Function.class).toString());
assertNotEquals(0, value.as(Function.class).hashCode());
if (value.hasMembers() && !value.hasMember("foobarbaz")) {
assertFails(() -> value.as(NonFunctionalInterface.class).foobarbaz(), UnsupportedOperationException.class);
}
}
private static Trait[] detectSupportedTypes(Value value) {
List valueTypes = new ArrayList<>();
if (value.isBoolean()) {
valueTypes.add(BOOLEAN);
}
if (value.isHostObject()) {
valueTypes.add(HOST_OBJECT);
}
if (value.isNativePointer()) {
valueTypes.add(NATIVE);
}
if (value.isString()) {
valueTypes.add(STRING);
}
if (value.isNumber()) {
valueTypes.add(NUMBER);
}
if (value.hasMembers()) {
valueTypes.add(MEMBERS);
}
if (value.hasArrayElements()) {
valueTypes.add(ARRAY_ELEMENTS);
}
if (value.hasBufferElements()) {
valueTypes.add(BUFFER_ELEMENTS);
}
if (value.canInstantiate()) {
valueTypes.add(INSTANTIABLE);
}
if (value.canExecute()) {
valueTypes.add(EXECUTABLE);
}
if (value.isNull()) {
valueTypes.add(NULL);
}
if (value.isProxyObject()) {
valueTypes.add(PROXY_OBJECT);
}
if (value.isDate()) {
valueTypes.add(DATE);
}
if (value.isTime()) {
valueTypes.add(TIME);
}
if (value.isTimeZone()) {
valueTypes.add(TIMEZONE);
}
if (value.isDuration()) {
valueTypes.add(DURATION);
}
if (value.isException()) {
valueTypes.add(EXCEPTION);
}
if (value.isMetaObject()) {
valueTypes.add(META);
}
if (value.hasIterator()) {
valueTypes.add(ITERABLE);
}
if (value.isIterator()) {
valueTypes.add(ITERATOR);
}
if (value.hasHashEntries()) {
valueTypes.add(HASH);
}
return valueTypes.toArray(new Trait[0]);
}
public enum Trait {
NULL,
HOST_OBJECT,
PROXY_OBJECT,
NUMBER,
STRING,
BOOLEAN,
NATIVE,
EXECUTABLE,
INSTANTIABLE,
MEMBERS,
ARRAY_ELEMENTS,
BUFFER_ELEMENTS,
DATE,
TIME,
TIMEZONE,
DURATION,
EXCEPTION,
META,
ITERABLE,
ITERATOR,
HASH
}
}