Merge branch 'master' of ssh://fjouault@git.eclipse.org/gitroot/mmt/org.eclipse.atl.git
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/ByteCodeSwitch.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/ByteCodeSwitch.java
index 1ad7716..ce4f2b3 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/ByteCodeSwitch.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/ByteCodeSwitch.java
@@ -11,13 +11,11 @@
 package org.eclipse.m2m.atl.emftvm.jit;
 
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 
@@ -664,7 +662,7 @@
 			localVariable("body", CodeBlock.class, bodyStart, ifOpNull, bodyIdx);
 		}
 		// Generate native method invocation code here
-		final Method method = findRootMethod(object.getNativeMethod());
+		final Method method = EMFTVMUtil.findRootMethod(object.getNativeMethod());
 		if (method != null) { // native method recorded - try first
 			// Labels
 			final Label subframeStart = new Label();
@@ -849,7 +847,7 @@
 			localVariable("body", CodeBlock.class, bodyStart, ifOpNull, bodyIdx);
 		}
 		// Generate native method invocation code here
-		final Method method = findRootMethod(object.getNativeMethod());
+		final Method method = EMFTVMUtil.findRootMethod(object.getNativeMethod());
 		if (method != null) { // native method recorded - try first
 			// Labels
 			final Label subframeStart = new Label();
@@ -1085,61 +1083,6 @@
 	}
 	
 	/**
-	 * Finds the root {@link Class} in which <code>method</code> was declared.
-	 * @param method the method for which to find the root {@link Class}
-	 * @return the root {@link Class} in which <code>method</code> was declared
-	 */
-	private Method findRootMethod(Method method) {
-		if (method == null) {
-			return null;
-		}
-		final int methodModifiers = getRelevantModifiers(method);
-		Class<?> dc = method.getDeclaringClass();
-		java.util.Set<Class<?>> dis = new LinkedHashSet<Class<?>>(
-				Arrays.asList(dc.getInterfaces()));
-		while ((dc = dc.getSuperclass()) != null) {
-			try {
-				Method superMethod = dc.getDeclaredMethod(method.getName(), method.getParameterTypes());
-				if (getRelevantModifiers(superMethod) == methodModifiers) {
-					method = superMethod;
-				} else {
-					break;
-				}
-			} catch (SecurityException e) {
-			} catch (NoSuchMethodException e) {
-			}
-			dis.addAll(Arrays.asList(dc.getInterfaces()));
-		}
-		while (!dis.isEmpty()) {
-			java.util.Set<Class<?>> newDis = new LinkedHashSet<Class<?>>();
-			for (Class<?> di : dis) {
-				try {
-					// Only replace by method declared in a super-interface
-					if (di.isAssignableFrom(method.getDeclaringClass())) {
-						method = di.getDeclaredMethod(method.getName(), method.getParameterTypes());
-					}
-				} catch (SecurityException e) {
-				} catch (NoSuchMethodException e) {
-				}
-				newDis.addAll(Arrays.asList(di.getInterfaces()));
-			}
-			newDis.removeAll(dis);
-			dis = newDis;
-		}
-		return method;
-	}
-
-	/**
-	 * Returns the relevant modifiers (visibility and static) for the given method.
-	 * @param method the method for which to return the modifiers
-	 * @return the relevant modifiers (visibility and static) for the given method
-	 */
-	private int getRelevantModifiers(final Method method) {
-		final int methodModifiers = method.getModifiers();
-		return methodModifiers & (Modifier.PRIVATE + Modifier.PROTECTED + Modifier.PUBLIC + Modifier.STATIC);
-	}
-
-	/**
 	 * {@inheritDoc}
 	 */
 	@Override
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EMFTVMUtil.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EMFTVMUtil.java
index d7a9164..8365df2 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EMFTVMUtil.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EMFTVMUtil.java
@@ -1109,7 +1109,9 @@
 	 *            the method arguments
 	 * @return the method result
 	 */
