Skip to content

Commit 9d74199

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 throw new ScriptExecutionException(new ScriptError( the implementation is the same as in XbaseInterpreter.java.
1 parent 5c4ce91 commit 9d74199

File tree

1 file changed

+40
-12
lines changed

1 file changed

+40
-12
lines changed

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

Lines changed: 40 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,42 @@ 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 throw new ScriptExecutionException(new ScriptError(} line being the only difference.
165+
* There is no other way to create an error message, stating what cannot be casted to what.
166+
* See <a href="https://github.com/eclipse-xtext/xtext/issues/3595">github.com/eclipse-xtext/xtext/issues/3595</a>.
167+
*/
168+
override protected Object _doEvaluate(XCastedExpression castedExpression, IEvaluationContext context,
156169
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-
}
170+
var result = internalEvaluate(castedExpression.target, context, indicator)
171+
val owner = new StandardTypeReferenceOwner(services, castedExpression)
172+
val targetType = owner.toLightweightTypeReference(castedExpression.type)
173+
result = wrapOrUnwrapArray(result, targetType)
174+
result = coerceArgumentType(result, castedExpression.type)
175+
val castType = castedExpression.type.type
176+
if (castType instanceof JvmPrimitiveType) {
177+
if (result === null) {
178+
throwNullPointerException(castedExpression, "Cannot cast null to primitive " + castType.identifier)
168179
}
180+
return castToPrimitiveType(result, services.primitives.primitiveKind(castType))
169181
}
182+
val typeName = castType.qualifiedName
183+
var Class<?> expectedType = null
184+
try {
185+
expectedType = getJavaType(castType)
186+
} catch (ClassNotFoundException e) {
187+
throw new EvaluationException(new NoClassDefFoundError(typeName))
188+
}
189+
try {
190+
expectedType.cast(result)
191+
} catch (ClassCastException e) {
192+
throw new ScriptExecutionException(new ScriptError(
193+
"Could not cast (" + result.class + ")" + result + " to " + castedExpression.type.type.qualifiedName,
194+
castedExpression))
195+
}
196+
return result
197+
}
170198

171199
}
172200

0 commit comments

Comments
 (0)