Skip to content

Commit

Permalink
more thorough testing of parameter types, plus classpath fixes #135
Browse files Browse the repository at this point in the history
  • Loading branch information
bergmanngabor committed Apr 15, 2024
1 parent eb8dce2 commit 980a24e
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.eclipse.viatra.query.patternlanguage.emf.util.SimpleClassLoaderProvider;
import org.eclipse.viatra.query.patternlanguage.emf.vql.Pattern;
import org.eclipse.viatra.query.runtime.exception.ViatraQueryException;
import org.eclipse.xtext.util.IResourceScopeCache;

import com.google.inject.Inject;
import com.google.inject.Singleton;
Expand All @@ -42,25 +43,40 @@ public class JavaProjectClassLoaderProvider extends SimpleClassLoaderProvider im

@Inject
private IWorkspaceRoot root;

@Inject
private IResourceScopeCache cache;

@Override
public ClassLoader getClassLoader(EObject ctx) {
try {
IFile file = getIFile(ctx);
ClassLoader l;
if (file != null && file.exists()) {
l = getClassLoader(file);
if (l == null) {
throw new ViatraQueryException(String.format("No classloader found for context object %s.", ctx), "No classloader found.");
if (null == ctx || null == ctx.eResource()) {
return super.getClassLoader(ctx);
}
// Caching is necessary not just for performance,
// but also in order to force the same source class URI to be loaded by the same ClassLoader
// (hence resulting in the same Class<T> object)
// for all parts of a Pattern.
//
// Caveat: the classpath may change without anybody touching this resource.
// However, a simple editing of this VQL resource, or a clean&build, will fix the stale classpath problem, while there is no simple fix for the alternative.
return cache.get(JavaProjectClassLoaderProvider.class, ctx.eResource(), () -> {
try {
IFile file = getIFile(ctx);
ClassLoader l;
if (file != null && file.exists()) {
l = getClassLoader(file);
if (l == null) {
throw new ViatraQueryException(String.format("No classloader found for context object %s.", ctx), "No classloader found.");
}
} else {
l = super.getClassLoader(ctx);
}
} else {
l = super.getClassLoader(ctx);
return l;
} catch (Exception e) {
throw new ViatraQueryException(String.format("Cannot initialize classloader for context object %s because %s",
ctx, e.getMessage()), "Cannot initialize classloader", e);
}
return l;
} catch (Exception e) {
throw new ViatraQueryException(String.format("Cannot initialize classloader for context object %s because %s",
ctx, e.getMessage()), "Cannot initialize classloader", e);
}
});
}
@Override
public IFile getIFile(Pattern pattern) {
Expand Down Expand Up @@ -92,8 +108,6 @@ public IFile getIFile(EObject ctx) {
*
* @param file
* @return {@link ClassLoader}
* @throws CoreException
* @throws MalformedURLException
*/
public ClassLoader getClassLoader(IFile file) throws CoreException, MalformedURLException {
if (file != null && file.exists()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ private void preprocessParameters(PatternModelAcceptor<?> acceptor) {
acceptor.acceptTypeConstraint(ImmutableList.of(variable.getName()), inputKey);
} else if (variable.getType() instanceof JavaType) {
JvmDeclaredType classRef = ((JavaType) variable.getType()).getClassRef();
IInputKey inputKey = ITypeInferrer.jvmTypeToInputKey(classRef);
IInputKey inputKey = typeSystem.fromJvmType(classRef, pattern);
acceptor.acceptTypeCheckConstraint(ImmutableList.of(variable.getName()), inputKey);
}
}
Expand Down Expand Up @@ -366,7 +366,7 @@ private void gatherClassifierConstraint(EClassifierConstraint constraint, Patter
private void gatherTypeConstraint(TypeCheckConstraint constraint, PatternModelAcceptor<?> acceptor) {
String variableName = getVariableName(constraint.getVar(), acceptor);
JvmDeclaredType classRef = ((JavaType)constraint.getType()).getClassRef();
IInputKey inputKey = ITypeInferrer.jvmTypeToInputKey(classRef);
IInputKey inputKey = typeSystem.fromJvmType(classRef, pattern);
acceptor.acceptTypeCheckConstraint(ImmutableList.of(variableName), inputKey);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ public IInputKey getDeclaredType(Expression ex) {
List<JvmType> returnTypes = AggregatorUtil.getReturnTypes(((AggregatedValue) ex).getAggregator());
if (returnTypes.size() == 1) {
JvmType jvmType = returnTypes.get(0);
return ITypeInferrer.jvmTypeToInputKey(jvmType);
return typeSystem.fromJvmType(jvmType, ex);

}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ import org.eclipse.xtext.common.types.util.TypeReferences
import org.eclipse.xtext.diagnostics.Severity

import static com.google.common.base.Preconditions.checkArgument
import org.eclipse.xtext.common.types.JvmIdentifiableElement
import org.eclipse.xtext.common.types.util.JavaReflectAccess
import com.google.inject.Injector
import org.eclipse.xtext.common.types.JvmType

/**
* @author Zoltan Ujhelyi
Expand Down Expand Up @@ -101,6 +105,7 @@ class EMFTypeSystem extends AbstractTypeSystem {
@Inject TypeReferences typeReferences
@Inject IClassLoaderProvider classLoaderProvider
@Inject IJvmTypeProvider.Factory typeProviderFactory;
@Inject Injector injector;

@Inject new(Logger logger) {
super(EMFQueryMetaContext.DEFAULT)
Expand All @@ -115,7 +120,7 @@ class EMFTypeSystem extends AbstractTypeSystem {
} else if (type instanceof ReferenceType) {
return type.refname?.EType.classifierToInputKey
} else if (type instanceof JavaType) {
return ITypeInferrer.jvmTypeToInputKey(type.classRef)
return this.fromJvmType(type.classRef, type);
}
// Never executed
throw new UnsupportedOperationException()
Expand Down Expand Up @@ -329,6 +334,24 @@ class EMFTypeSystem extends AbstractTypeSystem {
}
return typeReferences.getTypeForName(Object, context)
}

override fromJvmType(JvmType jvmType, EObject context) {
try {
// if the class is loadable, even from source files, try to use it directly for instance filtering
val javaReflect = injector.getInstance(JavaReflectAccess);
val classLoader = classLoaderProvider.getClassLoader(context);
if (classLoader !== null) {
javaReflect.setClassLoader(classLoader);
}
val Class<?> loadedClass = javaReflect.getRawType(jvmType);
return new JavaTransitiveInstancesKey(loadedClass);
} catch (Exception ex) {
// if loading the class failed, we can still construct the input key from its qualified name;
// however, this constraint will only be useful for generating code from it

return new JavaTransitiveInstancesKey(jvmType.identifier, jvmType.getQualifiedName('$'));
}
}

private def JvmTypeReference getJvmType(EClassifier classifier, EObject context) {
if (classifier !== null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
import org.eclipse.viatra.query.patternlanguage.emf.vql.Expression;
import org.eclipse.viatra.query.patternlanguage.emf.vql.Variable;
import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
import org.eclipse.viatra.query.runtime.matchers.context.common.JavaTransitiveInstancesKey;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmTypeReference;

/**
Expand Down Expand Up @@ -69,12 +67,4 @@ public interface ITypeInferrer {
* @since 1.3
*/
JvmTypeReference getJvmType(Expression ex, EObject context);

/**
* Helper method that embeds a JVM type or type reference into an input key
* @since 2.9
*/
public static IInputKey jvmTypeToInputKey(JvmIdentifiableElement jvmType) {
return new JavaTransitiveInstancesKey(jvmType.getIdentifier(), jvmType.getQualifiedName('.'));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import org.eclipse.viatra.query.patternlanguage.emf.vql.RelationType;
import org.eclipse.viatra.query.patternlanguage.emf.vql.Type;
import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
import org.eclipse.viatra.query.runtime.matchers.context.common.JavaTransitiveInstancesKey;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmTypeReference;

/**
Expand Down Expand Up @@ -113,6 +116,12 @@ default Type convertToVQLType(EObject context, IInputKey key) {
* Creates a type reference for model inference from a selected type.
*/
JvmTypeReference toJvmTypeReference(IInputKey type, EObject context);

/**
* Creates a Java type filter IInputKey from a JVM type referenced in the pattern language.
* @since 2.9
*/
JavaTransitiveInstancesKey fromJvmType(JvmType jvmType, EObject context);

/**
* Converts a type object to a user-visible description string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class PatternLanguageTypeRules {
return
}
val returnType = (returnTypes).get(0)
information.provideType(new TypeJudgement(reference, ITypeInferrer.jvmTypeToInputKey(returnType)))
information.provideType(new TypeJudgement(reference, typeSystem.fromJvmType(returnType, reference)))
} else {
if (values.size !== 1 || !AggregatorUtil.mustHaveAggregatorVariables(reference)) {
//Incorrect aggregation; reported separately
Expand All @@ -195,17 +195,17 @@ class PatternLanguageTypeRules {
for (var i=0; i < returnTypes.size; i++) {
information.provideType(new ConditionalJudgement(
reference,
ITypeInferrer.jvmTypeToInputKey(returnTypes.get(i)),
typeSystem.fromJvmType(returnTypes.get(i), reference),
callParameters.get(index),
ITypeInferrer.jvmTypeToInputKey(parameterTypes.get(i))
typeSystem.fromJvmType(parameterTypes.get(i), reference)
))
// If return types are not unique for each source type, do not provide backward conditions
if (returnTypeUnique) {
information.provideType(new ConditionalJudgement(
callParameters.get(index),
ITypeInferrer.jvmTypeToInputKey(parameterTypes.get(i)),
typeSystem.fromJvmType(parameterTypes.get(i), reference),
reference,
ITypeInferrer.jvmTypeToInputKey(returnTypes.get(i))
typeSystem.fromJvmType(returnTypes.get(i), reference)
))
}
}
Expand Down Expand Up @@ -246,14 +246,14 @@ class PatternLanguageTypeRules {
*/
def dispatch void inferTypes(JavaConstantValue reference, TypeInformation information) {
val fieldRef = reference.fieldRef
if (null != fieldRef) {
val type = ITypeInferrer.jvmTypeToInputKey(fieldRef.type.type)
if (null !== fieldRef && null !== fieldRef.type && null !== fieldRef.type.type) {
val type = typeSystem.fromJvmType(fieldRef.type.type, reference)
information.provideType(new TypeJudgement(reference, type))
}
}

def dispatch void inferTypes(FunctionEvaluationValue reference, TypeInformation information) {
information.provideType(new XbaseExpressionTypeJudgement(reference, reference.expression, typeResolver, reference.isUnwind()))
information.provideType(new XbaseExpressionTypeJudgement(reference, reference.expression, typeResolver, typeSystem, reference.isUnwind()))
}

def dispatch void inferTypes(BoolValue reference, TypeInformation information) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Set;

import org.eclipse.viatra.query.patternlanguage.emf.helper.PatternLanguageHelper;
import org.eclipse.viatra.query.patternlanguage.emf.types.ITypeSystem;
import org.eclipse.viatra.query.patternlanguage.emf.vql.Expression;
import org.eclipse.viatra.query.patternlanguage.emf.vql.PatternBody;
import org.eclipse.viatra.query.patternlanguage.emf.vql.Variable;
Expand All @@ -33,13 +34,15 @@ public class XbaseExpressionTypeJudgement extends AbstractTypeJudgement {

private XExpression xExpression;
private IBatchTypeResolver xbaseResolver;
private ITypeSystem typeSystem;
private boolean unwind;

public XbaseExpressionTypeJudgement(Expression expression, XExpression xExpression,
IBatchTypeResolver xbaseResolver, boolean unwind) {
IBatchTypeResolver xbaseResolver, ITypeSystem typeSystem, boolean unwind) {
super(expression);
this.xExpression = xExpression;
this.xbaseResolver = xbaseResolver;
this.typeSystem = typeSystem;
this.unwind = unwind;
}

Expand Down Expand Up @@ -78,8 +81,7 @@ private JavaTransitiveInstancesKey getComponentTypeKey(LightweightTypeReference
}

private JavaTransitiveInstancesKey asInputKey(LightweightTypeReference typeRef) {
LightweightTypeReference jvmType = typeRef.getWrapperTypeIfPrimitive().getRawTypeReference();
return new JavaTransitiveInstancesKey(jvmType.getJavaIdentifier(), jvmType.getHumanReadableName());
return typeSystem.fromJvmType(typeRef.getType(), xExpression);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package org.eclipse.viatra.query.patternlanguage.emf.tests.generator
import org.junit.rules.TestName
import org.junit.Rule
import org.junit.Test
import org.eclipse.emf.ecore.EcorePackage

class TypingEdgeCasesCompilerTest extends AbstractQueryCompilerTest {
static val TEST_PROJECT_NAME_PREFIX = "org.eclipse.viatra.query.test"
Expand Down Expand Up @@ -61,7 +62,27 @@ class TypingEdgeCasesCompilerTest extends AbstractQueryCompilerTest {
u == 4;
}
'''
static val TEST_CONTENTS_JAVACONST_INNERCLASS = '''
static val TEST_CONTENTS_JAVACONST = '''
package test
import "http://www.eclipse.org/emf/2002/Ecore"
pattern fee(e: java org.eclipse.emf.ecore.EcorePackage, u: java Integer) {
e == java org.eclipse.emf.ecore.EcorePackage::eINSTANCE;
u == 4;
}
'''
static val TEST_CONTENTS_JAVACONST_INFERRED = '''
package test
import "http://www.eclipse.org/emf/2002/Ecore"
pattern fie(e, u: java Integer) {
e == java org.eclipse.emf.ecore.EcorePackage::eINSTANCE;
u == 4;
}
'''
static val TEST_CONTENTS_JAVACONST_ENUM = '''
package test
import "http://www.eclipse.org/emf/2002/Ecore"
Expand All @@ -71,7 +92,7 @@ class TypingEdgeCasesCompilerTest extends AbstractQueryCompilerTest {
u == 4;
}
'''
static val TEST_CONTENTS_JAVACONST_INNERCLASS_INFERRED = '''
static val TEST_CONTENTS_JAVACONST_ENUM_INFERRED = '''
package test
import "http://www.eclipse.org/emf/2002/Ecore"
Expand Down Expand Up @@ -107,11 +128,19 @@ class TypingEdgeCasesCompilerTest extends AbstractQueryCompilerTest {
testFileCreationAndBuild(TEST_CONTENTS_GENERIC_INNERCLASS_INFERRED, 0)
}
@Test
def void compileJavaconstInnerClass() {
testFileCreationAndBuild(TEST_CONTENTS_JAVACONST_INNERCLASS, 0)
def void compileJavaconst() {
testFileCreationAndBuild(TEST_CONTENTS_JAVACONST, 0)
}
@Test
def void compileJavaconstInferred() {
testFileCreationAndBuild(TEST_CONTENTS_JAVACONST_INFERRED, 0)
}
@Test
def void compileJavaconstEnum() {
testFileCreationAndBuild(TEST_CONTENTS_JAVACONST_ENUM, 0)
}
@Test
def void compileJavaconstInnerClassInferred() {
testFileCreationAndBuild(TEST_CONTENTS_JAVACONST_INNERCLASS_INFERRED, 0)
def void compileJavaconstEnumInferred() {
testFileCreationAndBuild(TEST_CONTENTS_JAVACONST_ENUM_INFERRED, 0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,32 @@ public void testUnusualParameterTypes() throws Exception {
ViatraQueryEngine engine = ViatraQueryEngine.on(new EMFScope(rs));
String patternCode = "package org.eclipse.viatra.query.patternlanguage.emf.tests\n"
+ "import \"http://www.eclipse.org/viatra/query/patternlanguage/emf/PatternLanguage\"\n"
+ "pattern p(m: java ^java.util.Map, e: java ^java.util.Map.Entry, c, u: java Integer) = {\n"
+ " m == eval(^java.util.Collections.singletonMap(1,2));\n"
+ " e == eval(m.entrySet.head);\n"
+ " c == java org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint.BackendRequirement::SPECIFIC;\n"
+ " u == 4;\n"
+ "}";
+ "pattern p(\n"
+ " generic: java ^java.util.Map,\n"
+ " innerGeneric: java ^java.util.Map.Entry,\n"
+ " constField: java org.eclipse.emf.ecore.EcorePackage,\n"
+ " constInnerEnum: java org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint.BackendRequirement,\n"
+ " scalarJava: java Integer\n"
+ ")={\n"
+ " generic == eval(^java.util.Collections.singletonMap(1,2));\n"
+ " innerGeneric == eval(generic.entrySet.head);\n"
+ " constField == java org.eclipse.emf.ecore.EcorePackage::eINSTANCE;\n"
+ " constInnerEnum == java org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint.BackendRequirement::SPECIFIC;\n"
+ " scalarJava == 4;\n"
+ "}\n";
PatternModel model = parseHelper.parse(patternCode);

IQuerySpecification<? extends ViatraQueryMatcher<? extends IPatternMatch>> specification = new SpecificationBuilder().getOrCreateSpecification(model.getPatterns().get(0));

ViatraQueryMatcher<? extends IPatternMatch> matcher = engine.getMatcher(specification);
assertEquals(1, matcher.countMatches());
IPatternMatch match = matcher.getOneArbitraryMatch().get();
assertEquals(Collections.singleton(Collections.singletonMap(1,2)), matcher.getAllValues("m"));
assertTrue(match.get("e") instanceof Map.Entry);
assertEquals(1, ((Map.Entry)match.get("e")).getKey());
assertEquals(BackendRequirement.SPECIFIC, match.get("c"));
assertEquals(Collections.singleton(Collections.singletonMap(1,2)), matcher.getAllValues("generic"));
assertTrue(match.get("innerGeneric") instanceof Map.Entry);
assertEquals(1, ((Map.Entry)match.get("innerGeneric")).getKey());
assertEquals(org.eclipse.emf.ecore.EcorePackage.eINSTANCE, match.get("constField"));
assertEquals(BackendRequirement.SPECIFIC, match.get("constInnerEnum"));
assertEquals(4, match.get("scalarJava"));

}

Expand Down

0 comments on commit 980a24e

Please sign in to comment.