-	public static Object invokeNative(final StackFrame frame, final Object self, final Method method, final Object[] args) {
+	public static Object invokeNative(final StackFrame frame, final Object self, Method method, final Object[] args) {
+		// Fix for Bug # 461445: EMFTVM cannot invoke Java methods on instances of private classes:
+		method = findRootMethod(method);
 		final StackFrame subFrame = frame.prepareNativeArgs(method, self, args);
 		try {
 			return emf2vm(frame.getEnv(), self instanceof EObject ? (EObject) self : null, method.invoke(self, args));
@@ -1163,7 +1165,9 @@
 	 *            the method argument
 	 * @return the method result
 	 */
-	public static Object invokeNative(final StackFrame frame, final Object self, final Method method, Object arg) {
+	public static Object invokeNative(final StackFrame frame, final Object self, Method method, Object arg) {
+		// Fix for Bug # 461445: EMFTVM cannot invoke Java methods on instances of private classes:
+		method = findRootMethod(method);
 		StackFrame subFrame = frame.prepareNativeContext(method, self);
 		if (arg instanceof CodeBlock) {
 			if (subFrame == null) {
@@ -1220,7 +1224,9 @@
 	 *            the method
 	 * @return the method result
 	 */
-	public static Object invokeNative(final StackFrame frame, final Object self, final Method method) {
+	public static Object invokeNative(final StackFrame frame, final Object self, Method method) {
+		// Fix for Bug # 461445: EMFTVM cannot invoke Java methods on instances of private classes:
+		method = findRootMethod(method);
 		final StackFrame subFrame = frame.prepareNativeContext(method, self);
 		try {
 			return emf2vm(frame.getEnv(), self instanceof EObject ? (EObject) self : null, method.invoke(self));
@@ -2425,4 +2431,59 @@
 		}
 	}
 
+	/**
+	 * Finds the root {@link Class} declaration for the given <code>method</code>.
+	 * @param method the method for which to find the root declaration
+	 * @return the root {@link Method}
+	 */
+	public static Method findRootMethod(Method method) {
+		if (method == null) {
+			return null;
+		}
+		final int methodModifiers = getRelevantModifiers(method);
+		Class<?> dc = method.getDeclaringClass();
+		java.util.Set<Class<?>> dis = new LinkedHashSet<Class<?>>(
+				Arrays.asList(dc.getInterfaces()));
+		while ((dc = dc.getSuperclass()) != null) {
+			try {
+				Method superMethod = dc.getDeclaredMethod(method.getName(), method.getParameterTypes());
+				if (getRelevantModifiers(superMethod) == methodModifiers) {
+					method = superMethod;
+				} else {
+					break;
+				}
+			} catch (SecurityException e) {
+			} catch (NoSuchMethodException e) {
+			}
+			dis.addAll(Arrays.asList(dc.getInterfaces()));
+		}
+		while (!dis.isEmpty()) {
+			java.util.Set<Class<?>> newDis = new LinkedHashSet<Class<?>>();
+			for (Class<?> di : dis) {
+				try {
+					// Only replace by method declared in a super-interface
+					if (di.isAssignableFrom(method.getDeclaringClass())) {
+						method = di.getDeclaredMethod(method.getName(), method.getParameterTypes());
+					}
+				} catch (SecurityException e) {
+				} catch (NoSuchMethodException e) {
+				}
+				newDis.addAll(Arrays.asList(di.getInterfaces()));
+			}
+			newDis.removeAll(dis);
+			dis = newDis;
+		}
+		return method;
+	}
+
+	/**
+	 * Returns the relevant modifiers (visibility and static) for the given method.
+	 * @param method the method for which to return the modifiers
+	 * @return the relevant modifiers (visibility and static) for the given method
+	 */
+	private static int getRelevantModifiers(final Method method) {
+		final int methodModifiers = method.getModifiers();
+		return methodModifiers & (Modifier.PRIVATE + Modifier.PROTECTED + Modifier.PUBLIC + Modifier.STATIC);
+	}
+
 }
diff --git a/tests/org.eclipse.m2m.atl.emftvm.tests/launch/Bug461445.launch b/tests/org.eclipse.m2m.atl.emftvm.tests/launch/Bug461445.launch
new file mode 100644
index 0000000..6b308e8
--- /dev/null
+++ b/tests/org.eclipse.m2m.atl.emftvm.tests/launch/Bug461445.launch
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.m2m.atl.emftvm.launcher.EMFTVMTransformation">
+<stringAttribute key="ATL File Name" value="//org.eclipse.m2m.atl.emftvm.tests/test-data/Regression/Bug461445.atl"/>
+<booleanAttribute key="Disable JIT compiler" value="false"/>
+<booleanAttribute key="Display Profiling Data" value="false"/>
+<booleanAttribute key="Display Timing Data" value="true"/>
+<mapAttribute key="Inout Model Options"/>
+<mapAttribute key="Inout Models"/>
+<mapAttribute key="Inout Models Output Locations"/>
+<mapAttribute key="Input Model Options"/>
+<mapAttribute key="Input Models"/>
+<mapAttribute key="Metamodel Options"/>
+<mapAttribute key="Metamodels"/>
+<stringAttribute key="Module Name" value="Regression::Bug461445"/>
+<stringAttribute key="Module Path" value="/org.eclipse.m2m.atl.emftvm.tests/test-data/"/>
+<mapAttribute key="Output Model Options"/>
+<mapAttribute key="Output Models"/>
+<listAttribute key="Superimpose"/>
+</launchConfiguration>
diff --git a/tests/org.eclipse.m2m.atl.emftvm.tests/launch/EmftvmAllTests.launch b/tests/org.eclipse.m2m.atl.emftvm.tests/launch/EmftvmAllTests.launch
index 836b1d6..48a6214 100644
--- a/tests/org.eclipse.m2m.atl.emftvm.tests/launch/EmftvmAllTests.launch
+++ b/tests/org.eclipse.m2m.atl.emftvm.tests/launch/EmftvmAllTests.launch
@@ -24,7 +24,7 @@
 <booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
 <stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
 <stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
-<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/java-1.7.0-openjdk-1.7.0.71-2.5.3.0.fc19.x86_64"/>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
 <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.m2m.atl.emftvm.tests.EmftvmAllTests"/>
 <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/>
 <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.m2m.atl.emftvm.tests"/>
diff --git a/tests/org.eclipse.m2m.atl.emftvm.tests/src/org/eclipse/m2m/atl/emftvm/tests/integration/IntegrationTest.java b/tests/org.eclipse.m2m.atl.emftvm.tests/src/org/eclipse/m2m/atl/emftvm/tests/integration/IntegrationTest.java
index 537d20a..ab79a88 100644
--- a/tests/org.eclipse.m2m.atl.emftvm.tests/src/org/eclipse/m2m/atl/emftvm/tests/integration/IntegrationTest.java
+++ b/tests/org.eclipse.m2m.atl.emftvm.tests/src/org/eclipse/m2m/atl/emftvm/tests/integration/IntegrationTest.java
@@ -11,14 +11,24 @@
  *******************************************************************************/
 package org.eclipse.m2m.atl.emftvm.tests.integration;
 
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.logging.Level;
 
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EcorePackage;
 import org.eclipse.emf.ecore.resource.ResourceSet;
 import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
 import org.eclipse.m2m.atl.common.ATLLogger;
+import org.eclipse.m2m.atl.core.ATLCoreException;
+import org.eclipse.m2m.atl.core.IModel;
+import org.eclipse.m2m.atl.core.IReferenceModel;
+import org.eclipse.m2m.atl.core.ModelFactory;
 import org.eclipse.m2m.atl.emftvm.EmftvmFactory;
 import org.eclipse.m2m.atl.emftvm.EmftvmPackage;
 import org.eclipse.m2m.atl.emftvm.ExecEnv;
@@ -28,6 +38,7 @@
 import org.eclipse.m2m.atl.emftvm.trace.TracePackage;
 import org.eclipse.m2m.atl.emftvm.util.LazyList;
 import org.eclipse.m2m.atl.emftvm.util.TimingData;
+import org.eclipse.m2m.atl.engine.parser.AtlParser;
 
 /**
  * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
@@ -360,6 +371,44 @@
 	}
 
 	/**
+	 * Tests regression of <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=461445">Bug # 461445</a>.
+	 */
+	public void testBug461445() {
+		final ExecEnv env = EmftvmFactory.eINSTANCE.createExecEnv();
+		final TimingData td = new TimingData();
+		env.loadModule(createTestModuleResolver(), "Regression::Bug461445");
+		td.finishLoading();
+		final Object result = env.run(td);
+		td.finish();
+
+		assertEquals("value", result);
+	}
+
+	public void testATLAPI() throws ATLCoreException, IOException {
+		
+		ModelFactory mf = AtlParser.getDefault().getModelFactory();
+		IReferenceModel atlMM = AtlParser.getDefault().getAtlMetamodel();
+		IModel atlM = mf.newModel(atlMM);
+		
+		EObject module = (EObject) atlM.newElement(atlMM.getMetaElementByName("Module"));
+		module.eSet(module.eClass().getEStructuralFeature("name"), "testmodule");
+		EObject rule = (EObject) atlM.newElement(atlMM.getMetaElementByName("MatchedRule"));
+		rule.eSet(rule.eClass().getEStructuralFeature("name"), "Test");
+		EList<EObject> moduleElements = (EList<EObject>) module.eGet(module.eClass().getEStructuralFeature("elements"));
+		moduleElements.add(rule);
+		
+		FileOutputStream fos = new FileOutputStream("testmodule.atl");
+		try {
+			AtlParser.getDefault().extract(atlM, fos, Collections.emptyMap());
+		} finally {
+			if (fos != null) {
+				fos.close();
+			}
+		}
+		
+	}
+
+	/**
 	 * Tests "ToStringTest.atl".
 	 */
 	public void testToString() {
diff --git a/tests/org.eclipse.m2m.atl.emftvm.tests/test-data/Regression/Bug461445.atl b/tests/org.eclipse.m2m.atl.emftvm.tests/test-data/Regression/Bug461445.atl
new file mode 100644
index 0000000..6fb9e98
--- /dev/null
+++ b/tests/org.eclipse.m2m.atl.emftvm.tests/test-data/Regression/Bug461445.atl
@@ -0,0 +1,4 @@
+-- @atlcompiler emftvm
+query "Regression::Bug461445" = "#native"!"java::util::Collections"
+	.refInvokeStaticOperation('unmodifiableMap', Sequence{Map{('key', 'value')}})
+	.get('key').debug('Bug');
diff --git a/tests/org.eclipse.m2m.atl.emftvm.tests/test-data/Regression/Bug461445.emftvm b/tests/org.eclipse.m2m.atl.emftvm.tests/test-data/Regression/Bug461445.emftvm
new file mode 100644
index 0000000..88f4dc9
--- /dev/null
+++ b/tests/org.eclipse.m2m.atl.emftvm.tests/test-data/Regression/Bug461445.emftvm
Binary files differ