From b0e2b3ccc57bb65a7200bdbbd20849732eb6286b Mon Sep 17 00:00:00 2001 From: Allan Gregersen Date: Thu, 19 Dec 2024 11:24:15 +0100 Subject: [PATCH] Use ToEspressoNode for Polyglot.cast operations to streamline behavior --- .../espresso/nodes/interop/ToPrimitive.java | 57 +++- .../espresso/nodes/interop/ToReference.java | 38 ++- .../staticobject/EspressoInterop.java | 28 +- ...le_truffle_espresso_polyglot_Polyglot.java | 277 ++++++------------ 4 files changed, 177 insertions(+), 223 deletions(-) diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToPrimitive.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToPrimitive.java index b20ff88d6831..9cd769d79df1 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToPrimitive.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToPrimitive.java @@ -34,6 +34,7 @@ import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.espresso.meta.EspressoError; +import com.oracle.truffle.espresso.runtime.dispatch.staticobject.EspressoInterop; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; /** @@ -102,8 +103,13 @@ int doHost(Integer value) { @Specialization int doEspresso(StaticObject value, @Cached BranchProfile exceptionProfile) throws UnsupportedTypeException { - if (value != null && !StaticObject.isNull(value) && value.getKlass() == getMeta().java_lang_Integer) { - return (int) getMeta().java_lang_Integer_value.get(value); + if (value != null && !StaticObject.isNull(value) && EspressoInterop.fitsInInt(value)) { + try { + return EspressoInterop.asInt(value); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("Contract violation: if fitsInInt returns true, asInt must succeed."); + } } exceptionProfile.enter(); throw UnsupportedTypeException.create(new Object[]{value}, EspressoError.cat("Cannot cast ", value, " to int")); @@ -148,8 +154,13 @@ byte doHost(Byte value) { @Specialization byte doEspresso(StaticObject value, @Cached BranchProfile exceptionProfile) throws UnsupportedTypeException { - if (value != null && !StaticObject.isNull(value) && value.getKlass() == getMeta().java_lang_Byte) { - return (byte) getMeta().java_lang_Byte_value.get(value); + if (value != null && !StaticObject.isNull(value) && EspressoInterop.fitsInByte(value)) { + try { + return EspressoInterop.asByte(value); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("Contract violation: if fitsInByte returns true, asByte must succeed."); + } } exceptionProfile.enter(); throw UnsupportedTypeException.create(new Object[]{value}, EspressoError.cat("Cannot cast ", value, " to byte")); @@ -194,8 +205,13 @@ short doHost(Short value) { @Specialization short doEspresso(StaticObject value, @Cached BranchProfile exceptionProfile) throws UnsupportedTypeException { - if (value != null && !StaticObject.isNull(value) && value.getKlass() == getMeta().java_lang_Short) { - return (short) getMeta().java_lang_Short_value.get(value); + if (value != null && !StaticObject.isNull(value) && EspressoInterop.fitsInShort(value)) { + try { + return EspressoInterop.asShort(value); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("Contract violation: if fitsInShort returns true, asShort must succeed."); + } } exceptionProfile.enter(); throw UnsupportedTypeException.create(new Object[]{value}, EspressoError.cat("Cannot cast ", value, " to short")); @@ -264,7 +280,7 @@ char doForeign(Object value, throw UnsupportedTypeException.create(new Object[]{value}, EspressoError.cat("Cannot cast ", s, " to char")); } catch (UnsupportedMessageException e) { CompilerDirectives.transferToInterpreterAndInvalidate(); - throw EspressoError.shouldNotReachHere("Contract violation: if fitsInInt returns true, asInt must succeed."); + throw EspressoError.shouldNotReachHere("Contract violation: if isString returns true, asString must succeed."); } } @@ -292,8 +308,13 @@ long doHost(Long value) { @Specialization long doEspresso(StaticObject value, @Cached BranchProfile exceptionProfile) throws UnsupportedTypeException { - if (value != null && !StaticObject.isNull(value) && value.getKlass() == getMeta().java_lang_Long) { - return (long) getMeta().java_lang_Long_value.get(value); + if (value != null && !StaticObject.isNull(value) && EspressoInterop.fitsInLong(value)) { + try { + return EspressoInterop.asLong(value); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("Contract violation: if fitsInLong returns true, asLong must succeed."); + } } exceptionProfile.enter(); throw UnsupportedTypeException.create(new Object[]{value}, EspressoError.cat("Cannot cast ", value, " to long")); @@ -338,8 +359,13 @@ float doHost(Float value) { @Specialization float doEspresso(StaticObject value, @Cached BranchProfile exceptionProfile) throws UnsupportedTypeException { - if (value != null && !StaticObject.isNull(value) && value.getKlass() == getMeta().java_lang_Float) { - return (float) getMeta().java_lang_Float_value.get(value); + if (value != null && !StaticObject.isNull(value) && EspressoInterop.fitsInFloat(value)) { + try { + return EspressoInterop.asFloat(value); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("Contract violation: if fitsInFloat returns true, asFloat must succeed."); + } } exceptionProfile.enter(); throw UnsupportedTypeException.create(new Object[]{value}, EspressoError.cat("Cannot cast ", value, " to float")); @@ -384,8 +410,13 @@ public abstract static class ToDouble extends ToPrimitive { @Specialization double doEspresso(StaticObject value, @Cached BranchProfile exceptionProfile) throws UnsupportedTypeException { - if (value != null && !StaticObject.isNull(value) && value.getKlass() == getMeta().java_lang_Double) { - return (double) getMeta().java_lang_Double_value.get(value); + if (value != null && !StaticObject.isNull(value) && EspressoInterop.fitsInDouble(value)) { + try { + return EspressoInterop.asDouble(value); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("Contract violation: if fitsInDouble returns true, asDouble must succeed."); + } } exceptionProfile.enter(); throw UnsupportedTypeException.create(new Object[]{value}, EspressoError.cat("Cannot cast ", value, " to double")); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java index 29f696fb1dc8..7b1797069296 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/ToReference.java @@ -61,6 +61,7 @@ import com.oracle.truffle.espresso.nodes.bytecodes.InstanceOf; import com.oracle.truffle.espresso.runtime.EspressoContext; import com.oracle.truffle.espresso.runtime.EspressoException; +import com.oracle.truffle.espresso.runtime.dispatch.staticobject.EspressoInterop; import com.oracle.truffle.espresso.runtime.staticobject.StaticObject; @NodeInfo(shortName = "Convert to Espresso StaticObject") @@ -325,8 +326,33 @@ public abstract static class GenericToReference extends EspressoNode { @Specialization public StaticObject doStaticObject(StaticObject value, EspressoType targetType, @Cached InstanceOf.Dynamic instanceOf) throws UnsupportedTypeException { - if (StaticObject.isNull(value) || instanceOf.execute(value.getKlass(), targetType.getRawType())) { - return value; // pass through, NULL coercion not needed. + Klass rawType = targetType.getRawType(); + if (StaticObject.isNull(value) || instanceOf.execute(value.getKlass(), rawType)) { + return value; + } + try { + Meta meta = getMeta(); + if (rawType == meta.java_lang_Double && EspressoInterop.fitsInDouble(value)) { + return meta.boxDouble(EspressoInterop.asDouble(value)); + } + if (rawType == meta.java_lang_Float && EspressoInterop.fitsInFloat(value)) { + return meta.boxFloat(EspressoInterop.asFloat(value)); + } + if (rawType == meta.java_lang_Long && EspressoInterop.fitsInLong(value)) { + return meta.boxLong(EspressoInterop.asLong(value)); + } + if (rawType == meta.java_lang_Integer && EspressoInterop.fitsInInt(value)) { + return meta.boxInteger(EspressoInterop.asInt(value)); + } + if (rawType == meta.java_lang_Short && EspressoInterop.fitsInShort(value)) { + return meta.boxShort(EspressoInterop.asShort(value)); + } + if (rawType == meta.java_lang_Byte && EspressoInterop.fitsInByte(value)) { + return meta.boxByte(EspressoInterop.asByte(value)); + } + } catch (UnsupportedMessageException ex) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw EspressoError.shouldNotReachHere("Contract violation: if fitsIn* returns true, as* must succeed."); } throw UnsupportedTypeException.create(new Object[]{value}, EspressoError.cat("Cannot cast ", value, " to ", targetType.getRawType().getTypeAsString())); } @@ -498,6 +524,10 @@ public static StaticObject doGeneric(Object value, EspressoType targetType, if (result != null) { return result; } + // no generic conversion to abstract target types allowed + if (targetType.getRawType().isAbstract()) { + throw UnsupportedTypeException.create(new Object[]{value}, targetType.getRawType().getTypeAsString()); + } if (targetType instanceof ObjectKlass rawType) { noConverterProfile.enter(node); checkHasAllFieldsOrThrow(value, rawType, interop, meta); @@ -2609,6 +2639,10 @@ static StaticObject doForeignWrapper(Object value, if (result != null) { return result; } + // no generic conversion to abstract target types allowed + if (target.getRawType().isAbstract()) { + throw UnsupportedTypeException.create(new Object[]{value}, target.getRawType().getTypeAsString()); + } noConverterProfile.enter(node); checkHasAllFieldsOrThrow(value, (ObjectKlass) target.getRawType(), interop, context.getMeta()); return StaticObject.createForeign(EspressoLanguage.get(node), target.getRawType(), value, interop); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/EspressoInterop.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/EspressoInterop.java index b514ca696519..d49b98e607ad 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/EspressoInterop.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/EspressoInterop.java @@ -100,14 +100,14 @@ public static Meta getMeta() { } @ExportMessage - static boolean isBoolean(StaticObject receiver) { + public static boolean isBoolean(StaticObject receiver) { receiver.checkNotForeign(); assert !isNull(receiver) : "Null espresso object should be dispatched to BaseInterop"; return receiver.getKlass() == receiver.getKlass().getMeta().java_lang_Boolean; } @ExportMessage - static boolean asBoolean(StaticObject receiver) throws UnsupportedMessageException { + public static boolean asBoolean(StaticObject receiver) throws UnsupportedMessageException { receiver.checkNotForeign(); if (!isBoolean(receiver)) { throw UnsupportedMessageException.create(); @@ -128,7 +128,7 @@ static boolean isNumber(StaticObject receiver) { } @ExportMessage - static boolean fitsInByte(StaticObject receiver) { + public static boolean fitsInByte(StaticObject receiver) { receiver.checkNotForeign(); if (isNull(receiver)) { return false; @@ -163,7 +163,7 @@ static boolean fitsInByte(StaticObject receiver) { } @ExportMessage - static boolean fitsInShort(StaticObject receiver) { + public static boolean fitsInShort(StaticObject receiver) { receiver.checkNotForeign(); if (isNull(receiver)) { return false; @@ -194,7 +194,7 @@ static boolean fitsInShort(StaticObject receiver) { } @ExportMessage - static boolean fitsInInt(StaticObject receiver) { + public static boolean fitsInInt(StaticObject receiver) { receiver.checkNotForeign(); if (isNull(receiver)) { return false; @@ -221,7 +221,7 @@ static boolean fitsInInt(StaticObject receiver) { } @ExportMessage - static boolean fitsInLong(StaticObject receiver) { + public static boolean fitsInLong(StaticObject receiver) { receiver.checkNotForeign(); if (isNull(receiver)) { return false; @@ -244,7 +244,7 @@ static boolean fitsInLong(StaticObject receiver) { } @ExportMessage - static boolean fitsInFloat(StaticObject receiver) { + public static boolean fitsInFloat(StaticObject receiver) { receiver.checkNotForeign(); if (isNull(receiver)) { return false; @@ -278,7 +278,7 @@ static boolean fitsInFloat(StaticObject receiver) { } @ExportMessage - static boolean fitsInDouble(StaticObject receiver) { + public static boolean fitsInDouble(StaticObject receiver) { receiver.checkNotForeign(); if (isNull(receiver)) { return false; @@ -349,7 +349,7 @@ private static Number readNumberValue(StaticObject receiver) throws UnsupportedM } @ExportMessage - static byte asByte(StaticObject receiver) throws UnsupportedMessageException { + public static byte asByte(StaticObject receiver) throws UnsupportedMessageException { receiver.checkNotForeign(); if (!fitsInByte(receiver)) { CompilerDirectives.transferToInterpreter(); @@ -359,7 +359,7 @@ static byte asByte(StaticObject receiver) throws UnsupportedMessageException { } @ExportMessage - static short asShort(StaticObject receiver) throws UnsupportedMessageException { + public static short asShort(StaticObject receiver) throws UnsupportedMessageException { receiver.checkNotForeign(); if (!fitsInShort(receiver)) { CompilerDirectives.transferToInterpreter(); @@ -369,7 +369,7 @@ static short asShort(StaticObject receiver) throws UnsupportedMessageException { } @ExportMessage - static int asInt(StaticObject receiver) throws UnsupportedMessageException { + public static int asInt(StaticObject receiver) throws UnsupportedMessageException { receiver.checkNotForeign(); if (!fitsInInt(receiver)) { CompilerDirectives.transferToInterpreter(); @@ -379,7 +379,7 @@ static int asInt(StaticObject receiver) throws UnsupportedMessageException { } @ExportMessage - static long asLong(StaticObject receiver) throws UnsupportedMessageException { + public static long asLong(StaticObject receiver) throws UnsupportedMessageException { receiver.checkNotForeign(); if (!fitsInLong(receiver)) { CompilerDirectives.transferToInterpreter(); @@ -389,7 +389,7 @@ static long asLong(StaticObject receiver) throws UnsupportedMessageException { } @ExportMessage - static float asFloat(StaticObject receiver) throws UnsupportedMessageException { + public static float asFloat(StaticObject receiver) throws UnsupportedMessageException { receiver.checkNotForeign(); if (!fitsInFloat(receiver)) { CompilerDirectives.transferToInterpreter(); @@ -399,7 +399,7 @@ static float asFloat(StaticObject receiver) throws UnsupportedMessageException { } @ExportMessage - static double asDouble(StaticObject receiver) throws UnsupportedMessageException { + public static double asDouble(StaticObject receiver) throws UnsupportedMessageException { receiver.checkNotForeign(); if (!fitsInDouble(receiver)) { CompilerDirectives.transferToInterpreter(); diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_polyglot_Polyglot.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_polyglot_Polyglot.java index 10c596f412e7..5d2f2ec6ae1f 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_polyglot_Polyglot.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/Target_com_oracle_truffle_espresso_polyglot_Polyglot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -28,20 +28,15 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.Cached.Shared; -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.interop.UnsupportedTypeException; -import com.oracle.truffle.api.library.CachedLibrary; -import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.espresso.EspressoLanguage; -import com.oracle.truffle.espresso.impl.ArrayKlass; import com.oracle.truffle.espresso.impl.EspressoType; import com.oracle.truffle.espresso.impl.Klass; import com.oracle.truffle.espresso.impl.ObjectKlass; @@ -74,43 +69,71 @@ protected static InstanceOf createInstanceOf(Klass superType) { return InstanceOf.create(superType, true); } - @Specialization(guards = "targetClass.getMirrorKlass() == cachedTargetKlass", limit = "1") + static ToReference createToEspressoNode(EspressoType type, Meta meta) { + if (type.getRawType() instanceof PrimitiveKlass primitiveKlass) { + Klass boxedKlass = MethodArgsUtils.primitiveTypeToBoxedType(primitiveKlass); + assert boxedKlass != null; + return ToReference.createToReference(boxedKlass, meta); + } + return ToReference.createToReference(type, meta); + } + + @Specialization(guards = "targetClass.getMirrorKlass() == cachedTargetKlass", limit = "2") @JavaType(Object.class) - StaticObject doCached( + static StaticObject doCached( @JavaType(Class.class) StaticObject targetClass, @JavaType(Object.class) StaticObject value, - @Cached("targetClass.getMirrorKlass()") Klass cachedTargetKlass, - @Cached BranchProfile nullTargetClassProfile, - @Cached BranchProfile reWrappingProfile, + @Bind Node node, + @SuppressWarnings("unused") @Cached("targetClass.getMirrorKlass()") Klass cachedTargetKlass, + @Cached InlinedBranchProfile nullTargetClassProfile, + @Cached InlinedBranchProfile reWrappingProfile, + @Cached InlinedBranchProfile errorProfile, + @Bind("getMeta()") Meta meta, @Cached("createInstanceOf(cachedTargetKlass)") InstanceOf instanceOfTarget, - @Cached CastImpl castImpl) { + @Cached("createToEspressoNode(cachedTargetKlass, meta)") ToReference toEspresso) { if (StaticObject.isNull(targetClass)) { - nullTargetClassProfile.enter(); - Meta meta = getMeta(); + nullTargetClassProfile.enter(node); throw meta.throwException(meta.java_lang_NullPointerException); } - if (StaticObject.isNull(value) || (!value.isForeignObject() && instanceOfTarget.execute(value.getKlass()))) { + if (StaticObject.isNull(value) || instanceOfTarget.execute(value.getKlass())) { return value; } - reWrappingProfile.enter(); - return castImpl.execute(getContext(), cachedTargetKlass, value); + reWrappingProfile.enter(node); + try { + Object interopObject = value.isForeignObject() ? value.rawForeignObject(EspressoLanguage.get(node)) : value; + return toEspresso.execute(interopObject); + } catch (UnsupportedTypeException e) { + errorProfile.enter(node); + throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "%s cannot be cast to %s", value, targetClass.getMirrorKlass(meta).getTypeAsString()); + } } @ReportPolymorphism.Megamorphic @Specialization(replaces = "doCached") @JavaType(Object.class) - StaticObject doGeneric(@JavaType(Class.class) StaticObject targetClass, + static StaticObject doGeneric(@JavaType(Class.class) StaticObject targetClass, @JavaType(Object.class) StaticObject value, + @Bind Node node, @Cached InstanceOf.Dynamic instanceOfDynamic, - @Cached CastImpl castImpl) { - Meta meta = getMeta(); + @Cached ToReference.DynamicToReference toEspresso) { + Meta meta = EspressoContext.get(node).getMeta(); if (StaticObject.isNull(targetClass)) { throw meta.throwException(meta.java_lang_NullPointerException); } - if (StaticObject.isNull(value) || (!value.isForeignObject() && instanceOfDynamic.execute(value.getKlass(), targetClass.getMirrorKlass(meta)))) { + if (StaticObject.isNull(value) || instanceOfDynamic.execute(value.getKlass(), targetClass.getMirrorKlass(meta))) { return value; } - return castImpl.execute(getContext(), targetClass.getMirrorKlass(meta), value); + Klass mirrorKlass = targetClass.getMirrorKlass(meta); + try { + if (mirrorKlass instanceof PrimitiveKlass primitiveKlass) { + mirrorKlass = MethodArgsUtils.primitiveTypeToBoxedType(primitiveKlass); + assert mirrorKlass != null; + } + Object interopObject = value.isForeignObject() ? value.rawForeignObject(EspressoLanguage.get(node)) : value; + return toEspresso.execute(interopObject, mirrorKlass); + } catch (UnsupportedTypeException e) { + throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "%s cannot be cast to %s", value, targetClass.getMirrorKlass(meta).getTypeAsString()); + } } } @@ -136,27 +159,45 @@ protected static InstanceOf createInstanceOf(EspressoType superType) { @JavaType(Object.class) StaticObject value, @JavaType(internalName = "Lcom/oracle/truffle/espresso/polyglot/TypeLiteral;") StaticObject targetType); - @Specialization(guards = "getEspressoType(targetType, meta) == cachedTargetType", limit = "1") + @Specialization(guards = "getEspressoType(targetType, context.getMeta()) == cachedTargetType", limit = "2") @JavaType(Object.class) - StaticObject doCached( + static StaticObject doCached( @JavaType(Object.class) StaticObject value, @JavaType(internalName = "Lcom/oracle/truffle/espresso/polyglot/TypeLiteral;") StaticObject targetType, - @Bind("getMeta()") Meta meta, - @SuppressWarnings("unused") @Cached("getEspressoType(targetType, meta)") EspressoType cachedTargetType, + @Bind Node node, + @SuppressWarnings("unused") @Bind("get(node)") EspressoContext context, + @Cached InlinedBranchProfile nullTargetClassProfile, + @Cached InlinedBranchProfile reWrappingProfile, + @Cached InlinedBranchProfile errorProfile, + @SuppressWarnings("unused") @Cached("getEspressoType(targetType, context.getMeta())") EspressoType cachedTargetType, @Cached("createInstanceOf(cachedTargetType.getRawType())") InstanceOf instanceOfTarget, - @Cached("createToEspressoNode(cachedTargetType, meta)") ToReference toEspressoNode) { - assert !StaticObject.isNull(targetType); + @Cached("createToEspressoNode(cachedTargetType, context.getMeta())") ToReference toEspressoNode) { + Meta meta = context.getMeta(); + if (StaticObject.isNull(targetType)) { + nullTargetClassProfile.enter(node); + throw meta.throwException(meta.java_lang_NullPointerException); + } if (StaticObject.isNull(value) || (!value.isForeignObject() && instanceOfTarget.execute(value.getKlass()))) { return value; } + reWrappingProfile.enter(node); try { if (value.isForeignObject()) { - return toEspressoNode.execute(value.rawForeignObject(getLanguage())); + return toEspressoNode.execute(value.rawForeignObject(EspressoLanguage.get(node))); } else { // we know it's not instance of the target type, so throw CCE + errorProfile.enter(node); throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "%s cannot be cast to %s", value, targetType); } } catch (UnsupportedTypeException e) { + if (value.isForeignObject() && cachedTargetType.getRawType().isAbstract() && !cachedTargetType.getRawType().isArray()) { + // allow foreign objects of assignable type to be returned, but only after + // trying to convert to a guest value with ToEspresso + if (instanceOfTarget.execute(value.getKlass())) { + return value; + } + } + errorProfile.enter(node); throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "%s cannot be cast to %s", value, targetType); } } @@ -164,12 +205,13 @@ StaticObject doCached( @ReportPolymorphism.Megamorphic @Specialization(replaces = "doCached") @JavaType(Object.class) - StaticObject doGeneric( + static StaticObject doGeneric( @JavaType(Object.class) StaticObject value, @JavaType(internalName = "Lcom/oracle/truffle/espresso/polyglot/TypeLiteral;") StaticObject targetType, + @Bind Node node, @Cached InstanceOf.Dynamic instanceOfDynamic, @Cached ToReference.DynamicToReference toEspressoNode) { - Meta meta = getMeta(); + Meta meta = EspressoContext.get(node).getMeta(); if (StaticObject.isNull(targetType)) { throw meta.throwException(meta.java_lang_NullPointerException); } @@ -179,8 +221,15 @@ StaticObject doGeneric( EspressoType type = getEspressoType(targetType, meta); if (value.isForeignObject()) { try { - return toEspressoNode.execute(value.rawForeignObject(getLanguage()), type); + return toEspressoNode.execute(value.rawForeignObject(EspressoLanguage.get(node)), type); } catch (UnsupportedTypeException e) { + if (value.isForeignObject() && type.getRawType().isAbstract() && !type.getRawType().isArray()) { + // allow foreign objects of assignable type to be returned, but only after + // trying to convert to a guest value with ToEspresso + if (instanceOfDynamic.execute(value.getKlass(), type.getRawType())) { + return value; + } + } throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "%s cannot be cast to %s", value, type); } } else if (instanceOfDynamic.execute(value.getKlass(), type.getRawType())) { @@ -190,166 +239,6 @@ StaticObject doGeneric( } } } - - @GenerateUncached - abstract static class CastImpl extends SubstitutionNode { - - static final int LIMIT = 3; - - abstract @JavaType(Object.class) StaticObject execute(EspressoContext context, Klass targetKlass, @JavaType(Object.class) StaticObject value); - - static boolean isNull(StaticObject object) { - return StaticObject.isNull(object); - } - - static boolean isStringKlass(EspressoContext context, Klass targetKlass) { - return targetKlass == context.getMeta().java_lang_String; - } - - static boolean isForeignException(EspressoContext context, Klass targetKlass) { - Meta.PolyglotSupport polyglot = context.getMeta().polyglot; - return polyglot != null /* polyglot support is enabled */ - && targetKlass == polyglot.ForeignException; - } - - @Specialization(guards = "value.isEspressoObject()") - @JavaType(Object.class) - StaticObject doEspresso( - EspressoContext context, - Klass targetKlass, - @JavaType(Object.class) StaticObject value, - @Cached InstanceOf.Dynamic instanceOfDynamic, - @Cached BranchProfile exceptionProfile) { - if (isNull(value) || instanceOfDynamic.execute(value.getKlass(), targetKlass)) { - return value; - } - exceptionProfile.enter(); - Meta meta = context.getMeta(); - throw meta.throwException(meta.java_lang_ClassCastException); - } - - @Specialization(guards = "value.isForeignObject()") - @JavaType(Object.class) - StaticObject doPrimitive( - EspressoContext context, - PrimitiveKlass targetKlass, - @JavaType(Object.class) StaticObject value, - @Cached ToReference.DynamicToReference toEspresso, - @Cached BranchProfile exceptionProfile) { - Meta meta = context.getMeta(); - try { - Klass boxedKlass = MethodArgsUtils.primitiveTypeToBoxedType(targetKlass); - return toEspresso.execute(value.rawForeignObject(getLanguage()), boxedKlass); - } catch (UnsupportedTypeException e) { - exceptionProfile.enter(); - throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, - "Couldn't read %s value from foreign object", targetKlass.getType()); - } - } - - @Specialization(guards = "value.isForeignObject()") - @JavaType(Object.class) - StaticObject doArray( - EspressoContext context, - ArrayKlass targetKlass, - @JavaType(Object.class) StaticObject value, - @Shared("value") @CachedLibrary(limit = "LIMIT") InteropLibrary interop, - @Cached BranchProfile exceptionProfile) { - Meta meta = context.getMeta(); - Object foreignObject = value.rawForeignObject(getLanguage()); - - // Array-like foreign objects can be wrapped as *[]. - // Buffer-like foreign objects can be wrapped (only) as byte[]. - if (interop.hasArrayElements(foreignObject) || (targetKlass == meta._byte_array && interop.hasBufferElements(foreignObject))) { - return StaticObject.createForeign(meta.getLanguage(), targetKlass, foreignObject, interop); - } - - exceptionProfile.enter(); - throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "Cannot cast non-array value to an array type"); - } - - @Specialization(guards = { - "isStringKlass(context, targetKlass)", - "value.isForeignObject()" - }) - @JavaType(Object.class) - StaticObject doString( - EspressoContext context, - @SuppressWarnings("unused") ObjectKlass targetKlass, - @JavaType(Object.class) StaticObject value, - @Shared("value") @CachedLibrary(limit = "LIMIT") InteropLibrary interop, - @Cached BranchProfile exceptionProfile) { - Meta meta = context.getMeta(); - try { - return meta.toGuestString(interop.asString(value.rawForeignObject(getLanguage()))); - } catch (UnsupportedMessageException e) { - exceptionProfile.enter(); - throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "Cannot cast a non-string foreign object to String"); - } - } - - @Specialization(guards = { - "isForeignException(context, targetKlass)", - "value.isForeignObject()" - }) - @JavaType(Object.class) - StaticObject doForeignException( - EspressoContext context, - @SuppressWarnings("unused") ObjectKlass targetKlass, - @JavaType(Object.class) StaticObject value, - @Shared("value") @CachedLibrary(limit = "LIMIT") InteropLibrary interop, - @Cached BranchProfile exceptionProfile) { - Meta meta = context.getMeta(); - // Casting to ForeignException skip the field checks. - Object foreignObject = value.rawForeignObject(getLanguage()); - if (interop.isException(foreignObject)) { - return StaticObject.createForeignException(context, foreignObject, interop); - } - exceptionProfile.enter(); - throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "Cannot cast a non-exception foreign object to ForeignException"); - } - - @Fallback - @JavaType(Object.class) - StaticObject doDataClassOrThrow( - EspressoContext context, - Klass targetKlass, - @JavaType(Object.class) StaticObject value, - @Shared("value") @CachedLibrary(limit = "LIMIT") InteropLibrary interop, - @Cached InstanceOf.Dynamic instanceOfDynamic, - @Cached ToReference.DynamicToReference toEspresso, - @Cached BranchProfile exceptionProfile) { - Meta meta = context.getMeta(); - if (targetKlass.isAbstract()) { - // allow foreign objects of assignable type to be returned - if (instanceOfDynamic.execute(value.getKlass(), targetKlass)) { - return value; - } - exceptionProfile.enter(); - throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, "Invalid cast to non-array abstract class"); - } - assert targetKlass instanceof ObjectKlass; - assert value.isForeignObject(); - ObjectKlass targetObjectKlass = (ObjectKlass) targetKlass; - try { - if (meta.isBoxed(targetObjectKlass)) { - Object foreignObject = value.rawForeignObject(getLanguage()); - return StaticObject.createForeign(meta.getLanguage(), targetKlass, foreignObject, interop); - } else { - return toEspresso.execute(value.rawForeignObject(getLanguage()), targetKlass); - } - } catch (UnsupportedTypeException e) { - // allow foreign objects of assignable type to be returned, but only after trying to - // convert to a guest value with ToEspresso - if (instanceOfDynamic.execute(value.getKlass(), targetKlass)) { - return value; - } - exceptionProfile.enter(); - throw meta.throwExceptionWithMessage(meta.java_lang_ClassCastException, e.getMessage()); - } - } - } - // endregion Polyglot#cast @TruffleBoundary