Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfjouault2015-01-08 13:21:10 -0500
committerfjouault2015-01-08 13:21:10 -0500
commit40023d506041524802ee376050b865f0393e67eb (patch)
tree70d3fa1c26a67cb6af87eba63e720d1ff93d2039 /plugins/org.eclipse.m2m.atl.emftvm
parent127c7935c50ff8c57d0b98b2603cd3ef16d2aff8 (diff)
parent870db15287ab33363250cf288449aba7b7c04908 (diff)
downloadorg.eclipse.atl-40023d506041524802ee376050b865f0393e67eb.tar.gz
org.eclipse.atl-40023d506041524802ee376050b865f0393e67eb.tar.xz
org.eclipse.atl-40023d506041524802ee376050b865f0393e67eb.zip
Merge branch 'master' of ssh://fjouault@git.eclipse.org/gitroot/mmt/org.eclipse.atl.git
Diffstat (limited to 'plugins/org.eclipse.m2m.atl.emftvm')
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF8
-rwxr-xr-xplugins/org.eclipse.m2m.atl.emftvm/about.ini2
-rwxr-xr-xplugins/org.eclipse.m2m.atl.emftvm/about.properties3
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/build.properties4
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/pom.xml4
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/CodeBlockImpl.java9
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/ExecEnvImpl.java21
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/LocalVariableInstructionImpl.java14
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/MetamodelImpl.java8
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/RuleImpl.java11
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/ByteCodeSwitch.java119
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/CodeBlockJIT.java86
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/JITCodeBlock.java120
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/Profiler.java361
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/StopWatch.java79
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EMFTVMUtil.java71
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EnumLiteral.java7
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyBag.java45
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyCollection.java15
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyList.java70
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyOrderedSet.java47
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazySet.java47
-rw-r--r--plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/OCLOperations.java13
23 files changed, 957 insertions, 207 deletions
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF b/plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF
index d1bc0553..1b93ded0 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.m2m.atl.emftvm/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.m2m.atl.emftvm;singleton:=true
-Bundle-Version: 3.4.100.qualifier
+Bundle-Version: 3.6.0.qualifier
Bundle-ClassPath: .
Bundle-Vendor: %providerName
Bundle-Localization: plugin
@@ -12,13 +12,15 @@ Export-Package: org.eclipse.m2m.atl.emftvm,
org.eclipse.m2m.atl.emftvm.constraints,
org.eclipse.m2m.atl.emftvm.impl,
org.eclipse.m2m.atl.emftvm.impl.resource,
+ org.eclipse.m2m.atl.emftvm.jit,
+ org.eclipse.m2m.atl.emftvm.profiler,
org.eclipse.m2m.atl.emftvm.util
Require-Bundle: org.eclipse.core.runtime,
- org.eclipse.emf.ecore;visibility:=reexport;bundle-version="2.4.0",
+ org.eclipse.emf.ecore;bundle-version="2.4.0";visibility:=reexport,
org.eclipse.emf.ecore.xmi;bundle-version="2.4.0",
org.eclipse.m2m.atl.emftvm.trace;visibility:=reexport,
org.eclipse.m2m.atl.common;bundle-version="3.1.0",
org.eclipse.core.resources,
org.eclipse.emf.validation;bundle-version="1.4.0",
- org.objectweb.asm;bundle-version="3.3.1"
+ org.objectweb.asm;bundle-version="[3.3.1,4.0.0)"
Bundle-ActivationPolicy: lazy
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/about.ini b/plugins/org.eclipse.m2m.atl.emftvm/about.ini
index 54a6168a..d232c3d2 100755
--- a/plugins/org.eclipse.m2m.atl.emftvm/about.ini
+++ b/plugins/org.eclipse.m2m.atl.emftvm/about.ini
@@ -8,7 +8,7 @@
aboutText=%aboutText
# Property "featureImage" contains path to feature image (32x32)
-featureImage=modeling32.gif
+featureImage=modeling32.png
# Property "appName" contains name of the application (translated)
appName=%appName \ No newline at end of file
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/about.properties b/plugins/org.eclipse.m2m.atl.emftvm/about.properties
index 0129210a..eff498bf 100755
--- a/plugins/org.eclipse.m2m.atl.emftvm/about.properties
+++ b/plugins/org.eclipse.m2m.atl.emftvm/about.properties
@@ -1,8 +1,9 @@
appName=EMF Transformation Virtual Machine
aboutText=EMF Transformation Virtual Machine Plug-in\n\
+Version: {featureVersion}\n\
\n\
-(c) Copyright 2011-2012 Vrije Universiteit Brussel.\n\
+(c) Copyright 2011-2014 Dennis Wagelaar, Vrije Universiteit Brussel.\n\
\n\
The ATL EMF Transformation Virtual Machine (EMFTVM) provides a new, low-level\n\
model manipulation bytecode language that is based on an Ecore metamodel, as well as\n\
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/build.properties b/plugins/org.eclipse.m2m.atl.emftvm/build.properties
index be6c91b3..e9aee49b 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/build.properties
+++ b/plugins/org.eclipse.m2m.atl.emftvm/build.properties
@@ -8,7 +8,9 @@ bin.includes = .,\
plugin.xml,\
plugin.properties,\
modeling32.png,\
- about.*
+ about.html,\
+ about.ini,\
+ about.properties
jars.compile.order = .
source.. = src/
output.. = bin/
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/pom.xml b/plugins/org.eclipse.m2m.atl.emftvm/pom.xml
index a70dc15c..09a33414 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/pom.xml
+++ b/plugins/org.eclipse.m2m.atl.emftvm/pom.xml
@@ -15,12 +15,12 @@
<parent>
<artifactId>m2m.atl-parent</artifactId>
<groupId>org.eclipse.m2m.atl</groupId>
- <version>3.4.0-SNAPSHOT</version>
+ <version>3.6.0-SNAPSHOT</version>
<relativePath>../../releng/org.eclipse.m2m.atl.releng.parent</relativePath>
</parent>
<groupId>org.eclipse.m2m.atl</groupId>
<artifactId>org.eclipse.m2m.atl.emftvm</artifactId>
- <version>3.4.100-SNAPSHOT</version>
+ <version>3.6.0-SNAPSHOT</version>
<packaging>eclipse-plugin</packaging>
<build>
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/CodeBlockImpl.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/CodeBlockImpl.java
index bd4809ae..6c0e41d8 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/CodeBlockImpl.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/CodeBlockImpl.java
@@ -97,6 +97,7 @@ import org.eclipse.m2m.atl.emftvm.util.LazySetOnSet;
import org.eclipse.m2m.atl.emftvm.util.NativeTypes;
import org.eclipse.m2m.atl.emftvm.util.Stack;
import org.eclipse.m2m.atl.emftvm.util.StackFrame;
+import org.eclipse.m2m.atl.emftvm.util.Tuple;
import org.eclipse.m2m.atl.emftvm.util.VMException;
import org.eclipse.m2m.atl.emftvm.util.VMMonitor;
@@ -999,6 +1000,9 @@ public class CodeBlockImpl extends EObjectImpl implements CodeBlock {
} catch (Exception e) {
frame.setPc(pc);
throw new VMException(frame, e);
+ } catch (VerifyError e) {
+ frame.setPc(pc);
+ throw new VMException(frame, e);
}
}
}
@@ -1935,6 +1939,11 @@ public class CodeBlockImpl extends EObjectImpl implements CodeBlock {
if (field != null) {
return field.getValue(o, frame);
}
+
+ if (o instanceof Tuple && ((Tuple) o).asMap().containsKey(propname)) {
+ return ((Tuple) o).get(propname);
+ }
+
try {
final java.lang.reflect.Field f = type.getField(propname);
final Object result = f.get(o);
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/ExecEnvImpl.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/ExecEnvImpl.java
index e2ab8344..eff95be5 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/ExecEnvImpl.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/ExecEnvImpl.java
@@ -1113,6 +1113,10 @@ public class ExecEnvImpl extends EObjectImpl implements ExecEnv {
}
// Prevent ConcurrentModificationException by using eObjects copy
for (EObject o : eObjects) {
+ // Skip remapping on objects queued for deletion
+ if (deletionQueue.containsKey(o)) {
+ continue;
+ }
for (EReference ref : o.eClass().getEAllReferences()) {
// Only change changeable references that are not the reverse of a containment reference
if (ref.isChangeable() && !ref.isContainer()) {
@@ -1221,12 +1225,6 @@ public class ExecEnvImpl extends EObjectImpl implements ExecEnv {
*/
public synchronized Module loadModule(final ModuleResolver resolver, final String name, final boolean validate) {
resetJITCompiler();
- if (isRuleStateCompiled()) {
- for (Rule r : getRules()) {
- r.resetState();
- }
- }
- setRuleStateCompiled(false);
try {
//detect cyclic imports w.r.t. redefinition
if (internalModules.containsKey(name)) {
@@ -1256,6 +1254,11 @@ public class ExecEnvImpl extends EObjectImpl implements ExecEnv {
throw new VMException(null, String.format("Byte code validation of %s failed", invalidObject));
}
}
+ // Bug 426154: validation triggers partial rule state compilation, so always reset:
+ for (Rule r : getRules()) {
+ r.resetState();
+ }
+ setRuleStateCompiled(false);
loadedModules.add(name);
return module;
} catch (Exception e) {
@@ -1508,6 +1511,12 @@ public class ExecEnvImpl extends EObjectImpl implements ExecEnv {
for (OutputRuleElement re : r.getOutputElements()) {
resolveRuleElement(re);
+ if (!r.isAbstract()) {
+ EClassifier eType = re.getEType();
+ if (eType instanceof EClass && ((EClass)eType).isAbstract()) {
+ throw new VMException(null, String.format("Non-abstract %s cannot have output elements of an abstract type: \"%s\"", r, re));
+ }
+ }
}
for (Field field : r.getFields()) {
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/LocalVariableInstructionImpl.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/LocalVariableInstructionImpl.java
index a8b30f26..79c5698b 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/LocalVariableInstructionImpl.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/LocalVariableInstructionImpl.java
@@ -411,6 +411,20 @@ public abstract class LocalVariableInstructionImpl extends InstructionImpl imple
* {@inheritDoc}
*/
@Override
+ public void eNotify(Notification notification) {
+ super.eNotify(notification);
+ switch (notification.getFeatureID(null)) {
+ case EmftvmPackage.CODE_BLOCK__CODE:
+ setCbOffset(CB_OFFSET_EDEFAULT);
+ setLocalVariableIndex(LOCAL_VARIABLE_INDEX_EDEFAULT);
+ break;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public String toString() {
if (eIsProxy()) return super.toString();
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/MetamodelImpl.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/MetamodelImpl.java
index 3a3f3747..680e44c9 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/MetamodelImpl.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/MetamodelImpl.java
@@ -89,7 +89,7 @@ public class MetamodelImpl extends ModelImpl implements Metamodel {
throw new IllegalArgumentException(String.format("Type %s not found in metamodel %s", typeName, this));
}
if (ambiguousTypes.contains(typeName)) {
- ATLLogger.warning(String.format("Metamodel contains more than one type with name %s", typeName));
+ ATLLogger.warning(String.format("Metamodel %s contains more than one type with name %s", this, typeName));
}
return type;
}
@@ -137,7 +137,7 @@ public class MetamodelImpl extends ModelImpl implements Metamodel {
*/
private static void registerTypeChain(
final Map<String, EClassifier> types,
- final EList<EObject> objects,
+ final EList<? extends EObject> objects,
final String ns,
final Set<Object> ignore,
final Set<String> ambiguousTypes) {
@@ -150,7 +150,9 @@ public class MetamodelImpl extends ModelImpl implements Metamodel {
}
registerTypeChain(types, o.eContents(), pname, ignore, ambiguousTypes);
break;
- case EcorePackage.ECLASSIFIER: //TODO Report EMF BUG: only EClass instances are returned!
+ // Fix for bug # 423597: switch on all concrete EClassifier sub-types
+ case EcorePackage.EDATA_TYPE:
+ case EcorePackage.EENUM:
case EcorePackage.ECLASS:
registerTypeChain(types, (EClassifier)o, ns, ignore, ambiguousTypes);
break;
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/RuleImpl.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/RuleImpl.java
index 64e23c0a..0ce6078f 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/RuleImpl.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/impl/RuleImpl.java
@@ -26,6 +26,8 @@ import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
@@ -52,6 +54,7 @@ import org.eclipse.m2m.atl.emftvm.trace.TraceFactory;
import org.eclipse.m2m.atl.emftvm.trace.TraceLink;
import org.eclipse.m2m.atl.emftvm.trace.TraceLinkSet;
import org.eclipse.m2m.atl.emftvm.trace.TracedRule;
+import org.eclipse.m2m.atl.emftvm.util.EnumLiteral;
import org.eclipse.m2m.atl.emftvm.util.FieldContainer;
import org.eclipse.m2m.atl.emftvm.util.LazySet;
import org.eclipse.m2m.atl.emftvm.util.StackFrame;
@@ -2103,7 +2106,13 @@ public class RuleImpl extends NamedElementImpl implements Rule {
"Cannot match rule input element %s against null value for %s",
re, this));
}
- if (!re.getEType().isInstance(value)) {
+ EClassifier eType = re.getEType();
+ if (eType instanceof EEnum) {
+ // Fix for Bug # 441027
+ if (!(value instanceof EnumLiteral)) {
+ return false;
+ }
+ } else if (!eType.isInstance(value)) {
return false;
}
EList<Model> inmodels = re.getEModels();
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 43d9fa1d..1ad77166 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011-2013 Dennis Wagelaar, Vrije Universiteit Brussel.
+ * Copyright (c) 2011-2014 Dennis Wagelaar, Vrije Universiteit Brussel.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -11,10 +11,13 @@
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;
@@ -90,7 +93,6 @@ import org.eclipse.m2m.atl.emftvm.util.EnumLiteral;
import org.eclipse.m2m.atl.emftvm.util.LazyCollection;
import org.eclipse.m2m.atl.emftvm.util.LazyList;
import org.eclipse.m2m.atl.emftvm.util.LazyListOnList;
-import org.eclipse.m2m.atl.emftvm.util.LazySet;
import org.eclipse.m2m.atl.emftvm.util.NativeTypes;
import org.eclipse.m2m.atl.emftvm.util.StackFrame;
import org.eclipse.m2m.atl.emftvm.util.VMException;
@@ -150,6 +152,10 @@ public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcod
* Whether or not the current execution environment has a monitor attached.
*/
protected final boolean hasMonitor;
+ /**
+ * Set of instructions to skip while generating bytecode.
+ */
+ protected final java.util.Set<Instruction> skipInstructions = new HashSet<Instruction>();
/**
* Creates a new {@link ByteCodeSwitch}.
@@ -384,7 +390,15 @@ public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcod
if (EMFTVMUtil.NATIVE.equals(object.getModelname())) {
try {
final Class<?> type = NativeTypes.findType(object.getTypename());
- ldc(Type.getType(type)); // [..., type]
+ final Instruction nextInstr = nextInstruction(object);
+ if (nextInstr instanceof New) {
+ new_(type);
+ dup();
+ invokeSpec(type, "<init>", Void.TYPE);
+ skipInstructions.add(nextInstr);
+ } else {
+ ldc(Type.getType(type)); // [..., type]
+ }
return super.caseFindtype(object);
} catch (ClassNotFoundException e) {
// fall back - will generate same exception anyway
@@ -415,16 +429,18 @@ public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcod
*/
@Override
public MethodVisitor caseNew(final New object) {
- // [..., type]
- final String modelName = object.getModelname();
- if (modelName == null) {
- aconst_null(); // [..., type, null]
- } else {
- ldc(object.getModelname()) ; // [..., type, modelname]
+ if (!skipInstructions.contains(object)) {
+ // [..., type]
+ final String modelName = object.getModelname();
+ if (modelName == null) {
+ aconst_null(); // [..., type, null]
+ } else {
+ ldc(object.getModelname()) ; // [..., type, modelname]
+ }
+ aload(2); // env: [..., type, modelname, env]
+ invokeStat(JITCodeBlock.class, "newInstance", Object.class, // newInstance(type, modelname, env): [..., object]
+ Object.class, String.class, ExecEnv.class);
}
- aload(2); // env: [..., type, modelname, env]
- invokeStat(JITCodeBlock.class, "newInstance", Object.class, // newInstance(type, modelname, env): [..., object]
- Object.class, String.class, ExecEnv.class);
return super.caseNew(object);
}
@@ -1077,35 +1093,53 @@ public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcod
if (method == null) {
return null;
}
+ final int methodModifiers = getRelevantModifiers(method);
Class<?> dc = method.getDeclaringClass();
- Class<?>[] dis = dc.getInterfaces();
+ java.util.Set<Class<?>> dis = new LinkedHashSet<Class<?>>(
+ Arrays.asList(dc.getInterfaces()));
while ((dc = dc.getSuperclass()) != null) {
try {
- method = dc.getDeclaredMethod(method.getName(), method.getParameterTypes());
+ Method superMethod = dc.getDeclaredMethod(method.getName(), method.getParameterTypes());
+ if (getRelevantModifiers(superMethod) == methodModifiers) {
+ method = superMethod;
+ } else {
+ break;
+ }
} catch (SecurityException e) {
- break;
} catch (NoSuchMethodException e) {
- break;
}
- dis = dc.getInterfaces();
+ dis.addAll(Arrays.asList(dc.getInterfaces()));
}
- while (dis.length > 0) {
- Class<?>[] newDis = new Class<?>[0];
+ while (!dis.isEmpty()) {
+ java.util.Set<Class<?>> newDis = new LinkedHashSet<Class<?>>();
for (Class<?> di : dis) {
try {
- method = di.getDeclaredMethod(method.getName(), method.getParameterTypes());
- newDis = di.getInterfaces();
- break; // skip sibling interfaces
+ // 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
@@ -2102,11 +2136,11 @@ public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcod
invokeVirt(EnumConversionList.class, "cache", EnumConversionList.class); // enumlist.cache(): [..., enumlist]
label(ifModelNull); // [..., enumlist]
}
- } else if (Set.class.isAssignableFrom(cls)) {
+ } else if (java.util.Set.class.isAssignableFrom(cls)) {
new_(EnumConversionSetOnSet.class); // new EnumConversionSetOnSet: [..., val, enumset]
dup_x1(); // [..., enumset, val, enumset]
swap(); // [..., enumset, enumset, val]
- invokeCons(EnumConversionSetOnSet.class, Set.class); // enumset.<init>(val): [..., enumset]
+ invokeCons(EnumConversionSetOnSet.class, java.util.Set.class); // enumset.<init>(val): [..., enumset]
if (EObject.class.isAssignableFrom(selfCls)) {
final Label ifModelNull = new Label();
aload(2); // env: [..., enumset, env]
@@ -2116,7 +2150,6 @@ public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcod
invokeVirt(EnumConversionSetOnSet.class, "cache", EnumConversionSetOnSet.class); // enumlist.cache(): [..., enumset]
label(ifModelNull); // [..., enumset]
}
- invokeVirt(LazyCollection.class, "asSet", LazySet.class); // enumlist.asSet(): [..., enumset]
} else {
new_(EnumConversionList.class); // new EnumConversionList: [..., val, enumlist]
dup_x1(); // [..., enumlist, val, enumlist]
@@ -2135,20 +2168,18 @@ public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcod
}
} else if (cls.isArray()) {
final Class<?> cType = cls.getComponentType();
- // Array of cType
- final Label ifNull = new Label();
- dup(); // [..., array, array]
- ifnull(ifNull); // jump if array == null: [..., array]
if (Object.class.isAssignableFrom(cType)) {
+ // Array of cType
+ final Label ifNull = new Label();
+ dup(); // [..., array, array]
+ ifnull(ifNull); // jump if array == null: [..., array]
invokeStat(Arrays.class, "asList", List.class, Object[].class); // Arrays.asList(array): [..., list]
- } else {
- invokeStat(JITCodeBlock.class, "asList", List.class, cls); // JITCodeBlock.asList(array): [..., list]
- }
- new_(LazyListOnList.class); // new LazyListOnList: [..., list, lazylist]
- dup_x1(); // [..., lazylist, list, lazylist]
- swap(); // [..., lazylist, lazylist, list]
- invokeCons(LazyListOnList.class, List.class); // lazylist.<init>(list): [..., lazylist]
- label(ifNull);
+ new_(LazyListOnList.class); // new LazyListOnList: [..., list, lazylist]
+ dup_x1(); // [..., lazylist, list, lazylist]
+ swap(); // [..., lazylist, lazylist, list]
+ invokeCons(LazyListOnList.class, List.class); // lazylist.<init>(list): [..., lazylist]
+ label(ifNull);
+ } // don't wrap primitive type arrays
}
// [..., Object]
}
@@ -2413,5 +2444,19 @@ public class ByteCodeSwitch extends EmftvmSwitch<MethodVisitor> implements Opcod
final Label handler, final Class<?> type) {
mv.visitTryCatchBlock(start, end, handler, Type.getInternalName(type));
}
+
+ /**
+ * Returns the next instruction for the given instruction, or <code>null</code>.
+ * @param instruction the instruction
+ * @return the next instruction for the given instruction, or <code>null</code>
+ */
+ protected Instruction nextInstruction(final Instruction instruction) {
+ final List<Instruction> code = instruction.getOwningBlock().getCode();
+ final int index = code.indexOf(instruction);
+ if (index < code.size() - 1) {
+ return code.get(index + 1);
+ }
+ return null;
+ }
} \ No newline at end of file
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/CodeBlockJIT.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/CodeBlockJIT.java
index ca90522e..2bae9060 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/CodeBlockJIT.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/CodeBlockJIT.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011-2013 Dennis Wagelaar, Vrije Universiteit Brussel.
+ * Copyright (c) 2011-2014 Dennis Wagelaar, Vrije Universiteit Brussel.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -63,25 +63,9 @@ public class CodeBlockJIT implements Opcodes {
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (codeBlocks.containsKey(name)) {
final byte[] b = internalJit(codeBlocks.get(name), name);
+ byteCode.put(name, b);
if (isDumpBytecode()) {
- try {
- final String path = name.substring(0, name.lastIndexOf('.')).replace('.', File.separatorChar);
- final File p = new File(System.getProperty("java.io.tmpdir") + File.separatorChar + path);
- p.mkdirs();
- final File f = new File(p, name.substring(name.lastIndexOf('.') + 1) + ".class");
- f.createNewFile();
- final FileOutputStream fos = new FileOutputStream(f);
- try {
- fos.write(b);
- } finally {
- fos.close();
- }
- ATLLogger.info(String.format("Wrote JIT-ed code block %s to %s",
- codeBlocks.get(name), f.getAbsolutePath()));
- } catch (IOException e) {
- e.printStackTrace();
- throw new RuntimeException(e);
- }
+ dumpByteCode(name, b);
}
return defineClass(name, b, 0, b.length);
}
@@ -101,6 +85,10 @@ public class CodeBlockJIT implements Opcodes {
* The execution environment to JIT for.
*/
protected final ExecEnv env;
+ /**
+ * {@link Map} of class names to generated bytecode.
+ */
+ protected final Map<String, byte[]> byteCode = Collections.synchronizedMap(new HashMap<String, byte[]>());
/**
* Creates a new {@link CodeBlockJIT}.
@@ -129,8 +117,16 @@ public class CodeBlockJIT implements Opcodes {
InvocationTargetException, NoSuchMethodException {
final String className = getNextClassName();
codeBlocks.put(className, cb);
- return (JITCodeBlock)classLoader.findClass(className)
- .getConstructor(CodeBlock.class).newInstance(cb);
+ try {
+ return (JITCodeBlock)classLoader.findClass(className)
+ .getConstructor(CodeBlock.class).newInstance(cb);
+ } catch (VerifyError e) {
+ final byte[] b = byteCode.get(className);
+ if (b != null) {
+ dumpByteCode(className, b);
+ }
+ throw e;
+ }
}
/**
@@ -230,10 +226,20 @@ public class CodeBlockJIT implements Opcodes {
execute.visitVarInsn(ALOAD, 2); // env
execute.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(ExecEnv.class), "getMonitor", Type.getMethodDescriptor(Type.getType(VMMonitor.class), new Type[0]));
execute.visitVarInsn(ASTORE, 3); // monitor
+ final boolean hasMonitor = getEnv().getMonitor() != null;
+ if (hasMonitor) {
+ execute.visitVarInsn(ALOAD, 3); // monitor: [..., monitor]
+ execute.visitVarInsn(ALOAD, 1); // frame: [..., monitor, frame]
+ execute.visitMethodInsn(INVOKEINTERFACE, // monitor.enter(frame): [...]
+ Type.getInternalName(VMMonitor.class),
+ "enter",
+ Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{
+ Type.getType(StackFrame.class)
+ }));
+ }
execute.visitLabel(tryStart);
final ByteCodeSwitch bs = new ByteCodeSwitch(this, execute, ls);
final EList<Instruction> code = cb.getCode();
- final boolean hasMonitor = getEnv().getMonitor() != null;
for (Instruction instr : code) {
if (hasMonitor) {
// do checkMonitor() before each instruction
@@ -265,6 +271,16 @@ public class CodeBlockJIT implements Opcodes {
execute.visitInsn(ATHROW); // throw vme
// Regular method return
execute.visitLabel(catchEnd);
+ if (hasMonitor) {
+ execute.visitVarInsn(ALOAD, 3); // monitor: [..., monitor]
+ execute.visitVarInsn(ALOAD, 1); // frame: [..., monitor, frame]
+ execute.visitMethodInsn(INVOKEINTERFACE, // monitor.leave(frame): [...]
+ Type.getInternalName(VMMonitor.class),
+ "leave",
+ Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{
+ Type.getType(StackFrame.class)
+ }));
+ }
if (cb.getStackLevel() == 0) {
execute.visitInsn(ACONST_NULL); // push null
}
@@ -385,6 +401,32 @@ public class CodeBlockJIT implements Opcodes {
cb.setJITCodeBlock(null);
}
codeBlocks.clear();
+ byteCode.clear();
+ }
+
+ /**
+ * Dumps generated bytecode to the temp directory.
+ * @param name the class name
+ * @param b the bytecode to dump
+ */
+ private void dumpByteCode(String name, byte[] b) {
+ try {
+ final String path = name.substring(0, name.lastIndexOf('.')).replace('.', File.separatorChar);
+ final File p = new File(System.getProperty("java.io.tmpdir") + File.separatorChar + path);
+ p.mkdirs();
+ final File f = new File(p, name.substring(name.lastIndexOf('.') + 1) + ".class");
+ f.createNewFile();
+ final FileOutputStream fos = new FileOutputStream(f);
+ try {
+ fos.write(b);
+ } finally {
+ fos.close();
+ }
+ ATLLogger.info(String.format("Wrote JIT-ed code block %s to %s",
+ codeBlocks.get(name), f.getAbsolutePath()));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/JITCodeBlock.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/JITCodeBlock.java
index 7116750b..d0388563 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/JITCodeBlock.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/jit/JITCodeBlock.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011-2012 Dennis Wagelaar, Vrije Universiteit Brussel.
+ * Copyright (c) 2011-2014 Dennis Wagelaar, Vrije Universiteit Brussel.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -13,7 +13,6 @@ package org.eclipse.m2m.atl.emftvm.jit;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
@@ -41,6 +40,7 @@ import org.eclipse.m2m.atl.emftvm.util.LazyListOnList;
import org.eclipse.m2m.atl.emftvm.util.LazySetOnSet;
import org.eclipse.m2m.atl.emftvm.util.NativeTypes;
import org.eclipse.m2m.atl.emftvm.util.StackFrame;
+import org.eclipse.m2m.atl.emftvm.util.Tuple;
import org.eclipse.m2m.atl.emftvm.util.VMException;
/**
@@ -269,6 +269,11 @@ public abstract class JITCodeBlock {
if (field != null) {
return field.getValue(o, frame);
}
+
+ if (o instanceof Tuple && ((Tuple) o).asMap().containsKey(propname)) {
+ return ((Tuple) o).get(propname);
+ }
+
try {
final java.lang.reflect.Field f = type.getField(propname);
final Object result = f.get(o);
@@ -333,6 +338,11 @@ public abstract class JITCodeBlock {
if (field != null) {
return field.getValue(o, frame);
}
+
+ if (o instanceof Tuple && ((Tuple) o).asMap().containsKey(propname)) {
+ return ((Tuple) o).get(propname);
+ }
+
try {
final java.lang.reflect.Field f = type.getField(propname);
final Object result = f.get(o);
@@ -378,6 +388,8 @@ public abstract class JITCodeBlock {
}
throw new NoSuchFieldException(String.format("Field %s::%s not found",
EMFTVMUtil.toPrettyString(type, env), propname));
+ } else if (o instanceof Tuple && ((Tuple) o).asMap().containsKey(propname)) {
+ return ((Tuple) o).get(propname);
}
// o is a regular Java object
@@ -1498,108 +1510,4 @@ public abstract class JITCodeBlock {
throw new NoSuchFieldException(String.format("Field %s::%s not found", EMFTVMUtil.toPrettyString(type, env), propname));
}
- /**
- * Converts <code>array</code> to a {@link List}.
- * @param array the array to convert
- * @return the {@link List} containing the <code>array</code> values
- */
- protected static List<Boolean> asList(boolean[] array) {
- final List<Boolean> list = new ArrayList<Boolean>(array.length);
- for (int i = 0; i < array.length; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
- /**
- * Converts <code>array</code> to a {@link List}.
- * @param array the array to convert
- * @return the {@link List} containing the <code>array</code> values
- */
- protected static List<Character> asList(char[] array) {
- final List<Character> list = new ArrayList<Character>(array.length);
- for (int i = 0; i < array.length; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
- /**
- * Converts <code>array</code> to a {@link List}.
- * @param array the array to convert
- * @return the {@link List} containing the <code>array</code> values
- */
- protected static List<Byte> asList(byte[] array) {
- final List<Byte> list = new ArrayList<Byte>(array.length);
- for (int i = 0; i < array.length; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
- /**
- * Converts <code>array</code> to a {@link List}.
- * @param array the array to convert
- * @return the {@link List} containing the <code>array</code> values
- */
- protected static List<Short> asList(short[] array) {
- final List<Short> list = new ArrayList<Short>(array.length);
- for (int i = 0; i < array.length; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
- /**
- * Converts <code>array</code> to a {@link List}.
- * @param array the array to convert
- * @return the {@link List} containing the <code>array</code> values
- */
- protected static List<Integer> asList(int[] array) {
- final List<Integer> list = new ArrayList<Integer>(array.length);
- for (int i = 0; i < array.length; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
- /**
- * Converts <code>array</code> to a {@link List}.
- * @param array the array to convert
- * @return the {@link List} containing the <code>array</code> values
- */
- protected static List<Long> asList(long[] array) {
- final List<Long> list = new ArrayList<Long>(array.length);
- for (int i = 0; i < array.length; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
- /**
- * Converts <code>array</code> to a {@link List}.
- * @param array the array to convert
- * @return the {@link List} containing the <code>array</code> values
- */
- protected static List<Float> asList(float[] array) {
- final List<Float> list = new ArrayList<Float>(array.length);
- for (int i = 0; i < array.length; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
- /**
- * Converts <code>array</code> to a {@link List}.
- * @param array the array to convert
- * @return the {@link List} containing the <code>array</code> values
- */
- protected static List<Double> asList(double[] array) {
- final List<Double> list = new ArrayList<Double>(array.length);
- for (int i = 0; i < array.length; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/Profiler.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/Profiler.java
new file mode 100644
index 00000000..156e1790
--- /dev/null
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/Profiler.java
@@ -0,0 +1,361 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Dennis Wagelaar.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Dennis Wagelaar - initial API and
+ * implementation and/or initial documentation
+ *******************************************************************************/
+package org.eclipse.m2m.atl.emftvm.profiler;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.eclipse.m2m.atl.emftvm.CodeBlock;
+import org.eclipse.m2m.atl.emftvm.util.StackFrame;
+import org.eclipse.m2m.atl.emftvm.util.VMMonitor;
+
+/**
+ * Profiler for EMFTVM.
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ */
+public class Profiler implements VMMonitor {
+
+ /**
+ * Key-value pair.
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ *
+ * @param <K>
+ * the key type
+ * @param <V>
+ * the value type
+ */
+ static class Pair<K, V> {
+
+ private K key;
+ private V value;
+
+ public Pair(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * @return the key
+ */
+ public K getKey() {
+ return key;
+ }
+
+ /**
+ * @param key
+ * the key to set
+ */
+ public void setKey(K key) {
+ this.key = key;
+ }
+
+ /**
+ * @return the value
+ */
+ public V getValue() {
+ return value;
+ }
+
+ /**
+ * @param value
+ * the value to set
+ */
+ public void setValue(V value) {
+ this.value = value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "Pair [key=" + key + ", value=" + value + "]";
+ }
+
+ }
+
+ /**
+ * Profiling data for a single operation ({@link CodeBlock} or {@link Method}).
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ */
+ public static class ProfilingData {
+
+ private final CodeBlock codeBlock;
+ private final Method method;
+ private final long duration;
+ private final double durationRatio;
+ private final long invocations;
+
+ /**
+ * Creates a new {@link ProfilingData}.
+ *
+ * @param codeBlock
+ * the {@link CodeBlock}, if applicable
+ * @param method
+ * the native {@link Method}, if applicable
+ * @param duration
+ * the duration spent in nanoseconds
+ * @param durationRatio
+ * the duration ratio to the total execution time
+ * @param invocations
+ * the amount of invocations
+ */
+ public ProfilingData(CodeBlock codeBlock, Method method, long duration, double durationRatio, long invocations) {
+ this.codeBlock = codeBlock;
+ this.method = method;
+ this.duration = duration;
+ this.durationRatio = durationRatio;
+ this.invocations = invocations;
+ }
+
+ /**
+ * Returns the {@link CodeBlock}, if applicable.
+ *
+ * @return the codeBlock, or <code>null</code>
+ */
+ public CodeBlock getCodeBlock() {
+ return codeBlock;
+ }
+
+ /**
+ * Returns the native {@link Method}, if applicable.
+ *
+ * @return the method, or <code>null</code>
+ */
+ public Method getMethod() {
+ return method;
+ }
+
+ /**
+ * Returns the duration spent in nanoseconds.
+ *
+ * @return the duration
+ */
+ public long getDuration() {
+ return duration;
+ }
+
+ /**
+ * Returns the duration ratio to the total execution time.
+ *
+ * @return the durationRatio
+ */
+ public double getDurationRatio() {
+ return durationRatio;
+ }
+
+ /**
+ * Returns the amount of invocations.
+ *
+ * @return the invocations
+ */
+ public long getInvocations() {
+ return invocations;
+ }
+
+ /**
+ * Returns {@link #getCodeBlock()} or {@link #getMethod()}, whichever is not <code>null</code>.
+ * @return {@link #getCodeBlock()}, {@link #getMethod()}, or <code>null</code>
+ */
+ public Object getOperation() {
+ Object operation = getCodeBlock();
+ if (operation == null) {
+ operation = getMethod();
+ }
+ return operation;
+ }
+
+ }
+
+ private static final double DIVISOR = 1E9;
+ private static final double HUNDRED = 1E2;
+
+ private final StopWatch stopWatch = new StopWatch();
+ private final Map<CodeBlock, Pair<Long, StopWatch>> codeBlockTimings = new HashMap<CodeBlock, Pair<Long, StopWatch>>();
+ private final Map<Method, Pair<Long, StopWatch>> nativeMethodTimings = new HashMap<Method, Pair<Long, StopWatch>>();
+ private final Stack<Pair<Long, StopWatch>> timingStack = new Stack<Pair<Long, StopWatch>>();
+ private final List<ProfilingData> results = new ArrayList<ProfilingData>();
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isTerminated() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void enter(final StackFrame frame) {
+ // Start global stopwatch on first entry
+ if (timingStack.isEmpty()) {
+ stopWatch.start();
+ }
+ // Process current operation timings, and pause parent operation timings
+ final Pair<Long, StopWatch> timings = getTimings(frame);
+ if (timings != null) {
+ if (!timingStack.isEmpty()) {
+ Pair<Long, StopWatch> currentTimings = timingStack.peek();
+ if (currentTimings != timings) {
+ currentTimings.getValue().stop();
+ }
+ }
+ timings.setKey(timings.getKey() + 1L);
+ timings.getValue().start();
+ timingStack.push(timings);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void leave(final StackFrame frame) {
+ if (!timingStack.isEmpty()) {
+ final Pair<Long, StopWatch> timings = timingStack.pop();
+ assert timings == getTimings(frame);
+ if (!timingStack.isEmpty()) {
+ Pair<Long, StopWatch> parentTimings = timingStack.peek();
+ if (parentTimings != timings) {
+ timings.getValue().stop();
+ parentTimings.getValue().start();
+ }
+ } else {
+ timings.getValue().stop();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void step(StackFrame frame) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void terminated() {
+ // Stop any running stopwatches
+ while (!timingStack.isEmpty()) {
+ timingStack.pop().getValue().stop();
+ }
+ stopWatch.stop();
+ final long globalDuration = stopWatch.getDuration();
+ stopWatch.reset();
+
+ // Sort results by descending duration
+ final List<Object> sorted = new ArrayList<Object>(codeBlockTimings.size() + nativeMethodTimings.size());
+ sorted.addAll(codeBlockTimings.keySet());
+ sorted.addAll(nativeMethodTimings.keySet());
+ Collections.sort(sorted, new Comparator<Object>() {
+ public int compare(final Object o1, final Object o2) {
+ return -Long.valueOf(getTimings(o1).getValue().getDuration()).compareTo(
+ Long.valueOf(getTimings(o2).getValue().getDuration()));
+ }
+ });
+
+ // Compile profiling results
+ for (Object op : sorted) {
+ ProfilingData profilingData;
+ Pair<Long, StopWatch> timings = getTimings(op);
+ long duration = timings.getValue().getDuration();
+ double ratio = (double) duration / (double) globalDuration;
+ if (op instanceof CodeBlock) {
+ profilingData = new ProfilingData((CodeBlock) op, null, duration, ratio, timings.getKey());
+ } else {
+ profilingData = new ProfilingData(null, (Method) op, duration, ratio, timings.getKey());
+ }
+ results.add(profilingData);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void error(StackFrame frame, String msg, Exception e) {
+ }
+
+ /**
+ * Returns the profiling results.
+ *
+ * @return the results
+ */
+ public List<ProfilingData> getResults() {
+ return results;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("Profiling results:\n\tDuration (sec.)\tDuration (%)\tInvocations\tOperation\n");
+ for (ProfilingData profilingData : getResults()) {
+ sb.append(String.format("\t%15.6f\t%12.2f\t%11d\t%s\n", profilingData.getDuration() / DIVISOR, profilingData.getDurationRatio() * HUNDRED, profilingData.getInvocations(), profilingData.getOperation()));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Returns the timings for the given stack frame.
+ *
+ * @param frame
+ * the stack frame
+ * @return the timings for the given stack frame, or <code>null</code> if no operation is linked to the stack frame
+ */
+ private Pair<Long, StopWatch> getTimings(final StackFrame frame) {
+ final CodeBlock cb = frame.getCodeBlock();
+ if (cb != null) {
+ Pair<Long, StopWatch> timings = codeBlockTimings.get(cb);
+ if (timings == null) {
+ timings = new Pair<Long, StopWatch>(0L, new StopWatch());
+ codeBlockTimings.put(cb, timings);
+ }
+ return timings;
+ }
+ final Method m = frame.getNativeMethod();
+ if (m != null) {
+ Pair<Long, StopWatch> timings = nativeMethodTimings.get(m);
+ if (timings == null) {
+ timings = new Pair<Long, StopWatch>(0L, new StopWatch());
+ nativeMethodTimings.put(m, timings);
+ }
+ return timings;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the timings for the given codeblock or native method.
+ *
+ * @param operation
+ * the codeblock or native method
+ * @return the timings for the given codeblock or native method, or <code>null</code> if no operation is linked to the stack frame
+ */
+ private Pair<Long, StopWatch> getTimings(final Object operation) {
+ Pair<Long, StopWatch> timings = codeBlockTimings.get(operation);
+ if (timings == null) {
+ timings = nativeMethodTimings.get(operation);
+ }
+ return timings;
+ }
+
+}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/StopWatch.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/StopWatch.java
new file mode 100644
index 00000000..91e8d48d
--- /dev/null
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/profiler/StopWatch.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Dennis Wagelaar.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Dennis Wagelaar - initial API and
+ * implementation and/or initial documentation
+ *******************************************************************************/
+package org.eclipse.m2m.atl.emftvm.profiler;
+
+/**
+ * Stopwatch utility class.
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ */
+public class StopWatch {
+
+ private long duration;
+ private long start;
+ private boolean started;
+
+ /**
+ * Returns <code>true</code> if the stopwatch is started.
+ *
+ * @return <code>true</code> if the stopwatch is started
+ */
+ public boolean isStarted() {
+ return started;
+ }
+
+ /**
+ * Starts the stopwatch.
+ */
+ public void start() {
+ if (!isStarted()) {
+ start = System.nanoTime();
+ started = true;
+ }
+ }
+
+ /**
+ * Stops the stopwatch.
+ */
+ public void stop() {
+ if (isStarted()) {
+ duration += System.nanoTime() - start;
+ started = false;
+ }
+ }
+
+ /**
+ * Resets the stopwatch to "0".
+ */
+ public void reset() {
+ duration = 0L;
+ start = System.nanoTime();
+ }
+
+ /**
+ * Returns the measured duration in nanoseconds.
+ *
+ * @return the measured duration in nanoseconds
+ */
+ public long getDuration() {
+ return duration;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "StopWatch [isStarted()=" + isStarted() + ", getDuration()=" + getDuration() + "]";
+ }
+
+}
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 fbf6817a..d7a9164e 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
@@ -46,6 +46,7 @@ import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.util.EcoreSwitch;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.m2m.atl.common.ATLLogger;
@@ -70,6 +71,51 @@ import org.eclipse.m2m.atl.emftvm.trace.TracePackage;
public final class EMFTVMUtil {
/**
+ * Returns the registry type of the switched object.
+ *
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ */
+ public static class RegistryTypeSwitch extends EcoreSwitch<Object> {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object defaultCase(EObject object) {
+ throw new IllegalArgumentException("Unsupported type: " + object);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object caseEClass(EClass object) {
+ return object;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object caseEClassifier(EClassifier object) {
+ final Class<?> ic = object.getInstanceClass();
+ if (ic == null) {
+ throw new IllegalArgumentException(String.format("Primitive EMF type without instance class %s", object));
+ }
+ return ic;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object caseEEnum(EEnum object) {
+ return EnumLiteral.class;
+ }
+
+ }
+
+ /**
* Native type namespace.
*/
public static final String NATIVE = "#native";
@@ -111,6 +157,11 @@ public final class EMFTVMUtil {
*/
private static final Map<Class<?>, Map<Integer, Method>> METHOD_CACHE = new WeakHashMap<Class<?>, Map<Integer, Method>>();
+ /**
+ * Singleton {@link RegistryTypeSwitch} instance.
+ */
+ private static final RegistryTypeSwitch REGISTRY_TYPE_SWITCH = new RegistryTypeSwitch();
+
private static Metamodel ecoreMetamodel;
private static Metamodel emfTvmMetamodel;
private static Metamodel traceMetamodel;
@@ -177,12 +228,8 @@ public final class EMFTVMUtil {
* if type is a primitive EMF type without instance class
*/
public static Object getRegistryType(final Object type) throws IllegalArgumentException {
- if (type instanceof EClassifier && !(type instanceof EClass)) {
- final Class<?> ic = ((EClassifier) type).getInstanceClass();
- if (ic == null) {
- throw new IllegalArgumentException(String.format("Primitive EMF type without instance class %s", type));
- }
- return ic;
+ if (type instanceof EClassifier) {
+ return REGISTRY_TYPE_SWITCH.doSwitch((EClassifier) type);
}
return type;
}
@@ -277,16 +324,16 @@ public final class EMFTVMUtil {
* @return the string representation of <code>object</code>.
*/
public static String toPrettyString(final Object object, final ExecEnv env) {
- if (object instanceof EClass) {
+ if (object instanceof EClassifier) {
final StringBuffer sb = new StringBuffer();
if (env != null) {
- final Model model = env.getModelOf((EClass) object);
+ final Model model = env.getModelOf((EClassifier) object);
if (model != null) {
sb.append(env.getModelID(model));
sb.append('!');
}
}
- sb.append(((EClass) object).getName());
+ sb.append(((EClassifier) object).getName());
return sb.toString();
} else if (object instanceof EObject) { // EObjects have a notoriously bad toString()
final StringBuffer buf = new StringBuffer();
@@ -470,7 +517,11 @@ public final class EMFTVMUtil {
}
}
} else if (value != null && value.getClass().isArray()) {
- return new LazyListOnList<Object>(Arrays.asList((Object[]) value));
+ if (Object.class.isAssignableFrom(value.getClass().getComponentType())) {
+ return new LazyListOnList<Object>(Arrays.asList((Object[]) value));
+ } else {
+ return value; // don't wrap primitive type arrays
+ }
}
assert eo == null || !(value instanceof Collection<?>);
return value;
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EnumLiteral.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EnumLiteral.java
index 5bf6a734..591eb306 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EnumLiteral.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/EnumLiteral.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011 Vrije Universiteit Brussel.
+ * Copyright (c) 2011-2013 Dennis Wagelaar, Vrije Universiteit Brussel.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -49,11 +49,8 @@ public final class EnumLiteral implements Serializable {
*/
@Override
public String toString() {
- StringBuffer buf = new StringBuffer();
- buf.append(getClass().getSimpleName());
- buf.append('(');
+ StringBuffer buf = new StringBuffer("#");
buf.append(getName());
- buf.append(')');
return buf.toString();
}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyBag.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyBag.java
index f4fb351d..09dd2145 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyBag.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyBag.java
@@ -12,8 +12,10 @@
package org.eclipse.m2m.atl.emftvm.util;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -766,6 +768,47 @@ public class LazyBag<E> extends LazyCollection<E> {
});
}
- //TODO provide other iterator operations: collectNested, sortedBy
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LazyList<E> sortedBy(final CodeBlock body) {
+ // Parent frame may change after this method returns!
+ final StackFrame parentFrame = body.getParentFrame();
+ body.setParentFrame(null);
+ return new LazyList<E>(this) {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator<E> iterator() {
+ final Collection<E> inner = (Collection<E>) dataSource;
+ if (inner != null) {
+ final Iterator<Comparable<Object>> sortingKeys = new CollectIterator<Comparable<Object>>(inner, body, parentFrame);
+ final Object[] innerCopy = inner.toArray();
+ final Map<Object, Comparable<Object>> elementsToKeys = new HashMap<Object, Comparable<Object>>(innerCopy.length);
+ for (Object o : innerCopy) {
+ elementsToKeys.put(o, sortingKeys.next());
+ }
+ assert !sortingKeys.hasNext();
+ Arrays.sort(innerCopy, new Comparator<Object>() {
+ public int compare(Object o1, Object o2) {
+ return elementsToKeys.get(o1).compareTo(elementsToKeys.get(o2));
+ }
+ });
+ cache = (Collection<E>) Arrays.asList(innerCopy);
+ dataSource = null;
+ }
+ return super.iterator();
+ }
+ @Override
+ public int size() {
+ if (dataSource == null) {
+ return cache.size();
+ }
+ return ((Collection<E>) dataSource).size();
+ }
+ };
+ }
+
+ //TODO provide other iterator operations: collectNested
}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyCollection.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyCollection.java
index 2580d09d..250c476c 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyCollection.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyCollection.java
@@ -1369,7 +1369,7 @@ public abstract class LazyCollection<E> implements Collection<E> {
*/
@SuppressWarnings("unchecked")
public T next() {
- return (T) function.execute(parentFrame.getSubFrame(function, new Object[] { inner.next() }));
+ return (T) function.execute(parentFrame.getSubFrame(function, inner.next()));
}
}
@@ -2353,4 +2353,17 @@ public abstract class LazyCollection<E> implements Collection<E> {
return result;
}
+ /**
+ * Results in the Collection containing all elements of the source
+ * collection. The element for which body has the lowest value comes
+ * first, and so on. The type of the body expression must have the
+ * <code>&lt;</code> operation defined. The <code>&lt;</code> operation
+ * must return a Boolean value and must be transitive (i.e., if
+ * <code>a &lt; b</code> and <code>b &lt; c</code> then
+ * <code>a &lt; c</code>).
+ * @param body the function to evaluate on each element
+ * @return the sorted collection
+ */
+ public abstract LazyCollection<E> sortedBy(final CodeBlock body);
+
} \ No newline at end of file
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyList.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyList.java
index a10553a4..91d4a73d 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyList.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyList.java
@@ -12,11 +12,15 @@
package org.eclipse.m2m.atl.emftvm.util;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
+import java.util.Map;
import java.util.NoSuchElementException;
import org.eclipse.m2m.atl.emftvm.CodeBlock;
@@ -1546,6 +1550,25 @@ public class LazyList<E> extends LazyCollection<E> implements List<E> {
}
}
+
+ /**
+ * {@link LazyList} that implements the {@link LazyList#collect(CodeBlock)} function.
+ * @author <a href="dwagelaar@gmail.com">Dennis Wagelaar</a>
+ *
+ * @param <E> the element type
+ */
+ public static class CollectList<E> extends LazyList<E> {
+
+ /**
+ * Creates a {@link CollectList} around <code>dataSource</code>.
+ * @param dataSource the underlying {@link LazyList}
+ */
+ public CollectList(final LazyList<E> dataSource) {
+ super(dataSource);
+ assert dataSource != null;
+ }
+
+ }
/**
* Creates a {@link LazyList} around <code>dataSource</code>.
@@ -2162,7 +2185,7 @@ public class LazyList<E> extends LazyCollection<E> implements List<E> {
if (index < cache.size()) {
return ((List<T>)cache).get(index);
}
- return (T) function.execute(parentFrame.getSubFrame(function, new Object[] { inner.get(index) }));
+ return (T) function.execute(parentFrame.getSubFrame(function, inner.get(index)));
}
@SuppressWarnings("unchecked")
@Override
@@ -2174,7 +2197,7 @@ public class LazyList<E> extends LazyCollection<E> implements List<E> {
}
return ((List<T>)cache).get(size - 1);
}
- return (T) function.execute(parentFrame.getSubFrame(function, new Object[] { inner.last() }));
+ return (T) function.execute(parentFrame.getSubFrame(function, inner.last()));
}
@Override
public int size() {
@@ -2183,6 +2206,47 @@ public class LazyList<E> extends LazyCollection<E> implements List<E> {
};
}
- //TODO provide other iterator operations: collectNested, sortedBy
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LazyList<E> sortedBy(final CodeBlock body) {
+ // Parent frame may change after this method returns!
+ final StackFrame parentFrame = body.getParentFrame();
+ body.setParentFrame(null);
+ return new LazyList<E>(this) {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator<E> iterator() {
+ final Collection<E> inner = (Collection<E>) dataSource;
+ if (inner != null) {
+ final Iterator<Comparable<Object>> sortingKeys = new CollectIterator<Comparable<Object>>(inner, body, parentFrame);
+ final Object[] innerCopy = inner.toArray();
+ final Map<Object, Comparable<Object>> elementsToKeys = new HashMap<Object, Comparable<Object>>(innerCopy.length);
+ for (Object o : innerCopy) {
+ elementsToKeys.put(o, sortingKeys.next());
+ }
+ assert !sortingKeys.hasNext();
+ Arrays.sort(innerCopy, new Comparator<Object>() {
+ public int compare(Object o1, Object o2) {
+ return elementsToKeys.get(o1).compareTo(elementsToKeys.get(o2));
+ }
+ });
+ cache = (Collection<E>) Arrays.asList(innerCopy);
+ dataSource = null;
+ }
+ return super.iterator();
+ }
+ @Override
+ public int size() {
+ if (dataSource == null) {
+ return cache.size();
+ }
+ return ((Collection<E>) dataSource).size();
+ }
+ };
+ }
+
+ // TODO provide other iterator operations: collectNested
}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyOrderedSet.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyOrderedSet.java
index b8a785cd..b99369db 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyOrderedSet.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazyOrderedSet.java
@@ -12,11 +12,15 @@
package org.eclipse.m2m.atl.emftvm.util;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -2583,6 +2587,47 @@ public class LazyOrderedSet<E> extends LazyCollection<E> implements Set<E>, List
});
}
- //TODO provide other iterator operations: collectNested, sortedBy
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LazyOrderedSet<E> sortedBy(final CodeBlock body) {
+ // Parent frame may change after this method returns!
+ final StackFrame parentFrame = body.getParentFrame();
+ body.setParentFrame(null);
+ return new LazyOrderedSet<E>(this) {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator<E> iterator() {
+ final Collection<E> inner = (Collection<E>) dataSource;
+ if (inner != null) {
+ final Iterator<Comparable<Object>> sortingKeys = new CollectIterator<Comparable<Object>>(inner, body, parentFrame);
+ final Object[] innerCopy = inner.toArray();
+ final Map<Object, Comparable<Object>> elementsToKeys = new HashMap<Object, Comparable<Object>>(innerCopy.length);
+ for (Object o : innerCopy) {
+ elementsToKeys.put(o, sortingKeys.next());
+ }
+ assert !sortingKeys.hasNext();
+ Arrays.sort(innerCopy, new Comparator<Object>() {
+ public int compare(Object o1, Object o2) {
+ return elementsToKeys.get(o1).compareTo(elementsToKeys.get(o2));
+ }
+ });
+ cache = (Collection<E>) Arrays.asList(innerCopy);
+ dataSource = null;
+ }
+ return super.iterator();
+ }
+ @Override
+ public int size() {
+ if (dataSource == null) {
+ return cache.size();
+ }
+ return ((Collection<E>) dataSource).size();
+ }
+ };
+ }
+
+ //TODO provide other iterator operations: collectNested
}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazySet.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazySet.java
index 6061516c..7b1f30f6 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazySet.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/LazySet.java
@@ -11,10 +11,14 @@
*******************************************************************************/
package org.eclipse.m2m.atl.emftvm.util;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -912,6 +916,47 @@ public class LazySet<E> extends LazyCollection<E> implements Set<E> {
});
}
- //TODO provide other iterator operations: collectNested, sortedBy
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LazyOrderedSet<E> sortedBy(final CodeBlock body) {
+ // Parent frame may change after this method returns!
+ final StackFrame parentFrame = body.getParentFrame();
+ body.setParentFrame(null);
+ return new LazyOrderedSet<E>(this) {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator<E> iterator() {
+ final Collection<E> inner = (Collection<E>) dataSource;
+ if (inner != null) {
+ final Iterator<Comparable<Object>> sortingKeys = new CollectIterator<Comparable<Object>>(inner, body, parentFrame);
+ final Object[] innerCopy = inner.toArray();
+ final Map<Object, Comparable<Object>> elementsToKeys = new HashMap<Object, Comparable<Object>>(innerCopy.length);
+ for (Object o : innerCopy) {
+ elementsToKeys.put(o, sortingKeys.next());
+ }
+ assert !sortingKeys.hasNext();
+ Arrays.sort(innerCopy, new Comparator<Object>() {
+ public int compare(Object o1, Object o2) {
+ return elementsToKeys.get(o1).compareTo(elementsToKeys.get(o2));
+ }
+ });
+ cache = (Collection<E>) Arrays.asList(innerCopy);
+ dataSource = null;
+ }
+ return super.iterator();
+ }
+ @Override
+ public int size() {
+ if (dataSource == null) {
+ return cache.size();
+ }
+ return ((Collection<E>) dataSource).size();
+ }
+ };
+ }
+
+ //TODO provide other iterator operations: collectNested
}
diff --git a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/OCLOperations.java b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/OCLOperations.java
index 2f38f82a..085644ce 100644
--- a/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/OCLOperations.java
+++ b/plugins/org.eclipse.m2m.atl.emftvm/src/org/eclipse/m2m/atl/emftvm/util/OCLOperations.java
@@ -273,6 +273,15 @@ public final class OCLOperations {
return object;
}
});
+ createOperation(false, "toString", Types.OCL_ANY_TYPE, Types.STRING_TYPE,
+ new String[][][]{},
+ new NativeCodeBlock() {
+ @Override
+ public Object execute(final StackFrame frame) {
+ final Object object = frame.getLocal(0, 0);
+ return EMFTVMUtil.toPrettyString(object, frame.getEnv());
+ }
+ });
createOperation(false, "oclAsType", Types.OCL_ANY_TYPE, Types.OCL_ANY_TYPE,
new String[][][]{{{"type"}, Types.CLASSIFIER_TYPE}},
new NativeCodeBlock() {
@@ -312,8 +321,8 @@ public final class OCLOperations {
public Object execute(final StackFrame frame) {
final Object o = frame.getLocal(0, 0);
final EClassifier type = (EClassifier)frame.getLocal(0, 1);
- if (type instanceof EClass && o instanceof EObject) {
- return ((EObject) o).eClass() == type;
+ if (type instanceof EClass) {
+ return o instanceof EObject && ((EObject) o).eClass() == type;
} else if (o != null) {
final Class<?> ic = ((EClassifier)type).getInstanceClass();
if (ic == null) {

Back to the top