Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » Checking if JvmFormalParameter isAssignableFrom XExpression
Checking if JvmFormalParameter isAssignableFrom XExpression [message #1723955] Fri, 19 February 2016 09:32 Go to next message
Larry LeBron is currently offline Larry LeBronFriend
Messages: 124
Registered: October 2015
Senior Member
As part of my language, I am trying to determine if argument conforms to a parameter. Unfortunately, I must do this outside of an XBlockExpression.

I have the following check working in Junit:

@Inject IBatchTypeResolver typeResolver

def isAssignable(JvmFormalParameter param, XExpression arg) {

   val argLightweightRef = typeResolver.resolveTypes(expression).getActualType(expression)
   val argITypeReferenceOwner = arg.owner
   val paramLightweightRef = argITypeReferenceOwner.toLightweightTypeReference(param.parameterType)
   return paramLightweightRef.isAssignableFrom(argLightweightRef)
}


In my JUnit code, this works perfectly, however when I run in eclipse runtime, the same exact code reports that the types are not conformant. Any advice why this would be? Is there a better solution to this, which would still allow me to use the full suite of Java types, including generics? Thanks!
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1723960 is a reply to message #1723955] Fri, 19 February 2016 09:41 Go to previous messageGo to next message
Jan Koehnlein is currently offline Jan KoehnleinFriend
Messages: 760
Registered: July 2009
Location: Hamburg
Senior Member
Could be a classpath issue: JUnit tests are executed in a JVM with a flat classpath, while Eclipse Equinox uses separate classloaders per bundle. Have you tried debugging the isAssignableFrom call?

---
Get professional support from the Xtext committers at www.typefox.io
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1724020 is a reply to message #1723960] Fri, 19 February 2016 21:38 Go to previous messageGo to next message
Larry LeBron is currently offline Larry LeBronFriend
Messages: 124
Registered: October 2015
Senior Member
Ok, I've now done debugging on the isAssignableFrom call. Thanks for the advice. I should note that the test I've been doing is on references to java.lang.String.

In the end, the difference is that this test passes in JUnit and not in the Eclipse runtime:

In RawTypeConformanceComputer:

protected int doIsConformant(ParameterizedTypeReference left, ParameterizedTypeReference right, int flags) {
		if (left.getType() == right.getType()) {


In JUnit, these are at equal points in memory (same object id in the debugger), but in the runtime, they're not. In the runtime, they are two different JvmGenericTypeImplCustom objects.

They have all the same values internally, so I'd imagine if this test were instead:

if (left.getType().equals(right.getType()))


it would pass.

In the runtime, the method continues to this point:

		JvmType leftType = left.getType();
		JvmType rightType = right.getType();
		EClass leftEClass = leftType.eClass();
		if (leftEClass == TypesPackage.Literals.JVM_GENERIC_TYPE) {
			JvmGenericType castedLeftType = (JvmGenericType) leftType;
			EClass rightEClass = rightType.eClass();
			if (castedLeftType.isFinal()) {
				if (rightEClass == TypesPackage.Literals.JVM_TYPE_PARAMETER && getSuperType(right, castedLeftType) != null) {


Since this is java.lang.String, the castedLeftType is final, but this test fails:

rightEClass == TypesPackage.Literals.JVM_TYPE_PARAMETER


which causes the method to return.

Any advice?

Thanks!

Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1724028 is a reply to message #1724020] Fri, 19 February 2016 22:36 Go to previous messageGo to next message
Larry LeBron is currently offline Larry LeBronFriend
Messages: 124
Registered: October 2015
Senior Member
If it's helpful, I just did another test with:

param: java.util.AbsractList
arg: java.util.ArrayList

In this case, the system goes deeper into the doIsConformant() method, eventually reaching ParameterizedTypeReference.internalGetSuperType(JvmType rawType).

This ends with a call to getSuperType(rawType, interfaceType, type, new RecursionGuard<JvmType>()).

In the runtime, this then returned a null supertype resulting from this test returning true:
		if (!guard.tryNext(thisType)) {
			return null;
		}


In my JUnit test, it follows the same exact path but passes this point, as the recursion guard allows it to continue and it finds the superType.
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1724040 is a reply to message #1724028] Sat, 20 February 2016 06:49 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
can you share a grammar und the customizations needed to reproduce.

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1724970 is a reply to message #1724040] Sun, 28 February 2016 09:38 Go to previous messageGo to next message
Larry LeBron is currently offline Larry LeBronFriend
Messages: 124
Registered: October 2015
Senior Member
Ok, I finally got around to creating a project which cleanly reproduces this issue. In experimenting with it, I've learned a bit more about the underlying cause, and you may say that this is expected behavior.

I've attached the language project here. The file extension is ".refs"

Be aware that this is a VERY brittle project, created only to demonstrate this issue.

The grammar is very simple, allowing only one instance of each:


  • An entity type with one param
  • An entityRef type with one argument


During the first entityRef validation (see TyperefsValidator), the arg type is stored as a LightweightTypeReference in the TypeConformanceChecking class. Whatever the type of this argument, it will be stored permanently for the entire execution. Once this is initialized, it is never updated.

Subsequent calls to infer the model will query TypeConformanceChecking.checkMatchingParamAgainstArg, which compares the current entity param with that previously stored arg LightweightTypeReference, using LightweightTypeReference.isAssignagbleFrom()

Here is example code to use:

The test file I used in the runtime:

entity foo(String s) {
	ref foo(s);
}


In this example, the first validation will store the type of the arg s, which is java.lang.String.

In this case, the behavior I'm seeing is that, before I save, calls to infer() for an entity with the matching type (in this case, java.lang.String), will reflect that the types are conformant, meaning LightweightTypeReference.isAssignableFrom will return true. This is all logged for the test.

Example output before save:
resolving type for arg s
type is String
converting param JvmParameterizedTypeReference:  (type uri: platform:/resource/TypeRefExample/src/test/test.refs#|0) to lightweight type ref
lightweighttyperef is String
ParamType: String is assignable from String


However, when I save, then the test will not detect the matching types. Instead, I see output like:

converting param JvmParameterizedTypeReference: java.lang.String to lightweight type ref
lightweighttyperef is String
ParamType: String is not assignable from String


So, not sure if this is a bug, or if saving a LightweightTypeRef in this way is just an improper thing to do.

To reproduce, run the DSL in runtime eclipse. Create a test .refs file as above. Once the arg type has been stored, trigger infer() by cleaning the project or altering the entity name and saving.

I hope this helps! Let me know if you have any issues reproducing it.
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1724974 is a reply to message #1724970] Sun, 28 February 2016 11:02 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
is it intended that you dont care about indexing phases at all?

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1724975 is a reply to message #1724974] Sun, 28 February 2016 11:19 Go to previous messageGo to next message
Larry LeBron is currently offline Larry LeBronFriend
Messages: 124
Registered: October 2015
Senior Member
I haven't heard the term "indexing phases" before, but think I understand what you mean.

In this case, right, this should not regard indexing phases.

I would expect that LightweightTypeReference.isAssignableFrom() would always return true for two LightweightTypeReferences that both refer to the same Java class, even if they came from different phases. Is this not the intended case?

Thanks for looking into it!

I did find a workaround which builds Class objects by invoking Class.forName(qualifiedName), and then uses the Class.isAssignableFrom() method. However, I'd imagine this isn't as efficient as what you've implemented for LightweightTypeReference.isAssignableFrom().
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1724976 is a reply to message #1724975] Sun, 28 February 2016 11:31 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
The problem is. You call the method at places there String is not sure to be a Java.lang.String.
The interrer runs twice. Once during indexing. once during scoping. Thus the way you handle that won't work.

There main question is: why do you do this at all


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1724996 is a reply to message #1724976] Mon, 29 February 2016 01:30 Go to previous messageGo to next message
Larry LeBron is currently offline Larry LeBronFriend
Messages: 124
Registered: October 2015
Senior Member
As to the main question of why I'm doing this. My language has a one-to-many cross-reference system, where one entity reference can reference many conformant entities. For tracking this, I've built an index that tracks the names of "entities" and "entity references" as well as their parameters and arguments.

This way, when a new entity or entity reference is added, I can quickly check for conformance, without having to look through all resources.

So, I was hoping that storing the LightweightTypeReferences for an entityRef argument and the JvmFormalParameters for an entity would allow for this.

As to this issue itself, I'm confused by what you mean when you say "You call the method at places there String is not sure to be a Java.lang.String."

I've updated the example project, and am now calling the test at many different places:



  • At the beginning of the EntityDefinition infer(), both for pre-linking true and false
  • In the acceptor of the EntityDefinition infer(), both for pre-linking true and false
  • In an @Check validator method for EntityDefinition


I modified the logging to output the qualified name, for the LightweightTypeReferences. In all cases, the isAssignable test always knows that both the param type and arg type have qualified name java.lang.String.

However, as mentioned earlier, when the test runs after a "clean" or a "save", I get this result where isAssignableFrom returns false.

e.g.
converting param JvmParameterizedTypeReference: java.lang.String to lightweight type ref
lightweighttyperef is String
ParamType: java.lang.String is not assignable from java.lang.String


Is there a different point in the system where you would expect this check to work in all cases?
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1725002 is a reply to message #1724996] Mon, 29 February 2016 05:47 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
i intended you to try something like

if (!isPreIndexingPhase) {
				entity.param.checkMatchingParamAgainstArg
			}


and not to do the static thing


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de

[Updated on: Mon, 29 February 2016 05:47]

Report message to a moderator

Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1725003 is a reply to message #1725002] Mon, 29 February 2016 05:54 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
btw why no using a simple check ?



Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1725004 is a reply to message #1725003] Mon, 29 February 2016 05:54 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
something like

@Check
def checkEntParamMatchesArgType(EntityReference ref) {
val expectedType = ref.entity.param.actualType
val actualType = ref.arg.actualType
if (!expectedType.isAssignableFrom(actualType)) {
error("bad", ref, null)
}
}


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1725008 is a reply to message #1725004] Mon, 29 February 2016 06:46 Go to previous messageGo to next message
Larry LeBron is currently offline Larry LeBronFriend
Messages: 124
Registered: October 2015
Senior Member
The reason it needs to be handled this way is because of the one-to-many cross-reference, which needs to be fully tracked.

For example, something like this is valid:

// in resource A
ent foo (String s) {
...
}

// in resource B
ent foo (Object o) {
...
}

// in resource C
ent other() {
 entRef foo("hi");
}


In this case, that entRef in C.other is considered to be referencing both A.foo(String) and B.foo(Object). This needs to be tracked, both for eclipse UI tools and for producing an index file which is used by the executing language code.

If, after this, a programmer adds:
[code]
// in resource D
ent foo (String s) {
...
}
[code]

I need to efficiently be able to efficiently add that to the list of entity's referenced by entRef foo("hi"); This is where the indexing system using names and argument LightWeightTypeReferences comes into play. It makes it efficient to look up any possible matching references and index them, without having to poll all possible resources.

Therefore, in the end, the validation checker looks like:

@Check
def checkReferencesAtLeastOneEntity(EntityReference ref) {
if (!entRefIndex.indexesAtLeastOneEntity(ref)) {
error("bad", ref, null)
}
}


Either way, I still don't understand with the LightweightTypeReference.isAssignableFrom() is returning false when the qualified name is equal for both references.
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1725010 is a reply to message #1725008] Mon, 29 February 2016 07:07 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
Maybe you should turn your entity ref into a constructor call inside a
inferred field or method


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1725012 is a reply to message #1725008] Mon, 29 February 2016 07:17 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
I cannot give you insights what's happening but the static thing you do
won't work for sure. This goes over the barriers of xbase. Thus the best
would be to use the inferrer only to make this happen.

Doing polymorphic linking is the hardest thing to find to do in xtext.


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Checking if JvmFormalParameter isAssignableFrom XExpression [message #1725015 is a reply to message #1725012] Mon, 29 February 2016 07:40 Go to previous message
Larry LeBron is currently offline Larry LeBronFriend
Messages: 124
Registered: October 2015
Senior Member
Unfortunately, I don't think the entity ref maps well to a constructor call, because the object to construct (which entity to select) is actually dynamically decided at program execution time, based on the one-to-many mapping indexed as I've described here.

For now, I have this logic for checking whether an arg conforms to a param, which is working so far. I'm just not sure if its reliance on reflection is ever going to become a performance issue. This is Xtend code:

def private paramConformsToArg(JvmParameterizedTypeReference paramType, LightweightTypeReference argType) {

		/* a null arg always conforms to a param */
		if (argType.type == null) {
			return true
		}
		
		/* Confirm that the generic type arguments, if they exist, are strictly equal */
		if (!paramTypeStrictlyEqualsArgType(paramType, argType, true)) {
			log(VALIDATION, "Type args are not conformant:" + paramType + " arg: " + argType)
			return false
		}
		
		val paramQualifiedName = paramType.type.qualifiedName
		val paramClass = paramQualifiedName.classForQualifiedName
		
		val argQualifiedName = argType.type.qualifiedName
		var argClass = argQualifiedName.classForQualifiedName

		return paramClass.isAssignableFrom(argClass)		
	}
	
	def private boolean paramTypeStrictlyEqualsArgType(JvmParameterizedTypeReference paramType, LightweightTypeReference argType, boolean onlyCheckTypeArgs) {
		val paramTypeGenericArgs = paramType.arguments
		val argTypeGenericArgs = argType.typeArguments
		
		/*Only check the generic type arguments if the parameter has declared some. Otherwise, it should be considered equal based on its "base" alone
		e.g. arg of type List<String> equals param of type List */
		if (!paramTypeGenericArgs.empty) {
			if (paramTypeGenericArgs.size != argTypeGenericArgs.size) {
				return false
			}
			
			/* Recursively examine the generic type arguments, ensuring that they're all strictly equal */
			for (var i = 0; i < paramTypeGenericArgs.size; i++) {
				var paramGenericArg = paramTypeGenericArgs.get(i) as JvmParameterizedTypeReference
				var argGenericArg = argTypeGenericArgs.get(i)
				if (!paramTypeStrictlyEqualsArgType(paramGenericArg, argGenericArg, false)) {
					return false
				}
			}
		}
		
		if (onlyCheckTypeArgs) {
			return true
		}
		
		return paramType.type.qualifiedName == argType.type.qualifiedName
	}
	
	def getClassForQualifiedName(String qualifiedName) {
		if (qualifiedName.isPrimitiveTypeName) {
			qualifiedName.classForPrimitive
		} else {
			Class.forName(qualifiedName)
		}
	}


Previous Topic:Regarding Antlr rule matching
Next Topic:JvmEnumerationLiteral has no support for parameters
Goto Forum:
  


Current Time: Fri Mar 29 04:39:12 GMT 2024

Powered by FUDForum. Page generated in 0.02386 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top