Skip to content

Commit 2d20983

Browse files
script.model.ScriptInterpreter: emit what cannot be casted to what
after a single evaluation of the cast. Before this change internalEvaluate(castedExpression.getTarget(), context, indicator) was invoked twice. When on the first execution an error occured, the second execution was needed to create an error message, stating what cannot be casted to what. However at the time internalEvaluate was called for a second time, internal states have changed, so there might be no more errors with casting, thus producing incorrect error message. Except the line "Could not cast (" + result.class + ")" + result + " to " + typeName the implementation is the same as in XbaseInterpreter.java.
1 parent 669b791 commit 2d20983

File tree

1 file changed

+41
-12
lines changed

1 file changed

+41
-12
lines changed

bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/interpreter/ScriptInterpreter.xtend

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import org.openhab.core.model.script.scoping.StateAndCommandProvider
2525
import org.openhab.core.model.script.script.QuantityLiteral
2626
import org.eclipse.xtext.common.types.JvmField
2727
import org.eclipse.xtext.common.types.JvmIdentifiableElement
28+
import org.eclipse.xtext.common.types.JvmPrimitiveType
2829
import org.eclipse.xtext.naming.QualifiedName
2930
import org.eclipse.xtext.util.CancelIndicator
3031
import org.eclipse.xtext.xbase.XAbstractFeatureCall
@@ -33,9 +34,12 @@ import org.eclipse.xtext.xbase.XExpression
3334
import org.eclipse.xtext.xbase.XFeatureCall
3435
import org.eclipse.xtext.xbase.XMemberFeatureCall
3536
import org.eclipse.xtext.xbase.interpreter.IEvaluationContext
37+
import org.eclipse.xtext.xbase.interpreter.impl.EvaluationException
3638
import org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter
3739
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations
3840
import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver
41+
import org.eclipse.xtext.xbase.typesystem.references.StandardTypeReferenceOwner
42+
import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices;
3943

4044
/**
4145
* The script interpreter handles specific script components, which are not known
@@ -60,6 +64,9 @@ class ScriptInterpreter extends XbaseInterpreter {
6064
@Inject
6165
extension IJvmModelAssociations
6266

67+
@Inject
68+
CommonTypeComputationServices services
69+
6370
override protected _invokeFeature(JvmField jvmField, XAbstractFeatureCall featureCall, Object receiver,
6471
IEvaluationContext context, CancelIndicator indicator) {
6572

@@ -152,21 +159,43 @@ class ScriptInterpreter extends XbaseInterpreter {
152159
return QuantityType.valueOf(literal.value + " " + literal.unit.value);
153160
}
154161

155-
override Object _doEvaluate(XCastedExpression castedExpression, IEvaluationContext context,
162+
/**
163+
* This is the same implementation as in XbaseInterpreter.java with the
164+
* {@code "Could not cast (" + result.class + ")" + result + " to " + typeName }
165+
* line being the only difference. There is no other way to create an error message,
166+
* stating what cannot be casted to what. See
167+
* <a href="https://github.com/eclipse-xtext/xtext/issues/3595">github.com/eclipse-xtext/xtext/issues/3595</a>.
168+
*/
169+
override protected _doEvaluate(XCastedExpression castedExpression, IEvaluationContext context,
156170
CancelIndicator indicator) {
157-
try {
158-
return super._doEvaluate(castedExpression, context, indicator)
159-
} catch (RuntimeException e) {
160-
if (e.cause instanceof ClassCastException) {
161-
val Object result = internalEvaluate(castedExpression.getTarget(), context, indicator);
162-
throw new ScriptExecutionException(new ScriptError(
163-
"Could not cast " + result + " to " + castedExpression.getType().getType().getQualifiedName(),
164-
castedExpression));
165-
} else {
166-
throw e;
167-
}
171+
var result = internalEvaluate(castedExpression.target, context, indicator)
172+
val owner = new StandardTypeReferenceOwner(services, castedExpression)
173+
val targetType = owner.toLightweightTypeReference(castedExpression.type)
174+
result = wrapOrUnwrapArray(result, targetType)
175+
result = coerceArgumentType(result, castedExpression.type)
176+
val castType = castedExpression.type.type
177+
if (castType instanceof JvmPrimitiveType) {
178+
if (result === null) {
179+
throwNullPointerException(castedExpression, "Cannot cast null to primitive " + castType.identifier)
168180
}
181+
return castToPrimitiveType(result, services.primitives.primitiveKind(castType))
169182
}
183+
val typeName = castType.qualifiedName
184+
var Class<?> expectedType = null
185+
try {
186+
expectedType = getJavaType(castType)
187+
} catch (ClassNotFoundException e) {
188+
throw new EvaluationException(new NoClassDefFoundError(typeName))
189+
}
190+
try {
191+
expectedType.cast(result)
192+
} catch (ClassCastException e) {
193+
throw new EvaluationException(new ClassCastException(
194+
"Could not cast (" + result.class + ")" + result + " to " + typeName
195+
))
196+
}
197+
return result
198+
}
170199

171200
}
172201

0 commit comments

Comments
 (0)