diff --git a/plugins/org.eclipse.n4js.model/build.properties b/plugins/org.eclipse.n4js.model/build.properties
index 974cddd650..4bd779cde3 100644
--- a/plugins/org.eclipse.n4js.model/build.properties
+++ b/plugins/org.eclipse.n4js.model/build.properties
@@ -1,6 +1,5 @@
source.. = src/,\
- emf-gen/,\
- xtend-gen
+ emf-gen/
bin.includes = META-INF/,\
.,\
plugin.xml,\
diff --git a/plugins/org.eclipse.n4js.model/pom.xml b/plugins/org.eclipse.n4js.model/pom.xml
index 936d0056b1..6d70b4cdff 100644
--- a/plugins/org.eclipse.n4js.model/pom.xml
+++ b/plugins/org.eclipse.n4js.model/pom.xml
@@ -38,11 +38,6 @@ Contributors:
maven-resources-plugin
${maven-resources-plugin.version}
-
- org.eclipse.xtend
- xtend-maven-plugin
-
-
diff --git a/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructNode.java b/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructNode.java
new file mode 100644
index 0000000000..4592294f0d
--- /dev/null
+++ b/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructNode.java
@@ -0,0 +1,641 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.n4JS;
+
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.exists;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.head;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.n4js.ts.types.TypableElement;
+import org.eclipse.xtend.lib.annotations.Data;
+import org.eclipse.xtext.xbase.lib.Pair;
+import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;
+
+/**
+ * Destructuring patterns can appear in very different forms within the AST and in different contexts. This helper class
+ * is used to transform those heterogeneous representations into a single, uniform structure, that can be traversed more
+ * easily.
+ *
+ * All fields are optional, i.e. may be 'null'. At most one of 'varRef', 'varDecl' and 'nestedPattern' may be non-null;
+ * if all three are 'null' the node is a padding node.
+ *
+ *
Overview of Destructuring Patterns in the AST
Different forms:
+ *
+ * - as a {@link BindingPattern} (may contain nested {@code BindingPattern}s).
+ *
- as an {@link ArrayLiteral} (may contain nested patterns in form of {@code ArrayLiteral}s or
+ * {@code ObjectLiteral}s).
+ *
- as an {@link ObjectLiteral} (may contain nested patterns in form of {@code ArrayLiteral}s or
+ * {@code ObjectLiteral}s).
+ *
+ * Different contexts:
+ *
+ * - within a {@link VariableStatement} (then contained in a {@link VariableBinding}, which is an alternative to a
+ * {@link VariableDeclaration}).
+ *
- within an {@link AssignmentExpression} (then it appears as the left-hand side expression)
+ *
- within a {@link ForStatement} (only in for..in and for..of, because if used in plain for it is a use case of a
+ * variable statement or assignment expression inside the for statement).
+ *
- NOT SUPPORTED YET: in the context of lists of formal parameters or function argument lists
+ *
+ * The above 6 use cases have several special characteristics and constraints, most of which are unified in this class.
+ * It might be possible to generate more unified patterns in the parser, but the above situation is more in line with
+ * terminology in the ES6 specification.
+ *
+ */
+@Data
+@SuppressWarnings("javadoc")
+public class DestructNode {
+ final public EObject astElement;
+ // property name (iff in object destructuring pattern) or 'null' (iff in array destructuring pattern)
+ final public String propName;
+ final public IdentifierRef varRef;
+ final public VariableDeclaration varDecl;
+ // nested pattern that will be bound/assigned (or 'null' iff 'varName' is non-null)
+ final public DestructNode[] nestedNodes;
+ final public Expression defaultExpr;
+ // can be an Expression or an IdentifiableElement (in case of Getter/Setter/Method)
+ final public TypableElement assignedElem;
+ final public boolean rest;
+
+ public DestructNode(EObject astElement, String propName, IdentifierRef varRef, VariableDeclaration varDecl,
+ DestructNode[] nestedNodes, Expression defaultExpr, TypableElement assignedElem, boolean rest) {
+
+ this.astElement = astElement;
+ this.propName = propName;
+ this.varRef = varRef;
+ this.varDecl = varDecl;
+ this.nestedNodes = nestedNodes;
+ this.defaultExpr = defaultExpr;
+ this.assignedElem = assignedElem;
+ this.rest = rest;
+ }
+
+ /**
+ * Returns true if if receiving node belongs to a positional destructuring pattern (i.e. an array destructuring
+ * pattern).
+ */
+ public boolean isPositional() {
+ return propName == null;
+ }
+
+ /**
+ * Returns true if if the given nodes belong to a positional destructuring pattern (i.e. an array destructuring
+ * pattern).
+ */
+ static public boolean arePositional(List nodes) {
+ return nodes != null && exists(nodes, n -> n.isPositional());
+ }
+
+ /**
+ * Returns true if if this is a padding node.
+ */
+ public boolean isPadding() {
+ return varRef == null && varDecl == null && nestedNodes == null;
+ }
+
+ /**
+ * If this node has a reference to a variable or a variable declaration, returns the variable's name,
+ * null
otherwise.
+ */
+ public String varName() {
+ if (varRef != null) {
+ return varRef.getId() == null ? null : varRef.getId().getName();
+ } else if (varDecl != null) {
+ return varDecl.getName();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the variable declaration contained in this node's astElement or null
.
+ */
+ public VariableDeclaration getVariableDeclaration() {
+ if (astElement instanceof BindingElement) {
+ return ((BindingElement) astElement).getVarDecl();
+ }
+ if (astElement instanceof BindingProperty && ((BindingProperty) astElement).getValue() != null) {
+ return ((BindingProperty) astElement).getValue().getVarDecl();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the AST node and EStructuralFeature to be used when showing an error message on the receiving node's
+ * propName attribute. Intended for issue generation in validations.
+ */
+ public Pair getEObjectAndFeatureForPropName() {
+ if (propName != null) {
+ if (astElement instanceof PropertyNameValuePairSingleName) {
+ PropertyNameValuePairSingleName pnvpsn = (PropertyNameValuePairSingleName) astElement;
+ if (pnvpsn.getExpression() instanceof AssignmentExpression) {
+ return Pair.of(pnvpsn.getExpression(), N4JSPackage.eINSTANCE.getAssignmentExpression_Lhs());
+ }
+ }
+ if (astElement instanceof PropertyNameValuePairSingleName) {
+ return Pair.of(astElement, N4JSPackage.eINSTANCE.getPropertyNameValuePair_Expression());
+ }
+ if (astElement instanceof BindingProperty) {
+ BindingProperty bp = (BindingProperty) astElement;
+
+ if (bp.getDeclaredName() != null) {
+ return Pair.of(bp, N4JSPackage.eINSTANCE.getPropertyNameOwner_DeclaredName());
+ }
+ if (bp.getProperty() != null) {
+ return Pair.of(bp, N4JSPackage.eINSTANCE.getBindingProperty_Property());
+ }
+ if (bp.getValue() != null && bp.getValue().getVarDecl() != null
+ && bp.getValue().getVarDecl().getName() != null) {
+ return Pair.of(bp.getValue().getVarDecl(), N4JSPackage.eINSTANCE.getAbstractVariable_Name());
+ }
+ }
+ if (astElement instanceof PropertyNameValuePair
+ && ((PropertyNameValuePair) astElement).getProperty() != null) {
+
+ return Pair.of(astElement, N4JSPackage.eINSTANCE.getPropertyNameValuePair_Property());
+ }
+ if (astElement instanceof PropertyNameOwner) {
+ return Pair.of(astElement, N4JSPackage.eINSTANCE.getPropertyNameOwner_DeclaredName());
+ }
+ }
+ return Pair.of(astElement, null);// show error on entire node
+ }
+
+ /**
+ * Returns the node with the given astElement
.
+ */
+ public DestructNode findNodeForElement(EObject pAstElement) {
+ return findNodeOrParentForElement(pAstElement, false);
+ }
+
+ /**
+ * Returns the node with the given astElement
or its parent node.
+ */
+ public DestructNode findNodeOrParentForElement(EObject pAstElement, boolean returnParent) {
+ EObject reprAstElem = pAstElement;
+ if (pAstElement instanceof BindingElement && pAstElement.eContainer() instanceof BindingProperty) {
+ reprAstElem = pAstElement.eContainer();
+ }
+
+ if (this.astElement == reprAstElem) {
+ if (returnParent) {
+ return null;
+ }
+ return this;
+ }
+ if (this.nestedNodes == null) {
+ return null;
+ }
+ for (DestructNode nested : this.nestedNodes) {
+ if (nested.astElement == reprAstElem) {
+ if (returnParent) {
+ return this;
+ }
+ return nested;
+ }
+ DestructNode resNested = nested.findNodeOrParentForElement(reprAstElem, returnParent);
+ if (resNested != null) {
+ return resNested;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns stream of this node and all its descendants, i.e. directly and indirectly nested nodes.
+ */
+ public Stream stream() {
+ if (nestedNodes == null) {
+ return Stream.of(this);
+ } else {
+ return Stream.concat(Stream.of(this), Stream.of(nestedNodes).flatMap(dn -> dn.stream()));
+ }
+ }
+
+ public static DestructNode unify(EObject eobj) {
+ if (eobj instanceof VariableBinding) {
+ return unify((VariableBinding) eobj);
+ }
+ if (eobj instanceof AssignmentExpression) {
+ return unify((AssignmentExpression) eobj);
+ }
+ if (eobj instanceof ForStatement) {
+ return unify((ForStatement) eobj);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a unified copy of the given destructuring pattern or null
if it is invalid. This is helpful
+ * because these patterns can appear in very different forms and locations within the AST.
+ */
+ public static DestructNode unify(EObject astElem, EObject lhs, Expression rhs) {
+ return new DestructNode(
+ astElem, // astElement
+ null, // propName
+ null, // varRef
+ null, // varDecl
+ toEntries(lhs, rhs), // nestedNodes
+ rhs, // defaultExpr
+ rhs, // assignedExpr
+ false // rest
+ );
+ }
+
+ /**
+ * Returns a unified copy of the given destructuring pattern or null
if it is invalid. This is helpful
+ * because these patterns can appear in very different forms and locations within the AST.
+ */
+ public static DestructNode unify(VariableBinding binding) {
+ if (binding != null && binding.getPattern() != null
+ // note: binding.expression is mandatory in variable statements
+ // but optional in for..in/of statements
+ && (binding.getExpression() != null || binding.eContainer() instanceof ForStatement)) {
+
+ return unify(binding, binding.getPattern(), binding.getExpression());
+ }
+ return null;
+ }
+
+ /**
+ * Returns a unified copy of the given destructuring pattern or null
if it is invalid. This is helpful
+ * because these patterns can appear in very different forms and locations within the AST.
+ */
+ public static DestructNode unify(AssignmentExpression assignExpr) {
+ if (assignExpr != null && assignExpr.getLhs() != null && assignExpr.getRhs() != null
+ && DestructureUtils.isTopOfDestructuringAssignment(assignExpr)) {
+
+ return unify(assignExpr, assignExpr.getLhs(), assignExpr.getRhs());
+ }
+ return null;
+ }
+
+ /**
+ * Returns a unified copy of the given destructuring pattern or null
if it is invalid. This is helpful
+ * because these patterns can appear in very different forms and locations within the AST.
+ */
+ public static DestructNode unify(ForStatement forStmnt) {
+ if (forStmnt != null && DestructureUtils.isTopOfDestructuringForStatement(forStmnt)) {
+ Expression valueToBeDestructured;
+ Expression defaultExpression;
+
+ if (forStmnt.isForOf()) {
+ valueToBeDestructured = firstArrayElement(forStmnt.getExpression());
+ defaultExpression = forStmnt.getExpression();
+ } else if (forStmnt.isForIn()) {
+ StringLiteral slit1 = N4JSFactory.eINSTANCE.createStringLiteral();
+ slit1.setValue("");
+ valueToBeDestructured = slit1;
+
+ StringLiteral slit2 = N4JSFactory.eINSTANCE.createStringLiteral();
+ slit2.setValue("");
+ defaultExpression = slit2;
+ } else {
+ // impossible because #isTopOfDestructuringForStatement() returned true
+ throw new IllegalStateException();
+ }
+
+ if (DestructureUtils.containsDestructuringPattern(forStmnt)) {
+ // case: for(var [a,b] of arr) {}
+ VariableBinding binding = head(filter(forStmnt.getVarDeclsOrBindings(), VariableBinding.class));
+ Expression rhs = firstArrayElement(forStmnt.getExpression());
+ return new DestructNode(
+ forStmnt, // astElement
+ null, // propName
+ null, // varRef
+ null, // varDecl
+ toEntries(binding.getPattern(), rhs), // nestedNodes
+ defaultExpression, // defaultExpr
+ valueToBeDestructured, // assignedExpr
+ false // rest
+ );
+ } else if (DestructureUtils.isObjectOrArrayLiteral(forStmnt.getInitExpr())) {
+ // case: for([a,b] of arr) {}
+ return new DestructNode(
+ forStmnt, // astElement
+ null, // propName
+ null, // varRef
+ null, // varDecl
+ toEntries(forStmnt.getInitExpr(), null), // nestedNodes
+ defaultExpression, // defaultExpr
+ defaultExpression, // assignedExpr
+ false // rest
+ );
+ }
+ }
+ return null;
+ }
+
+ private static Expression firstArrayElement(Expression expr) {
+ return (expr instanceof ArrayLiteral) ? ((ArrayLiteral) expr).getElements().get(0).getExpression() : expr;
+ }
+
+ private static DestructNode[] toEntries(EObject pattern, TypableElement rhs) {
+ Iterator extends EObject> patElemIter = null;
+ if (pattern instanceof ArrayLiteral) {
+ patElemIter = ((ArrayLiteral) pattern).getElements().iterator();
+ } else if (pattern instanceof ObjectLiteral) {
+ patElemIter = ((ObjectLiteral) pattern).getPropertyAssignments().iterator();
+ } else if (pattern instanceof ArrayBindingPattern) {
+ patElemIter = ((ArrayBindingPattern) pattern).getElements().iterator();
+ } else if (pattern instanceof ObjectBindingPattern) {
+ patElemIter = ((ObjectBindingPattern) pattern).getProperties().iterator();
+ } else {
+ return null;
+ }
+
+ Iterator extends TypableElement> rhsElemIter = null;
+ if (rhs instanceof ArrayLiteral) {
+ rhsElemIter = ((ArrayLiteral) rhs).getElements().iterator();
+ }
+ if (rhs instanceof ObjectLiteral) {
+ rhsElemIter = ((ObjectLiteral) rhs).getPropertyAssignments().iterator();
+ }
+
+ BasicEList nestedDNs = new BasicEList<>();
+ while (patElemIter.hasNext()) {
+ EObject patElem = patElemIter.next();
+ TypableElement litElem = (rhsElemIter == null) ? rhs : (rhsElemIter.hasNext()) ? rhsElemIter.next() : null;
+
+ DestructNode nestedNode = null;
+ if (patElem instanceof ArrayElement) {
+ nestedNode = toEntry((ArrayElement) patElem, litElem);
+ }
+ if (patElem instanceof PropertyNameValuePair) {
+ nestedNode = toEntry((PropertyNameValuePair) patElem, litElem);
+ }
+ if (patElem instanceof BindingElement) {
+ nestedNode = toEntry((BindingElement) patElem, litElem);
+ }
+ if (patElem instanceof BindingProperty) {
+ nestedNode = toEntry((BindingProperty) patElem, litElem);
+ }
+
+ if (nestedNode != null) {
+ nestedDNs.add(nestedNode);
+ }
+ }
+ return nestedDNs.toArray(new DestructNode[0]);
+ }
+
+ private static DestructNode toEntry(ArrayElement elem, TypableElement rhs) {
+ TypableElement rhsExpr = (rhs instanceof ArrayElement) ? ((ArrayElement) rhs).getExpression() : rhs;
+ Expression expr = elem.getExpression(); // note: ArrayPadding will return null for getExpression()
+ if (expr instanceof AssignmentExpression) {
+ AssignmentExpression ae = (AssignmentExpression) expr;
+ return toEntry(elem, null, ae.getLhs(), ae.getRhs(), elem.isSpread(), rhsExpr);
+ } else {
+ return toEntry(elem, null, expr, null, elem.isSpread(), rhsExpr);
+ }
+ }
+
+ private static DestructNode toEntry(PropertyNameValuePair pa, TypableElement rhs) {
+ TypableElement rhsExpr = (rhs instanceof PropertyNameValuePair)
+ ? ((PropertyNameValuePair) rhs).getExpression()
+ : rhs;
+ Expression expr = pa.getExpression();
+ if (expr instanceof AssignmentExpression) {
+ AssignmentExpression ae = (AssignmentExpression) expr;
+ return toEntry(pa, pa.getName(), ae.getLhs(), ae.getRhs(), false, rhsExpr);
+ } else {
+ return toEntry(pa, pa.getName(), expr, null, false, rhsExpr);
+ }
+ }
+
+ private static DestructNode toEntry(BindingElement elem, TypableElement rhs) {
+ TypableElement expr = (rhs instanceof ArrayElement) ? ((ArrayElement) rhs).getExpression() : rhs;
+
+ if (elem.getVarDecl() != null) {
+ return toEntry(elem, null, elem.getVarDecl(), elem.getVarDecl().getExpression(), elem.isRest(), expr);
+ } else if (elem.getNestedPattern() != null) {
+ return toEntry(elem, null, elem.getNestedPattern(), elem.getExpression(), elem.isRest(), expr);
+ } else {
+ return toEntry(elem, null, null, null, false, expr); // return dummy entry to not break indices
+ }
+ }
+
+ private static DestructNode toEntry(BindingProperty prop, TypableElement rhs) {
+ if (prop.getValue() != null && prop.getValue().getVarDecl() != null) {
+ TypableElement expr = getPropertyAssignmentExpression(rhs);
+ return toEntry(prop, prop.getName(), prop.getValue().getVarDecl(),
+ prop.getValue().getVarDecl().getExpression(), false, expr);
+
+ } else if (prop.getValue() != null && prop.getValue().getNestedPattern() != null) {
+ TypableElement expr = getPropertyAssignmentExpression(rhs);
+ return toEntry(prop, prop.getName(), prop.getValue().getNestedPattern(), prop.getValue().getExpression(),
+ false, expr);
+
+ } else {
+ return toEntry(prop, null, null, null, false, rhs);
+ }
+ }
+
+ /**
+ * @param bindingTarget
+ * an IdentifierRef/VariableDeclaration or a nested pattern (which may be a BindingPattern, ArrayLiteral,
+ * or ObjectLiteral)
+ */
+ private static DestructNode toEntry(EObject astElement, String propName, EObject bindingTarget,
+ Expression defaultExpr, boolean rest, TypableElement rhs) {
+
+ if (bindingTarget == null) {
+ // no target -> create a padding node
+ return new DestructNode(astElement, propName, null, null, null, defaultExpr, null, rest);
+
+ } else if (bindingTarget instanceof IdentifierRef) {
+ return new DestructNode(astElement, propName, (IdentifierRef) bindingTarget, null, null, defaultExpr, rhs,
+ rest);
+
+ } else if (bindingTarget instanceof VariableDeclaration) {
+ return new DestructNode(astElement, propName, null, (VariableDeclaration) bindingTarget, null, defaultExpr,
+ rhs, rest);
+
+ } else if (bindingTarget instanceof ArrayLiteral || bindingTarget instanceof ObjectLiteral ||
+ bindingTarget instanceof BindingPattern) {
+ return new DestructNode(astElement, propName, null, null, toEntries(bindingTarget, rhs), defaultExpr, rhs,
+ rest);
+
+ } else {
+ // invalid binding target (probably a corrupt AST) -> create a padding node
+ return new DestructNode(astElement, propName, null, null, null, defaultExpr, null, rest);
+ }
+ }
+
+ /** @return the expression or function of the given PropertyAssignment */
+ private static TypableElement getPropertyAssignmentExpression(TypableElement rhs) {
+ if (rhs instanceof PropertyGetterDeclaration) {
+ return ((PropertyGetterDeclaration) rhs).getDefinedFunctionOrAccessor();
+ }
+ if (rhs instanceof PropertySetterDeclaration) {
+ return ((PropertySetterDeclaration) rhs).getDefinedFunctionOrAccessor();
+ }
+ if (rhs instanceof PropertyMethodDeclaration) {
+ return ((PropertyMethodDeclaration) rhs).getDefinedFunctionOrAccessor();
+ }
+ if (rhs instanceof PropertyNameValuePair) {
+ return ((PropertyNameValuePair) rhs).getExpression();
+ }
+ if (rhs instanceof PropertyAssignmentAnnotationList) {
+ return null;
+ }
+
+ return rhs;
+ }
+
+ /** @return all {@link IdentifierRef} of variables that are written in the given assignment */
+ public List getAllDeclaredIdRefs() {
+ List idRefs = new LinkedList<>();
+ Iterator allNestedNodes = this.stream().iterator();
+
+ while (allNestedNodes.hasNext()) {
+ EObject eobj = allNestedNodes.next().astElement;
+ if (eobj instanceof ArrayElement) {
+ Expression expr = ((ArrayElement) eobj).getExpression();
+ if (expr instanceof AssignmentExpression) {
+ idRefs.add((((AssignmentExpression) expr).getLhs()));
+ } else {
+ idRefs.add(expr);
+ }
+
+ } else if (eobj instanceof PropertyNameValuePairSingleName) {
+ idRefs.add(((PropertyNameValuePairSingleName) eobj).getIdentifierRef());
+
+ } else if (eobj instanceof PropertyNameValuePair) {
+ Expression expr = ((PropertyNameValuePair) eobj).getExpression();
+ if (expr instanceof AssignmentExpression) {
+ idRefs.add(((AssignmentExpression) expr).getLhs());
+ } else {
+ idRefs.add(expr);
+ }
+ }
+ }
+ return idRefs;
+ }
+
+ /**
+ * @return a pair where its key is the assigned EObject and its value is the default EObject to the given lhs AST
+ * element
+ */
+ public static Pair getValueFromDestructuring(EObject nodeElem) {
+ EObject node = nodeElem;
+ EObject topNode = null;
+ EObject dNodeElem = null;
+ boolean breakSearch = false;
+
+ while (!breakSearch) {
+ EObject parent = node.eContainer();
+ dNodeElem = getDNodeElem(dNodeElem, parent, node);
+ topNode = getTopElem(topNode, parent);
+ breakSearch = parent instanceof Statement;
+ node = parent;
+ }
+
+ DestructNode dNode = null;
+ if (topNode instanceof AssignmentExpression) {
+ dNode = DestructNode.unify(topNode);
+ } else if (topNode instanceof VariableBinding) {
+ dNode = DestructNode.unify(topNode);
+ } else if (topNode instanceof ForStatement) {
+ dNode = DestructNode.unify(topNode);
+ }
+
+ if (dNode != null) {
+ dNode = dNode.findNodeForElement(dNodeElem);
+ if (dNode != null) {
+ EObject assgnValue = dNode.assignedElem;
+ EObject defaultValue = dNode.defaultExpr;
+ return Pair.of(assgnValue, defaultValue);
+ }
+ }
+
+ return null;
+ }
+
+ private static EObject getDNodeElem(EObject dNodeElem, EObject parent, EObject node) {
+ if (dNodeElem != null) {
+ return dNodeElem;
+ }
+ if (node instanceof BindingElement && parent instanceof BindingProperty) {
+ return parent;
+ }
+ if (node instanceof BindingElement || node instanceof ArrayElement || node instanceof PropertyAssignment) {
+ return node;
+ }
+ return null;
+ }
+
+ private static EObject getTopElem(EObject oldTopNode, EObject parent) {
+ EObject newTopNode = null;
+ if (parent instanceof ForStatement) {
+ newTopNode = parent;
+ }
+ if (parent instanceof AssignmentExpression) {
+ newTopNode = parent;
+ }
+ if (parent instanceof VariableBinding) {
+ newTopNode = parent;
+ }
+
+ if (newTopNode != null) {
+ return newTopNode;
+ } else {
+ return oldTopNode;
+ }
+ }
+
+ public static List getAllDeclaredIdRefs(EObject eobj) {
+ DestructNode dnode = null;
+ if (eobj instanceof ForStatement) {
+ dnode = unify(eobj);
+ }
+ if (eobj instanceof VariableBinding) {
+ dnode = unify(eobj);
+ }
+ if (eobj instanceof AssignmentExpression) {
+ dnode = unify(eobj);
+ }
+
+ if (dnode == null) {
+ return Collections.emptyList();
+ }
+ return dnode.getAllDeclaredIdRefs();
+ }
+
+ @Override
+ public String toString() {
+ ToStringBuilder b = new ToStringBuilder(this);
+ b.add("astElement", this.astElement);
+ b.add("propName", this.propName);
+ b.add("varRef", this.varRef);
+ b.add("varDecl", this.varDecl);
+ b.add("nestedNodes", this.nestedNodes);
+ b.add("defaultExpr", this.defaultExpr);
+ b.add("assignedElem", this.assignedElem);
+ b.add("rest", this.rest);
+ return b.toString();
+ }
+
+ public List getNestedNodes() {
+ if (nestedNodes == null) {
+ return Collections.emptyList();
+ }
+ return Arrays.asList(this.nestedNodes);
+ }
+
+}
diff --git a/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructNode.xtend b/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructNode.xtend
deleted file mode 100644
index 648f3500a9..0000000000
--- a/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructNode.xtend
+++ /dev/null
@@ -1,547 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.n4JS
-
-import java.util.Iterator
-import java.util.LinkedList
-import java.util.List
-import java.util.stream.Stream
-import org.eclipse.emf.common.util.BasicEList
-import org.eclipse.emf.ecore.EObject
-import org.eclipse.emf.ecore.EStructuralFeature
-import org.eclipse.n4js.ts.types.TypableElement
-import org.eclipse.xtend.lib.annotations.Data
-
-/**
- * Destructuring patterns can appear in very different forms within the AST and in different contexts.
- * This helper class is used to transform those heterogeneous representations into a single, uniform
- * structure, that can be traversed more easily.
- *
- * All fields are optional, i.e. may be 'null'. At most one of 'varRef', 'varDecl' and 'nestedPattern'
- * may be non-null; if all three are 'null' the node is a padding node.
- *
- *
Overview of Destructuring Patterns in the AST
- * Different forms:
- *
- * - as a {@link BindingPattern} (may contain nested {@code BindingPattern}s).
- *
- as an {@link ArrayLiteral} (may contain nested patterns in form of {@code ArrayLiteral}s or {@code ObjectLiteral}s).
- *
- as an {@link ObjectLiteral} (may contain nested patterns in form of {@code ArrayLiteral}s or {@code ObjectLiteral}s).
- *
- * Different contexts:
- *
- * - within a {@link VariableStatement} (then contained in a {@link VariableBinding}, which is an alternative
- * to a {@link VariableDeclaration}).
- *
- within an {@link AssignmentExpression} (then it appears as the left-hand side expression)
- *
- within a {@link ForStatement} (only in for..in and for..of, because if used in plain for it is a use case
- * of a variable statement or assignment expression inside the for statement).
- *
- NOT SUPPORTED YET: in the context of lists of formal parameters or function argument lists
- *
- * The above 6 use cases have several special characteristics and constraints, most of which are unified in this class.
- * It might be possible to generate more unified patterns in the parser, but the above situation is more in line with
- * terminology in the ES6 specification.
- *
- */
-@Data
-public class DestructNode {
- EObject astElement;
- String propName; // property name (iff in object destructuring pattern) or 'null' (iff in array destructuring pattern)
- IdentifierRef varRef;
- VariableDeclaration varDecl;
- DestructNode[] nestedNodes; // nested pattern that will be bound/assigned (or 'null' iff 'varName' is non-null)
- Expression defaultExpr;
- TypableElement assignedElem; // can be an Expression or an IdentifiableElement (in case of Getter/Setter/Method)
- boolean rest;
-
- /**
- * Tells if receiving node belongs to a positional destructuring pattern
- * (i.e. an array destructuring pattern).
- */
- def boolean isPositional() {
- propName === null
- }
-
- /**
- * Tells if the given nodes belong to a positional destructuring pattern
- * (i.e. an array destructuring pattern).
- */
- static def boolean arePositional(DestructNode[] nodes) {
- nodes !== null && nodes.exists[positional]
- }
-
- /**
- * Tells if this is a padding node.
- */
- def boolean isPadding() {
- varRef === null && varDecl === null && nestedNodes === null
- }
-
- /**
- * If this node has a reference to a variable or a variable declaration,
- * returns the variable's name, null
otherwise.
- */
- def String varName() {
- if (varRef !== null)
- varRef.id?.name
- else if (varDecl !== null)
- varDecl.name
- }
-
- /**
- * Returns the variable declaration contained in this node's astElement or null
.
- */
- def VariableDeclaration getVariableDeclaration() {
- switch (astElement) {
- BindingElement:
- astElement.varDecl
- BindingProperty case astElement.value !== null:
- astElement.value.varDecl
- }
- }
-
- /**
- * Returns the AST node and EStructuralFeature to be used when showing an error message
- * on the receiving node's propName attribute. Intended for issue generation in validations.
- */
- def Pair getEObjectAndFeatureForPropName() {
- if (propName !== null) {
- switch (astElement) {
- PropertyNameValuePairSingleName case astElement.expression instanceof AssignmentExpression:
- astElement.expression -> N4JSPackage.eINSTANCE.assignmentExpression_Lhs
- PropertyNameValuePairSingleName:
- astElement -> N4JSPackage.eINSTANCE.propertyNameValuePair_Expression
- BindingProperty case astElement.declaredName !== null:
- astElement -> N4JSPackage.eINSTANCE.propertyNameOwner_DeclaredName
- BindingProperty case astElement.property !== null:
- astElement -> N4JSPackage.eINSTANCE.bindingProperty_Property
- BindingProperty case astElement.value?.varDecl?.name !== null:
- astElement.value.varDecl -> N4JSPackage.eINSTANCE.abstractVariable_Name
- PropertyNameValuePair case astElement.property !== null:
- astElement -> N4JSPackage.eINSTANCE.propertyNameValuePair_Property
- PropertyNameOwner:
- astElement -> N4JSPackage.eINSTANCE.propertyNameOwner_DeclaredName
- default:
- astElement -> null // show error on entire node
- }
- } else {
- astElement -> null // show error on entire node
- }
- }
-
- /**
- * Returns the node with the given astElement
.
- */
- def DestructNode findNodeForElement(EObject astElement) {
- return findNodeOrParentForElement(astElement, false);
- }
-
- /**
- * Returns the node with the given astElement
or its parent node.
- */
- def DestructNode findNodeOrParentForElement(EObject astElement, boolean returnParent) {
- val reprAstElem = if (astElement instanceof BindingElement && astElement.eContainer instanceof BindingProperty)
- astElement.eContainer else astElement;
-
- if (this.astElement === reprAstElem) {
- if (returnParent) {
- return null;
- }
- return this;
- }
- if (this.nestedNodes === null) {
- return null;
- }
- for (nested : this.nestedNodes) {
- if (nested.astElement === reprAstElem) {
- if (returnParent) {
- return this;
- }
- return nested;
- }
- val resNested = nested.findNodeOrParentForElement(reprAstElem, returnParent);
- if (resNested !== null) {
- return resNested;
- }
- }
- return null;
- }
-
- /**
- * Returns stream of this node and all its descendants, i.e. directly and indirectly nested nodes.
- */
- def Stream stream() {
- if (nestedNodes === null || nestedNodes.empty) {
- Stream.of(this)
- } else {
- Stream.concat(Stream.of(this), Stream.of(nestedNodes).flatMap[stream]);
- }
- }
-
- public static def DestructNode unify(EObject eobj) {
- return switch (eobj) {
- VariableBinding: unify(eobj)
- AssignmentExpression: unify(eobj)
- ForStatement: unify(eobj)
- default: null
- }
- }
-
- /**
- * Returns a unified copy of the given destructuring pattern or null
if it is invalid.
- * This is helpful because these patterns can appear in very different forms and locations within the AST.
- */
- public static def DestructNode unify(EObject astElem, EObject lhs, Expression rhs) {
- new DestructNode(
- astElem, // astElement
- null, // propName
- null, // varRef
- null, // varDecl
- toEntries(lhs, rhs), // nestedNodes
- rhs, // defaultExpr
- rhs, // assignedExpr
- false // rest
- )
- }
-
- /**
- * Returns a unified copy of the given destructuring pattern or null
if it is invalid.
- * This is helpful because these patterns can appear in very different forms and locations within the AST.
- */
- public static def DestructNode unify(VariableBinding binding) {
- if (binding !== null && binding.pattern !== null // note: binding.expression is mandatory in variable statements but optional in for..in/of statements
- && (binding.expression !== null || binding.eContainer instanceof ForStatement
- )) {
- unify(binding, binding.pattern, binding.expression);
- }
- }
-
- /**
- * Returns a unified copy of the given destructuring pattern or null
if it is invalid.
- * This is helpful because these patterns can appear in very different forms and locations within the AST.
- */
- public static def DestructNode unify(AssignmentExpression assignExpr) {
- if (assignExpr !== null && assignExpr.lhs !== null && assignExpr.rhs !== null
- && DestructureUtils.isTopOfDestructuringAssignment(assignExpr)
- ) {
- unify(assignExpr, assignExpr.lhs, assignExpr.rhs);
- }
- }
-
- /**
- * Returns a unified copy of the given destructuring pattern or null
if it is invalid.
- * This is helpful because these patterns can appear in very different forms and locations within the AST.
- */
- public static def DestructNode unify(ForStatement forStmnt) {
- if (forStmnt !== null && DestructureUtils.isTopOfDestructuringForStatement(forStmnt)) {
- val valueToBeDestructured = if (forStmnt.forOf) {
- firstArrayElement(forStmnt.expression)
- } else if (forStmnt.forIn) {
- N4JSFactory.eINSTANCE.createStringLiteral() => [
- value = ""
- ]
- } else {
- // impossible because #isTopOfDestructuringForStatement() returned true
- throw new IllegalStateException
- };
- val defaultExpression = if (forStmnt.forOf) {
- forStmnt.expression
- } else if (forStmnt.forIn) {
- N4JSFactory.eINSTANCE.createStringLiteral() => [
- value = ""
- ]
- } else {
- // impossible because #isTopOfDestructuringForStatement() returned true
- throw new IllegalStateException
- };
-
- if (DestructureUtils.containsDestructuringPattern(forStmnt)) {
- // case: for(var [a,b] of arr) {}
- val binding = forStmnt.varDeclsOrBindings.filter(VariableBinding).head;
- val rhs = firstArrayElement(forStmnt.expression);
- return new DestructNode(
- forStmnt, // astElement
- null, // propName
- null, // varRef
- null, // varDecl
- toEntries(binding.pattern, rhs), // nestedNodes
- defaultExpression, // defaultExpr
- valueToBeDestructured, // assignedExpr
- false // rest
- )
- } else if (DestructureUtils.isObjectOrArrayLiteral(forStmnt.getInitExpr())) {
- // case: for([a,b] of arr) {}
- return new DestructNode(
- forStmnt, // astElement
- null, // propName
- null, // varRef
- null, // varDecl
- toEntries(forStmnt.initExpr, null), // nestedNodes
- defaultExpression, // defaultExpr
- defaultExpression, // assignedExpr
- false // rest
- )
- }
- }
- return null;
- }
-
- private static def Expression firstArrayElement(Expression expr) {
- return if (expr instanceof ArrayLiteral) expr.elements.head.expression else expr;
- }
-
- private static def DestructNode[] toEntries(EObject pattern, TypableElement rhs) {
-
- val Iterator extends EObject> patElemIter = switch (pattern) {
- ArrayLiteral:
- pattern.elements.iterator
- ObjectLiteral:
- pattern.propertyAssignments.iterator
- ArrayBindingPattern:
- pattern.elements.iterator
- ObjectBindingPattern:
- pattern.properties.iterator
- }
-
- var Iterator extends TypableElement> rhsElemIter = switch (rhs) {
- ArrayLiteral:
- rhs.elements.iterator
- ObjectLiteral:
- rhs.propertyAssignments.iterator
- }
-
- val nestedDNs = new BasicEList();
- while (patElemIter.hasNext) {
- val patElem = patElemIter.next;
- val litElem = if (rhsElemIter === null) rhs else if (rhsElemIter.hasNext) rhsElemIter.next else null;
-
- val nestedNode = switch (patElem) {
- ArrayElement:
- toEntry(patElem, litElem)
- PropertyNameValuePair:
- toEntry(patElem, litElem)
- BindingElement:
- toEntry(patElem, litElem)
- BindingProperty:
- toEntry(patElem, litElem)
- }
-
- if (nestedNode !== null) {
- nestedDNs.add(nestedNode);
- }
- }
- return nestedDNs;
- }
-
- private static def DestructNode toEntry(ArrayElement elem, TypableElement rhs) {
- val TypableElement rhsExpr = if (rhs instanceof ArrayElement) rhs.expression else rhs;
- val expr = elem.expression; // note: ArrayPadding will return null for getExpression()
- if (expr instanceof AssignmentExpression)
- toEntry(elem, null, expr.lhs, expr.rhs, elem.spread, rhsExpr)
- else
- toEntry(elem, null, expr, null, elem.spread, rhsExpr)
- }
-
- private static def DestructNode toEntry(PropertyNameValuePair pa, TypableElement rhs) {
- val TypableElement rhsExpr = if (rhs instanceof PropertyNameValuePair) rhs.expression else rhs;
- val expr = pa.expression;
- if (expr instanceof AssignmentExpression)
- toEntry(pa, pa.name, expr.lhs, expr.rhs, false, rhsExpr)
- else
- toEntry(pa, pa.name, expr, null, false, rhsExpr)
- }
-
- private static def DestructNode toEntry(BindingElement elem, TypableElement rhs) {
- val TypableElement expr = if (rhs instanceof ArrayElement) rhs.expression else rhs;
-
- if (elem.varDecl !== null)
- toEntry(elem, null, elem.varDecl, elem.varDecl.expression, elem.rest, expr)
- else if (elem.nestedPattern !== null)
- toEntry(elem, null, elem.nestedPattern, elem.expression, elem.rest, expr)
- else
- toEntry(elem, null, null, null, false, expr) // return dummy entry to not break indices
- }
-
- private static def DestructNode toEntry(BindingProperty prop, TypableElement rhs) {
- if (prop.value?.varDecl !== null) {
- val expr = getPropertyAssignmentExpression(rhs);
- toEntry(prop, prop.name, prop.value.varDecl, prop.value.varDecl.expression, false, expr)
-
- } else if (prop.value?.nestedPattern !== null) {
- val expr = getPropertyAssignmentExpression(rhs);
- toEntry(prop, prop.name, prop.value.nestedPattern, prop.value.expression, false, expr)
-
- } else {
- toEntry(prop, null, null, null, false, rhs)
- }
- }
-
- /**
- * @param bindingTarget
- * an IdentifierRef/VariableDeclaration or a nested pattern (which may be
- * a BindingPattern, ArrayLiteral, or ObjectLiteral)
- */
- private static def DestructNode toEntry(EObject astElement, String propName, EObject bindingTarget,
- Expression defaultExpr, boolean rest, TypableElement rhs) {
-
- if (bindingTarget === null) {
- // no target -> create a padding node
- new DestructNode(astElement, propName, null, null, null, defaultExpr, null, rest)
-
- } else if (bindingTarget instanceof IdentifierRef) {
- new DestructNode(astElement, propName, bindingTarget, null, null, defaultExpr, rhs, rest)
-
- } else if (bindingTarget instanceof VariableDeclaration) {
- new DestructNode(astElement, propName, null, bindingTarget, null, defaultExpr, rhs, rest)
-
- } else if (bindingTarget instanceof ArrayLiteral || bindingTarget instanceof ObjectLiteral ||
- bindingTarget instanceof BindingPattern) {
- new DestructNode(astElement, propName, null, null, toEntries(bindingTarget, rhs), defaultExpr, rhs, rest)
-
- } else {
- // invalid binding target (probably a corrupt AST) -> create a padding node
- new DestructNode(astElement, propName, null, null, null, defaultExpr, null, rest)
- }
- }
-
- /** @return the expression or function of the given PropertyAssignment */
- private static def TypableElement getPropertyAssignmentExpression(TypableElement rhs) {
- switch (rhs) {
- PropertyGetterDeclaration:
- return rhs.definedFunctionOrAccessor
- PropertySetterDeclaration:
- return rhs.definedFunctionOrAccessor
- PropertyMethodDeclaration:
- return rhs.definedFunctionOrAccessor
- PropertyNameValuePair:
- return rhs.expression
- PropertyAssignmentAnnotationList:
- return null
- default:
- return rhs
- }
- }
-
- /** @return all {@link IdentifierRef} of variables that are written in the given assignment */
- public def List getAllDeclaredIdRefs() {
- val List idRefs = new LinkedList();
- val Iterator allNestedNodes = this.stream().iterator();
-
- while (allNestedNodes.hasNext()) {
- val EObject eobj = allNestedNodes.next().getAstElement();
- if (eobj instanceof ArrayElement) {
- val Expression expr = eobj.getExpression();
- if (expr instanceof AssignmentExpression) {
- idRefs.add((expr.getLhs()));
- } else {
- idRefs.add(expr);
- }
-
- } else if (eobj instanceof PropertyNameValuePairSingleName) {
- idRefs.add(eobj.getIdentifierRef());
-
- } else if (eobj instanceof PropertyNameValuePair) {
- val Expression expr = eobj.getExpression();
- if (expr instanceof AssignmentExpression) {
- idRefs.add(expr.getLhs());
- } else {
- idRefs.add(expr);
- }
- }
- }
- return idRefs;
- }
-
- /** @return a pair where its key is the assigned EObject and its value is the default EObject to the given lhs AST element */
- public static def Pair getValueFromDestructuring(EObject nodeElem) {
- var EObject node = nodeElem;
- var EObject topNode = null;
- var EObject dNodeElem = null;
- var boolean breakSearch = false;
-
- while (!breakSearch) {
- var EObject parent = node.eContainer();
- dNodeElem = getDNodeElem(dNodeElem, parent, node);
- topNode = getTopElem(topNode, parent);
- breakSearch = parent instanceof Statement;
- node = parent;
- }
-
- var DestructNode dNode = if (topNode instanceof AssignmentExpression) {
- DestructNode.unify(topNode);
- } else if (topNode instanceof VariableBinding) {
- DestructNode.unify(topNode);
- } else if (topNode instanceof ForStatement) {
- DestructNode.unify(topNode);
- } else {
- null;
- };
-
- if (dNode !== null) {
- dNode = dNode.findNodeForElement(dNodeElem);
- if (dNode !== null) {
- var EObject assgnValue = dNode.getAssignedElem();
- var EObject defaultValue = dNode.getDefaultExpr();
- return assgnValue -> defaultValue;
- }
- }
-
- return null;
- }
-
- private static def EObject getDNodeElem(EObject dNodeElem, EObject parent, EObject node) {
- if (dNodeElem !== null) {
- return dNodeElem;
- }
- if (node instanceof BindingElement && parent instanceof BindingProperty) {
- return parent;
- }
- if (node instanceof BindingElement || node instanceof ArrayElement || node instanceof PropertyAssignment) {
- return node;
- }
- }
-
- private static def EObject getTopElem(EObject oldTopNode, EObject parent) {
- val EObject newTopNode = switch (parent) {
- ForStatement:
- parent
- AssignmentExpression:
- parent
- VariableBinding:
- parent
- default:
- null
- };
-
- if (newTopNode !== null) {
- return newTopNode
- } else {
- oldTopNode;
- }
- }
-
- public static def List getAllDeclaredIdRefs(EObject eobj) {
- val DestructNode dnode = switch (eobj) {
- ForStatement:
- unify(eobj)
- VariableBinding:
- unify(eobj)
- AssignmentExpression:
- unify(eobj)
- default:
- null
- };
-
- if (dnode === null) {
- return #[];
- }
-
- return dnode.allDeclaredIdRefs;
- }
-}
diff --git a/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructureUtils.java b/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructureUtils.java
index c1d0340739..777b2887f7 100644
--- a/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructureUtils.java
+++ b/plugins/org.eclipse.n4js.model/src/org/eclipse/n4js/n4JS/DestructureUtils.java
@@ -275,7 +275,7 @@ public static boolean isInDestructuringPattern(EObject obj) {
return isParentPartOfSameDestructuringPattern(obj);
}
- /** @return true iff the given {@link EObject} can be {@link DestructNode#getAstElement()} */
+ /** @return true iff the given {@link EObject} can be {@link DestructNode#astElement} */
public static boolean isRepresentingElement(EObject eobj) {
boolean isRepresentingElement = false;
isRepresentingElement |= eobj instanceof ArrayElement;
diff --git a/plugins/org.eclipse.n4js.model/xtend-gen/.gitignore b/plugins/org.eclipse.n4js.model/xtend-gen/.gitignore
deleted file mode 100644
index c96a04f008..0000000000
--- a/plugins/org.eclipse.n4js.model/xtend-gen/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
\ No newline at end of file
diff --git a/plugins/org.eclipse.n4js.regex.ide/.classpath b/plugins/org.eclipse.n4js.regex.ide/.classpath
index d28af888cb..fc609eff89 100644
--- a/plugins/org.eclipse.n4js.regex.ide/.classpath
+++ b/plugins/org.eclipse.n4js.regex.ide/.classpath
@@ -2,7 +2,6 @@
-
diff --git a/plugins/org.eclipse.n4js.regex.ide/build.properties b/plugins/org.eclipse.n4js.regex.ide/build.properties
index bef28d3d93..ec65859a4c 100644
--- a/plugins/org.eclipse.n4js.regex.ide/build.properties
+++ b/plugins/org.eclipse.n4js.regex.ide/build.properties
@@ -1,5 +1,4 @@
source.. = src/,\
- src-gen/,\
- xtend-gen/
+ src-gen/
bin.includes = META-INF/,\
.
diff --git a/plugins/org.eclipse.n4js.regex.ide/pom.xml b/plugins/org.eclipse.n4js.regex.ide/pom.xml
index 0fd5ff17d0..6da554c206 100644
--- a/plugins/org.eclipse.n4js.regex.ide/pom.xml
+++ b/plugins/org.eclipse.n4js.regex.ide/pom.xml
@@ -30,10 +30,6 @@ Contributors:
org.apache.maven.plugins
maven-clean-plugin
-
- org.eclipse.xtend
- xtend-maven-plugin
-
diff --git a/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeModule.xtend b/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeModule.java
similarity index 76%
rename from plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeModule.xtend
rename to plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeModule.java
index 2daca727c9..c67769bc34 100644
--- a/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeModule.xtend
+++ b/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeModule.java
@@ -8,11 +8,11 @@
* Contributors:
* NumberFour AG - Initial API and implementation
*/
-package org.eclipse.n4js.regex.ide
-
+package org.eclipse.n4js.regex.ide;
/**
* Use this class to register ide components.
*/
-class RegularExpressionIdeModule extends AbstractRegularExpressionIdeModule {
+public class RegularExpressionIdeModule extends AbstractRegularExpressionIdeModule {
+ // empty
}
diff --git a/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeSetup.java b/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeSetup.java
new file mode 100644
index 0000000000..41d24e8772
--- /dev/null
+++ b/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeSetup.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2017 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.regex.ide;
+
+import org.eclipse.n4js.regex.RegularExpressionRuntimeModule;
+import org.eclipse.n4js.regex.RegularExpressionStandaloneSetup;
+import org.eclipse.xtext.util.Modules2;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * Initialization support for running Xtext languages as language servers.
+ */
+public class RegularExpressionIdeSetup extends RegularExpressionStandaloneSetup {
+
+ @Override
+ public Injector createInjector() {
+ return Guice
+ .createInjector(Modules2.mixin(new RegularExpressionRuntimeModule(), new RegularExpressionIdeModule()));
+ }
+
+}
diff --git a/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeSetup.xtend b/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeSetup.xtend
deleted file mode 100644
index 926c575f3c..0000000000
--- a/plugins/org.eclipse.n4js.regex.ide/src/org/eclipse/n4js/regex/ide/RegularExpressionIdeSetup.xtend
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2017 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.regex.ide
-
-import com.google.inject.Guice
-import org.eclipse.n4js.regex.RegularExpressionRuntimeModule
-import org.eclipse.n4js.regex.RegularExpressionStandaloneSetup
-import org.eclipse.xtext.util.Modules2
-
-/**
- * Initialization support for running Xtext languages as language servers.
- */
-class RegularExpressionIdeSetup extends RegularExpressionStandaloneSetup {
-
- override createInjector() {
- Guice.createInjector(Modules2.mixin(new RegularExpressionRuntimeModule, new RegularExpressionIdeModule))
- }
-
-}
diff --git a/plugins/org.eclipse.n4js.regex.ide/xtend-gen/.gitignore b/plugins/org.eclipse.n4js.regex.ide/xtend-gen/.gitignore
deleted file mode 100644
index c96a04f008..0000000000
--- a/plugins/org.eclipse.n4js.regex.ide/xtend-gen/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
\ No newline at end of file
diff --git a/plugins/org.eclipse.n4js.regex/.classpath b/plugins/org.eclipse.n4js.regex/.classpath
index 217ca618e4..5153825dfe 100644
--- a/plugins/org.eclipse.n4js.regex/.classpath
+++ b/plugins/org.eclipse.n4js.regex/.classpath
@@ -12,6 +12,5 @@
-
diff --git a/plugins/org.eclipse.n4js.regex/build.properties b/plugins/org.eclipse.n4js.regex/build.properties
index 4e5f9fdb5f..ac1f3722dc 100644
--- a/plugins/org.eclipse.n4js.regex/build.properties
+++ b/plugins/org.eclipse.n4js.regex/build.properties
@@ -1,6 +1,5 @@
source.. = src/,\
- src-gen/,\
- xtend-gen/
+ src-gen/
bin.includes = model/,\
META-INF/,\
.,\
diff --git a/plugins/org.eclipse.n4js.regex/pom.xml b/plugins/org.eclipse.n4js.regex/pom.xml
index b32fd2695f..613d662dde 100644
--- a/plugins/org.eclipse.n4js.regex/pom.xml
+++ b/plugins/org.eclipse.n4js.regex/pom.xml
@@ -73,11 +73,6 @@ Contributors:
-
-
- org.eclipse.xtend
- xtend-maven-plugin
-
diff --git a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionRuntimeModule.xtend b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionRuntimeModule.java
similarity index 65%
rename from plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionRuntimeModule.xtend
rename to plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionRuntimeModule.java
index 7e0943ffd4..5cd4418fb9 100644
--- a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionRuntimeModule.xtend
+++ b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionRuntimeModule.java
@@ -8,20 +8,20 @@
* Contributors:
* NumberFour AG - Initial API and implementation
*/
-package org.eclipse.n4js.regex
+package org.eclipse.n4js.regex;
-import org.eclipse.xtext.conversion.impl.INTValueConverter
+import org.eclipse.xtext.conversion.impl.INTValueConverter;
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
-class RegularExpressionRuntimeModule extends AbstractRegularExpressionRuntimeModule {
+public class RegularExpressionRuntimeModule extends AbstractRegularExpressionRuntimeModule {
/**
* INT is a data type rule thus the specialized binding
*/
- def Class extends INTValueConverter> bindINTValueConverter() {
- return RegExINTValueConverter;
+ public Class extends INTValueConverter> bindINTValueConverter() {
+ return RegExINTValueConverter.class;
}
}
diff --git a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionStandaloneSetup.xtend b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionStandaloneSetup.java
similarity index 74%
rename from plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionStandaloneSetup.xtend
rename to plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionStandaloneSetup.java
index b69feb848e..6f00167592 100644
--- a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionStandaloneSetup.xtend
+++ b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/RegularExpressionStandaloneSetup.java
@@ -8,15 +8,15 @@
* Contributors:
* NumberFour AG - Initial API and implementation
*/
-package org.eclipse.n4js.regex
-
+package org.eclipse.n4js.regex;
/**
* Initialization support for running Xtext languages without Equinox extension registry.
*/
-class RegularExpressionStandaloneSetup extends RegularExpressionStandaloneSetupGenerated {
+public class RegularExpressionStandaloneSetup extends RegularExpressionStandaloneSetupGenerated {
- def static void doSetup() {
- new RegularExpressionStandaloneSetup().createInjectorAndDoEMFRegistration()
+ /***/
+ static public void doSetup() {
+ new RegularExpressionStandaloneSetup().createInjectorAndDoEMFRegistration();
}
}
diff --git a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/generator/RegularExpressionGenerator.java b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/generator/RegularExpressionGenerator.java
new file mode 100644
index 0000000000..35eb206c3b
--- /dev/null
+++ b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/generator/RegularExpressionGenerator.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.regex.generator;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.generator.AbstractGenerator;
+import org.eclipse.xtext.generator.IFileSystemAccess2;
+import org.eclipse.xtext.generator.IGeneratorContext;
+
+/**
+ * Generates code from your model files on save.
+ *
+ * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
+ */
+public class RegularExpressionGenerator extends AbstractGenerator {
+
+ @Override
+ public void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
+ // fsa.generateFile('greetings.txt', 'People to greet: ' +
+ // resource.allContents
+ // .filter(typeof(Greeting))
+ // .map[name]
+ // .join(', '))
+ }
+}
diff --git a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/generator/RegularExpressionGenerator.xtend b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/generator/RegularExpressionGenerator.xtend
deleted file mode 100644
index b63de1fecc..0000000000
--- a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/generator/RegularExpressionGenerator.xtend
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.regex.generator
-
-import org.eclipse.emf.ecore.resource.Resource
-import org.eclipse.xtext.generator.AbstractGenerator
-import org.eclipse.xtext.generator.IFileSystemAccess2
-import org.eclipse.xtext.generator.IGeneratorContext
-
-/**
- * Generates code from your model files on save.
- *
- * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
- */
-class RegularExpressionGenerator extends AbstractGenerator {
-
- override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
-// fsa.generateFile('greetings.txt', 'People to greet: ' +
-// resource.allContents
-// .filter(typeof(Greeting))
-// .map[name]
-// .join(', '))
- }
-}
diff --git a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/scoping/RegularExpressionScopeProvider.xtend b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/scoping/RegularExpressionScopeProvider.java
similarity index 71%
rename from plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/scoping/RegularExpressionScopeProvider.xtend
rename to plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/scoping/RegularExpressionScopeProvider.java
index ed6c9c4794..d974dbbbdf 100644
--- a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/scoping/RegularExpressionScopeProvider.xtend
+++ b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/scoping/RegularExpressionScopeProvider.java
@@ -8,14 +8,13 @@
* Contributors:
* NumberFour AG - Initial API and implementation
*/
-package org.eclipse.n4js.regex.scoping
+package org.eclipse.n4js.regex.scoping;
/**
* This class contains custom scoping description.
*
- * see : http://www.eclipse.org/Xtext/documentation.html#scoping
- * on how and when to use it
+ * see : http://www.eclipse.org/Xtext/documentation.html#scoping on how and when to use it
*/
-class RegularExpressionScopeProvider extends org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider {
-
+public class RegularExpressionScopeProvider extends org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider {
+ // empty
}
diff --git a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/validation/RegularExpressionValidator.xtend b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/validation/RegularExpressionValidator.java
similarity index 54%
rename from plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/validation/RegularExpressionValidator.xtend
rename to plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/validation/RegularExpressionValidator.java
index 141632cdf4..2ecc798cd8 100644
--- a/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/validation/RegularExpressionValidator.xtend
+++ b/plugins/org.eclipse.n4js.regex/src/org/eclipse/n4js/regex/validation/RegularExpressionValidator.java
@@ -8,7 +8,7 @@
* Contributors:
* NumberFour AG - Initial API and implementation
*/
-package org.eclipse.n4js.regex.validation
+package org.eclipse.n4js.regex.validation;
//import org.eclipse.xtext.validation.Check
/**
@@ -16,16 +16,16 @@
*
* see http://www.eclipse.org/Xtext/documentation.html#validation
*/
-class RegularExpressionValidator extends AbstractRegularExpressionValidator {
+public class RegularExpressionValidator extends AbstractRegularExpressionValidator {
-// public static val INVALID_NAME = 'invalidName'
-//
-// @Check
-// def checkGreetingStartsWithCapital(Greeting greeting) {
-// if (!Character.isUpperCase(greeting.name.charAt(0))) {
-// warning('Name should start with a capital',
-// MyDslPackage.Literals.GREETING__NAME,
-// INVALID_NAME)
-// }
-// }
+ // public static val INVALID_NAME = 'invalidName'
+ //
+ // @Check
+ // def checkGreetingStartsWithCapital(Greeting greeting) {
+ // if (!Character.isUpperCase(greeting.name.charAt(0))) {
+ // warning('Name should start with a capital',
+ // MyDslPackage.Literals.GREETING__NAME,
+ // INVALID_NAME)
+ // }
+ // }
}
diff --git a/plugins/org.eclipse.n4js.regex/xtend-gen/.gitignore b/plugins/org.eclipse.n4js.regex/xtend-gen/.gitignore
deleted file mode 100644
index c96a04f008..0000000000
--- a/plugins/org.eclipse.n4js.regex/xtend-gen/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
\ No newline at end of file
diff --git a/plugins/org.eclipse.n4js.semver.ide/.classpath b/plugins/org.eclipse.n4js.semver.ide/.classpath
index d28af888cb..fc609eff89 100644
--- a/plugins/org.eclipse.n4js.semver.ide/.classpath
+++ b/plugins/org.eclipse.n4js.semver.ide/.classpath
@@ -2,7 +2,6 @@
-
diff --git a/plugins/org.eclipse.n4js.semver.ide/build.properties b/plugins/org.eclipse.n4js.semver.ide/build.properties
index bef28d3d93..ec65859a4c 100644
--- a/plugins/org.eclipse.n4js.semver.ide/build.properties
+++ b/plugins/org.eclipse.n4js.semver.ide/build.properties
@@ -1,5 +1,4 @@
source.. = src/,\
- src-gen/,\
- xtend-gen/
+ src-gen/
bin.includes = META-INF/,\
.
diff --git a/plugins/org.eclipse.n4js.semver.ide/pom.xml b/plugins/org.eclipse.n4js.semver.ide/pom.xml
index 0e1fd68378..78d94531f6 100644
--- a/plugins/org.eclipse.n4js.semver.ide/pom.xml
+++ b/plugins/org.eclipse.n4js.semver.ide/pom.xml
@@ -30,10 +30,6 @@ Contributors:
org.apache.maven.plugins
maven-clean-plugin
-
- org.eclipse.xtend
- xtend-maven-plugin
-
diff --git a/plugins/org.eclipse.n4js.semver.ide/xtend-gen/.gitignore b/plugins/org.eclipse.n4js.semver.ide/xtend-gen/.gitignore
deleted file mode 100644
index d6b7ef32c8..0000000000
--- a/plugins/org.eclipse.n4js.semver.ide/xtend-gen/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
diff --git a/plugins/org.eclipse.n4js.semver.model/pom.xml b/plugins/org.eclipse.n4js.semver.model/pom.xml
index 760864b701..7464ebfba8 100644
--- a/plugins/org.eclipse.n4js.semver.model/pom.xml
+++ b/plugins/org.eclipse.n4js.semver.model/pom.xml
@@ -38,11 +38,6 @@ Contributors:
maven-resources-plugin
${maven-resources-plugin.version}
-
- org.eclipse.xtend
- xtend-maven-plugin
-
-
diff --git a/plugins/org.eclipse.n4js.transpiler.es/.classpath b/plugins/org.eclipse.n4js.transpiler.es/.classpath
index 14dd6eb29b..e3378d07f0 100644
--- a/plugins/org.eclipse.n4js.transpiler.es/.classpath
+++ b/plugins/org.eclipse.n4js.transpiler.es/.classpath
@@ -1,7 +1,6 @@
-
diff --git a/plugins/org.eclipse.n4js.transpiler.es/build.properties b/plugins/org.eclipse.n4js.transpiler.es/build.properties
index aaedd5b241..17daa5b49c 100644
--- a/plugins/org.eclipse.n4js.transpiler.es/build.properties
+++ b/plugins/org.eclipse.n4js.transpiler.es/build.properties
@@ -1,5 +1,4 @@
-source.. = src/,\
- xtend-gen/
+source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
diff --git a/plugins/org.eclipse.n4js.transpiler.es/pom.xml b/plugins/org.eclipse.n4js.transpiler.es/pom.xml
index cda3552311..3856d6b132 100644
--- a/plugins/org.eclipse.n4js.transpiler.es/pom.xml
+++ b/plugins/org.eclipse.n4js.transpiler.es/pom.xml
@@ -34,11 +34,6 @@ Contributors:
maven-resources-plugin
${maven-resources-plugin.version}
-
- org.eclipse.xtend
- xtend-maven-plugin
-
-
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/BlockAssistant.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/BlockAssistant.java
new file mode 100644
index 0000000000..4fd6b166a4
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/BlockAssistant.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.assistants;
+
+import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.voidType;
+
+import org.eclipse.n4js.n4JS.ArrowFunction;
+import org.eclipse.n4js.n4JS.Expression;
+import org.eclipse.n4js.transpiler.TransformationAssistant;
+import org.eclipse.n4js.ts.typeRefs.TypeRef;
+import org.eclipse.n4js.typesystem.N4JSTypeSystem;
+
+import com.google.inject.Inject;
+
+/**
+ */
+public class BlockAssistant extends TransformationAssistant {
+
+ @Inject
+ private N4JSTypeSystem ts;
+
+ /***/
+ public final boolean needsReturnInsertionForBody(ArrowFunction arrowFuncInIM) {
+ // unfortunately, we need a properly contained AST element below (see preconditions above)
+ ArrowFunction origAST = getState().tracer.getOriginalASTNodeOfSameType(arrowFuncInIM, false);
+ if (origAST == null) {
+ // this arrow function does not come from the N4JS source code but was created by some transformation
+ // -> we assume it was intended to return a value and hence a return must be inserted
+ return true;
+ }
+ if (!origAST.isSingleExprImplicitReturn()) {
+ return false;
+ }
+ // check if body is typed void, in which case no return will be inserted
+ Expression singleExpr = origAST.implicitReturnExpr();
+ TypeRef implicitReturnTypeRef = ts.type(getState().G, singleExpr);
+ if (implicitReturnTypeRef != null && implicitReturnTypeRef.getDeclaredType() == voidType(getState().G)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/BlockAssistant.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/BlockAssistant.xtend
deleted file mode 100644
index 21eec2cdcc..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/BlockAssistant.xtend
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.assistants
-
-import com.google.inject.Inject
-import org.eclipse.n4js.n4JS.ArrowFunction
-import org.eclipse.n4js.transpiler.TransformationAssistant
-import org.eclipse.n4js.typesystem.N4JSTypeSystem
-
-import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.*
-
-/**
- */
-class BlockAssistant extends TransformationAssistant {
-
- @Inject
- private N4JSTypeSystem ts
-
- def public final boolean needsReturnInsertionForBody(ArrowFunction arrowFuncInIM) {
- // unfortunately, we need a properly contained AST element below (see preconditions above)
- val origAST = state.tracer.getOriginalASTNodeOfSameType(arrowFuncInIM, false);
- if (origAST === null) {
- // this arrow function does not come from the N4JS source code but was created by some transformation
- // -> we assume it was intended to return a value and hence a return must be inserted
- return true;
- }
- if (!origAST.isSingleExprImplicitReturn) {
- return false;
- }
- // check if body is typed void, in which case no return will be inserted
- val singleExpr = origAST.implicitReturnExpr();
- val implicitReturnTypeRef = ts.type(state.G, singleExpr);
- if (implicitReturnTypeRef?.declaredType === state.G.voidType) {
- return false;
- }
- return true;
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassConstructorAssistant.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassConstructorAssistant.java
new file mode 100644
index 0000000000..7ad80f6149
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassConstructorAssistant.java
@@ -0,0 +1,580 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.assistants;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Argument;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._AssignmentExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._CallExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ConditionalExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._EqualityExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ExprStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Fpar;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IfStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4MethodDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._OR;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ObjLit;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Parenthesis;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyNameValuePair;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._RelationalExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Snippet;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._StringLiteralForSTE;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._SuperLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._TRUE;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ThisLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableDeclaration;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableStatement;
+import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.errorType;
+import static org.eclipse.n4js.utils.N4JSLanguageUtils.builtInOrProvidedByRuntime;
+import static org.eclipse.n4js.utils.N4JSLanguageUtils.builtInOrProvidedByRuntimeOrShape;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.head;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.last;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.map;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.toList;
+import static org.eclipse.xtext.xbase.lib.ListExtensions.map;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.n4js.AnnotationDefinition;
+import org.eclipse.n4js.N4JSLanguageConstants;
+import org.eclipse.n4js.n4JS.Argument;
+import org.eclipse.n4js.n4JS.Block;
+import org.eclipse.n4js.n4JS.EqualityOperator;
+import org.eclipse.n4js.n4JS.Expression;
+import org.eclipse.n4js.n4JS.ExpressionStatement;
+import org.eclipse.n4js.n4JS.FormalParameter;
+import org.eclipse.n4js.n4JS.ModifierUtils;
+import org.eclipse.n4js.n4JS.N4ClassDeclaration;
+import org.eclipse.n4js.n4JS.N4FieldDeclaration;
+import org.eclipse.n4js.n4JS.N4MemberDeclaration;
+import org.eclipse.n4js.n4JS.N4MethodDeclaration;
+import org.eclipse.n4js.n4JS.N4Modifier;
+import org.eclipse.n4js.n4JS.N4SetterDeclaration;
+import org.eclipse.n4js.n4JS.ParameterizedCallExpression;
+import org.eclipse.n4js.n4JS.PropertyNameValuePair;
+import org.eclipse.n4js.n4JS.RelationalOperator;
+import org.eclipse.n4js.n4JS.Statement;
+import org.eclipse.n4js.n4JS.SuperLiteral;
+import org.eclipse.n4js.n4JS.TypeReferenceNode;
+import org.eclipse.n4js.n4JS.VariableDeclaration;
+import org.eclipse.n4js.n4JS.VariableStatementKeyword;
+import org.eclipse.n4js.transpiler.TransformationAssistant;
+import org.eclipse.n4js.transpiler.assistants.TypeAssistant;
+import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryInternal;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal;
+import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef;
+import org.eclipse.n4js.ts.typeRefs.TypeRef;
+import org.eclipse.n4js.ts.types.MemberAccessModifier;
+import org.eclipse.n4js.ts.types.TClass;
+import org.eclipse.n4js.ts.types.TClassifier;
+import org.eclipse.n4js.ts.types.TFormalParameter;
+import org.eclipse.n4js.ts.types.TInterface;
+import org.eclipse.n4js.ts.types.TMethod;
+import org.eclipse.n4js.ts.types.Type;
+import org.eclipse.n4js.types.utils.TypeUtils;
+import org.eclipse.xtext.xbase.lib.Pair;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+/**
+ * Modify or create the constructor of a class declaration.
+ */
+public class ClassConstructorAssistant extends TransformationAssistant {
+
+ @Inject
+ private TypeAssistant typeAssistant;
+ @Inject
+ private ClassifierAssistant classifierAssistant;
+
+ private static final class SpecInfo {
+ /**
+ * The @Spec
-fpar in the intermediate model. Never null
.
+ */
+ public final FormalParameter fpar;
+ /**
+ * Names of properties in the with-clause of the @Spec
-fpar's type. For example, as in:
+ *
+ *
+ * constructor(@Spec specObj: ~i~this with { addProp: string }) { ... } .
+ */
+ public final ImmutableSet additionalProps;
+
+ public SpecInfo(FormalParameter fpar, Iterable additionalProps) {
+ this.fpar = Objects.requireNonNull(fpar);
+ this.additionalProps = ImmutableSet.copyOf(additionalProps);
+ }
+ }
+
+ /**
+ * Amend the constructor of the given class with implicit functionality (e.g. initialization of instance fields).
+ * Will create an implicit constructor declaration iff no constructor is defined in the N4JS source code AND an
+ * implicit constructor is actually required.
+ */
+ public void amendConstructor(N4ClassDeclaration classDecl, SymbolTableEntry classSTE,
+ SymbolTableEntryOriginal superClassSTE,
+ LinkedHashSet fieldsRequiringExplicitDefinition) {
+
+ N4MethodDeclaration explicitCtorDecl = classDecl.getOwnedCtor(); // the constructor defined in the N4JS source
+ // code or 'null' if none was defined
+ N4MethodDeclaration ctorDecl = explicitCtorDecl != null ? explicitCtorDecl
+ : _N4MethodDecl(N4JSLanguageConstants.CONSTRUCTOR);
+
+ // amend formal parameters
+ SpecInfo specInfo = amendFormalParametersOfConstructor(classDecl, ctorDecl, explicitCtorDecl);
+
+ // amend body
+ boolean isNonTrivial = amendBodyOfConstructor(classDecl, classSTE, superClassSTE, ctorDecl, explicitCtorDecl,
+ specInfo, fieldsRequiringExplicitDefinition);
+
+ // add constructor to classDecl (if necessary)
+ if (ctorDecl.eContainer() == null && isNonTrivial) {
+ classDecl.getOwnedMembersRaw().add(0, ctorDecl);
+ }
+ }
+
+ private SpecInfo amendFormalParametersOfConstructor(N4ClassDeclaration classDecl, N4MethodDeclaration ctorDecl,
+ N4MethodDeclaration explicitCtorDecl) {
+ FormalParameter specFpar = null;
+ TypeRef specFparTypeRef = null;
+
+ boolean hasExplicitCtor = explicitCtorDecl != null;
+ if (hasExplicitCtor) {
+ // explicitly defined constructor
+ // --> nothing to be changed (use fpars from N4JS source code)
+
+ specFpar = head(filter(ctorDecl.getFpars(), fpar -> AnnotationDefinition.SPEC.hasAnnotation(fpar)));
+ if (specFpar != null) {
+ TypeReferenceNode typeRefNode = specFpar.getDeclaredTypeRefNode();
+ if (typeRefNode != null) {
+ specFparTypeRef = getState().info.getOriginalProcessedTypeRef(typeRefNode);
+ }
+ }
+ } else {
+ // implicit constructor
+ // --> create fpars using fpars of nearest constructor in hierarchy as template
+
+ TMethod templateCtor = getNearestConstructorInHierarchy(classDecl);
+ if (templateCtor != null) {
+ for (TFormalParameter templateFpar : templateCtor.getFpars()) {
+ boolean isSpecFpar = AnnotationDefinition.SPEC.hasAnnotation(templateFpar);
+ FormalParameter newFpar = _Fpar(templateFpar.getName(), templateFpar.isVariadic(), isSpecFpar);
+ ctorDecl.getFpars().add(newFpar);
+
+ if (isSpecFpar && specFpar == null) {
+ specFpar = newFpar;
+ specFparTypeRef = TypeUtils.copy(templateFpar.getTypeRef());
+ }
+ }
+ }
+ }
+
+ if (specFpar != null) {
+ Iterable additionalProps = Collections.emptyList();
+ if (specFparTypeRef != null && specFparTypeRef.getStructuralMembers() != null) {
+ additionalProps = map(specFparTypeRef.getStructuralMembers(), m -> m.getName());
+ }
+ return new SpecInfo(specFpar, additionalProps);
+ }
+ return null;
+ }
+
+ /**
+ * Returns true
iff the constructor is non-trivial, i.e. non-empty and containing more than just the
+ * default super call.
+ */
+ private boolean amendBodyOfConstructor(N4ClassDeclaration classDecl, SymbolTableEntry classSTE,
+ SymbolTableEntryOriginal superClassSTE,
+ N4MethodDeclaration ctorDecl, N4MethodDeclaration explicitCtorDecl, SpecInfo specInfo,
+ LinkedHashSet fieldsRequiringExplicitDefinition) {
+
+ boolean hasExplicitCtor = explicitCtorDecl != null;
+ Block body = ctorDecl.getBody();
+
+ boolean isDirectSubclassOfError = superClassSTE == null ? null
+ : superClassSTE.getOriginalTarget() == errorType(getState().G);
+ int superCallIndex = (explicitCtorDecl != null && explicitCtorDecl.getBody() != null)
+ ? getSuperCallIndex(explicitCtorDecl)
+ : -1;
+ boolean hasExplicitSuperCall = superCallIndex >= 0;
+ ExpressionStatement defaultSuperCall = null;
+
+ int idx = (hasExplicitSuperCall) ? superCallIndex + 1 : 0;
+
+ // add/replace/modify super call
+ if (hasExplicitSuperCall) {
+ // keep existing, explicit super call unchanged
+ } else {
+ // no explicitCtorDecl OR no body OR no explicit super call (and no direct subclass of Error)
+ // --> add default super call (if required)
+ if (superClassSTE != null) {
+ List fparsOfSuperCtor = (hasExplicitCtor) ?
+ // explicit ctor without an explicit super call: only allowed if the super constructor
+ // does not have any arguments, so we can simply assume empty fpars here, without actually
+ // looking up the super constructor with #getNearestConstructorInHierarchy():
+ Collections.emptyList()
+ :
+ // no explicit ctor: the already created implicit constructor in 'ctorDecl' has the
+ // same fpars as the super constructor, so we can use those as a template:
+ ctorDecl.getFpars();
+ defaultSuperCall = createDefaultSuperCall(fparsOfSuperCtor);
+ idx = insertAt(body.getStatements(), idx, defaultSuperCall);
+ }
+ }
+ if (isDirectSubclassOfError) {
+ // special case: add oddities for sub-classing Error
+ idx = insertAt(body.getStatements(), idx, createSubclassingErrorOddities());
+ }
+
+ // if we are in a spec-constructor: prepare a local variable for the spec-object and
+ // ensure it is never 'undefined' or 'null'
+ SymbolTableEntry specObjSTE = null;
+ if (specInfo != null) {
+ // let $specObj = specFpar || {};
+ SymbolTableEntry specFparSTE = findSymbolTableEntryForElement(specInfo.fpar, true);
+ VariableDeclaration specObjVarDecl = _VariableDeclaration("$specObj",
+ _OR(_IdentRef(specFparSTE), _ObjLit()));
+ idx = insertAt(body.getStatements(), idx,
+ _VariableStatement(VariableStatementKeyword.CONST, specObjVarDecl));
+
+ specObjSTE = findSymbolTableEntryForElement(specObjVarDecl, true);
+ }
+
+ // add explicit definitions of instance fields (only for fields that actually require this)
+ idx = insertAt(body.getStatements(), idx,
+ classifierAssistant.createExplicitFieldDefinitions(classSTE, false, fieldsRequiringExplicitDefinition));
+
+ // add initialization code for instance fields
+ idx = insertAt(body.getStatements(), idx,
+ createInstanceFieldInitCode(classDecl, specInfo, specObjSTE, fieldsRequiringExplicitDefinition));
+
+ // add delegation to field initialization functions of all directly implemented interfaces
+ idx = insertAt(body.getStatements(), idx,
+ createDelegationToFieldInitOfImplementedInterfaces(classDecl, specObjSTE));
+
+ // check if constructor is non-trivial
+ EList ctorDeclStmnts = ctorDecl.getBody().getStatements();
+ boolean bodyContainsOnlyDefaultSuperCall = defaultSuperCall != null
+ && ctorDeclStmnts.size() == 1
+ && ctorDeclStmnts.get(0) == defaultSuperCall;
+ boolean isNonTrivialCtor = !ctorDeclStmnts.isEmpty() && !bodyContainsOnlyDefaultSuperCall;
+
+ return isNonTrivialCtor;
+ }
+
+ private List createInstanceFieldInitCode(N4ClassDeclaration classDecl, SpecInfo specInfo,
+ SymbolTableEntry specObjSTE, Set fieldsWithExplicitDefinition) {
+ List allFields = toList(filter(classDecl.getOwnedFields(), f -> !f.isStatic()
+ && !isConsumedFromInterface(f)
+ && !builtInOrProvidedByRuntime(getState().info.getOriginalDefinedMember(f))));
+ if (specInfo != null) {
+ // we have a spec-parameter -> we are in a spec-style constructor
+ List result = new ArrayList<>();
+
+ // step #1: initialize all fields either with data from the specFpar OR or their initializer expression
+ List currFields = new ArrayList<>();
+ boolean currFieldsAreSpecced = false;
+ for (N4FieldDeclaration field : allFields) {
+ boolean isSpecced = isPublic(field) || specInfo.additionalProps.contains(field.getName());
+ if (isSpecced == currFieldsAreSpecced) {
+ currFields.add(field);
+ } else {
+ result.addAll(createFieldInitCodeForSeveralFields(currFields, currFieldsAreSpecced, specObjSTE));
+ currFields.clear();
+ currFields.add(field);
+ currFieldsAreSpecced = isSpecced;
+ }
+ }
+ result.addAll(createFieldInitCodeForSeveralFields(currFields, currFieldsAreSpecced, specObjSTE));
+
+ // step #2: invoke setters with data from specFpar
+ // (note: in case of undefined specFpar at runtime, setters should not be invoked, so we wrap in if
+ // statement)
+ Iterable speccedSetters = filter(classDecl.getOwnedSetters(),
+ sd -> !sd.isStatic() && sd.getDeclaredModifiers().contains(N4Modifier.PUBLIC));
+ result.addAll(toList(map(speccedSetters, sd -> createFieldInitCodeForSingleSpeccedSetter(sd, specObjSTE))));
+
+ return result;
+ } else {
+ // simple: just initialize fields with data from their initializer expression
+ return toList(map(filter(allFields,
+ f -> !(f.getExpression() == null && fieldsWithExplicitDefinition.contains(f))),
+ f -> createFieldInitCodeForSingleField(f)));
+ }
+ }
+
+ private List createFieldInitCodeForSeveralFields(List fieldDecls,
+ boolean fieldsAreSpecced, SymbolTableEntry specObjSTE) {
+ if (fieldDecls.isEmpty()) {
+ return Collections.emptyList();
+ }
+ if (!fieldsAreSpecced) {
+ return toList(map(fieldDecls, fd -> createFieldInitCodeForSingleField(fd)));
+ }
+ if (fieldDecls.size() == 1) {
+ return List.of(createFieldInitCodeForSingleSpeccedField(fieldDecls.get(0), specObjSTE));
+ }
+ return List.of(createFieldInitCodeForSeveralSpeccedFields(fieldDecls, specObjSTE));
+ }
+
+ private Statement createFieldInitCodeForSingleField(N4FieldDeclaration fieldDecl) {
+ // here we create:
+ //
+ // this.fieldName = ;
+ // or
+ // this.fieldName = undefined;
+ //
+ // NOTE: we set the field to 'undefined' even in the second case, because ...
+ // 1) it makes a difference when #hasOwnProperty(), etc. is used after the constructor returns,
+ // 2) for consistency with the method #createFieldInitCodeForSeveralSpeccedFields(), because with
+ // destructuring as used by that method, the property will always be assigned (even if the
+ // value is 'undefined' and there is no default).
+ //
+ SymbolTableEntry fieldSTE = findSymbolTableEntryForElement(fieldDecl, true);
+ return _ExprStmnt(_AssignmentExpr(
+ _PropertyAccessExpr(_ThisLiteral(), fieldSTE),
+ (fieldDecl.getExpression() != null) ? fieldDecl.getExpression() // reusing the expression here
+ : undefinedRef()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private Statement createFieldInitCodeForSeveralSpeccedFields(Iterable fieldDecls,
+ SymbolTableEntry specObjSTE) {
+ // here we create:
+ //
+ // ({
+ // fieldName: this.fieldName = ,
+ // ...
+ // } = spec);
+ //
+
+ Iterable> pairs = map(fieldDecls, fieldDecl -> {
+ SymbolTableEntry fieldSTE = findSymbolTableEntryForElement(fieldDecl, true);
+ ParameterizedPropertyAccessExpression_IM thisFieldName = _PropertyAccessExpr(_ThisLiteral(), fieldSTE);
+ Expression expr = hasNonTrivialInitExpression(fieldDecl)
+ ? _AssignmentExpr(thisFieldName, fieldDecl.getExpression()) // reusing the expression here
+ : thisFieldName;
+ return Pair.of(fieldDecl.getName(), expr);
+ });
+
+ return _ExprStmnt(
+ _Parenthesis(
+ _AssignmentExpr(
+ _ObjLit(
+ Iterables.toArray(pairs, Pair.class)),
+ _IdentRef(specObjSTE))));
+ }
+
+ private Statement createFieldInitCodeForSingleSpeccedField(N4FieldDeclaration fieldDecl,
+ SymbolTableEntry specObjSTE) {
+ SymbolTableEntry fieldSTE = findSymbolTableEntryForElement(fieldDecl, true);
+ if (hasNonTrivialInitExpression(fieldDecl)) {
+ // here we create:
+ //
+ // this.fieldName = spec.fieldName == undefined ? : spec.fieldName;
+ //
+ // NOTE: don't use something like "'fieldName' in spec" as the condition above, because that would
+ // not have the same behavior as destructuring in method #createFieldInitCodeForSeveralSpeccedFields()
+ // in case the property is present but has value 'undefined'!
+ //
+ return _ExprStmnt(_AssignmentExpr(
+ _PropertyAccessExpr(_ThisLiteral(), fieldSTE),
+ // ? :
+ _ConditionalExpr(
+ // spec.fieldName == undefined
+ _EqualityExpr(_PropertyAccessExpr(specObjSTE, fieldSTE), EqualityOperator.SAME,
+ undefinedRef()),
+ //
+ (fieldDecl.getExpression() != null) ? copy(fieldDecl.getExpression()) // need to copy
+ // expression here,
+ // because it will
+ // be used again!
+ : undefinedRef(),
+ // spec.fieldName
+ _PropertyAccessExpr(specObjSTE, fieldSTE))));
+ } else {
+ // we have a trivial init-expression ...
+
+ // here we create:
+ //
+ // this.fieldName = spec.fieldName;
+ //
+ return _ExprStmnt(_AssignmentExpr(
+ _PropertyAccessExpr(_ThisLiteral(), fieldSTE),
+ _PropertyAccessExpr(specObjSTE, fieldSTE)));
+ }
+ }
+
+ private Statement createFieldInitCodeForSingleSpeccedSetter(N4SetterDeclaration setterDecl,
+ SymbolTableEntry specObjSTE) {
+ // here we create:
+ //
+ // if ('fieldName' in spec) {
+ // this.fieldName = spec.fieldName;
+ // }
+ //
+
+ SymbolTableEntry setterSTE = findSymbolTableEntryForElement(setterDecl, true);
+ return _IfStmnt(
+ _RelationalExpr(
+ _StringLiteralForSTE(setterSTE), RelationalOperator.IN, _IdentRef(specObjSTE)),
+ _ExprStmnt(
+ _AssignmentExpr(
+ _PropertyAccessExpr(_ThisLiteral(), setterSTE),
+ _PropertyAccessExpr(specObjSTE, setterSTE))));
+ }
+
+ // NOTE: compare this to the super calls generated from SuperLiterals in SuperLiteralTransformation
+ private ExpressionStatement createDefaultSuperCall(List fpars) {
+
+ boolean variadicCase = !fpars.isEmpty() && last(fpars).isVariadic();
+ List args = map(fpars, fpar -> _Argument(
+ _IdentRef(findSymbolTableEntryForElement(fpar, true))));
+
+ if (variadicCase) {
+ last(args).setSpread(true);
+ }
+
+ ParameterizedCallExpression callExpr = _CallExpr();
+ callExpr.setTarget(_SuperLiteral());
+ callExpr.getArguments().addAll(args);
+ return _ExprStmnt(callExpr);
+ }
+
+ /** To be inserted where the explicit or implicit super call would be located, normally. */
+ private List createSubclassingErrorOddities() {
+ return List.of(_ExprStmnt(_Snippet("""
+ this.name = this.constructor.n4type.name;
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, this.name);
+ }
+ """)));
+ }
+
+ private List createDelegationToFieldInitOfImplementedInterfaces(N4ClassDeclaration classDecl,
+ SymbolTableEntry /* nullable */ specObjSTE) {
+
+ List implementedIfcSTEs = toList(
+ filter(typeAssistant.getSuperInterfacesSTEs(classDecl), intf ->
+ // regarding the cast to TInterface: see preconditions of ClassDeclarationTransformation
+ // regarding the entire line: generate $fieldInit call only if the interface is neither built-in nor
+ // provided by runtime nor shape
+ !builtInOrProvidedByRuntimeOrShape((TInterface) intf.getOriginalTarget())));
+ if (implementedIfcSTEs.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ LinkedHashSet ownedInstanceDataFieldsSupressMixin = new LinkedHashSet<>();
+ ownedInstanceDataFieldsSupressMixin.addAll(
+ toList(map(filter(classDecl.getOwnedGetters(), g -> !isConsumedFromInterface(g)), g -> g.getName())));
+ ownedInstanceDataFieldsSupressMixin.addAll(
+ toList(map(filter(classDecl.getOwnedSetters(), s -> !isConsumedFromInterface(s)), s -> s.getName())));
+
+ SymbolTableEntry classSTE = findSymbolTableEntryForElement(classDecl, false);
+ SymbolTableEntryInternal $initFieldsFromInterfacesSTE = steFor_$initFieldsFromInterfaces();
+
+ return List.of(_ExprStmnt(
+ _CallExpr(
+ _IdentRef($initFieldsFromInterfacesSTE),
+ _ThisLiteral(),
+ _IdentRef(classSTE),
+ (specObjSTE != null) ? _IdentRef(specObjSTE)
+ : undefinedRef(),
+ _ObjLit(
+ Iterables.toArray(map(ownedInstanceDataFieldsSupressMixin,
+ str -> _PropertyNameValuePair(str, _TRUE())), PropertyNameValuePair.class)))));
+ }
+
+ // ################################################################################################################
+ // UTILITY STUFF
+
+ private int getSuperCallIndex(N4MethodDeclaration ownedCtor) {
+ if (ownedCtor != null && ownedCtor.getBody() != null && ownedCtor.getBody().getStatements() != null) {
+ EList stmnts = ownedCtor.getBody().getStatements();
+
+ for (int i = 0; i < stmnts.size(); i++) {
+ Statement stmnt = stmnts.get(i);
+ if (stmnt instanceof ExpressionStatement) {
+ Expression expr = ((ExpressionStatement) stmnt).getExpression();
+ if (expr instanceof ParameterizedCallExpression) {
+ if (((ParameterizedCallExpression) expr).getTarget() instanceof SuperLiteral) {
+ return i;
+ }
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ private TMethod getNearestConstructorInHierarchy(N4ClassDeclaration classDecl) {
+ TClass tClass = getState().info.getOriginalDefinedType(classDecl);
+ return getNearestConstructorInHierarchy(tClass);
+ }
+
+ private TMethod getNearestConstructorInHierarchy(TClassifier clazz) {
+ TMethod ownedCtor = null;
+ if (clazz instanceof TClass) {
+ ownedCtor = clazz.getOwnedCtor();
+ }
+
+ if (ownedCtor != null) {
+ return ownedCtor;
+ } else {
+ Type superType = null;
+ if (clazz instanceof TClass) {
+ ParameterizedTypeRef superClassRef = ((TClass) clazz).getSuperClassRef();
+ superType = superClassRef == null ? null : superClassRef.getDeclaredType();
+ }
+
+ if (superType instanceof TClassifier) {
+ return getNearestConstructorInHierarchy((TClassifier) superType);
+ }
+ }
+ return null;
+ }
+
+ private boolean isConsumedFromInterface(N4MemberDeclaration memberDecl) {
+ return getState().info.isConsumedFromInterface(memberDecl);
+ }
+
+ // TODO GH-2153 use reusable utility method for computing actual accessibility
+ private boolean isPublic(N4MemberDeclaration memberDecl) {
+ // no need to bother with default accessibility, here, because 'public' is never the default
+ // (not ideal; would be better if the utility method handled default accessibility)
+ MemberAccessModifier accessModifier = ModifierUtils.convertToMemberAccessModifier(
+ memberDecl.getDeclaredModifiers(),
+ memberDecl.getAnnotations());
+ return accessModifier == MemberAccessModifier.PUBLIC;
+ }
+
+ private static int insertAt(List list, int index, T element) {
+ list.add(index, element);
+ return index + 1;
+ }
+
+ private static int insertAt(List list, int index, Collection extends T> elements) {
+ list.addAll(index, elements);
+ return index + elements.size();
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassConstructorAssistant.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassConstructorAssistant.xtend
deleted file mode 100644
index 2a5b95892f..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassConstructorAssistant.xtend
+++ /dev/null
@@ -1,517 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.assistants
-
-import com.google.common.collect.ImmutableSet
-import com.google.inject.Inject
-import java.util.ArrayList
-import java.util.Collection
-import java.util.LinkedHashSet
-import java.util.List
-import java.util.Objects
-import java.util.Set
-import org.eclipse.n4js.AnnotationDefinition
-import org.eclipse.n4js.N4JSLanguageConstants
-import org.eclipse.n4js.n4JS.EqualityOperator
-import org.eclipse.n4js.n4JS.ExpressionStatement
-import org.eclipse.n4js.n4JS.FormalParameter
-import org.eclipse.n4js.n4JS.ModifierUtils
-import org.eclipse.n4js.n4JS.N4ClassDeclaration
-import org.eclipse.n4js.n4JS.N4FieldDeclaration
-import org.eclipse.n4js.n4JS.N4MemberDeclaration
-import org.eclipse.n4js.n4JS.N4MethodDeclaration
-import org.eclipse.n4js.n4JS.N4Modifier
-import org.eclipse.n4js.n4JS.N4SetterDeclaration
-import org.eclipse.n4js.n4JS.ParameterizedCallExpression
-import org.eclipse.n4js.n4JS.RelationalOperator
-import org.eclipse.n4js.n4JS.Statement
-import org.eclipse.n4js.n4JS.SuperLiteral
-import org.eclipse.n4js.n4JS.VariableStatementKeyword
-import org.eclipse.n4js.transpiler.TransformationAssistant
-import org.eclipse.n4js.transpiler.assistants.TypeAssistant
-import org.eclipse.n4js.transpiler.im.SymbolTableEntry
-import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal
-import org.eclipse.n4js.ts.typeRefs.TypeRef
-import org.eclipse.n4js.ts.types.MemberAccessModifier
-import org.eclipse.n4js.ts.types.TClass
-import org.eclipse.n4js.ts.types.TClassifier
-import org.eclipse.n4js.ts.types.TInterface
-import org.eclipse.n4js.ts.types.TMethod
-import org.eclipse.n4js.types.utils.TypeUtils
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.*
-import static extension org.eclipse.n4js.utils.N4JSLanguageUtils.*
-
-/**
- * Modify or create the constructor of a class declaration.
- */
-class ClassConstructorAssistant extends TransformationAssistant {
-
- @Inject private TypeAssistant typeAssistant;
- @Inject private ClassifierAssistant classifierAssistant;
-
- private static final class SpecInfo {
- /**
- * The @Spec
-fpar in the intermediate model. Never null
.
- */
- public final FormalParameter fpar;
- /**
- * Names of properties in the with-clause of the @Spec
-fpar's type. For example, as in:
- *
- * constructor(@Spec specObj: ~i~this with { addProp: string }) { ... }
- * .
- */
- public final ImmutableSet additionalProps;
-
- new(FormalParameter fpar, Iterable additionalProps) {
- this.fpar = Objects.requireNonNull(fpar);
- this.additionalProps = ImmutableSet.copyOf(additionalProps);
- }
- }
-
- /**
- * Amend the constructor of the given class with implicit functionality (e.g. initialization of instance fields).
- * Will create an implicit constructor declaration iff no constructor is defined in the N4JS source code AND an
- * implicit constructor is actually required.
- */
- def public void amendConstructor(N4ClassDeclaration classDecl, SymbolTableEntry classSTE, SymbolTableEntryOriginal superClassSTE,
- LinkedHashSet fieldsRequiringExplicitDefinition) {
-
- val explicitCtorDecl = classDecl.ownedCtor; // the constructor defined in the N4JS source code or 'null' if none was defined
- val ctorDecl = explicitCtorDecl ?: _N4MethodDecl(N4JSLanguageConstants.CONSTRUCTOR);
-
- // amend formal parameters
- val specInfo = amendFormalParametersOfConstructor(classDecl, ctorDecl, explicitCtorDecl);
-
- // amend body
- val isNonTrivial = amendBodyOfConstructor(classDecl, classSTE, superClassSTE,
- ctorDecl, explicitCtorDecl, specInfo, fieldsRequiringExplicitDefinition);
-
- // add constructor to classDecl (if necessary)
- if (ctorDecl.eContainer === null && isNonTrivial) {
- classDecl.ownedMembersRaw.add(0, ctorDecl);
- }
- }
-
- def private SpecInfo amendFormalParametersOfConstructor(N4ClassDeclaration classDecl, N4MethodDeclaration ctorDecl, N4MethodDeclaration explicitCtorDecl) {
- var specFpar = null as FormalParameter;
- var specFparTypeRef = null as TypeRef;
-
- val hasExplicitCtor = explicitCtorDecl !== null;
- if (hasExplicitCtor) {
- // explicitly defined constructor
- // --> nothing to be changed (use fpars from N4JS source code)
-
- specFpar = ctorDecl.fpars.filter[AnnotationDefinition.SPEC.hasAnnotation(it)].head;
- if (specFpar !== null) {
- val typeRefNode = specFpar.declaredTypeRefNode;
- if (typeRefNode !== null) {
- specFparTypeRef = state.info.getOriginalProcessedTypeRef(typeRefNode);
- }
- }
- } else {
- // implicit constructor
- // --> create fpars using fpars of nearest constructor in hierarchy as template
-
- val templateCtor = getNearestConstructorInHierarchy(classDecl);
- if (templateCtor !== null) {
- for (templateFpar : templateCtor.fpars) {
- val isSpecFpar = AnnotationDefinition.SPEC.hasAnnotation(templateFpar);
- val newFpar = _Fpar(templateFpar.name, templateFpar.variadic, isSpecFpar);
- ctorDecl.fpars += newFpar;
-
- if (isSpecFpar && specFpar === null) {
- specFpar = newFpar;
- specFparTypeRef = TypeUtils.copy(templateFpar.typeRef);
- }
- }
- }
- }
-
- if (specFpar !== null) {
- return new SpecInfo(specFpar, specFparTypeRef?.structuralMembers?.map[name] ?: #[]);
- }
- return null;
- }
-
- /**
- * Returns true
iff the constructor is non-trivial, i.e. non-empty and containing more
- * than just the default super call.
- */
- def private boolean amendBodyOfConstructor(N4ClassDeclaration classDecl, SymbolTableEntry classSTE, SymbolTableEntryOriginal superClassSTE,
- N4MethodDeclaration ctorDecl, N4MethodDeclaration explicitCtorDecl, SpecInfo specInfo,
- LinkedHashSet fieldsRequiringExplicitDefinition) {
-
- val hasExplicitCtor = explicitCtorDecl !== null;
- val body = ctorDecl.body;
-
- val isDirectSubclassOfError = superClassSTE?.originalTarget===state.G.errorType;
- val superCallIndex = if(explicitCtorDecl?.body!==null) explicitCtorDecl.superCallIndex else -1;
- val hasExplicitSuperCall = superCallIndex>=0;
- val explicitSuperCall = if(hasExplicitSuperCall) explicitCtorDecl.body.statements.get(superCallIndex);
- var defaultSuperCall = null as ExpressionStatement;
-
- var idx = if(hasExplicitSuperCall) superCallIndex + 1 else 0;
-
- // add/replace/modify super call
- if(hasExplicitSuperCall) {
- // keep existing, explicit super call unchanged
- } else {
- // no explicitCtorDecl OR no body OR no explicit super call (and no direct subclass of Error)
- // --> add default super call (if required)
- if(superClassSTE!==null) {
- val fparsOfSuperCtor = if (hasExplicitCtor) {
- // explicit ctor without an explicit super call: only allowed if the super constructor
- // does not have any arguments, so we can simply assume empty fpars here, without actually
- // looking up the super constructor with #getNearestConstructorInHierarchy():
- #[]
- } else {
- // no explicit ctor: the already created implicit constructor in 'ctorDecl' has the
- // same fpars as the super constructor, so we can use those as a template:
- ctorDecl.fpars
- };
- defaultSuperCall = createDefaultSuperCall(classDecl, superClassSTE, fparsOfSuperCtor);
- idx = body.statements.insertAt(idx, defaultSuperCall);
- }
- }
- if(isDirectSubclassOfError) {
- // special case: add oddities for sub-classing Error
- idx = body.statements.insertAt(idx,
- createSubclassingErrorOddities(classDecl, ctorDecl.fpars, explicitSuperCall));
- }
-
- // if we are in a spec-constructor: prepare a local variable for the spec-object and
- // ensure it is never 'undefined' or 'null'
- var specObjSTE = null as SymbolTableEntry;
- if (specInfo !== null) {
- // let $specObj = specFpar || {};
- val specFparSTE = findSymbolTableEntryForElement(specInfo.fpar, true);
- val specObjVarDecl = _VariableDeclaration("$specObj", _OR(_IdentRef(specFparSTE), _ObjLit));
- idx = body.statements.insertAt(idx, _VariableStatement(VariableStatementKeyword.CONST, specObjVarDecl));
-
- specObjSTE = findSymbolTableEntryForElement(specObjVarDecl, true);
- }
-
- // add explicit definitions of instance fields (only for fields that actually require this)
- idx = body.statements.insertAt(idx, classifierAssistant.createExplicitFieldDefinitions(classSTE, false, fieldsRequiringExplicitDefinition));
-
- // add initialization code for instance fields
- idx = body.statements.insertAt(idx, createInstanceFieldInitCode(classDecl, specInfo, specObjSTE, fieldsRequiringExplicitDefinition));
-
- // add delegation to field initialization functions of all directly implemented interfaces
- idx = body.statements.insertAt(idx, createDelegationToFieldInitOfImplementedInterfaces(classDecl, specObjSTE));
-
- // check if constructor is non-trivial
- val ctorDeclStmnts = ctorDecl.body.statements;
- val bodyContainsOnlyDefaultSuperCall = defaultSuperCall !== null
- && ctorDeclStmnts.size === 1
- && ctorDeclStmnts.head === defaultSuperCall;
- val isNonTrivialCtor = !ctorDeclStmnts.empty && !bodyContainsOnlyDefaultSuperCall;
-
- return isNonTrivialCtor;
- }
-
- def private Statement[] createInstanceFieldInitCode(N4ClassDeclaration classDecl, SpecInfo specInfo, SymbolTableEntry specObjSTE, Set fieldsWithExplicitDefinition) {
- val allFields = classDecl.ownedFields.filter[
- !isStatic
- && !isConsumedFromInterface
- && !builtInOrProvidedByRuntime(state.info.getOriginalDefinedMember(it))
- ].toList;
- if(specInfo!==null) {
- // we have a spec-parameter -> we are in a spec-style constructor
- val result = newArrayList;
-
- // step #1: initialize all fields either with data from the specFpar OR or their initializer expression
- val currFields = newArrayList;
- var currFieldsAreSpecced = false;
- for(field : allFields) {
- val isSpecced = isPublic(field) || specInfo.additionalProps.contains(field.name);
- if (isSpecced === currFieldsAreSpecced) {
- currFields += field;
- } else {
- result += createFieldInitCodeForSeveralFields(currFields, currFieldsAreSpecced, specObjSTE);
- currFields.clear();
- currFields += field;
- currFieldsAreSpecced = isSpecced;
- }
- }
- result += createFieldInitCodeForSeveralFields(currFields, currFieldsAreSpecced, specObjSTE);
-
- // step #2: invoke setters with data from specFpar
- // (note: in case of undefined specFpar at runtime, setters should not be invoked, so we wrap in if statement)
- val speccedSetters = classDecl.ownedSetters.filter[!isStatic && declaredModifiers.contains(N4Modifier.PUBLIC)].toList;
- result += speccedSetters.map[createFieldInitCodeForSingleSpeccedSetter(specObjSTE)];
-
- return result;
- } else {
- // simple: just initialize fields with data from their initializer expression
- return allFields
- .filter[!(expression===null && fieldsWithExplicitDefinition.contains(it))]
- .map[createFieldInitCodeForSingleField];
- }
- }
-
- def private Statement[] createFieldInitCodeForSeveralFields(Collection fieldDecls, boolean fieldsAreSpecced, SymbolTableEntry specObjSTE) {
- if (fieldDecls.empty) {
- return #[];
- }
- if (!fieldsAreSpecced) {
- return fieldDecls.map[createFieldInitCodeForSingleField];
- }
- if (fieldDecls.size === 1) {
- return #[ fieldDecls.head.createFieldInitCodeForSingleSpeccedField(specObjSTE) ];
- }
- return #[ fieldDecls.createFieldInitCodeForSeveralSpeccedFields(specObjSTE) ];
- }
-
- def private Statement createFieldInitCodeForSingleField(N4FieldDeclaration fieldDecl) {
- // here we create:
- //
- // this.fieldName = ;
- // or
- // this.fieldName = undefined;
- //
- // NOTE: we set the field to 'undefined' even in the second case, because ...
- // 1) it makes a difference when #hasOwnProperty(), etc. is used after the constructor returns,
- // 2) for consistency with the method #createFieldInitCodeForSeveralSpeccedFields(), because with
- // destructuring as used by that method, the property will always be assigned (even if the
- // value is 'undefined' and there is no default).
- //
- val fieldSTE = findSymbolTableEntryForElement(fieldDecl, true);
- return _ExprStmnt(_AssignmentExpr(
- _PropertyAccessExpr(_ThisLiteral, fieldSTE),
- if(fieldDecl.expression!==null) {
- fieldDecl.expression // reusing the expression here
- } else {
- undefinedRef()
- }
- ));
- }
-
- def private Statement createFieldInitCodeForSeveralSpeccedFields(Iterable fieldDecls, SymbolTableEntry specObjSTE) {
- // here we create:
- //
- // ({
- // fieldName: this.fieldName = ,
- // ...
- // } = spec);
- //
- return _ExprStmnt(
- _Parenthesis(
- _AssignmentExpr(
- _ObjLit(
- fieldDecls.map[fieldDecl|
- val fieldSTE = findSymbolTableEntryForElement(fieldDecl, true);
- val thisFieldName = _PropertyAccessExpr(_ThisLiteral, fieldSTE);
- return fieldDecl.name -> if (fieldDecl.hasNonTrivialInitExpression) {
- _AssignmentExpr(thisFieldName, fieldDecl.expression) // reusing the expression here
- } else {
- thisFieldName
- };
- ]
- ),
- _IdentRef(specObjSTE)
- )
- )
- );
- }
-
- def private Statement createFieldInitCodeForSingleSpeccedField(N4FieldDeclaration fieldDecl, SymbolTableEntry specObjSTE) {
- val fieldSTE = findSymbolTableEntryForElement(fieldDecl, true);
- if (fieldDecl.hasNonTrivialInitExpression) {
- // here we create:
- //
- // this.fieldName = spec.fieldName === undefined ? : spec.fieldName;
- //
- // NOTE: don't use something like "'fieldName' in spec" as the condition above, because that would
- // not have the same behavior as destructuring in method #createFieldInitCodeForSeveralSpeccedFields()
- // in case the property is present but has value 'undefined'!
- //
- return _ExprStmnt(_AssignmentExpr(
- _PropertyAccessExpr(_ThisLiteral, fieldSTE),
- // ? :
- _ConditionalExpr(
- // spec.fieldName === undefined
- _EqualityExpr(_PropertyAccessExpr(specObjSTE, fieldSTE), EqualityOperator.SAME, undefinedRef()),
- //
- if(fieldDecl.expression!==null) {
- copy(fieldDecl.expression) // need to copy expression here, because it will be used again!
- } else {
- undefinedRef()
- },
- // spec.fieldName
- _PropertyAccessExpr(specObjSTE, fieldSTE)
- )
- ));
- } else {
- // we have a trivial init-expression ...
-
- // here we create:
- //
- // this.fieldName = spec.fieldName;
- //
- return _ExprStmnt(_AssignmentExpr(
- _PropertyAccessExpr(_ThisLiteral, fieldSTE),
- _PropertyAccessExpr(specObjSTE, fieldSTE)
- ));
- }
- }
-
- def private Statement createFieldInitCodeForSingleSpeccedSetter(N4SetterDeclaration setterDecl, SymbolTableEntry specObjSTE) {
- // here we create:
- //
- // if ('fieldName' in spec) {
- // this.fieldName = spec.fieldName;
- // }
- //
-
- val setterSTE = findSymbolTableEntryForElement(setterDecl, true);
- return _IfStmnt(
- _RelationalExpr(
- _StringLiteralForSTE(setterSTE), RelationalOperator.IN, _IdentRef(specObjSTE)
- ),
- _ExprStmnt(
- _AssignmentExpr(
- _PropertyAccessExpr(_ThisLiteral, setterSTE),
- _PropertyAccessExpr(specObjSTE, setterSTE)
- )));
- }
-
- // NOTE: compare this to the super calls generated from SuperLiterals in SuperLiteralTransformation
- def private ExpressionStatement createDefaultSuperCall(N4ClassDeclaration classDecl, SymbolTableEntry superClassSTE, FormalParameter[] fpars) {
- val variadicCase = !fpars.empty && fpars.last.isVariadic;
- val argsIter = fpars.map[findSymbolTableEntryForElement(it, true)].map[_Argument(_IdentRef(it))];
- val args = new ArrayList(argsIter); // WARNING: .toList won't work!
- if (variadicCase) {
- args.last.spread = true;
- }
- return _ExprStmnt(_CallExpr() => [
- target = _SuperLiteral();
- arguments += args;
- ]);
- }
-
- /** To be inserted where the explicit or implicit super call would be located, normally. */
- def private Statement[] createSubclassingErrorOddities(N4ClassDeclaration classDecl, FormalParameter[] fpars, Statement explicitSuperCall) {
- return #[ _ExprStmnt(_Snippet('''
- this.name = this.constructor.n4type.name;
- if (Error.captureStackTrace) {
- Error.captureStackTrace(this, this.name);
- }
- ''')) ];
- }
-
- def private Statement[] createDelegationToFieldInitOfImplementedInterfaces(N4ClassDeclaration classDecl, SymbolTableEntry /*nullable*/ specObjSTE ) {
-
- val implementedIfcSTEs = typeAssistant.getSuperInterfacesSTEs(classDecl).filter [
- // regarding the cast to TInterface: see preconditions of ClassDeclarationTransformation
- // regarding the entire line: generate $fieldInit call only if the interface is neither built-in nor provided by runtime nor shape
- !builtInOrProvidedByRuntimeOrShape(originalTarget as TInterface);
- ];
- if (implementedIfcSTEs.empty) {
- return #[];
- }
-
-
- val LinkedHashSet ownedInstanceDataFieldsSupressMixin = newLinkedHashSet
- ownedInstanceDataFieldsSupressMixin.addAll(classDecl.ownedGetters.filter[!isConsumedFromInterface].map[name])
- ownedInstanceDataFieldsSupressMixin.addAll(classDecl.ownedSetters.filter[!isConsumedFromInterface].map[name])
-
- val classSTE = findSymbolTableEntryForElement(classDecl, false);
- val $initFieldsFromInterfacesSTE = steFor_$initFieldsFromInterfaces;
-
- return #[ _ExprStmnt(
- _CallExpr(
- _IdentRef($initFieldsFromInterfacesSTE),
- _ThisLiteral,
- _IdentRef(classSTE),
- if (specObjSTE !== null) {
- _IdentRef(specObjSTE)
- } else {
- undefinedRef()
- },
- _ObjLit(
- ownedInstanceDataFieldsSupressMixin.map[
- _PropertyNameValuePair(it, _TRUE)
- ]
- )
- )
- )];
- }
-
- // ################################################################################################################
- // UTILITY STUFF
-
- def private int getSuperCallIndex(N4MethodDeclaration ownedCtor) {
- val stmnts = ownedCtor?.body?.statements;
- if(stmnts!==null && !stmnts.empty) {
- for(i : 0.. int insertAt(List list, int index, T element) {
- list.add(index, element);
- return index + 1;
- }
-
- def private static int insertAt(List list, int index, Collection extends T> elements) {
- list.addAll(index, elements);
- return index + elements.size();
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassifierAssistant.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassifierAssistant.java
new file mode 100644
index 0000000000..9ac9a2eba8
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassifierAssistant.java
@@ -0,0 +1,163 @@
+/**
+ * Copyright (c) 2020 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.assistants;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._AssignmentExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._CallExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ExprStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._StringLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ThisLiteral;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.n4js.AnnotationDefinition;
+import org.eclipse.n4js.n4JS.Expression;
+import org.eclipse.n4js.n4JS.ExpressionStatement;
+import org.eclipse.n4js.n4JS.N4ClassDeclaration;
+import org.eclipse.n4js.n4JS.N4ClassifierDeclaration;
+import org.eclipse.n4js.n4JS.N4FieldDeclaration;
+import org.eclipse.n4js.n4JS.N4MemberDeclaration;
+import org.eclipse.n4js.n4JS.Statement;
+import org.eclipse.n4js.n4JS.StringLiteral;
+import org.eclipse.n4js.transpiler.TransformationAssistant;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+import org.eclipse.n4js.ts.types.TClass;
+import org.eclipse.n4js.ts.types.TField;
+import org.eclipse.xtext.xbase.lib.IterableExtensions;
+import org.eclipse.xtext.xbase.lib.Pair;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * Helper methods for transformations of class and interface declarations.
+ */
+public class ClassifierAssistant extends TransformationAssistant {
+
+ /**
+ * Returns a set/list of data fields that require an explicit property definition. Actual creation of those explicit
+ * property definitions is done with method
+ * {@link #createExplicitFieldDefinitions(SymbolTableEntry, boolean, LinkedHashSet)}.
+ *
+ * Background
+ *
+ * Data fields that override an accessor require an explicit property definition along the lines of
+ *
+ *
+ * Object.defineProperty(this, "myField", {writable: true, ...});
+ *
+ *
+ * A simple initialization of the form this.myField = undefined;
would throw an exception at runtime
+ * (in case of overriding only a getter) or would simply invoke the setter (in case of overriding a setter or an
+ * accessor pair).
+ *
+ * This applies to both instance and static fields.
+ */
+ public LinkedHashSet findFieldsRequiringExplicitDefinition(N4ClassDeclaration classDecl) {
+ TClass tClass = getState().info.getOriginalDefinedType(classDecl);
+ Set> fieldsOverridingAnAccessor = null;
+ if (tClass != null) {
+ fieldsOverridingAnAccessor = IterableExtensions.toSet(IterableExtensions.map(
+ getState().memberCollector.computeOwnedFieldsOverridingAnAccessor(tClass, true),
+ f -> Pair.of(f.isStatic(), f.getName())));
+ }
+
+ LinkedHashSet result = new LinkedHashSet<>();
+ for (N4MemberDeclaration member : classDecl.getOwnedMembers()) {
+ if (member instanceof N4FieldDeclaration
+ && AnnotationDefinition.OVERRIDE.hasAnnotation(member)
+ && (fieldsOverridingAnAccessor == null
+ || fieldsOverridingAnAccessor.contains(Pair.of(member.isStatic(), member.getName())))) {
+ result.add((N4FieldDeclaration) member);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Creates explicit property definitions for the fields identified by method
+ * {@link #findFieldsRequiringExplicitDefinition(N4ClassDeclaration)}.
+ */
+ public List createExplicitFieldDefinitions(SymbolTableEntry steClass, boolean staticCase,
+ LinkedHashSet fieldsRequiringExplicitDefinition) {
+
+ // Creates either
+ // $defineFields(D, "staticFieldName1", "staticFieldName2", ...);
+ // or
+ // $defineFields(this, "instanceFieldName1", "instanceFieldName2", ...);
+ List names = new ArrayList<>();
+ for (N4FieldDeclaration fd : fieldsRequiringExplicitDefinition) {
+ if (fd.isStatic() == staticCase) {
+ names.add(_StringLiteral(fd.getName()));
+ }
+ }
+
+ if (names.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List args = new ArrayList<>();
+ args.add((staticCase) ? __NSSafe_IdentRef(steClass) : _ThisLiteral());
+ args.addAll(names);
+
+ ExpressionStatement result = _ExprStmnt(
+ _CallExpr(
+ _IdentRef(steFor_$defineFields()),
+ Iterables.toArray(args, Expression.class)));
+ return List.of(result);
+ }
+
+ /**
+ * Creates a new list of statements to initialize the static fields of the given classifier declaration.
+ *
+ * Clients of this method may modify the returned list.
+ */
+ public List createStaticFieldInitializations(N4ClassifierDeclaration classifierDecl,
+ SymbolTableEntry classifierSTE,
+ Set fieldsWithExplicitDefinition) {
+
+ List result = new ArrayList<>();
+ for (N4MemberDeclaration member : classifierDecl.getOwnedMembers()) {
+ if (member.isStatic() && member instanceof N4FieldDeclaration
+ && !(((N4FieldDeclaration) member).getExpression() == null
+ && fieldsWithExplicitDefinition.contains(member))) {
+
+ Statement sic = createStaticInitialiserCode((N4FieldDeclaration) member, classifierSTE);
+ result.add(sic);
+ }
+ }
+
+ return result;
+ }
+
+ private Statement createStaticInitialiserCode(N4FieldDeclaration fieldDecl, SymbolTableEntry classSTE) {
+ TField tField = (TField) getState().info.getOriginalDefinedMember(fieldDecl);
+ SymbolTableEntry fieldSTE = (tField != null) ? getSymbolTableEntryOriginal(tField, true)
+ : findSymbolTableEntryForElement(fieldDecl, true);
+
+ ExpressionStatement exprStmnt = _ExprStmnt(
+ _AssignmentExpr(
+ _PropertyAccessExpr(__NSSafe_IdentRef(classSTE), fieldSTE),
+ fieldDecl.getExpression() == null ? undefinedRef() : fieldDecl.getExpression()// reuse existing
+ // expression
+ // (if present)
+ ));
+ getState().tracer.copyTrace(fieldDecl, exprStmnt);
+
+ return exprStmnt;
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassifierAssistant.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassifierAssistant.xtend
deleted file mode 100644
index c5f58728dd..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ClassifierAssistant.xtend
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * Copyright (c) 2020 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.assistants
-
-import java.util.LinkedHashSet
-import java.util.List
-import java.util.Set
-import org.eclipse.n4js.AnnotationDefinition
-import org.eclipse.n4js.n4JS.N4ClassDeclaration
-import org.eclipse.n4js.n4JS.N4ClassifierDeclaration
-import org.eclipse.n4js.n4JS.N4FieldDeclaration
-import org.eclipse.n4js.n4JS.Statement
-import org.eclipse.n4js.transpiler.TransformationAssistant
-import org.eclipse.n4js.transpiler.im.SymbolTableEntry
-import org.eclipse.n4js.ts.types.TField
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-/**
- * Helper methods for transformations of class and interface declarations.
- */
-class ClassifierAssistant extends TransformationAssistant {
-
- /**
- * Returns a set/list of data fields that require an explicit property definition. Actual creation of those explicit
- * property definitions is done with method {@link #createExplicitFieldDefinitions(SymbolTableEntry, boolean, LinkedHashSet)}.
- *
- * Background
- *
- * Data fields that override an accessor require an explicit property definition along the lines of
- *
- * Object.defineProperty(this, "myField", {writable: true, ...});
- *
- * A simple initialization of the form this.myField = undefined;
would throw an exception at runtime (in case of
- * overriding only a getter) or would simply invoke the setter (in case of overriding a setter or an accessor pair).
- *
- * This applies to both instance and static fields.
- */
- def public LinkedHashSet findFieldsRequiringExplicitDefinition(N4ClassDeclaration classDecl) {
- val tClass = state.info.getOriginalDefinedType(classDecl);
- val fieldsOverridingAnAccessor = if (tClass !== null) {
- state.memberCollector.computeOwnedFieldsOverridingAnAccessor(tClass, true)
- .map[static -> name]
- .toSet;
- };
- val result = newLinkedHashSet;
- result += classDecl.ownedMembers
- .filter[AnnotationDefinition.OVERRIDE.hasAnnotation(it)]
- .filter(N4FieldDeclaration)
- .filter[fieldsOverridingAnAccessor === null || fieldsOverridingAnAccessor.contains(static -> name)];
- return result;
- }
-
- /**
- * Creates explicit property definitions for the fields identified by method
- * {@link #findFieldsRequiringExplicitDefinition(N4ClassDeclaration)}.
- */
- def public List createExplicitFieldDefinitions(SymbolTableEntry steClass, boolean staticCase,
- LinkedHashSet fieldsRequiringExplicitDefinition) {
-
- // Creates either
- // $defineFields(D, "staticFieldName1", "staticFieldName2", ...);
- // or
- // $defineFields(this, "instanceFieldName1", "instanceFieldName2", ...);
- val names = fieldsRequiringExplicitDefinition.filter[static === staticCase].map[name].map[_StringLiteral(it)].toList;
- if (names.empty) {
- return #[];
- }
- val result = _ExprStmnt(
- _CallExpr(
- _IdentRef(steFor_$defineFields),
- #[
- if (staticCase) {
- __NSSafe_IdentRef(steClass)
- } else {
- _ThisLiteral
- }
- ] + names
- )
- );
- return #[ result ];
- }
-
- /**
- * Creates a new list of statements to initialize the static fields of the given classifier declaration.
- *
- * Clients of this method may modify the returned list.
- */
- def public List createStaticFieldInitializations(N4ClassifierDeclaration classifierDecl, SymbolTableEntry classifierSTE,
- Set fieldsWithExplicitDefinition) {
-
- return classifierDecl.ownedMembers
- .filter[isStatic]
- .filter(N4FieldDeclaration)
- .filter[!(expression===null && fieldsWithExplicitDefinition.contains(it))]
- .map[createStaticInitialiserCode(classifierSTE)]
- .filterNull
- .toList;
- }
-
- def private Statement createStaticInitialiserCode(N4FieldDeclaration fieldDecl, SymbolTableEntry classSTE) {
- val tField = state.info.getOriginalDefinedMember(fieldDecl) as TField;
- val fieldSTE = if (tField !== null) getSymbolTableEntryOriginal(tField, true) else findSymbolTableEntryForElement(fieldDecl, true);
-
- val exprStmnt = _ExprStmnt(
- _AssignmentExpr(
- _PropertyAccessExpr(__NSSafe_IdentRef(classSTE), fieldSTE),
- fieldDecl.expression ?: undefinedRef // reuse existing expression (if present)
- )
- );
- state.tracer.copyTrace(fieldDecl, exprStmnt);
-
- return exprStmnt;
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DelegationAssistant.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DelegationAssistant.java
new file mode 100644
index 0000000000..bded2e2cf1
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DelegationAssistant.java
@@ -0,0 +1,435 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.assistants;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Block;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._CallExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ExprStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Fpar;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IndexAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._LiteralOrComputedPropertyName;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4GetterDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4MethodDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4SetterDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ReturnStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._StringLiteralForSTE;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ThisLiteral;
+import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.functionType;
+import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.objectType;
+
+import org.eclipse.n4js.n4JS.Block;
+import org.eclipse.n4js.n4JS.Expression;
+import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor;
+import org.eclipse.n4js.n4JS.LiteralOrComputedPropertyName;
+import org.eclipse.n4js.n4JS.N4ClassifierDeclaration;
+import org.eclipse.n4js.n4JS.N4FieldDeclaration;
+import org.eclipse.n4js.n4JS.N4GetterDeclaration;
+import org.eclipse.n4js.n4JS.N4MemberDeclaration;
+import org.eclipse.n4js.n4JS.N4MethodDeclaration;
+import org.eclipse.n4js.n4JS.N4Modifier;
+import org.eclipse.n4js.n4JS.N4SetterDeclaration;
+import org.eclipse.n4js.n4JS.ParameterizedCallExpression;
+import org.eclipse.n4js.transpiler.TransformationAssistant;
+import org.eclipse.n4js.transpiler.assistants.TypeAssistant;
+import org.eclipse.n4js.transpiler.im.DelegatingGetterDeclaration;
+import org.eclipse.n4js.transpiler.im.DelegatingMember;
+import org.eclipse.n4js.transpiler.im.DelegatingMethodDeclaration;
+import org.eclipse.n4js.transpiler.im.DelegatingSetterDeclaration;
+import org.eclipse.n4js.transpiler.im.ImFactory;
+import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal;
+import org.eclipse.n4js.ts.types.ContainerType;
+import org.eclipse.n4js.ts.types.TClass;
+import org.eclipse.n4js.ts.types.TClassifier;
+import org.eclipse.n4js.ts.types.TField;
+import org.eclipse.n4js.ts.types.TGetter;
+import org.eclipse.n4js.ts.types.TInterface;
+import org.eclipse.n4js.ts.types.TMember;
+import org.eclipse.n4js.ts.types.TMethod;
+import org.eclipse.n4js.ts.types.TSetter;
+import org.eclipse.n4js.ts.types.util.SuperInterfacesIterable;
+import org.eclipse.n4js.utils.N4JSLanguageUtils;
+import org.eclipse.n4js.utils.RecursionGuard;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Inject;
+
+/**
+ * This assistant provides helper methods to create members that delegate to some other target member (see
+ * {@link DelegationAssistant#createDelegatingMember(TClassifier, TMember) createDelegatingMember(TClassifier,
+ * TMember)}) and to create the Javascript output code to actually implement these delegating members in the transpiler
+ * output (see {@link DelegationAssistant#createOrdinaryMemberForDelegatingMember(DelegatingMember) }).
+ *
+ * Usually inherited members in a classifier do not require any special code, because they will be accessed via the
+ * native prototype chain mechanism of Javascript. However, there are special cases when some special code has to be
+ * generated for an inherited member in order to properly access that inherited member, because it is not available via
+ * the ordinary prototype chain.
+ */
+public class DelegationAssistant extends TransformationAssistant {
+
+ @Inject
+ private TypeAssistant typeAssistant;
+
+ /**
+ * Creates a new delegating member intended to be inserted into classifier origin
in order to delegate
+ * from origin
to the given member target
. The target member is assumed to be an inherited
+ * or consumed member of classifier origin
, i.e. it is assumed to be located in one of the ancestor
+ * classes of origin
or one of its directly or indirectly implemented interfaces (but not in origin
+ * itself!).
+ *
+ * Throws exceptions in case of invalid arguments or an invalid internal getState(), see implementation for details.
+ */
+ public DelegatingMember createDelegatingMember(TClassifier origin, TMember target) {
+ if (target.getContainingType() == origin) {
+ throw new IllegalArgumentException("no point in delegating to an owned member");
+ }
+ DelegatingMember result = null;
+ if (target instanceof TGetter) {
+ result = ImFactory.eINSTANCE.createDelegatingGetterDeclaration();
+ ((DelegatingGetterDeclaration) result).setDeclaredName(_LiteralOrComputedPropertyName(target.getName()));
+ }
+ if (target instanceof TSetter) {
+ result = ImFactory.eINSTANCE.createDelegatingSetterDeclaration();
+ ((DelegatingSetterDeclaration) result).setDeclaredName(_LiteralOrComputedPropertyName(target.getName()));
+ }
+ if (target instanceof TMethod) {
+ result = ImFactory.eINSTANCE.createDelegatingMethodDeclaration();
+ ((DelegatingMethodDeclaration) result).setDeclaredName(_LiteralOrComputedPropertyName(target.getName()));
+ }
+ if (target instanceof TField || result == null) {
+ throw new IllegalArgumentException("delegation to fields not supported yet");
+ }
+
+ // set simple properties
+ result.setDelegationTarget(getSymbolTableEntryOriginal(target, true));
+ if (target.isStatic()) {
+ result.getDeclaredModifiers().add(N4Modifier.STATIC);
+ }
+ // set delegationBaseType and delegationSuperClassSteps
+ if (origin instanceof TInterface) {
+ // we are in an interface and the target must also be in an interface
+ if (!(target.eContainer() instanceof TInterface)) {
+ throw new IllegalArgumentException("cannot delegate from an interface to member of a class");
+ }
+ ContainerType> tSuper = getDirectSuperTypeBequestingMember(origin, target);
+ // we know the STE of tSuper must already exist, because it is a direct super type and must therefore be
+ // referenced in the declaration of classifier origin
+ result.setDelegationBaseType(getSymbolTableEntryOriginal(tSuper, true));
+ result.setDelegationSuperClassSteps(0);
+ } else if (origin instanceof TClass) {
+ // we are in a class and the target may either be in a class or an interface
+ TClass tAncestor = getAncestorClassBequestingMember((TClass) origin, target);
+ if (tAncestor != origin) {
+ // we are inheriting 'target' from one of our ancestor classes -> delegate to that ancestor class
+ // (note: this includes the case the 'target' is contained in an interface and one of our ancestor
+ // classes implements that interface)
+ TClass tSuper = (TClass) ((TClass) origin).getSuperClassRef().getDeclaredType();
+ result.setDelegationBaseType(getSymbolTableEntryOriginal(tSuper, true));
+ result.setDelegationSuperClassSteps(getDistanceToAncestorClass((TClass) origin, tAncestor) - 1);
+ } else if (tAncestor != null) {
+ // we are consuming 'target' from one of our directly implemented interfaces or its extended interfaces
+ // (similar as case "origin instanceof TInterface" above)
+ ContainerType> tSuper = getDirectSuperTypeBequestingMember(origin, target);
+ result.setDelegationBaseType(getSymbolTableEntryOriginal(tSuper, true));
+ result.setDelegationSuperClassSteps(0);
+ } else {
+ throw new IllegalStateException("cannot find target (probably not an inherited member)");
+ }
+ } else {
+ throw new IllegalArgumentException("unsupported subtype of TClassifier: " + origin.eClass().getName());
+ }
+ // set some properties to let this delegating member behave more like an ordinary member of the same type
+ result.setDelegationTargetIsAbstract(target.isAbstract());
+ if (!target.isAbstract()) {
+ ((FunctionOrFieldAccessor) result).setBody(_Block());
+ }
+ return result;
+ }
+
+ /**
+ * Convenience method for replacing each delegating member in the given declaration by an ordinary member created
+ * with method {@link #createOrdinaryMemberForDelegatingMember(DelegatingMember)}. Will modify the given classifier
+ * declaration.
+ */
+ public void replaceDelegatingMembersByOrdinaryMembers(N4ClassifierDeclaration classifierDecl) {
+ for (N4MemberDeclaration currMember : Lists.newArrayList(classifierDecl.getOwnedMembersRaw())) {
+ if (currMember instanceof DelegatingMember) {
+ N4MemberDeclaration resolvedDelegatingMember = createOrdinaryMemberForDelegatingMember(
+ (DelegatingMember) currMember);
+ replace(currMember, resolvedDelegatingMember);
+ }
+ }
+ }
+
+ /** Creates a {@link N4MemberDeclaration} for the given delegator */
+ public N4MemberDeclaration createOrdinaryMemberForDelegatingMember(DelegatingMember delegator) {
+ String targetNameStr = delegator.getDelegationTarget().getName();
+ LiteralOrComputedPropertyName targetName;
+ if (targetNameStr != null && targetNameStr.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX)) {
+ targetName = _LiteralOrComputedPropertyName(typeAssistant.getMemberNameAsSymbol(targetNameStr),
+ targetNameStr);
+ } else {
+ targetName = _LiteralOrComputedPropertyName(targetNameStr);
+ }
+
+ Block body = createBodyForDelegatingMember(delegator);
+ if (delegator instanceof DelegatingGetterDeclaration) {
+ return _N4GetterDecl(targetName, body);
+ }
+ if (delegator instanceof DelegatingSetterDeclaration) {
+ return _N4SetterDecl(targetName, _Fpar("value"), body);
+ }
+ if (delegator instanceof DelegatingMethodDeclaration) {
+ return _N4MethodDecl(targetName, body);
+ }
+ return null;
+ }
+
+ private Block createBodyForDelegatingMember(DelegatingMember delegator) {
+ SymbolTableEntryOriginal baseSTE = delegator.getDelegationBaseType();
+ boolean baseIsInterface = baseSTE == null ? false : baseSTE.getOriginalTarget() instanceof TInterface;
+
+ Expression targetAccess;
+ if (baseIsInterface) {
+ boolean targetIsStatic = delegator.isStatic();
+ Expression objOfInterfaceOfTargetExpr = createAccessToInterfaceObject(baseSTE, targetIsStatic);
+ targetAccess = createAccessToMemberFunction(objOfInterfaceOfTargetExpr, !targetIsStatic, delegator);
+ } else {
+ Expression ctorOfClassOfTarget = createAccessToClassConstructor(baseSTE,
+ delegator.getDelegationSuperClassSteps());
+
+ // based on that constructor expression, now create an expression that evaluates to the member function of
+ // the target member:
+ targetAccess = createAccessToMemberFunction(ctorOfClassOfTarget, false, delegator);
+ }
+
+ ParameterizedCallExpression callExpr = _CallExpr(
+ _PropertyAccessExpr(
+ targetAccess,
+ getSymbolTableEntryForMember(functionType(getState().G), "apply", false, false, true)),
+ _ThisLiteral(),
+ _IdentRef(steFor_arguments()));
+
+ if (delegator instanceof DelegatingSetterDeclaration) {
+ return _Block(
+ _ExprStmnt(callExpr));
+ } else {
+ return _Block(
+ _ReturnStmnt(callExpr));
+ }
+ }
+
+ /**
+ * Creates an expression that will evaluate to the constructor of the class denoted by the given symbol table entry
+ * or one of its super classes (depending on argument superClassSteps
).
+ *
+ * For example, if classSTE
denotes a class "C", then this will produce an expression like
+ *
+ *
+ * C |
+ * for superClassSteps == 0 |
+ *
+ *
+ * Object.getPrototypeOf(C) |
+ * for superClassSteps == 1 |
+ *
+ *
+ * Object.getPrototypeOf(Object.getPrototypeOf(C)) |
+ * for superClassSteps == 2 |
+ *
+ *
+ */
+ private Expression createAccessToClassConstructor(SymbolTableEntry classSTE, int superClassSteps) {
+ TClassifier objectType = objectType(getState().G);
+ SymbolTableEntryOriginal objectSTE = getSymbolTableEntryOriginal(objectType, true);
+ Expression result = __NSSafe_IdentRef(classSTE); // this is the "C" in the above examples
+ // for each super-class-step wrap 'result' into "Object.getPrototypeOf(result)"
+ if (superClassSteps > 0) {
+ SymbolTableEntryOriginal getPrototypeOfSTE = getSymbolTableEntryForMember(objectType, "getPrototypeOf",
+ false, true, true);
+ for (int n = 0; n < superClassSteps; n++) {
+ result = _CallExpr(
+ _PropertyAccessExpr(objectSTE, getPrototypeOfSTE),
+ result);
+ }
+ }
+ return result;
+ }
+
+ private Expression createAccessToInterfaceObject(SymbolTableEntry ifcSTE, boolean targetIsStatic) {
+ Expression result = __NSSafe_IdentRef(ifcSTE);
+ if (!targetIsStatic) {
+ result = _PropertyAccessExpr(result, steFor_$defaultMembers());
+ }
+ return result;
+ }
+
+ /**
+ * Same as {@link #createAccessToMemberDescriptor(Expression, boolean, N4MemberDeclaration)}, but will add a
+ * property access to the Javascript function representing the member, which is stored in the member definition (by
+ * appending ".get", ".set", or ".value" depending on the type of member).
+ *
+ * Since fields do not have such a function this will throw an exception if given member is a field.
+ */
+ private Expression createAccessToMemberFunction(Expression protoOrCtorExpr, boolean exprIsProto,
+ N4MemberDeclaration member) {
+ if (member instanceof N4FieldDeclaration) {
+ throw new IllegalArgumentException("no member function available for fields");
+ }
+ if (member instanceof N4MethodDeclaration) {
+ // for methods we can use a simple property access instead of Object.getOwnPropertyDescriptor()
+ String memberName = member.getName();
+ boolean memberIsSymbol = memberName != null
+ && memberName.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX);
+ SymbolTableEntry memberSTE = findSymbolTableEntryForElement(member, true);
+ return (!memberIsSymbol) ? _PropertyAccessExpr(protoOrCtorExpr, memberSTE)
+ : _IndexAccessExpr(protoOrCtorExpr, typeAssistant.getMemberNameAsSymbol(memberName));
+ }
+ // for other members (i.e. getters and setters) we need to retrieve the property descriptor:
+ Expression accessToMemberDefinition = createAccessToMemberDescriptor(protoOrCtorExpr, exprIsProto, member);
+ // append ".get", ".set", or ".value" depending on member type
+ ParameterizedPropertyAccessExpression_IM result = _PropertyAccessExpr(accessToMemberDefinition,
+ getPropertyDescriptorValueProperty(member));
+ return result;
+ }
+
+ /**
+ * Given an expression that will evaluate to a prototype or constructor, this method returns an expression that will
+ * evaluate to the property descriptor of a particular member.
+ *
+ * @param protoOrCtorExpr
+ * an expression that is expected to evaluate to a prototype or a constructor.
+ * @param exprIsProto
+ * tells whether argument protoOrCtorExpr
will evaluate to a prototype or a constructor: if
+ * true, it will evaluate to a prototype, if false it will evaluate to a constructor.
+ * @param member
+ * the member.
+ */
+ private Expression createAccessToMemberDescriptor(Expression protoOrCtorExpr, boolean exprIsProto,
+ N4MemberDeclaration member) {
+ String memberName = member.getName();
+ boolean memberIsSymbol = memberName != null
+ && memberName.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX);
+ SymbolTableEntry memberSTE = findSymbolTableEntryForElement(member, true);
+ TClassifier objectType = objectType(getState().G);
+ SymbolTableEntryOriginal objectSTE = getSymbolTableEntryOriginal(objectType, true);
+ SymbolTableEntryOriginal getOwnPropertyDescriptorSTE = getSymbolTableEntryForMember(objectType,
+ "getOwnPropertyDescriptor", false, true, true);
+ // compute first argument to the #getOwnPropertyDescriptor() call:
+ boolean isStatic = member.isStatic();
+ var arg0 = protoOrCtorExpr;
+ if (!exprIsProto && !isStatic) {
+ // got a constructor, but need a prototype -> append ".prototype"
+ SymbolTableEntryOriginal prototypeSTE = getSymbolTableEntryForMember(objectType, "prototype", false, true,
+ true);
+ arg0 = _PropertyAccessExpr(arg0, prototypeSTE);
+ } else if (exprIsProto && isStatic) {
+ // got a prototype, but need a constructor -> append ".constructor"
+ SymbolTableEntryOriginal constructorSTE = getSymbolTableEntryForMember(objectType, "constructor", false,
+ false, true);
+ arg0 = _PropertyAccessExpr(arg0, constructorSTE);
+ }
+
+ // compute second argument to the #getOwnPropertyDescriptor() call:
+ Expression arg1 = (!memberIsSymbol) ? _StringLiteralForSTE(memberSTE)
+ : typeAssistant.getMemberNameAsSymbol(memberName);
+ // create #getOwnPropertyDescriptor() call
+ ParameterizedCallExpression result = _CallExpr(_PropertyAccessExpr(objectSTE, getOwnPropertyDescriptorSTE),
+ arg0, arg1);
+ return result;
+ }
+
+ /**
+ * Returns the direct super type (i.e. immediate super class or directly implemented interface) of the given
+ * classifier through which the given classifier inherits the given inherited member. Fails fast in case of
+ * inconsistencies.
+ */
+ private ContainerType> getDirectSuperTypeBequestingMember(TClassifier classifier, TMember inheritedMember) {
+ return getState().memberCollector.directSuperTypeBequestingMember(classifier, inheritedMember);
+ }
+
+ /**
+ * Returns the ancestor class (i.e. direct or indirect super class) of the given classifier that either contains the
+ * given inherited member (if given member is contained in a class) or consumes the given member (if given member is
+ * contained in an interface).
+ *
+ * For example:
+ *
+ *
+ * interface I {
+ * m(){}
+ * }
+ * class A implements I {
+ * }
+ * class B extends A {
+ * }
+ * class C extends B {
+ * }
+ *
+ *
+ * With the above type declarations, for arguments C
and m
this method would return class
+ * A
.
+ */
+ private TClass getAncestorClassBequestingMember(TClass classifier, TMember inheritedOrConsumedMember) {
+ ContainerType> containingType = inheritedOrConsumedMember.getContainingType();
+ if (containingType == classifier) {
+ return classifier;
+ } else if (containingType instanceof TInterface) {
+ return SuperInterfacesIterable.of(classifier).findClassImplementingInterface((TInterface) containingType);
+ } else if (containingType instanceof TClass) {
+ return (TClass) containingType;// note: we do not check if containingType is actually an ancestor of
+ // 'classifier'
+ } else {
+ throw new IllegalArgumentException(
+ "unsupported subtype of TClassifier: " + containingType.eClass().getName());
+ }
+ }
+
+ /**
+ * Returns distance to given ancestor or 0 if second argument is 'base' or not an ancestor or null
.
+ */
+ private static int getDistanceToAncestorClass(TClass base, TClass ancestorClass) {
+ if (ancestorClass == null || ancestorClass == base) {
+ return 0;
+ }
+ RecursionGuard guard = new RecursionGuard<>();
+ int result = 0;
+ TClass curr = base;
+ while (curr != null && curr != ancestorClass) {
+ if (guard.tryNext(curr)) {
+ result++;
+ curr = curr.getSuperClassRef() == null ? null : (TClass) curr.getSuperClassRef().getDeclaredType();
+ // no need to call guard.done()
+ }
+ }
+ return (curr != null) ? result : 0;
+ }
+
+ /**
+ * Returns symbol table entry for the property in a Javascript property descriptor that holds the actual value, i.e.
+ * the function expression implementing the member.
+ */
+ private SymbolTableEntry getPropertyDescriptorValueProperty(N4MemberDeclaration delegator) {
+ if (delegator instanceof N4GetterDeclaration) {
+ return steFor_get();
+ }
+ if (delegator instanceof N4SetterDeclaration) {
+ return steFor_set();
+ }
+ if (delegator instanceof N4MethodDeclaration) {
+ return steFor_value();
+ }
+ return null;
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DelegationAssistant.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DelegationAssistant.xtend
deleted file mode 100644
index 0db5dc519a..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DelegationAssistant.xtend
+++ /dev/null
@@ -1,376 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.assistants
-
-import com.google.common.collect.Lists
-import com.google.inject.Inject
-import org.eclipse.n4js.n4JS.Block
-import org.eclipse.n4js.n4JS.Expression
-import org.eclipse.n4js.n4JS.N4ClassifierDeclaration
-import org.eclipse.n4js.n4JS.N4FieldDeclaration
-import org.eclipse.n4js.n4JS.N4GetterDeclaration
-import org.eclipse.n4js.n4JS.N4MemberDeclaration
-import org.eclipse.n4js.n4JS.N4MethodDeclaration
-import org.eclipse.n4js.n4JS.N4Modifier
-import org.eclipse.n4js.n4JS.N4SetterDeclaration
-import org.eclipse.n4js.transpiler.TransformationAssistant
-import org.eclipse.n4js.transpiler.assistants.TypeAssistant
-import org.eclipse.n4js.transpiler.im.DelegatingGetterDeclaration
-import org.eclipse.n4js.transpiler.im.DelegatingMember
-import org.eclipse.n4js.transpiler.im.DelegatingMethodDeclaration
-import org.eclipse.n4js.transpiler.im.DelegatingSetterDeclaration
-import org.eclipse.n4js.transpiler.im.ImFactory
-import org.eclipse.n4js.transpiler.im.SymbolTableEntry
-import org.eclipse.n4js.ts.types.ContainerType
-import org.eclipse.n4js.ts.types.TClass
-import org.eclipse.n4js.ts.types.TClassifier
-import org.eclipse.n4js.ts.types.TField
-import org.eclipse.n4js.ts.types.TGetter
-import org.eclipse.n4js.ts.types.TInterface
-import org.eclipse.n4js.ts.types.TMember
-import org.eclipse.n4js.ts.types.TMethod
-import org.eclipse.n4js.ts.types.TSetter
-import org.eclipse.n4js.ts.types.util.SuperInterfacesIterable
-import org.eclipse.n4js.utils.N4JSLanguageUtils
-import org.eclipse.n4js.utils.RecursionGuard
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.*
-
-/**
- * This assistant provides helper methods to create members that delegate to some other target member (see
- * {@link DelegationAssistant#createDelegatingMember(TClassifier, TMember) createDelegatingMember(TClassifier, TMember)})
- * and to create the Javascript output code to actually implement these delegating members in the transpiler output
- * (see {@link DelegationAssistant#createDelegation(DelegatingMember) createDelegation(DelegatingMember)}).
- *
- * Usually inherited members in a classifier do not require any special code, because they will be accessed via the
- * native prototype chain mechanism of Javascript. However, there are special cases when some special code has to be
- * generated for an inherited member in order to properly access that inherited member, because it is not available
- * via the ordinary prototype chain.
- */
-class DelegationAssistant extends TransformationAssistant {
-
- @Inject private TypeAssistant typeAssistant;
-
-
- /**
- * Creates a new delegating member intended to be inserted into classifier origin
in order to delegate
- * from origin
to the given member target
. The target member is assumed to be an inherited
- * or consumed member of classifier origin
, i.e. it is assumed to be located in one of the ancestor
- * classes of origin
or one of its directly or indirectly implemented interfaces (but not in origin
- * itself!).
- *
- * Throws exceptions in case of invalid arguments or an invalid internal state, see implementation for details.
- */
- def public DelegatingMember createDelegatingMember(TClassifier origin, TMember target) {
- if(target.containingType===origin) {
- throw new IllegalArgumentException("no point in delegating to an owned member");
- }
- val result = switch(target) {
- TField: throw new IllegalArgumentException("delegation to fields not supported yet")
- TGetter: ImFactory.eINSTANCE.createDelegatingGetterDeclaration
- TSetter: ImFactory.eINSTANCE.createDelegatingSetterDeclaration
- TMethod: ImFactory.eINSTANCE.createDelegatingMethodDeclaration
- };
- // set simple properties
- result.declaredName = _LiteralOrComputedPropertyName(target.name);
- result.delegationTarget = getSymbolTableEntryOriginal(target, true);
- if(target.static) {
- result.declaredModifiers += N4Modifier.STATIC;
- }
- // set delegationBaseType and delegationSuperClassSteps
- if(origin instanceof TInterface) {
- // we are in an interface and the target must also be in an interface
- if(!(target.eContainer instanceof TInterface)) {
- throw new IllegalArgumentException("cannot delegate from an interface to member of a class");
- }
- val tSuper = getDirectSuperTypeBequestingMember(origin, target);
- // we know the STE of tSuper must already exist, because it is a direct super type and must therefore be
- // referenced in the declaration of classifier origin
- result.delegationBaseType = getSymbolTableEntryOriginal(tSuper, true);
- result.delegationSuperClassSteps = 0;
- } else if(origin instanceof TClass) {
- // we are in a class and the target may either be in a class or an interface
- val tAncestor = getAncestorClassBequestingMember(origin, target);
- if(tAncestor!==origin) {
- // we are inheriting 'target' from one of our ancestor classes -> delegate to that ancestor class
- // (note: this includes the case the 'target' is contained in an interface and one of our ancestor
- // classes implements that interface)
- val tSuper = origin.superClassRef.declaredType as TClass;
- result.delegationBaseType = getSymbolTableEntryOriginal(tSuper, true);
- result.delegationSuperClassSteps = origin.getDistanceToAncestorClass(tAncestor) - 1;
- } else if(tAncestor!==null) {
- // we are consuming 'target' from one of our directly implemented interfaces or its extended interfaces
- // (similar as case "origin instanceof TInterface" above)
- val tSuper = getDirectSuperTypeBequestingMember(origin, target);
- result.delegationBaseType = getSymbolTableEntryOriginal(tSuper, true);
- result.delegationSuperClassSteps = 0;
- } else {
- throw new IllegalStateException("cannot find target (probably not an inherited member)");
- }
- } else {
- throw new IllegalArgumentException("unsupported subtype of TClassifier: " + origin.eClass.name);
- }
- // set some properties to let this delegating member behave more like an ordinary member of the same type
- result.delegationTargetIsAbstract = target.isAbstract;
- if( ! target.isAbstract ) result.body = _Block();
- return result;
- }
-
- /**
- * Convenience method for replacing each delegating member in the given declaration by an ordinary member
- * created with method {@link #createOrdinaryMemberForDelegatingMember(DelegatingMember)}. Will modify the
- * given classifier declaration.
- */
- def public void replaceDelegatingMembersByOrdinaryMembers(N4ClassifierDeclaration classifierDecl) {
- for (currMember : Lists.newArrayList(classifierDecl.ownedMembersRaw)) {
- if (currMember instanceof DelegatingMember) {
- val resolvedDelegatingMember = createOrdinaryMemberForDelegatingMember(currMember);
- replace(currMember, resolvedDelegatingMember);
- }
- }
- }
-
- def public N4MemberDeclaration createOrdinaryMemberForDelegatingMember(DelegatingMember delegator) {
- val targetNameStr = delegator.delegationTarget.name;
- val targetName = if (targetNameStr!==null && targetNameStr.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX)) {
- _LiteralOrComputedPropertyName(typeAssistant.getMemberNameAsSymbol(targetNameStr), targetNameStr)
- } else {
- _LiteralOrComputedPropertyName(targetNameStr)
- };
-
- val body = createBodyForDelegatingMember(delegator);
-
- return switch(delegator) {
- DelegatingGetterDeclaration:
- _N4GetterDecl(targetName, body)
- DelegatingSetterDeclaration:
- _N4SetterDecl(targetName, _Fpar("value"), body)
- DelegatingMethodDeclaration:
- _N4MethodDecl(targetName, body)
- };
- }
-
- def private Block createBodyForDelegatingMember(DelegatingMember delegator) {
- val baseSTE = delegator.delegationBaseType;
- val baseIsInterface = baseSTE?.originalTarget instanceof TInterface;
-
- val targetAccess = if(baseIsInterface) {
- val targetIsStatic = delegator.static;
- val objOfInterfaceOfTargetExpr = createAccessToInterfaceObject(baseSTE, targetIsStatic);
- createAccessToMemberFunction(objOfInterfaceOfTargetExpr, !targetIsStatic, delegator);
- } else {
- val ctorOfClassOfTarget = createAccessToClassConstructor(baseSTE, delegator.delegationSuperClassSteps);
-
- // based on that constructor expression, now create an expression that evaluates to the member function of
- // the target member:
- createAccessToMemberFunction(ctorOfClassOfTarget, false, delegator)
- };
-
- val callExpr = _CallExpr(
- _PropertyAccessExpr(
- targetAccess,
- getSymbolTableEntryForMember(state.G.functionType, "apply", false, false, true)
- ),
- _ThisLiteral,
- _IdentRef(steFor_arguments)
- );
-
- if (delegator instanceof DelegatingSetterDeclaration) {
- return _Block(
- _ExprStmnt(callExpr)
- );
- } else {
- return _Block(
- _ReturnStmnt(callExpr)
- );
- }
- }
-
- /**
- * Creates an expression that will evaluate to the constructor of the class denoted by the given symbol table
- * entry or one of its super classes (depending on argument superClassSteps
).
- *
- * For example, if classSTE
denotes a class "C", then this will produce an expression like
- *
- * C | for superClassSteps == 0 |
- * Object.getPrototypeOf(C) | for superClassSteps == 1 |
- * Object.getPrototypeOf(Object.getPrototypeOf(C)) | for superClassSteps == 2 |
- *
- */
- def private Expression createAccessToClassConstructor(SymbolTableEntry classSTE, int superClassSteps) {
- val objectType = state.G.objectType;
- val objectSTE = getSymbolTableEntryOriginal(objectType, true);
- var Expression result = __NSSafe_IdentRef(classSTE); // this is the "C" in the above examples
- // for each super-class-step wrap 'result' into "Object.getPrototypeOf(result)"
- if(superClassSteps>0) {
- val getPrototypeOfSTE = getSymbolTableEntryForMember(objectType, "getPrototypeOf", false, true, true);
- for(n : 0..
- * Since fields do not have such a function this will throw an exception if given member is a field.
- */
- def private Expression createAccessToMemberFunction(Expression protoOrCtorExpr, boolean exprIsProto, N4MemberDeclaration member) {
- if(member instanceof N4FieldDeclaration) {
- throw new IllegalArgumentException("no member function available for fields");
- }
- if(member instanceof N4MethodDeclaration) {
- // for methods we can use a simple property access instead of Object.getOwnPropertyDescriptor()
- val memberName = member.name;
- val memberIsSymbol = memberName!==null && memberName.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX);
- val memberSTE = findSymbolTableEntryForElement(member, true);
- return if(!memberIsSymbol) {
- _PropertyAccessExpr(protoOrCtorExpr, memberSTE)
- } else {
- _IndexAccessExpr(protoOrCtorExpr, typeAssistant.getMemberNameAsSymbol(memberName))
- };
- }
- // for other members (i.e. getters and setters) we need to retrieve the property descriptor:
- val accessToMemberDefinition = createAccessToMemberDescriptor(protoOrCtorExpr, exprIsProto, member);
- // append ".get", ".set", or ".value" depending on member type
- val result = _PropertyAccessExpr(accessToMemberDefinition, getPropertyDescriptorValueProperty(member));
- return result;
- }
-
- /**
- * Given an expression that will evaluate to a prototype or constructor, this method returns an expression that
- * will evaluate to the property descriptor of a particular member.
- *
- * @param protoOrCtorExpr
- * an expression that is expected to evaluate to a prototype or a constructor.
- * @param exprIsProto
- * tells whether argument protoOrCtorExpr
will evaluate to a prototype or a constructor:
- * if true, it will evaluate to a prototype, if false it will evaluate to a constructor.
- * @param member
- * the member.
- */
- def private Expression createAccessToMemberDescriptor(Expression protoOrCtorExpr, boolean exprIsProto, N4MemberDeclaration member) {
- val memberName = member.name;
- val memberIsSymbol = memberName!==null && memberName.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX);
- val memberSTE = findSymbolTableEntryForElement(member, true);
- val objectType = state.G.objectType;
- val objectSTE = getSymbolTableEntryOriginal(objectType, true);
- val getOwnPropertyDescriptorSTE = getSymbolTableEntryForMember(objectType, "getOwnPropertyDescriptor", false, true, true);
- // compute first argument to the #getOwnPropertyDescriptor() call:
- val isStatic = member.static;
- var arg0 = protoOrCtorExpr;
- if(!exprIsProto && !isStatic) {
- // got a constructor, but need a prototype -> append ".prototype"
- val prototypeSTE = getSymbolTableEntryForMember(objectType, "prototype", false, true, true);
- arg0 = _PropertyAccessExpr(arg0, prototypeSTE);
- } else if(exprIsProto && isStatic) {
- // got a prototype, but need a constructor -> append ".constructor"
- val constructorSTE = getSymbolTableEntryForMember(objectType, "constructor", false, false, true);
- arg0 = _PropertyAccessExpr(arg0, constructorSTE);
- };
- // compute second argument to the #getOwnPropertyDescriptor() call:
- val arg1 = if(!memberIsSymbol) {
- _StringLiteralForSTE(memberSTE)
- } else {
- typeAssistant.getMemberNameAsSymbol(memberName)
- };
- // create #getOwnPropertyDescriptor() call
- val result = _CallExpr(_PropertyAccessExpr(objectSTE, getOwnPropertyDescriptorSTE), arg0, arg1);
- return result;
- }
-
- /**
- * Returns the direct super type (i.e. immediate super class or directly implemented interface) of the given
- * classifier through which the given classifier inherits the given inherited member. Fails fast in case of
- * inconsistencies.
- */
- def private ContainerType> getDirectSuperTypeBequestingMember(TClassifier classifier, TMember inheritedMember) {
- return state.memberCollector.directSuperTypeBequestingMember(classifier, inheritedMember);
- }
-
- /**
- * Returns the ancestor class (i.e. direct or indirect super class) of the given classifier that either contains
- * the given inherited member (if given member is contained in a class) or consumes the given member (if given
- * member is contained in an interface).
- *
- * For example:
- *
- * interface I {
- * m() {}
- * }
- * class A implements I {}
- * class B extends A {}
- * class C extends B {}
- *
- * With the above type declarations, for arguments C
and m
this method would return class
- * A
.
- */
- def private TClass getAncestorClassBequestingMember(TClass classifier, TMember inheritedOrConsumedMember) {
- val containingType = inheritedOrConsumedMember.containingType;
- if(containingType===classifier) {
- return classifier
- } else if(containingType instanceof TInterface) {
- SuperInterfacesIterable.of(classifier).findClassImplementingInterface(containingType)
- } else if(containingType instanceof TClass) {
- containingType // note: we do not check if containingType is actually an ancestor of 'classifier'
- } else {
- throw new IllegalArgumentException("unsupported subtype of TClassifier: " + containingType.eClass.name);
- }
- }
-
- /**
- * Returns distance to given ancestor or 0 if second argument is 'base' or not an ancestor or null
.
- */
- def private static int getDistanceToAncestorClass(TClass base, TClass ancestorClass) {
- if(ancestorClass===null || ancestorClass===base) {
- return 0;
- }
- val guard = new RecursionGuard();
- var result = 0;
- var curr = base;
- while(curr!==null && curr!==ancestorClass) {
- if(guard.tryNext(curr)) {
- result++;
- curr = curr.superClassRef?.declaredType as TClass;
- // no need to call guard.done()
- }
- }
- return if(curr!==null) result else 0;
- }
-
- /**
- * Returns symbol table entry for the property in a Javascript property descriptor that holds the actual value,
- * i.e. the function expression implementing the member.
- */
- def private SymbolTableEntry getPropertyDescriptorValueProperty(N4MemberDeclaration delegator) {
- switch(delegator) {
- N4GetterDeclaration: steFor_get
- N4SetterDeclaration: steFor_set
- N4MethodDeclaration: steFor_value
- }
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DestructuringAssistant.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DestructuringAssistant.java
new file mode 100644
index 0000000000..ebc5b56d9b
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DestructuringAssistant.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2017 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.assistants;
+
+import static com.google.common.collect.Iterables.toArray;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ArrLit;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ArrayElement;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ArrayPadding;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._AssignmentExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ObjLit;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyNameValuePair;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.map;
+
+import org.eclipse.n4js.n4JS.ArrayBindingPattern;
+import org.eclipse.n4js.n4JS.ArrayElement;
+import org.eclipse.n4js.n4JS.ArrayLiteral;
+import org.eclipse.n4js.n4JS.BindingElement;
+import org.eclipse.n4js.n4JS.BindingPattern;
+import org.eclipse.n4js.n4JS.BindingProperty;
+import org.eclipse.n4js.n4JS.Expression;
+import org.eclipse.n4js.n4JS.ObjectBindingPattern;
+import org.eclipse.n4js.n4JS.ObjectLiteral;
+import org.eclipse.n4js.n4JS.PrimaryExpression;
+import org.eclipse.n4js.n4JS.PropertyNameValuePair;
+import org.eclipse.n4js.n4JS.VariableDeclaration;
+import org.eclipse.n4js.n4JS.VariableDeclarationOrBinding;
+import org.eclipse.n4js.transpiler.TransformationAssistant;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+
+/**
+ * A {@link TransformationAssistant} providing helper functionality for dealing with ES2015 destructuring.
+ */
+public class DestructuringAssistant extends TransformationAssistant {
+
+ /**
+ * Converts the given array or object binding pattern into an array or object literal that, if used on the
+ * right-hand side of an assignment expression, performs the equivalent destructuring operation.
+ *
+ * Expression for default values are removed from the given binding, so the given binding is incomplete after this
+ * method returns. It is only guaranteed that (1) the given binding is not removed from its contained and (2) it
+ * will still include the same variable declarations as before, which can be retrieved via
+ * {@link VariableDeclarationOrBinding#getAllVariableDeclarations()} on the containing variable binding.
+ */
+ public PrimaryExpression convertBindingPatternToArrayOrObjectLiteral(BindingPattern binding) {
+ if (binding instanceof ArrayBindingPattern) {
+ return convertArrayBindingPatternToArrayLiteral((ArrayBindingPattern) binding);
+ }
+ if (binding instanceof ObjectBindingPattern) {
+ return convertObjectBindingPatternToObjectLiteral((ObjectBindingPattern) binding);
+ }
+ return null;
+ }
+
+ /**
+ * Same as {@link #convertBindingPatternToArrayOrObjectLiteral(BindingPattern)}, but only for array binding
+ * patterns.
+ */
+ public ArrayLiteral convertArrayBindingPatternToArrayLiteral(ArrayBindingPattern binding) {
+ ArrayElement[] elems = toArray(map(binding.getElements(), elem -> convertBindingElementToArrayElement(elem)),
+ ArrayElement.class);
+
+ return _ArrLit(elems);
+ }
+
+ /**
+ * Same as {@link #convertBindingPatternToArrayOrObjectLiteral(BindingPattern)}, but only for object binding
+ * patterns.
+ */
+ public ObjectLiteral convertObjectBindingPatternToObjectLiteral(ObjectBindingPattern binding) {
+ PropertyNameValuePair[] pairs = toArray(
+ map(binding.getProperties(), prop -> convertBindingPropertyToPropertyNameValuePair(prop)),
+ PropertyNameValuePair.class);
+ return _ObjLit(pairs);
+ }
+
+ private ArrayElement convertBindingElementToArrayElement(BindingElement element) {
+ BindingPattern nestedPattern = element.getNestedPattern();
+ VariableDeclaration varDecl = element.getVarDecl();
+
+ Expression lhs;
+ Expression rhs;
+ if (nestedPattern != null) {
+ lhs = convertBindingPatternToArrayOrObjectLiteral(nestedPattern);
+ rhs = element.getExpression(); // may be null (which is ok, see below)
+ } else if (varDecl != null) {
+ SymbolTableEntry ste_varDecl = findSymbolTableEntryForElement(varDecl, true);
+ lhs = _IdentRef(ste_varDecl);
+ rhs = varDecl.getExpression(); // may be null (which is ok, see below)
+ } else {
+ return _ArrayPadding();
+ }
+
+ return _ArrayElement(
+ element.isRest(),
+ (rhs != null) ? _AssignmentExpr(lhs, rhs) : lhs);
+ }
+
+ private PropertyNameValuePair convertBindingPropertyToPropertyNameValuePair(BindingProperty property) {
+ return _PropertyNameValuePair(
+ property.getName(),
+ convertBindingElementToArrayElement(property.getValue()).getExpression());
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DestructuringAssistant.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DestructuringAssistant.xtend
deleted file mode 100644
index c0c59d0265..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/DestructuringAssistant.xtend
+++ /dev/null
@@ -1,104 +0,0 @@
-/**
- * Copyright (c) 2017 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.assistants
-
-import org.eclipse.n4js.n4JS.ArrayBindingPattern
-import org.eclipse.n4js.n4JS.ArrayElement
-import org.eclipse.n4js.n4JS.ArrayLiteral
-import org.eclipse.n4js.n4JS.BindingElement
-import org.eclipse.n4js.n4JS.BindingPattern
-import org.eclipse.n4js.n4JS.BindingProperty
-import org.eclipse.n4js.n4JS.Expression
-import org.eclipse.n4js.n4JS.ObjectBindingPattern
-import org.eclipse.n4js.n4JS.ObjectLiteral
-import org.eclipse.n4js.n4JS.PrimaryExpression
-import org.eclipse.n4js.n4JS.PropertyNameValuePair
-import org.eclipse.n4js.n4JS.VariableDeclarationOrBinding
-import org.eclipse.n4js.transpiler.TransformationAssistant
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-/**
- * A {@link TransformationAssistant} providing helper functionality for dealing with ES2015 destructuring.
- */
-class DestructuringAssistant extends TransformationAssistant {
-
-
- /**
- * Converts the given array or object binding pattern into an array or object literal that, if used on the
- * right-hand side of an assignment expression, performs the equivalent destructuring operation.
- *
- * Expression for default values are removed from the given binding, so the given binding is incomplete after this
- * method returns. It is only guaranteed that (1) the given binding is not removed from its contained and (2) it
- * will still include the same variable declarations as before, which can be retrieved via
- * {@link VariableDeclarationOrBinding#getVariableDeclarations()} on the containing variable binding.
- */
- public def PrimaryExpression convertBindingPatternToArrayOrObjectLiteral(BindingPattern binding) {
- return switch(binding) {
- ArrayBindingPattern: convertArrayBindingPatternToArrayLiteral(binding)
- ObjectBindingPattern: convertObjectBindingPatternToObjectLiteral(binding)
- };
- }
-
- /**
- * Same as {@link #convertBindingPatternToArrayOrObjectLiteral(BindingPattern)}, but only for array binding
- * patterns.
- */
- public def ArrayLiteral convertArrayBindingPatternToArrayLiteral(ArrayBindingPattern binding) {
- return _ArrLit(
- binding.elements.map[convertBindingElementToArrayElement]
- );
- }
-
- /**
- * Same as {@link #convertBindingPatternToArrayOrObjectLiteral(BindingPattern)}, but only for object binding
- * patterns.
- */
- public def ObjectLiteral convertObjectBindingPatternToObjectLiteral(ObjectBindingPattern binding) {
- return _ObjLit(
- binding.properties.map[convertBindingPropertyToPropertyNameValuePair]
- );
- }
-
- private def ArrayElement convertBindingElementToArrayElement(BindingElement element) {
- val nestedPattern = element.nestedPattern;
- val varDecl = element.varDecl;
-
- var Expression lhs;
- var Expression rhs;
- if(nestedPattern!==null) {
- lhs = convertBindingPatternToArrayOrObjectLiteral(nestedPattern);
- rhs = element.expression; // may be null (which is ok, see below)
- } else if(varDecl!==null) {
- val ste_varDecl = findSymbolTableEntryForElement(varDecl, true);
- lhs = _IdentRef(ste_varDecl);
- rhs = varDecl.expression; // may be null (which is ok, see below)
- } else {
- return _ArrayPadding();
- }
-
- return _ArrayElement(
- element.rest,
- if(rhs!==null) {
- _AssignmentExpr(lhs, rhs)
- } else {
- lhs
- }
- );
- }
-
- private def PropertyNameValuePair convertBindingPropertyToPropertyNameValuePair(BindingProperty property) {
- return _PropertyNameValuePair(
- property.name,
- convertBindingElementToArrayElement(property.value).expression
- );
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ReflectionAssistant.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ReflectionAssistant.java
new file mode 100644
index 0000000000..4e6aae441d
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ReflectionAssistant.java
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.assistants;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Block;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._CallExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._LiteralOrComputedPropertyName;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4GetterDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ReturnStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._StringLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ThisLiteral;
+
+import org.eclipse.n4js.N4JSLanguageConstants;
+import org.eclipse.n4js.n4JS.N4ClassDeclaration;
+import org.eclipse.n4js.n4JS.N4ClassifierDeclaration;
+import org.eclipse.n4js.n4JS.N4EnumDeclaration;
+import org.eclipse.n4js.n4JS.N4GetterDeclaration;
+import org.eclipse.n4js.n4JS.N4InterfaceDeclaration;
+import org.eclipse.n4js.n4JS.N4Modifier;
+import org.eclipse.n4js.n4JS.N4TypeDeclaration;
+import org.eclipse.n4js.transpiler.InformationRegistry;
+import org.eclipse.n4js.transpiler.TransformationAssistant;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryInternal;
+import org.eclipse.n4js.ts.types.Type;
+import org.eclipse.n4js.utils.ResourceNameComputer;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.inject.Inject;
+
+/**
+ * Helper methods for creating output code related to reflection for classes, interfaces, and enums in N4JS.
+ */
+public class ReflectionAssistant extends TransformationAssistant {
+ @Inject
+ private ResourceNameComputer resourceNameComputer;
+
+ /**
+ * Convenience method. Same as {@link #createN4TypeGetter(N4TypeDeclaration)}, but if creation of the 'n4type'
+ * reflection getter is successful, the getter will be added to the given classifier; otherwise, this method does
+ * nothing.
+ */
+ public void addN4TypeGetter(N4TypeDeclaration typeDecl, N4ClassifierDeclaration addHere) {
+ N4GetterDeclaration getter = createN4TypeGetter(typeDecl);
+ if (getter != null) {
+ addHere.getOwnedMembersRaw().add(getter);
+ }
+ }
+
+ /**
+ * Returns the 'n4type' getter for obtaining reflection information of the given class, interface, or enum
+ * declaration or null
if it cannot be created.
+ *
+ * NOTE: Reflection is only supported for declarations that were given in the original source code, i.e. when method
+ * {@link InformationRegistry#getOriginalDefinedType(N4ClassifierDeclaration)} returns a non-null value. Otherwise,
+ * this method will return null
as well.
+ *
+ * @return the 'n4type' getter for the given declaration or null
iff the declaration does not have an
+ * original defined type.
+ */
+ public N4GetterDeclaration createN4TypeGetter(N4TypeDeclaration typeDecl) {
+ Type originalType = getState().info.getOriginalDefinedType(typeDecl);
+ if (originalType == null) {
+ return null;
+ }
+
+ SymbolTableEntry typeSTE = findSymbolTableEntryForElement(typeDecl, true);
+ ReflectionBuilder reflectionBuilder = new ReflectionBuilder(this, getState(), resourceNameComputer);
+ JsonElement reflectInfo = reflectionBuilder.createReflectionInfo(typeDecl, typeSTE);
+ Gson gson = new GsonBuilder().disableHtmlEscaping().create();
+ String origJsonString = gson.toJson(reflectInfo);
+ String quotedJsonString = "'" + origJsonString.replaceAll("\'", "\\\\\'") + "'";
+
+ SymbolTableEntryInternal methodName = null;
+ if (typeDecl instanceof N4ClassDeclaration) {
+ methodName = steFor_$getReflectionForClass();
+ }
+ if (typeDecl instanceof N4InterfaceDeclaration) {
+ methodName = steFor_$getReflectionForInterface();
+ }
+ if (typeDecl instanceof N4EnumDeclaration) {
+ methodName = steFor_$getReflectionForEnum();
+ }
+
+ N4GetterDeclaration getterDecl = _N4GetterDecl(
+ _LiteralOrComputedPropertyName(N4JSLanguageConstants.N4TYPE_NAME),
+ _Block(
+ _ReturnStmnt(
+ _CallExpr(
+ _IdentRef(methodName),
+ _ThisLiteral(),
+ _StringLiteral(quotedJsonString, quotedJsonString)))));
+
+ getterDecl.getDeclaredModifiers().add(N4Modifier.STATIC);
+
+ return getterDecl;
+ }
+
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ReflectionAssistant.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ReflectionAssistant.xtend
deleted file mode 100644
index ca9cbb8fc3..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/assistants/ReflectionAssistant.xtend
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.assistants
-
-import com.google.gson.GsonBuilder
-import com.google.inject.Inject
-import org.eclipse.n4js.N4JSLanguageConstants
-import org.eclipse.n4js.n4JS.N4ClassDeclaration
-import org.eclipse.n4js.n4JS.N4ClassifierDeclaration
-import org.eclipse.n4js.n4JS.N4EnumDeclaration
-import org.eclipse.n4js.n4JS.N4GetterDeclaration
-import org.eclipse.n4js.n4JS.N4InterfaceDeclaration
-import org.eclipse.n4js.n4JS.N4Modifier
-import org.eclipse.n4js.n4JS.N4TypeDeclaration
-import org.eclipse.n4js.transpiler.InformationRegistry
-import org.eclipse.n4js.transpiler.TransformationAssistant
-import org.eclipse.n4js.utils.ResourceNameComputer
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-/**
- * Helper methods for creating output code related to reflection for classes, interfaces, and enums in N4JS.
- */
-class ReflectionAssistant extends TransformationAssistant {
- @Inject private ResourceNameComputer resourceNameComputer;
-
- /**
- * Convenience method. Same as {@link #createN4TypeGetter(N4TypeDeclaration)}, but if creation of the
- * 'n4type' reflection getter is successful, the getter will be added to the given classifier; otherwise,
- * this method does nothing.
- */
- def public void addN4TypeGetter(N4TypeDeclaration typeDecl, N4ClassifierDeclaration addHere) {
- val getter = createN4TypeGetter(typeDecl);
- if (getter !== null) {
- addHere.ownedMembersRaw += getter;
- }
- }
-
- /**
- * Returns the 'n4type' getter for obtaining reflection information of the given class, interface, or enum declaration
- * or null
if it cannot be created.
- *
- * NOTE: Reflection is only supported for declarations that were given in the original source code, i.e. when method
- * {@link InformationRegistry#getOriginalDefinedType(N4TypeDeclaration) getOriginalDefinedType()}
- * returns a non-null value. Otherwise, this method will return null
as well.
- *
- * @return the 'n4type' getter for the given declaration or null
iff the declaration does not have
- * an original defined type.
- */
- def public N4GetterDeclaration createN4TypeGetter(N4TypeDeclaration typeDecl) {
- val originalType = state.info.getOriginalDefinedType(typeDecl);
- if (originalType === null) {
- return null;
- }
-
- val typeSTE = findSymbolTableEntryForElement(typeDecl, true);
- val reflectionBuilder = new ReflectionBuilder(this, state, resourceNameComputer);
- val reflectInfo = reflectionBuilder.createReflectionInfo(typeDecl, typeSTE);
- val gson = new GsonBuilder().disableHtmlEscaping().create();
- val origJsonString = gson.toJson(reflectInfo);
- val quotedJsonString = "'" + origJsonString.replaceAll("\'", "\\\\\'") + "'";
-
- val methodName = switch (typeDecl) {
- N4ClassDeclaration: steFor_$getReflectionForClass()
- N4InterfaceDeclaration: steFor_$getReflectionForInterface()
- N4EnumDeclaration: steFor_$getReflectionForEnum()
- }
-
- return _N4GetterDecl(
- _LiteralOrComputedPropertyName(N4JSLanguageConstants.N4TYPE_NAME),
- _Block(
- _ReturnStmnt(
- _CallExpr(
- _IdentRef(methodName),
- _ThisLiteral,
- _StringLiteral(quotedJsonString, quotedJsonString)
- )
- )
- )
- ) => [
- declaredModifiers += N4Modifier.STATIC
- ];
- }
-
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ApiImplStubGenerationTransformation.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ApiImplStubGenerationTransformation.java
new file mode 100644
index 0000000000..6adde5476d
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ApiImplStubGenerationTransformation.java
@@ -0,0 +1,356 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.transform;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._AnnotationList;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._EnumDeclaration;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._EnumLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ExportDeclaration;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._FunDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4ClassDeclaration;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4InterfaceDeclaration;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4MemberDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4MethodDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._NewExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._StringLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ThrowStmnt;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.last;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.map;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.toList;
+
+import java.util.List;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.n4js.AnnotationDefinition;
+import org.eclipse.n4js.n4JS.AnnotableScriptElement;
+import org.eclipse.n4js.n4JS.ExportableElement;
+import org.eclipse.n4js.n4JS.FunctionDeclaration;
+import org.eclipse.n4js.n4JS.N4ClassDeclaration;
+import org.eclipse.n4js.n4JS.N4ClassifierDeclaration;
+import org.eclipse.n4js.n4JS.N4EnumDeclaration;
+import org.eclipse.n4js.n4JS.N4InterfaceDeclaration;
+import org.eclipse.n4js.n4JS.N4MemberDeclaration;
+import org.eclipse.n4js.n4JS.Script;
+import org.eclipse.n4js.n4JS.ScriptElement;
+import org.eclipse.n4js.tooling.compare.ProjectComparisonEntry;
+import org.eclipse.n4js.transpiler.Transformation;
+import org.eclipse.n4js.transpiler.TransformationDependency.RequiresBefore;
+import org.eclipse.n4js.transpiler.assistants.TypeAssistant;
+import org.eclipse.n4js.transpiler.es.assistants.DelegationAssistant;
+import org.eclipse.n4js.transpiler.im.DelegatingMember;
+import org.eclipse.n4js.transpiler.im.Script_IM;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryInternal;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal;
+import org.eclipse.n4js.transpiler.utils.ConcreteMembersOrderedForTranspiler;
+import org.eclipse.n4js.transpiler.utils.MissingApiMembersForTranspiler;
+import org.eclipse.n4js.transpiler.utils.ScriptApiTracker;
+import org.eclipse.n4js.transpiler.utils.ScriptApiTracker.ProjectComparisonAdapter;
+import org.eclipse.n4js.ts.types.IdentifiableElement;
+import org.eclipse.n4js.ts.types.TClass;
+import org.eclipse.n4js.ts.types.TClassifier;
+import org.eclipse.n4js.ts.types.TEnum;
+import org.eclipse.n4js.ts.types.TField;
+import org.eclipse.n4js.ts.types.TFunction;
+import org.eclipse.n4js.ts.types.TGetter;
+import org.eclipse.n4js.ts.types.TInterface;
+import org.eclipse.n4js.ts.types.TMember;
+import org.eclipse.n4js.ts.types.TMethod;
+import org.eclipse.n4js.ts.types.TSetter;
+import org.eclipse.n4js.ts.types.TVariable;
+import org.eclipse.n4js.ts.types.util.AccessorTuple;
+import org.eclipse.n4js.ts.types.util.MemberList;
+import org.eclipse.n4js.utils.ContainerTypesHelper;
+import org.eclipse.n4js.utils.N4JSLanguageUtils;
+import org.eclipse.n4js.utils.N4JSLanguageUtils.EnumKind;
+import org.eclipse.n4js.validation.N4JSElementKeywordProvider;
+
+import com.google.inject.Inject;
+
+/**
+ * Generation of code for missing implementations in projects implementing a specific API.
+ */
+@RequiresBefore(MemberPatchingTransformation.class)
+public class ApiImplStubGenerationTransformation extends Transformation {
+
+ @Inject
+ private DelegationAssistant delegationAssistant;
+ @Inject
+ private TypeAssistant typeAssistant;
+ @Inject
+ private ScriptApiTracker scriptApiTracker;
+ @Inject
+ private ContainerTypesHelper containerTypesHelper;
+ @Inject
+ private N4JSElementKeywordProvider n4jsElementKeywordProvider;
+
+ @Override
+ public void assertPreConditions() {
+ // empty
+ }
+
+ @Override
+ public void assertPostConditions() {
+ // empty
+ }
+
+ @Override
+ public void analyze() {
+ // perform API/Impl compare for the resource to compile
+ scriptApiTracker.initApiCompare(getState().resource.getScript());
+ // (reminder: code in #analyze() will be invoked once per resource to compile, so this is what we want)
+ }
+
+ @Override
+ public void transform() {
+ // add stubs for missing members in EXISTING classes and interfaces
+ for (N4ClassifierDeclaration cd : collectNodes(getState().im, N4ClassifierDeclaration.class, false)) {
+ addMissingMembers(cd);
+ }
+ // add missing types (classes, interfaces, enums) and other top-level elements (declared functions, variables)
+ addMissingTopLevelElements();
+ }
+
+ private void addMissingMembers(N4ClassifierDeclaration classifierDecl) {
+ TClassifier type = getState().info.getOriginalDefinedType(classifierDecl);
+ MissingApiMembersForTranspiler mamft = createMAMFT(type);
+
+ // add API/Impl method stubs
+ for (TMethod m : mamft.missingApiMethods) {
+ N4MemberDeclaration member = createApiImplStub(classifierDecl, m);
+ classifierDecl.getOwnedMembersRaw().add(member);
+ }
+ // add API/Impl field accessor stubs
+ for (AccessorTuple accTuple : mamft.missingApiAccessorTuples) {
+ if (accTuple.getGetter() != null) {
+ TGetter g = accTuple.getGetter();
+ N4MemberDeclaration member = createApiImplStub(classifierDecl, g);
+ classifierDecl.getOwnedMembersRaw().add(member);
+ }
+ if (accTuple.getSetter() != null) {
+ TSetter s = accTuple.getSetter();
+ N4MemberDeclaration member = createApiImplStub(classifierDecl, s);
+ classifierDecl.getOwnedMembersRaw().add(member);
+ }
+ }
+
+ // add delegates to inherited fields/getters/setters shadowed by an owned setter XOR getter
+ // NOTE: Partial shadowing in general is disallowed by validation. However, in incomplete
+ // API-impl situation we still support this feature here to propagate generated stubs for
+ // test reporting-purposes.
+ for (AccessorTuple accTuple : mamft.missingApiAccessorTuples) {
+ if (accTuple.getInheritedGetter() != null && accTuple.getGetter() == null && accTuple.getSetter() != null) {
+ // an owned setter is shadowing an inherited getter -> delegate to the inherited getter
+ DelegatingMember delegator = delegationAssistant.createDelegatingMember(type,
+ accTuple.getInheritedGetter());
+ classifierDecl.getOwnedMembersRaw().add(delegator);
+ }
+ if (accTuple.getInheritedSetter() != null && accTuple.getGetter() != null && accTuple.getSetter() == null) {
+ // an owned getter is shadowing an inherited setter -> delegate to the inherited setter
+ DelegatingMember delegator = delegationAssistant.createDelegatingMember(type,
+ accTuple.getInheritedSetter());
+ classifierDecl.getOwnedMembersRaw().add(delegator);
+ }
+ }
+
+ }
+
+ private void addMissingTopLevelElements() {
+ Script script = getState().resource.getScript();
+
+ scriptApiTracker.initApiCompare(script);
+ ProjectComparisonAdapter comparison = ScriptApiTracker.firstProjectComparisonAdapter(script.eResource())
+ .orElse(null);
+ if (null == comparison) {
+ return;
+ }
+
+ for (ProjectComparisonEntry pce : comparison.getEntryFor(script.getModule()).allChildren().toList()) {
+ if (null == pce.getElementImpl(0)) {// no implementation
+ EObject x = pce.getElementAPI();
+ if (x instanceof TMethod) {
+ /* do nothing */
+ }
+ if (x instanceof TFunction) {
+ missing((TFunction) x);
+ }
+ if (x instanceof TClass) {
+ missing((TClass) x);
+ }
+ if (x instanceof TInterface) {
+ missing((TInterface) x);
+ }
+ if (x instanceof TEnum) {
+ missing((TEnum) x);
+ }
+ if (x instanceof TVariable) {
+ missing((TVariable) x);
+ }
+ }
+ }
+ }
+
+ private void missing(TInterface tinter) {
+ N4InterfaceDeclaration stub0 = _N4InterfaceDeclaration(tinter.getName());
+
+ // annotations
+ stub0.setAnnotationList(_AnnotationList(
+ toList(map(tinter.getAnnotations(), a -> AnnotationDefinition.find(a.getName())))));
+
+ // export
+ ScriptElement stub = (ScriptElement) wrapExported(tinter.isDirectlyExported(), stub0);
+
+ // members
+ // (in an interface stub we need member stubs for static AND non-static members)
+ MemberList members = getState().memberCollector.members(tinter, false, false);
+ for (TMember m : members) {
+ if (!(m instanceof TField)) {
+ stub0.getOwnedMembersRaw().add(createApiImplStub(stub0, m));
+ }
+ }
+
+ appendToScript(stub);
+
+ getState().info.setOriginalDefinedType(stub0, tinter);
+
+ createSymbolTableEntryIMOnly(stub0);
+ }
+
+ private void missing(TClass tclass) {
+ N4ClassDeclaration stub0 = _N4ClassDeclaration(tclass.getName());
+
+ // at least a ctor throwing an exception is required here.
+ stub0.getOwnedMembersRaw().add(_N4MethodDecl("constructor",
+ _ThrowStmnt(_NewExpr(
+ _IdentRef(N4ApiNotImplementedErrorSTE()),
+ _StringLiteral("Class " + tclass.getName() + " is not implemented yet.")))));
+
+ // annotations
+ stub0.setAnnotationList(_AnnotationList(
+ toList(map(tclass.getAnnotations(), a -> AnnotationDefinition.find(a.getName())))));
+
+ // export
+ ScriptElement stub = (ScriptElement) wrapExported(tclass.isDirectlyExported(), stub0);
+
+ // members
+ // (in a class stub we need member stubs ONLY for static members)
+ MemberList members = getState().memberCollector.members(tclass, false, false);
+ for (TMember m : members) {
+ if (!(m instanceof TField) && m.isStatic()) {
+ stub0.getOwnedMembersRaw().add(createApiImplStub(stub0, m));
+ }
+ }
+
+ appendToScript(stub);
+
+ getState().info.setOriginalDefinedType(stub0, tclass);
+
+ createSymbolTableEntryIMOnly(stub0);
+ }
+
+ private void missing(TEnum tenum) {
+ N4EnumDeclaration stub0 = _EnumDeclaration(tenum.getName(),
+ toList(map(tenum.getLiterals(), l -> _EnumLiteral(l.getName(), l.getName()))));
+
+ // exported
+ ScriptElement stub = (ScriptElement) wrapExported(tenum.isDirectlyExported(), stub0);
+
+ // number-/string-based
+ EnumKind enumKind = N4JSLanguageUtils.getEnumKind(tenum);
+ switch (enumKind) {
+ case Normal: {
+ // do nothing
+ break;
+ }
+ case NumberBased: {
+ ((AnnotableScriptElement) stub)
+ .setAnnotationList(_AnnotationList(List.of(AnnotationDefinition.NUMBER_BASED)));
+ break;
+ }
+ case StringBased: {
+ ((AnnotableScriptElement) stub)
+ .setAnnotationList(_AnnotationList(List.of(AnnotationDefinition.STRING_BASED)));
+ break;
+ }
+ }
+
+ appendToScript(stub);
+
+ getState().info.setOriginalDefinedType(stub0, tenum);
+
+ createSymbolTableEntryIMOnly(stub0);
+ }
+
+ private void appendToScript(ScriptElement stub) {
+ Script_IM script = getState().im;
+ if (script.getScriptElements().isEmpty()) {
+ script.getScriptElements().add(stub);
+ } else {
+ insertAfter(last(script.getScriptElements()), stub);
+ }
+ }
+
+ private void missing(TVariable tvar) {
+ missingFuncOrVar(tvar, tvar.isDirectlyExported(), "variable");
+ }
+
+ private void missing(TFunction func) {
+ missingFuncOrVar(func, func.isDirectlyExported(), "function");
+ }
+
+ private SymbolTableEntryInternal N4ApiNotImplementedErrorSTE() {
+ return steFor_N4ApiNotImplementedError();
+ }
+
+ private void missingFuncOrVar(IdentifiableElement func, boolean exported, String description) {
+ SymbolTableEntryOriginal funcSTE = getSymbolTableEntryOriginal(func, true); // createSymbolTableEntry(fun);
+
+ FunctionDeclaration funcDecl = _FunDecl(funcSTE.getName(), _ThrowStmnt(_NewExpr(
+ _IdentRef(N4ApiNotImplementedErrorSTE()),
+ _StringLiteral(description + " " + funcSTE.getName() + " is not implemented yet."))));
+ EObject stub = wrapExported(exported, funcDecl);
+
+ insertAfter(last(getState().im.getScriptElements()), stub);
+ }
+
+ private EObject wrapExported(boolean exported, ExportableElement toExportOrNotToExport) {
+ return (exported) ? _ExportDeclaration(toExportOrNotToExport) : toExportOrNotToExport;
+ }
+
+ /**
+ * Creates a member that servers as the stub for a missing member on implementation side, corresponding to the given
+ * member apiMember
on API side.
+ */
+ private N4MemberDeclaration createApiImplStub(N4ClassifierDeclaration classifierDecl, TMember apiMember) {
+ // here we create:
+ //
+ // public m() { // or a getter or setter
+ // throw new N4ApiNotImplementedError("API for method C.m not implemented yet.");
+ // }
+ //
+ SymbolTableEntryInternal N4ApiNotImplementedErrorSTE = steFor_N4ApiNotImplementedError();
+ String typeName = classifierDecl.getName();
+ String memberKeyword = n4jsElementKeywordProvider.keyword(apiMember);
+ String memberName = apiMember.getName();
+ return _N4MemberDecl(apiMember,
+ _ThrowStmnt(
+ _NewExpr(_IdentRef(N4ApiNotImplementedErrorSTE), _StringLiteral(
+ "API for " + memberKeyword + " " + typeName + "." + memberName
+ + " not implemented yet."))));
+ }
+
+ // note: the following uses logic from old transpiler (MissingApiMembersForTranspiler, ScriptApiTracker)
+ private MissingApiMembersForTranspiler createMAMFT(TClassifier classifier) {
+ ConcreteMembersOrderedForTranspiler cmoft = typeAssistant.getOrCreateCMOFT(classifier);
+ return MissingApiMembersForTranspiler.create(containerTypesHelper, scriptApiTracker,
+ classifier, cmoft, getState().resource.getScript());
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ApiImplStubGenerationTransformation.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ApiImplStubGenerationTransformation.xtend
deleted file mode 100644
index 52665e3e65..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ApiImplStubGenerationTransformation.xtend
+++ /dev/null
@@ -1,301 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.transform
-
-import com.google.inject.Inject
-import java.util.stream.Stream
-import org.eclipse.n4js.AnnotationDefinition
-import org.eclipse.n4js.n4JS.AnnotableScriptElement
-import org.eclipse.n4js.n4JS.ExportableElement
-import org.eclipse.n4js.n4JS.N4ClassifierDeclaration
-import org.eclipse.n4js.n4JS.N4MemberDeclaration
-import org.eclipse.n4js.n4JS.ScriptElement
-import org.eclipse.n4js.transpiler.Transformation
-import org.eclipse.n4js.transpiler.TransformationDependency.RequiresBefore
-import org.eclipse.n4js.transpiler.assistants.TypeAssistant
-import org.eclipse.n4js.transpiler.es.assistants.DelegationAssistant
-import org.eclipse.n4js.transpiler.utils.MissingApiMembersForTranspiler
-import org.eclipse.n4js.transpiler.utils.ScriptApiTracker
-import org.eclipse.n4js.ts.types.IdentifiableElement
-import org.eclipse.n4js.ts.types.TClass
-import org.eclipse.n4js.ts.types.TClassifier
-import org.eclipse.n4js.ts.types.TEnum
-import org.eclipse.n4js.ts.types.TField
-import org.eclipse.n4js.ts.types.TFunction
-import org.eclipse.n4js.ts.types.TInterface
-import org.eclipse.n4js.ts.types.TMember
-import org.eclipse.n4js.ts.types.TMethod
-import org.eclipse.n4js.ts.types.TVariable
-import org.eclipse.n4js.utils.ContainerTypesHelper
-import org.eclipse.n4js.utils.N4JSLanguageUtils
-import org.eclipse.n4js.validation.N4JSElementKeywordProvider
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-import static extension org.eclipse.n4js.transpiler.utils.ScriptApiTracker.firstProjectComparisonAdapter
-
-/**
- * Generation of code for missing implementations in projects implementing a specific API.
- */
-@RequiresBefore(MemberPatchingTransformation)
-class ApiImplStubGenerationTransformation extends Transformation {
-
- @Inject private DelegationAssistant delegationAssistant;
- @Inject private TypeAssistant typeAssistant;
- @Inject private ScriptApiTracker scriptApiTracker;
- @Inject private ContainerTypesHelper containerTypesHelper;
- @Inject private N4JSElementKeywordProvider n4jsElementKeywordProvider;
-
-
- override assertPreConditions() {
- }
- override assertPostConditions() {
- }
-
- override analyze() {
- // perform API/Impl compare for the resource to compile
- scriptApiTracker.initApiCompare(state.resource.script);
- // (reminder: code in #analyze() will be invoked once per resource to compile, so this is what we want)
- }
-
- override transform() {
- // add stubs for missing members in EXISTING classes and interfaces
- collectNodes(state.im, N4ClassifierDeclaration, false).forEach[addMissingMembers];
- // add missing types (classes, interfaces, enums) and other top-level elements (declared functions, variables)
- addMissingTopLevelElements();
- }
-
- def private void addMissingMembers(N4ClassifierDeclaration classifierDecl) {
- val type = state.info.getOriginalDefinedType(classifierDecl);
- val mamft = createMAMFT(type);
-
- // add API/Impl method stubs
- for(m : mamft.missingApiMethods) {
- val member = createApiImplStub(classifierDecl, m);
- classifierDecl.ownedMembersRaw += member;
- }
- // add API/Impl field accessor stubs
- for(accTuple : mamft.missingApiAccessorTuples) {
- if(accTuple.getter!==null) {
- val g = accTuple.getter;
- val member = createApiImplStub(classifierDecl, g);
- classifierDecl.ownedMembersRaw += member;
- }
- if(accTuple.setter!==null) {
- val s = accTuple.setter;
- val member = createApiImplStub(classifierDecl, s);
- classifierDecl.ownedMembersRaw += member;
- }
- }
-
-
- // add delegates to inherited fields/getters/setters shadowed by an owned setter XOR getter
- // NOTE: Partial shadowing in general is disallowed by validation. However, in incomplete
- // API-impl situation we still support this feature here to propagate generated stubs for
- // test reporting-purposes.
- for(accTuple : mamft.missingApiAccessorTuples) {
- if(accTuple.inheritedGetter!==null && accTuple.getter===null && accTuple.setter!==null) {
- // an owned setter is shadowing an inherited getter -> delegate to the inherited getter
- val delegator = delegationAssistant.createDelegatingMember(type, accTuple.inheritedGetter);
- classifierDecl.ownedMembersRaw += delegator;
- }
- if(accTuple.inheritedSetter!==null && accTuple.getter!==null && accTuple.setter===null) {
- // an owned getter is shadowing an inherited setter -> delegate to the inherited setter
- val delegator = delegationAssistant.createDelegatingMember(type, accTuple.inheritedSetter);
- classifierDecl.ownedMembersRaw += delegator;
- }
- }
-
-
- }
-
- def private void addMissingTopLevelElements() {
-
- val script = state.resource.script;
-
- scriptApiTracker.initApiCompare(script)
- val comparison = script.eResource.firstProjectComparisonAdapter.orElse(null);
- if (null === comparison) {
- return;
- }
-
- comparison.getEntryFor(script.module).allChildren.toIterable
- .filter[null === getElementImpl(0)] // no implementation
- .forEach[
- switch x:elementAPI{
- TMethod : { /* do nothing */ }
- TFunction : missing(x)
- TClass : missing(x)
- TInterface : missing(x)
- TEnum : missing(x)
- TVariable : missing(x)
- }
- ];
- }
-
- def private dispatch missing(TInterface tinter){
- val stub0 = _N4InterfaceDeclaration( tinter.name ) ;
-
- // annotations
- stub0.annotationList = _AnnotationList( tinter.annotations.map[ AnnotationDefinition.find(it.name) ] )
-
- // export
- val stub = wrapExported(tinter.isDirectlyExported, stub0) as ScriptElement
-
- // members
- // (in an interface stub we need member stubs for static AND non-static members)
- val members = state.memberCollector.members(tinter, false, false);
- for(TMember m : members) {
- if(!(m instanceof TField)) {
- stub0.ownedMembersRaw += createApiImplStub(stub0, m);
- }
- }
-
- appendToScript(stub);
-
- state.info.setOriginalDefinedType(stub0, tinter);
-
- createSymbolTableEntryIMOnly(stub0)
- }
- def private dispatch missing(TClass tclass){
- val stub0 = _N4ClassDeclaration( tclass.name ) =>[
- // at least a ctor throwing an exception is required here.
- it.ownedMembersRaw += _N4MethodDecl("constructor",
- _ThrowStmnt( _NewExpr(
- _IdentRef( N4ApiNotImplementedErrorSTE ),
- _StringLiteral( '''Class «tclass.name» is not implemented yet.''')
- ) ) )
- ];
-
- // annotations
- stub0.annotationList = _AnnotationList( tclass.annotations.map[ AnnotationDefinition.find(it.name) ] )
-
- // export
- val stub = wrapExported(tclass.isDirectlyExported, stub0) as ScriptElement
-
- // members
- // (in a class stub we need member stubs ONLY for static members)
- val members = state.memberCollector.members(tclass, false, false);
- for(TMember m : members) {
- if(!(m instanceof TField) && m.static) {
- stub0.ownedMembersRaw += createApiImplStub(stub0, m);
- }
- }
-
- appendToScript(stub);
-
- state.info.setOriginalDefinedType(stub0, tclass);
-
- createSymbolTableEntryIMOnly(stub0)
- }
- def private dispatch missing(TEnum tenum){
-
- val stub0 = _EnumDeclaration(tenum.name, tenum.literals.map[ _EnumLiteral(name, name) ] );
-
- // exported
- var ScriptElement stub = wrapExported( tenum.isDirectlyExported ,
- stub0
- ) as ScriptElement;
-
- // number-/string-based
- val enumKind = N4JSLanguageUtils.getEnumKind(tenum);
- switch (enumKind) {
- case Normal: {
- // do nothing
- }
- case NumberBased: {
- (stub as AnnotableScriptElement).annotationList = _AnnotationList(#[AnnotationDefinition.NUMBER_BASED]);
- }
- case StringBased: {
- (stub as AnnotableScriptElement).annotationList = _AnnotationList(#[AnnotationDefinition.STRING_BASED]);
- }
- };
-
- appendToScript( stub )
-
- state.info.setOriginalDefinedType(stub0, tenum);
-
- createSymbolTableEntryIMOnly(stub0)
- }
-
- def private appendToScript(ScriptElement stub) {
- val script = state.im;
- if ( script.scriptElements.isEmpty ) script.scriptElements+=stub
- else insertAfter ( script.scriptElements.last, stub );
- }
-
- def private dispatch missing(TVariable tvar){
- missingFuncOrVar(tvar, tvar.isDirectlyExported, "variable")
- }
- def private dispatch missing(TFunction func){
- missingFuncOrVar(func, func.isDirectlyExported, "function")
- }
-
- def private N4ApiNotImplementedErrorSTE() {
- steFor_N4ApiNotImplementedError;
- }
-
- def private missingFuncOrVar(IdentifiableElement func, boolean exported, String description) {
- val funcSTE = func.getSymbolTableEntryOriginal(true); // createSymbolTableEntry(func);
-
- val funcDecl = _FunDecl( funcSTE.name, _ThrowStmnt( _NewExpr(
- _IdentRef( N4ApiNotImplementedErrorSTE ),
- _StringLiteral( '''«description» «funcSTE.name» is not implemented yet.''')
- ) ) )
- => [
- // Do some more configuration.
- ];
- val stub = wrapExported(exported,funcDecl);
-
- insertAfter( state.im.scriptElements.last , stub );
- }
-
- def private wrapExported( boolean exported, ExportableElement toExportOrNotToExport )
- {
- if( exported ) _ExportDeclaration( toExportOrNotToExport ) else toExportOrNotToExport
- }
-
- /**
- * Creates a member that servers as the stub for a missing member on implementation side, corresponding to the given
- * member apiMember
on API side.
- */
- def private N4MemberDeclaration createApiImplStub(N4ClassifierDeclaration classifierDecl, TMember apiMember) {
- // here we create:
- //
- // public m() { // or a getter or setter
- // throw new N4ApiNotImplementedError("API for method C.m not implemented yet.");
- // }
- //
- val N4ApiNotImplementedErrorSTE = steFor_N4ApiNotImplementedError;
- val typeName = classifierDecl.name;
- val memberKeyword = n4jsElementKeywordProvider.keyword(apiMember);
- val memberName = apiMember.name;
- return _N4MemberDecl(apiMember,
- _ThrowStmnt(
- _NewExpr(_IdentRef(N4ApiNotImplementedErrorSTE), _StringLiteral(
- '''API for «memberKeyword» «typeName».«memberName» not implemented yet.'''
- ))
- )
- );
- }
-
- /** Converts the stream into an iterable. */
- def private Iterable toIterable(Stream stream) {
- return [stream.iterator];
- }
-
- // note: the following uses logic from old transpiler (MissingApiMembersForTranspiler, ScriptApiTracker)
- def private MissingApiMembersForTranspiler createMAMFT(TClassifier classifier) {
- val cmoft = typeAssistant.getOrCreateCMOFT(classifier);
- return MissingApiMembersForTranspiler.create(containerTypesHelper, scriptApiTracker,
- classifier, cmoft, state.resource.script);
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part1_Transformation.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part1_Transformation.java
new file mode 100644
index 0000000000..c642e50cad
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part1_Transformation.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.transform;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ReturnStmnt;
+
+import org.eclipse.n4js.generator.GeneratorOption;
+import org.eclipse.n4js.n4JS.ArrowFunction;
+import org.eclipse.n4js.n4JS.ExpressionStatement;
+import org.eclipse.n4js.transpiler.Transformation;
+import org.eclipse.n4js.transpiler.TransformationDependency.Optional;
+import org.eclipse.n4js.transpiler.es.assistants.BlockAssistant;
+
+import com.google.inject.Inject;
+
+/**
+ * Transforms ES2015 arrow functions to an ES5 equivalent, using ordinary function expressions.
+ */
+@Optional(GeneratorOption.ArrowFunctions)
+public class ArrowFunction_Part1_Transformation extends Transformation {
+
+ @Inject
+ BlockAssistant blockAssistant;
+
+ @Override
+ public void analyze() {
+ // empty
+ }
+
+ @Override
+ public void assertPreConditions() {
+ // empty
+ }
+
+ @Override
+ public void assertPostConditions() {
+ // empty
+ }
+
+ @Override
+ public void transform() {
+ for (ArrowFunction af : collectNodes(getState().im, ArrowFunction.class, true)) {
+ transformArrowFunction(af);
+ }
+ }
+
+ /** turn implicit returns into explicit ones. */
+ private void transformArrowFunction(ArrowFunction arrowFunc) {
+
+ // PART 1
+ if (arrowFunc.isSingleExprImplicitReturn()) {
+ if (blockAssistant.needsReturnInsertionForBody(arrowFunc)) {
+ // Wrap in return.
+ ExpressionStatement exprToWrap = (ExpressionStatement) arrowFunc.getBody().getStatements().get(0);
+ replace(exprToWrap, _ReturnStmnt(exprToWrap.getExpression())); // reuse expression
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part1_Transformation.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part1_Transformation.xtend
deleted file mode 100644
index 760dbfc949..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part1_Transformation.xtend
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.transform
-
-import com.google.inject.Inject
-import org.eclipse.n4js.n4JS.ArrowFunction
-import org.eclipse.n4js.n4JS.ExpressionStatement
-import org.eclipse.n4js.transpiler.Transformation
-import org.eclipse.n4js.transpiler.TransformationDependency.Optional
-import org.eclipse.n4js.transpiler.es.assistants.BlockAssistant
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-/**
- * Transforms ES2015 arrow functions to an ES5 equivalent, using ordinary function expressions.
- */
-@Optional(ArrowFunctions)
-class ArrowFunction_Part1_Transformation extends Transformation {
-
- @Inject BlockAssistant blockAssistant;
-
-
- override analyze() {
-
- }
-
- override assertPreConditions() {
- }
-
- override assertPostConditions() {
- }
-
- override transform() {
- collectNodes(state.im, ArrowFunction, true).forEach[transformArrowFunction];
- }
-
- /** turn implicit returns into explicit ones. */
- private def void transformArrowFunction(ArrowFunction arrowFunc ) {
-
- // PART 1
- if( arrowFunc.isSingleExprImplicitReturn ) {
- if( blockAssistant.needsReturnInsertionForBody(arrowFunc)) {
- // Wrap in return.
- var exprToWrap = arrowFunc.body.statements.get(0) as ExpressionStatement;
- replace(exprToWrap, _ReturnStmnt(exprToWrap.expression)); // reuse expression
- }
- }
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part2_Transformation.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part2_Transformation.java
new file mode 100644
index 0000000000..f7bdccc043
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part2_Transformation.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.transform;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._CallExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._FunExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Parenthesis;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ThisLiteral;
+import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.functionType;
+
+import org.eclipse.n4js.generator.GeneratorOption;
+import org.eclipse.n4js.n4JS.ArrowFunction;
+import org.eclipse.n4js.n4JS.FormalParameter;
+import org.eclipse.n4js.n4JS.FunctionExpression;
+import org.eclipse.n4js.n4JS.ParameterizedCallExpression;
+import org.eclipse.n4js.transpiler.AbstractTranspiler;
+import org.eclipse.n4js.transpiler.Transformation;
+import org.eclipse.n4js.transpiler.TransformationDependency.Optional;
+import org.eclipse.n4js.transpiler.TransformationDependency.Requires;
+
+/**
+ * Part 2 of {@link ArrowFunction_Part1_Transformation}.
+ */
+@Optional(GeneratorOption.ArrowFunctions)
+@Requires(ArrowFunction_Part1_Transformation.class)
+public class ArrowFunction_Part2_Transformation extends Transformation {
+
+ @Override
+ public void analyze() {
+ // empty
+ }
+
+ @Override
+ public void assertPreConditions() {
+ // empty
+ }
+
+ @Override
+ public void assertPostConditions() {
+ if (AbstractTranspiler.DEBUG_PERFORM_ASSERTIONS) {
+ assertTrue("No arrow-function left in IM",
+ collectNodes(getState().im, ArrowFunction.class, true).isEmpty());
+ }
+ }
+
+ @Override
+ public void transform() {
+ for (ArrowFunction af : collectNodes(getState().im, ArrowFunction.class, true)) {
+ transformArrowFunction(af);
+ }
+ }
+
+ /** replace arrow-function by function-expression */
+ private void transformArrowFunction(ArrowFunction arrowFunc) {
+
+ // PART 2
+ FunctionExpression fe = _FunExpr(arrowFunc.isAsync(), arrowFunc.getName(),
+ arrowFunc.getFpars().toArray(new FormalParameter[0]), arrowFunc.getBody());
+ // note: arrow functions cannot be generators, so we do *not* need to do:
+ // fe.generator = arrowFunc.generator;
+
+ ParameterizedCallExpression thisBinder = _CallExpr(
+ _PropertyAccessExpr(
+ _Parenthesis(fe),
+ getSymbolTableEntryForMember(functionType(getState().G), "bind", false, false, true)),
+ _ThisLiteral()); // end Call function*()
+
+ replace(arrowFunc, thisBinder, fe);
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part2_Transformation.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part2_Transformation.xtend
deleted file mode 100644
index 7aeeae2cb5..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ArrowFunction_Part2_Transformation.xtend
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.transform
-
-import org.eclipse.n4js.n4JS.ArrowFunction
-import org.eclipse.n4js.transpiler.Transformation
-import org.eclipse.n4js.transpiler.TransformationDependency.Optional
-import org.eclipse.n4js.transpiler.TransformationDependency.Requires
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.*
-import org.eclipse.n4js.transpiler.AbstractTranspiler
-
-/**
- * Part 2 of {@link ArrowFunction_Part1_Transformation}.
- */
-@Optional(ArrowFunctions)
-@Requires(ArrowFunction_Part1_Transformation)
-class ArrowFunction_Part2_Transformation extends Transformation {
-
-
- override analyze() {
-
- }
-
- override assertPreConditions() {
-
- }
-
- override assertPostConditions() {
- if (AbstractTranspiler.DEBUG_PERFORM_ASSERTIONS) {
- "No arrow-function left in IM".assertTrue( collectNodes(state.im, ArrowFunction, true).isEmpty );
- }
- }
-
- override transform() {
- collectNodes(state.im, ArrowFunction, true).toList.forEach[transformArrowFunction];
- }
-
- /** replace arrow-function by function-expression */
- private def void transformArrowFunction(ArrowFunction arrowFunc ) {
-
- // PART 2
- val fe = _FunExpr(arrowFunc.async, arrowFunc.name, arrowFunc.fpars, arrowFunc.body);
- // note: arrow functions cannot be generators, so we do *not* need to do:
- // fe.generator = arrowFunc.generator;
-
- val thisBinder = _CallExpr(
- _PropertyAccessExpr(
- _Parenthesis( fe ),
- getSymbolTableEntryForMember(state.G.functionType, "bind", false, false, true)
- ),
- _ThisLiteral
- ); // end Call function*()
-
- replace(arrowFunc, thisBinder, fe);
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/BlockTransformation.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/BlockTransformation.java
new file mode 100644
index 0000000000..9e32609496
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/BlockTransformation.java
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.transform;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableDeclaration;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableStatement;
+
+import org.eclipse.n4js.n4JS.ArrowFunction;
+import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor;
+import org.eclipse.n4js.n4JS.Statement;
+import org.eclipse.n4js.transpiler.Transformation;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryInternal;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal;
+import org.eclipse.n4js.ts.types.TVariable;
+
+/**
+ */
+public class BlockTransformation extends Transformation {
+
+ /** Name for capturing local-arguments-environment in distinct variable on entering a block. */
+ public static final String $CAPTURE_ARGS = "$capturedArgs";
+
+ @Override
+ public void assertPreConditions() {
+ // true
+ }
+
+ @Override
+ public void assertPostConditions() {
+ // true
+ }
+
+ @Override
+ public void analyze() {
+ // ignore
+ }
+
+ @Override
+ public void transform() {
+ for (FunctionOrFieldAccessor fofa : collectNodes(getState().im, FunctionOrFieldAccessor.class, true)) {
+ transformArguments(fofa);
+ }
+ }
+
+ /** capture arguments-variable to be accessible in re-written arrow-functions */
+ private void transformArguments(FunctionOrFieldAccessor funcOrAccess) {
+ FunctionOrFieldAccessor fofa = getState().tracer.getOriginalASTNodeOfSameType(funcOrAccess, false);
+ if (fofa == null) {
+ return;
+ }
+ TVariable argsVar = fofa.getImplicitArgumentsVariable();
+ if (argsVar == null) {
+ return;
+ }
+
+ // 1. rename old IdentifierRef_IM pointing to --> arguments with fresh name.
+ // 2. introduce new IdentifierRef_IM with (new) name 'arguments'
+ // 3. wire up freshname to new arguments in first line of block.
+ String newName = $CAPTURE_ARGS;
+
+ SymbolTableEntryOriginal arguments_STE = getSymbolTableEntryOriginal(argsVar, false);
+ if (arguments_STE == null || arguments_STE.getReferencingElements().isEmpty()) {
+ // no references to this local arguments variable -> no capturing required
+ return;
+ }
+
+ // arguments_STE.referencingElements.empty // + also need to check if we contain a arrow-function accessing
+ // arguments.
+
+ // 1.) RENAME
+ rename(arguments_STE, newName);
+
+ // skip ArrowFunctions: (this is the main reason for the capturing here :-)
+ if (funcOrAccess instanceof ArrowFunction)
+ return;
+ // skip empty bodies:
+ if (funcOrAccess.getBody().getStatements().isEmpty())
+ return;
+
+ // 2 + 3.) CAPTURE arguments:
+
+ Statement bodyFirstStatement = funcOrAccess.getBody().getStatements().get(0);
+ // note, there must be something in the body otherwise the 'arguments' variable could not have been accessed!
+
+ // new SymbolTableEntry for 'real' arguments ( the other was renamed above )
+ SymbolTableEntryInternal argumentsSTE = steFor_arguments();
+
+ insertBefore(bodyFirstStatement,
+ _VariableStatement(_VariableDeclaration(
+ newName, _IdentRef(argumentsSTE))));
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/BlockTransformation.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/BlockTransformation.xtend
deleted file mode 100644
index 6cb1afd6ca..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/BlockTransformation.xtend
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.transform
-
-import org.eclipse.n4js.n4JS.ArrowFunction
-import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor
-import org.eclipse.n4js.transpiler.Transformation
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-/**
- */
-class BlockTransformation extends Transformation {
-
- /** Name for capturing local-arguments-environment in distinct variable on entering a block.*/
- public static final String $CAPTURE_ARGS = "$capturedArgs";
-
-
- override assertPreConditions() {
- // true
- }
-
- override assertPostConditions() {
- // true
- }
-
- override analyze() {
- // ignore
- }
-
- override transform() {
- collectNodes(state.im, FunctionOrFieldAccessor, true).toList.forEach[transformArguments];
- }
-
- /** capture arguments-variable to be accessible in re-written arrow-functions */
- private def void transformArguments(FunctionOrFieldAccessor funcOrAccess ) {
-
- val argsVar = state.tracer.getOriginalASTNodeOfSameType(funcOrAccess, false)?.implicitArgumentsVariable
- if(argsVar === null) {
- return;
- }
-
- // 1. rename old IdentifierRef_IM pointing to --> arguments with fresh name.
- // 2. introduce new IdentifierRef_IM with (new) name 'arguments'
- // 3. wire up freshname to new arguments in first line of block.
- val newName = $CAPTURE_ARGS;
-
- val arguments_STE = getSymbolTableEntryOriginal(argsVar, false);
- if(arguments_STE===null || arguments_STE.referencingElements.empty) {
- // no references to this local arguments variable -> no capturing required
- return;
- }
-
- // arguments_STE.referencingElements.empty // + also need to check if we contain a arrow-function accessing arguments.
-
- // 1.) RENAME
- rename( arguments_STE, newName );
-
- // skip ArrowFunctions: (this is the main reason for the capturing here :-)
- if( funcOrAccess instanceof ArrowFunction ) return;
- // skip empty bodies:
- if( funcOrAccess.body.statements.empty ) return;
-
- // 2 + 3.) CAPTURE arguments:
-
- val bodyFirstStatement = funcOrAccess.body.statements.get(0);
- // note, there must be something in the body otherwise the 'arguments' variable could not have been accessed!
-
- // new SymbolTableEntry for 'real' arguments ( the other was renamed above )
- val argumentsSTE = steFor_arguments() ;
-
- insertBefore( bodyFirstStatement,
- _VariableStatement( _VariableDeclaration(
- newName, _IdentRef( argumentsSTE )
- ))
- );
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ClassDeclarationTransformation.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ClassDeclarationTransformation.java
new file mode 100644
index 0000000000..d30b5affbc
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ClassDeclarationTransformation.java
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.transform;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ArrLit;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Block;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._LiteralOrComputedPropertyName;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._N4GetterDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ReturnStmnt;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.exists;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.map;
+import static org.eclipse.xtext.xbase.lib.IteratorExtensions.exists;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.eclipse.n4js.n4JS.ArrayLiteral;
+import org.eclipse.n4js.n4JS.GenericDeclaration;
+import org.eclipse.n4js.n4JS.IdentifierRef;
+import org.eclipse.n4js.n4JS.N4ClassDeclaration;
+import org.eclipse.n4js.n4JS.N4ClassExpression;
+import org.eclipse.n4js.n4JS.N4FieldAccessor;
+import org.eclipse.n4js.n4JS.N4FieldDeclaration;
+import org.eclipse.n4js.n4JS.N4GetterDeclaration;
+import org.eclipse.n4js.n4JS.N4MemberDeclaration;
+import org.eclipse.n4js.n4JS.N4MethodDeclaration;
+import org.eclipse.n4js.n4JS.N4Modifier;
+import org.eclipse.n4js.n4JS.Statement;
+import org.eclipse.n4js.transpiler.Transformation;
+import org.eclipse.n4js.transpiler.TransformationDependency.RequiresBefore;
+import org.eclipse.n4js.transpiler.assistants.TypeAssistant;
+import org.eclipse.n4js.transpiler.es.assistants.ClassConstructorAssistant;
+import org.eclipse.n4js.transpiler.es.assistants.ClassifierAssistant;
+import org.eclipse.n4js.transpiler.es.assistants.DelegationAssistant;
+import org.eclipse.n4js.transpiler.es.assistants.ReflectionAssistant;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal;
+import org.eclipse.n4js.transpiler.utils.TranspilerUtils;
+
+import com.google.common.collect.Iterables;
+import com.google.inject.Inject;
+
+/**
+ * Transforms {@link N4ClassDeclaration}s into a constructor function and a $makeClass
call.
+ *
+ * Dependencies:
+ *
+ * - requiresBefore {@link MemberPatchingTransformation}: additional members must be in place before they are
+ * transformed within this transformation.
+ *
+ */
+@RequiresBefore(MemberPatchingTransformation.class)
+public class ClassDeclarationTransformation extends Transformation {
+
+ @Inject
+ private ClassConstructorAssistant classConstructorAssistant;
+ @Inject
+ private ClassifierAssistant classifierAssistant;
+ @Inject
+ private ReflectionAssistant reflectionAssistant;
+ @Inject
+ private DelegationAssistant delegationAssistant;
+ @Inject
+ private TypeAssistant typeAssistant;
+
+ @Override
+ public void assertPreConditions() {
+ typeAssistant.assertClassifierPreConditions();
+ assertFalse("class expressions are not supported yet",
+ exists(getState().im.eAllContents(), el -> el instanceof N4ClassExpression));
+ assertFalse("only top-level classes are supported, for now",
+ exists(collectNodes(getState().im, N4ClassDeclaration.class, false),
+ cd -> !typeAssistant.isTopLevel(cd)));
+ }
+
+ @Override
+ public void assertPostConditions() {
+ // none
+ }
+
+ @Override
+ public void analyze() {
+ // ignore
+ }
+
+ @Override
+ public void transform() {
+ for (N4ClassDeclaration cd : collectNodes(getState().im, N4ClassDeclaration.class, false)) {
+ transformClassDecl(cd);
+ }
+ }
+
+ private void transformClassDecl(N4ClassDeclaration classDecl) {
+ SymbolTableEntry classSTE = findSymbolTableEntryForElement(classDecl, false);
+ SymbolTableEntryOriginal superClassSTE = typeAssistant.getSuperClassSTE(classDecl);
+ LinkedHashSet fieldsRequiringExplicitDefinition = classifierAssistant
+ .findFieldsRequiringExplicitDefinition(classDecl);
+
+ reflectionAssistant.addN4TypeGetter(classDecl, classDecl);
+
+ classConstructorAssistant.amendConstructor(classDecl, classSTE, superClassSTE,
+ fieldsRequiringExplicitDefinition);
+
+ List belowClassDecl = new ArrayList<>();
+ belowClassDecl.addAll(
+ classifierAssistant.createExplicitFieldDefinitions(classSTE, true, fieldsRequiringExplicitDefinition));
+ belowClassDecl.addAll(classifierAssistant.createStaticFieldInitializations(classDecl, classSTE,
+ fieldsRequiringExplicitDefinition));
+ belowClassDecl.addAll(createAdditionalClassDeclarationCode());
+ insertAfter(TranspilerUtils.orContainingExportDeclaration(classDecl), belowClassDecl.toArray(new Statement[0]));
+
+ removeFieldsAndAbstractMembers(classDecl);
+ delegationAssistant.replaceDelegatingMembersByOrdinaryMembers(classDecl);
+ removeTypeInformation(classDecl);
+
+ ArrayLiteral implementedInterfaces = createDirectlyImplementedInterfaces(classDecl);
+ String $implements = steFor_$implementsInterfaces().getName();
+ if (!implementedInterfaces.getElements().isEmpty()) {
+ N4GetterDeclaration newGetter = _N4GetterDecl(
+ _LiteralOrComputedPropertyName($implements),
+ _Block(
+ _ReturnStmnt(implementedInterfaces)));
+ newGetter.getDeclaredModifiers().add(N4Modifier.STATIC);
+ classDecl.getOwnedMembersRaw().add(newGetter);
+ }
+
+ // change superClassRef to an equivalent extends-expression
+ // (this is a minor quirk required because superClassRef is not supported by the PrettyPrinterSwitch;
+ // for details see PrettyPrinterSwitch#caseN4ClassDeclaration())
+ classDecl.setSuperClassRef(null);
+ classDecl.setSuperClassExpression(__NSSafe_IdentRef(superClassSTE));
+ }
+
+ /** Removes fields and abstract members (they do not have a representation in the output code). */
+ private void removeFieldsAndAbstractMembers(N4ClassDeclaration classDecl) {
+ classDecl.getOwnedMembersRaw().removeIf(m -> {
+ if (m instanceof N4FieldDeclaration) {
+ return true;
+ }
+ if (m instanceof N4FieldAccessor) {
+ return m.isAbstract();
+ }
+ if (m instanceof N4MethodDeclaration) {
+ return m.isAbstract();
+ }
+ return false;
+ });
+ }
+
+ private void removeTypeInformation(N4ClassDeclaration classDecl) {
+ for (N4MemberDeclaration currMember : classDecl.getOwnedMembersRaw()) {
+ if (currMember instanceof GenericDeclaration) {
+ ((GenericDeclaration) currMember).getTypeVars().clear();
+ }
+ if (currMember instanceof N4GetterDeclaration) {
+ ((N4GetterDeclaration) currMember).setDeclaredTypeRefNode(null);
+ }
+ if (currMember instanceof N4MethodDeclaration) {
+ ((N4MethodDeclaration) currMember).setDeclaredReturnTypeRefNode(null);
+ }
+ }
+ }
+
+ /** Override to add additional output code directly after the default class declaration output code. */
+ private List createAdditionalClassDeclarationCode() {
+ return Collections.emptyList(); // no additional statements by default
+ }
+
+ private ArrayLiteral createDirectlyImplementedInterfaces(N4ClassDeclaration classDecl) {
+ List interfaces = typeAssistant.getSuperInterfacesSTEs(classDecl);
+
+ // the return value of this method is intended for default method patching; for this purpose, we have to
+ // filter out some of the directly implemented interfaces:
+ Iterable directlyImplementedInterfacesFiltered = TranspilerUtils
+ .filterNominalInterfaces(interfaces);
+ return _ArrLit(Iterables.toArray(map(directlyImplementedInterfacesFiltered, ste -> _IdentRef(ste)),
+ IdentifierRef.class));
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ClassDeclarationTransformation.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ClassDeclarationTransformation.xtend
deleted file mode 100644
index 309077e2b7..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/ClassDeclarationTransformation.xtend
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.transform
-
-import com.google.inject.Inject
-import java.util.List
-import org.eclipse.n4js.n4JS.ArrayLiteral
-import org.eclipse.n4js.n4JS.GenericDeclaration
-import org.eclipse.n4js.n4JS.N4ClassDeclaration
-import org.eclipse.n4js.n4JS.N4ClassExpression
-import org.eclipse.n4js.n4JS.N4FieldAccessor
-import org.eclipse.n4js.n4JS.N4FieldDeclaration
-import org.eclipse.n4js.n4JS.N4GetterDeclaration
-import org.eclipse.n4js.n4JS.N4MethodDeclaration
-import org.eclipse.n4js.n4JS.N4Modifier
-import org.eclipse.n4js.n4JS.Statement
-import org.eclipse.n4js.transpiler.Transformation
-import org.eclipse.n4js.transpiler.TransformationDependency.RequiresBefore
-import org.eclipse.n4js.transpiler.assistants.TypeAssistant
-import org.eclipse.n4js.transpiler.es.assistants.ClassConstructorAssistant
-import org.eclipse.n4js.transpiler.es.assistants.ClassifierAssistant
-import org.eclipse.n4js.transpiler.es.assistants.DelegationAssistant
-import org.eclipse.n4js.transpiler.es.assistants.ReflectionAssistant
-import org.eclipse.n4js.transpiler.im.SymbolTableEntry
-import org.eclipse.n4js.transpiler.utils.TranspilerUtils
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-import static extension org.eclipse.n4js.transpiler.utils.TranspilerUtils.*
-
-/**
- * Transforms {@link N4ClassDeclaration}s into a constructor function and a $makeClass
call.
- *
- * Dependencies:
- *
- * - requiresBefore {@link MemberPatchingTransformation}:
- * additional members must be in place before they are transformed within this transformation.
- *
- */
-@RequiresBefore(MemberPatchingTransformation)
-class ClassDeclarationTransformation extends Transformation {
-
- @Inject private ClassConstructorAssistant classConstructorAssistant;
- @Inject private ClassifierAssistant classifierAssistant;
- @Inject private ReflectionAssistant reflectionAssistant;
- @Inject private DelegationAssistant delegationAssistant;
- @Inject private TypeAssistant typeAssistant;
-
- override assertPreConditions() {
- typeAssistant.assertClassifierPreConditions();
- assertFalse("class expressions are not supported yet",
- state.im.eAllContents.exists[it instanceof N4ClassExpression]);
- assertFalse("only top-level classes are supported, for now",
- collectNodes(state.im, N4ClassDeclaration, false).exists[!typeAssistant.isTopLevel(it)]);
- }
-
- override assertPostConditions() {
- // none
- }
-
- override analyze() {
- // ignore
- }
-
- override transform() {
- collectNodes(state.im, N4ClassDeclaration, false).forEach[transformClassDecl];
- }
-
- def private void transformClassDecl(N4ClassDeclaration classDecl) {
- val classSTE = findSymbolTableEntryForElement(classDecl, false);
- val superClassSTE = typeAssistant.getSuperClassSTE(classDecl);
- val fieldsRequiringExplicitDefinition = classifierAssistant.findFieldsRequiringExplicitDefinition(classDecl);
-
- reflectionAssistant.addN4TypeGetter(classDecl, classDecl);
-
- classConstructorAssistant.amendConstructor(classDecl, classSTE, superClassSTE, fieldsRequiringExplicitDefinition);
-
- val belowClassDecl = newArrayList;
- belowClassDecl += classifierAssistant.createExplicitFieldDefinitions(classSTE, true, fieldsRequiringExplicitDefinition);
- belowClassDecl += classifierAssistant.createStaticFieldInitializations(classDecl, classSTE, fieldsRequiringExplicitDefinition);
- belowClassDecl += createAdditionalClassDeclarationCode(classDecl, classSTE);
- insertAfter(classDecl.orContainingExportDeclaration, belowClassDecl);
-
- removeFieldsAndAbstractMembers(classDecl);
- delegationAssistant.replaceDelegatingMembersByOrdinaryMembers(classDecl);
- removeTypeInformation(classDecl);
-
- val implementedInterfaces = createDirectlyImplementedInterfaces(classDecl);
- val $implements = steFor_$implementsInterfaces.name;
- if (!implementedInterfaces.elements.empty) {
- classDecl.ownedMembersRaw += _N4GetterDecl(
- _LiteralOrComputedPropertyName($implements),
- _Block(
- _ReturnStmnt(implementedInterfaces)
- )
- ) => [
- declaredModifiers += N4Modifier.STATIC;
- ];
- }
-
- // change superClassRef to an equivalent extends-expression
- // (this is a minor quirk required because superClassRef is not supported by the PrettyPrinterSwitch;
- // for details see PrettyPrinterSwitch#caseN4ClassDeclaration())
- classDecl.superClassRef = null;
- classDecl.superClassExpression = __NSSafe_IdentRef(superClassSTE);
- }
-
- /** Removes fields and abstract members (they do not have a representation in the output code). */
- def private void removeFieldsAndAbstractMembers(N4ClassDeclaration classDecl) {
- classDecl.ownedMembersRaw.removeIf[m|
- switch (m) {
- N4FieldDeclaration: true
- N4FieldAccessor: m.isAbstract()
- N4MethodDeclaration: m.isAbstract()
- default: false
- }
- ];
- }
-
- def private void removeTypeInformation(N4ClassDeclaration classDecl) {
- for (currMember : classDecl.ownedMembersRaw) {
- if (currMember instanceof GenericDeclaration) {
- currMember.typeVars.clear();
- }
- switch (currMember) {
- N4GetterDeclaration:
- currMember.declaredTypeRefNode = null
- N4MethodDeclaration:
- currMember.declaredReturnTypeRefNode = null
- }
- }
- }
-
- /** Override to add additional output code directly after the default class declaration output code. */
- def protected List createAdditionalClassDeclarationCode(N4ClassDeclaration classDecl, SymbolTableEntry classSTE) {
- return #[]; // no additional statements by default
- }
-
- def public ArrayLiteral createDirectlyImplementedInterfaces(N4ClassDeclaration classDecl) {
- val interfaces = typeAssistant.getSuperInterfacesSTEs(classDecl);
-
- // the return value of this method is intended for default method patching; for this purpose, we have to
- // filter out some of the directly implemented interfaces:
- val directlyImplementedInterfacesFiltered = TranspilerUtils.filterNominalInterfaces(interfaces);
- return _ArrLit( directlyImplementedInterfacesFiltered.map[ _IdentRef(it) ] );
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/CommonJsImportsTransformation.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/CommonJsImportsTransformation.java
new file mode 100644
index 0000000000..57b9c7168e
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/CommonJsImportsTransformation.java
@@ -0,0 +1,291 @@
+/**
+ * Copyright (c) 2021 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.transform;
+
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ConditionalExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._LiteralOrComputedPropertyName;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Parenthesis;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableBinding;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableDeclaration;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableStatement;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.drop;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.exists;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.last;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.n4js.N4JSGlobals;
+import org.eclipse.n4js.n4JS.BindingElement;
+import org.eclipse.n4js.n4JS.BindingProperty;
+import org.eclipse.n4js.n4JS.DefaultImportSpecifier;
+import org.eclipse.n4js.n4JS.ImportDeclaration;
+import org.eclipse.n4js.n4JS.ImportSpecifier;
+import org.eclipse.n4js.n4JS.ModuleSpecifierForm;
+import org.eclipse.n4js.n4JS.N4JSFactory;
+import org.eclipse.n4js.n4JS.NamedImportSpecifier;
+import org.eclipse.n4js.n4JS.NamespaceImportSpecifier;
+import org.eclipse.n4js.n4JS.VariableDeclaration;
+import org.eclipse.n4js.n4JS.VariableStatement;
+import org.eclipse.n4js.n4JS.VariableStatementKeyword;
+import org.eclipse.n4js.packagejson.PackageJsonProperties;
+import org.eclipse.n4js.transpiler.Transformation;
+import org.eclipse.n4js.transpiler.TransformationDependency.ExcludesAfter;
+import org.eclipse.n4js.transpiler.TransformationDependency.ExcludesBefore;
+import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryInternal;
+import org.eclipse.n4js.ts.types.TModule;
+import org.eclipse.n4js.utils.N4JSLanguageHelper;
+import org.eclipse.n4js.utils.ProjectDescriptionUtils;
+import org.eclipse.n4js.utils.Strings;
+import org.eclipse.n4js.workspace.N4JSProjectConfigSnapshot;
+import org.eclipse.n4js.workspace.WorkspaceAccess;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.inject.Inject;
+
+/**
+ * Since switching to node's native support for ES6 modules, we have to re-write all import declarations that import
+ * from an old CommonJS module. Note that this behavior can be turned on/off in the package.json file, see
+ * {@link PackageJsonProperties#GENERATOR_REWRITE_CJS_IMPORTS}.
+ */
+@ExcludesAfter(SanitizeImportsTransformation.class) // CommonJsImportsTransformation must not run before
+ // SanitizeImportsTransformation
+@ExcludesBefore(ModuleWrappingTransformation.class) // CommonJsImportsTransformation must not run after
+ // ModuleWrappingTransformation
+public class CommonJsImportsTransformation extends Transformation {
+
+ @Inject
+ private N4JSLanguageHelper n4jsLanguageHelper;
+
+ @Inject
+ private WorkspaceAccess workspaceAccess;
+
+ @Override
+ public void assertPreConditions() {
+ // true
+ }
+
+ @Override
+ public void assertPostConditions() {
+ // true
+ }
+
+ @Override
+ public void analyze() {
+ // nothing to be done
+ }
+
+ @Override
+ public void transform() {
+ if (!getState().project.getProjectDescription().isGeneratorEnabledRewriteCjsImports()) {
+ // rewriting of CJS imports is not enabled for the containing project
+ return;
+ }
+
+ ImmutableListMultimap importDeclsPerImportedModule = FluentIterable
+ .from(getState().im.getScriptElements())
+ .filter(ImportDeclaration.class)
+ .filter(id -> !id.isBare()) // ignore bare imports
+ .index(importDecl -> getState().info.getImportedModule(importDecl));
+
+ List varStmnts = new ArrayList<>();
+ for (TModule targetModule : importDeclsPerImportedModule.keySet()) {
+ varStmnts.addAll(transformImportDecl(targetModule, importDeclsPerImportedModule.get(targetModule)));
+ }
+
+ ImportDeclaration lastImportDecl = last(filter(getState().im.getScriptElements(), ImportDeclaration.class));
+ insertAfter(lastImportDecl, varStmnts.toArray(new VariableStatement[0]));
+ }
+
+ /**
+ * For those of the given imports that actually require rewriting, this method will change them in place *and*
+ * return one or more variable statements that have to be inserted (by the client code) after all imports.
+ *
+ * For example: this method will rewrite the following imports
+ *
+ *
+ * import defaultImport+ from "plainJsModule"
+ * import {namedImport1+} from "plainJsModule"
+ * import {namedImport2+} from "plainJsModule"
+ * import * as NamespaceImport+ from "plainJsModule"
+ *
+ *
+ * to this import:
+ *
+ *
+ * import $tempVar from './plainJsModule.cjs'
+ *
+ *
+ * and will return these variable statements:
+ *
+ *
+ * const defaultImport = ($tempVar?.__esModule ? $tempVar.default : $tempVar);
+ * const NamespaceImport = $tempVar;
+ * const {
+ * namedImport1,
+ * namedImport2
+ * } = $tempVar;
+ *
+ */
+ private List transformImportDecl(TModule targetModule,
+ List allImportDeclsForThisModule) {
+ if (allImportDeclsForThisModule.isEmpty()) {
+ return Collections.emptyList();
+ }
+ if (!requiresRewrite(targetModule)) {
+ return Collections.emptyList();
+ }
+
+ List importDeclsToRewrite = new ArrayList<>(allImportDeclsForThisModule);
+ if (exists(importDeclsToRewrite, id -> id.getModuleSpecifierForm() == ModuleSpecifierForm.PROJECT)) {
+ N4JSProjectConfigSnapshot targetProject = n4jsLanguageHelper.replaceDefinitionProjectByDefinedProject(
+ getState().resource, workspaceAccess.findProjectContaining(targetModule), true);
+ if (targetProject != null && targetProject.getProjectDescription().hasModuleProperty()) {
+ // don't rewrite project imports in case the target project has a top-level property "module" in its
+ // package.json,
+ // because in that case project imports will be redirected to an esm-ready file:
+ importDeclsToRewrite.removeIf(id -> id.getModuleSpecifierForm() == ModuleSpecifierForm.PROJECT);
+ }
+ }
+ if (importDeclsToRewrite.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ // special case with a simpler approach:
+ if (importDeclsToRewrite.size() == 1 && importDeclsToRewrite.get(0).getImportSpecifiers().size() == 1) {
+ ImportSpecifier importSpec = importDeclsToRewrite.get(0).getImportSpecifiers().get(0);
+ if (importSpec instanceof NamespaceImportSpecifier) {
+ // in this case we can simply replace the namespace import by a default import
+ String namespaceName = ((NamespaceImportSpecifier) importSpec).getAlias();
+ DefaultImportSpecifier newImportSpec = N4JSFactory.eINSTANCE.createDefaultImportSpecifier();
+ newImportSpec.setImportedElementAsText(namespaceName);
+ newImportSpec.setFlaggedUsedInCode(true);
+ newImportSpec.setRetainedAtRuntime(true);
+ // NOTE: don't copy the following lines to other places! Use methods #replace() instead!
+ getState().tracer.copyTrace(importSpec, newImportSpec);
+ insertAfter(importSpec, newImportSpec);
+ remove(importSpec);
+ return Collections.emptyList();
+ }
+ }
+
+ String tempVarName = computeNameForIntermediateDefaultImport(targetModule);
+ SymbolTableEntryInternal tempVarSTE = getSymbolTableEntryInternal(tempVarName, true);
+
+ List varDecls = new ArrayList<>();
+ List bindingProps = new ArrayList<>();
+
+ createVarDeclsOrBindings(importDeclsToRewrite, tempVarSTE, varDecls, bindingProps);
+
+ List result = new ArrayList<>();
+ for (VariableDeclaration varDecl : varDecls) {
+ result.add(_VariableStatement(VariableStatementKeyword.CONST, varDecl));
+ }
+ if (!bindingProps.isEmpty()) {
+ result.add(_VariableStatement(VariableStatementKeyword.CONST,
+ _VariableBinding(bindingProps, _IdentRef(tempVarSTE))));
+ }
+
+ ImportDeclaration firstImportDecl = importDeclsToRewrite.get(0);
+ removeAll(firstImportDecl.getImportSpecifiers());
+ DefaultImportSpecifier dsi = N4JSFactory.eINSTANCE.createDefaultImportSpecifier();
+ firstImportDecl.getImportSpecifiers().add(dsi);
+ dsi.setImportedElementAsText(tempVarSTE.getName());
+ dsi.setFlaggedUsedInCode(true);
+ dsi.setRetainedAtRuntime(true);
+ removeAll(drop(importDeclsToRewrite, 1));
+
+ return result;
+ }
+
+ private void createVarDeclsOrBindings(List importDecls, SymbolTableEntry steTempVar,
+ List varDecls, List bindingProps) {
+
+ SymbolTableEntry steEsModule = null;
+ SymbolTableEntry steDefault = null;
+ for (ImportDeclaration importDecl : importDecls) {
+ for (ImportSpecifier importSpec : importDecl.getImportSpecifiers()) {
+ if (importSpec instanceof NamedImportSpecifier) {
+ NamedImportSpecifier nis = (NamedImportSpecifier) importSpec;
+ String importedName = nis.getImportedElementAsText();
+ String localName = nis.getAlias() != null ? nis.getAlias() : importedName;
+ boolean isDefaultImport = nis.isDefaultImport() || importedName == "default";
+ if (isDefaultImport) {
+ if (steEsModule == null) {
+ steEsModule = steFor_interopProperty_esModule();
+ }
+ if (steDefault == null) {
+ steDefault = getSymbolTableEntryInternal("default", true);
+ }
+ ParameterizedPropertyAccessExpression_IM pae = _PropertyAccessExpr(steTempVar,
+ steFor_interopProperty_esModule());
+ pae.setOptionalChaining(true);
+ varDecls.add(_VariableDeclaration(localName, _Parenthesis(_ConditionalExpr(
+ pae,
+ _PropertyAccessExpr(steTempVar, steDefault),
+ _IdentRef(steTempVar)))));
+ } else {
+ BindingProperty bp = N4JSFactory.eINSTANCE.createBindingProperty();
+ bindingProps.add(bp);
+ if (localName != importedName) {
+ bp.setDeclaredName(_LiteralOrComputedPropertyName(importedName));
+ }
+ BindingElement be = N4JSFactory.eINSTANCE.createBindingElement();
+ bp.setValue(be);
+ be.setVarDecl(_VariableDeclaration(localName));
+ }
+ } else if (importSpec instanceof NamespaceImportSpecifier) {
+ NamespaceImportSpecifier nis = (NamespaceImportSpecifier) importSpec;
+ String namespaceName = nis.getAlias();
+ varDecls.add(_VariableDeclaration(namespaceName, _IdentRef(steTempVar)));
+ } else {
+ throw new IllegalStateException(
+ "unsupported subclass of ImportSpecifier: " + importSpec.eClass().getName());
+ }
+ }
+ }
+ }
+
+ private boolean requiresRewrite(TModule targetModule) {
+ return !n4jsLanguageHelper.isES6Module(getState().index, targetModule);
+ }
+
+ private String computeNameForIntermediateDefaultImport(TModule targetModule) {
+ String packageNameRaw = targetModule.getPackageName();
+ if (packageNameRaw != null) {
+ String n4jsdScopeWithSep = N4JSGlobals.N4JSD_SCOPE + ProjectDescriptionUtils.NPM_SCOPE_SEPARATOR;
+ if (packageNameRaw.startsWith(n4jsdScopeWithSep)) {
+ packageNameRaw = packageNameRaw.substring(n4jsdScopeWithSep.length());
+ }
+ }
+
+ String packageName = Strings.toIdentifier(packageNameRaw, '_');
+ String moduleName = Strings.toIdentifier(targetModule.getQualifiedName(), '_');
+ String baseName = "$cjsImport__" + packageName + "__" + moduleName;
+
+ int idx = 0;
+ String candidate = baseName;
+ // TODO: we won't find name conflicts with SymbolTableEntryOriginals (requires refactoring in
+ // TranspilerState.STECache)
+ while (getSymbolTableEntryInternal(candidate, false) != null) {
+ idx++;
+ candidate = baseName + idx;
+ }
+ return candidate;
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/CommonJsImportsTransformation.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/CommonJsImportsTransformation.xtend
deleted file mode 100644
index a7de33852e..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/CommonJsImportsTransformation.xtend
+++ /dev/null
@@ -1,254 +0,0 @@
-/**
- * Copyright (c) 2021 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.transform
-
-import com.google.common.collect.FluentIterable
-import com.google.inject.Inject
-import java.util.ArrayList
-import java.util.List
-import org.eclipse.n4js.N4JSGlobals
-import org.eclipse.n4js.n4JS.BindingProperty
-import org.eclipse.n4js.n4JS.ImportDeclaration
-import org.eclipse.n4js.n4JS.ModuleSpecifierForm
-import org.eclipse.n4js.n4JS.N4JSFactory
-import org.eclipse.n4js.n4JS.NamedImportSpecifier
-import org.eclipse.n4js.n4JS.NamespaceImportSpecifier
-import org.eclipse.n4js.n4JS.VariableDeclaration
-import org.eclipse.n4js.n4JS.VariableStatement
-import org.eclipse.n4js.n4JS.VariableStatementKeyword
-import org.eclipse.n4js.packagejson.PackageJsonProperties
-import org.eclipse.n4js.transpiler.Transformation
-import org.eclipse.n4js.transpiler.TransformationDependency.ExcludesAfter
-import org.eclipse.n4js.transpiler.TransformationDependency.ExcludesBefore
-import org.eclipse.n4js.transpiler.im.SymbolTableEntry
-import org.eclipse.n4js.ts.types.TModule
-import org.eclipse.n4js.utils.N4JSLanguageHelper
-import org.eclipse.n4js.utils.ProjectDescriptionUtils
-import org.eclipse.n4js.utils.Strings
-import org.eclipse.n4js.workspace.WorkspaceAccess
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-/**
- * Since switching to node's native support for ES6 modules, we have to re-write all import declarations
- * that import from an old CommonJS module. Note that this behavior can be turned on/off in the package.json
- * file, see {@link PackageJsonProperties#GENERATOR_CJS_DEFAULT_IMPORTS}.
- */
-@ExcludesAfter(SanitizeImportsTransformation) // CommonJsImportsTransformation must not run before SanitizeImportsTransformation
-@ExcludesBefore(ModuleWrappingTransformation) // CommonJsImportsTransformation must not run after ModuleWrappingTransformation
-class CommonJsImportsTransformation extends Transformation {
-
- @Inject
- private N4JSLanguageHelper n4jsLanguageHelper;
-
- @Inject
- private WorkspaceAccess workspaceAccess;
-
- override void assertPreConditions() {
- // true
- }
-
- override void assertPostConditions() {
- // true
- }
-
- override void analyze() {
- // nothing to be done
- }
-
- override void transform() {
- if (!state.project.projectDescription.generatorEnabledRewriteCjsImports) {
- // rewriting of CJS imports is not enabled for the containing project
- return;
- }
-
- val importDeclsPerImportedModule = FluentIterable.from(state.im.scriptElements)
- .filter(ImportDeclaration)
- .filter[!bare] // ignore bare imports
- .index[importDecl | state.info.getImportedModule(importDecl)];
-
- val varStmnts = newArrayList;
- for (targetModule : importDeclsPerImportedModule.keySet) {
- varStmnts += transformImportDecl(targetModule, importDeclsPerImportedModule.get(targetModule));
- }
-
- val lastImportDecl = state.im.scriptElements.filter(ImportDeclaration).last();
- insertAfter(lastImportDecl, varStmnts);
- }
-
- /**
- * For those of the given imports that actually require rewriting, this method will change them in place *and* return one or more
- * variable statements that have to be inserted (by the client code) after all imports.
- *
- * For example: this method will rewrite the following imports
- *
- * import defaultImport+ from "plainJsModule"
- * import {namedImport1+} from "plainJsModule"
- * import {namedImport2+} from "plainJsModule"
- * import * as NamespaceImport+ from "plainJsModule"
- *
- * to this import:
- *
- * import $tempVar from './plainJsModule.cjs'
- *
- * and will return these variable statements:
- *
- * const defaultImport = ($tempVar?.__esModule ? $tempVar.default : $tempVar);
- * const NamespaceImport = $tempVar;
- * const {
- * namedImport1,
- * namedImport2
- * } = $tempVar;
- *
- */
- def private List transformImportDecl(TModule targetModule, List allImportDeclsForThisModule) {
- if (allImportDeclsForThisModule.empty) {
- return #[];
- }
- if (!requiresRewrite(targetModule)) {
- return #[];
- }
-
- val importDeclsToRewrite = new ArrayList(allImportDeclsForThisModule);
- if (importDeclsToRewrite.exists[moduleSpecifierForm === ModuleSpecifierForm.PROJECT]) {
- val targetProject = n4jsLanguageHelper.replaceDefinitionProjectByDefinedProject(state.resource,
- workspaceAccess.findProjectContaining(targetModule), true);
- if (targetProject !== null && targetProject.projectDescription.hasModuleProperty) {
- // don't rewrite project imports in case the target project has a top-level property "module" in its package.json,
- // because in that case project imports will be redirected to an esm-ready file:
- importDeclsToRewrite.removeIf[moduleSpecifierForm === ModuleSpecifierForm.PROJECT];
- }
- }
- if (importDeclsToRewrite.empty) {
- return #[];
- }
-
- // special case with a simpler approach:
- if (importDeclsToRewrite.size === 1 && importDeclsToRewrite.head.importSpecifiers.size === 1) {
- val importSpec = importDeclsToRewrite.head.importSpecifiers.head;
- if (importSpec instanceof NamespaceImportSpecifier) {
- // in this case we can simply replace the namespace import by a default import
- val namespaceName = importSpec.alias;
- val newImportSpec = N4JSFactory.eINSTANCE.createDefaultImportSpecifier() => [
- importedElementAsText = namespaceName;
- flaggedUsedInCode = true;
- retainedAtRuntime = true;
- ];
- // NOTE: don't copy the following lines to other places! Use methods #replace() instead!
- state.tracer.copyTrace(importSpec, newImportSpec);
- insertAfter(importSpec, newImportSpec);
- remove(importSpec);
- return #[]
- }
- }
-
- val tempVarName = computeNameForIntermediateDefaultImport(targetModule);
- val tempVarSTE = getSymbolTableEntryInternal(tempVarName, true);
-
- val varDecls = newArrayList;
- val bindingProps = newArrayList;
-
- createVarDeclsOrBindings(importDeclsToRewrite, tempVarSTE, varDecls, bindingProps);
-
- val result = newArrayList;
- for (varDecl : varDecls) {
- result += _VariableStatement(VariableStatementKeyword.CONST, varDecl);
- }
- if (!bindingProps.empty) {
- result += _VariableStatement(VariableStatementKeyword.CONST, _VariableBinding(bindingProps, _IdentRef(tempVarSTE)));
- }
-
- val firstImportDecl = importDeclsToRewrite.head;
- removeAll(firstImportDecl.importSpecifiers);
- firstImportDecl.importSpecifiers += N4JSFactory.eINSTANCE.createDefaultImportSpecifier() => [
- importedElementAsText = tempVarSTE.name;
- flaggedUsedInCode = true;
- retainedAtRuntime = true;
- ];
- removeAll(importDeclsToRewrite.drop(1));
-
- return result;
- }
-
- def private void createVarDeclsOrBindings(List importDecls, SymbolTableEntry steTempVar,
- List varDecls, List bindingProps) {
-
- var steEsModule = null as SymbolTableEntry;
- var steDefault = null as SymbolTableEntry;
- for (importDecl : importDecls) {
- for (importSpec : importDecl.importSpecifiers) {
- switch (importSpec) {
- NamedImportSpecifier: { // including DefaultImportSpecifier
- val importedName = importSpec.importedElementAsText;
- val localName = importSpec.alias ?: importedName;
- val isDefaultImport = importSpec.isDefaultImport || importedName == "default";
- if (isDefaultImport) {
- if (steEsModule === null) {
- steEsModule = steFor_interopProperty_esModule();
- }
- if (steDefault === null) {
- steDefault = getSymbolTableEntryInternal("default", true);
- }
- varDecls += _VariableDeclaration(localName, _Parenthesis(_ConditionalExpr(
- _PropertyAccessExpr(steTempVar, steFor_interopProperty_esModule) => [ optionalChaining = true ],
- _PropertyAccessExpr(steTempVar, steDefault),
- _IdentRef(steTempVar)
- )));
- } else {
- bindingProps += N4JSFactory.eINSTANCE.createBindingProperty => [ newBindingProp |
- if (localName != importedName) {
- newBindingProp.declaredName = _LiteralOrComputedPropertyName(importedName);
- }
- newBindingProp.value = N4JSFactory.eINSTANCE.createBindingElement => [ newBindingElem |
- newBindingElem.varDecl = _VariableDeclaration(localName);
- ]
- ];
- }
- }
- NamespaceImportSpecifier: {
- val namespaceName = importSpec.alias;
- varDecls += _VariableDeclaration(namespaceName, _IdentRef(steTempVar));
- }
- default: {
- throw new IllegalStateException("unsupported subclass of ImportSpecifier: " + importSpec.eClass.name);
- }
- }
- }
- }
- }
-
- def private boolean requiresRewrite(TModule targetModule) {
- return !n4jsLanguageHelper.isES6Module(state.index, targetModule);
- }
-
- def private String computeNameForIntermediateDefaultImport(TModule targetModule) {
- var packageNameRaw = targetModule.packageName;
- if (packageNameRaw !== null) {
- val n4jsdScopeWithSep = N4JSGlobals.N4JSD_SCOPE + ProjectDescriptionUtils.NPM_SCOPE_SEPARATOR;
- if (packageNameRaw.startsWith(n4jsdScopeWithSep)) {
- packageNameRaw = packageNameRaw.substring(n4jsdScopeWithSep.length);
- }
- }
-
- val packageName = Strings.toIdentifier(packageNameRaw, '_');
- val moduleName = Strings.toIdentifier(targetModule.qualifiedName, '_');
- val baseName = "$cjsImport__" + packageName + "__" + moduleName;
-
- var idx = 0;
- var candidate = baseName;
- // TODO: we won't find name conflicts with SymbolTableEntryOriginals (requires refactoring in TranspilerState.STECache)
- while (getSymbolTableEntryInternal(candidate, false) !== null) {
- idx++;
- candidate = baseName + idx;
- }
- return candidate;
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DependencyInjectionTransformation.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DependencyInjectionTransformation.java
new file mode 100644
index 0000000000..77cf19b81a
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DependencyInjectionTransformation.java
@@ -0,0 +1,296 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.transform;
+
+import static com.google.common.collect.Iterables.toArray;
+import static org.eclipse.n4js.tooling.organizeImports.DIUtility.findParentDIC;
+import static org.eclipse.n4js.tooling.organizeImports.DIUtility.hasParentInjector;
+import static org.eclipse.n4js.tooling.organizeImports.DIUtility.isBinder;
+import static org.eclipse.n4js.tooling.organizeImports.DIUtility.isDIComponent;
+import static org.eclipse.n4js.tooling.organizeImports.DIUtility.isInjectedClass;
+import static org.eclipse.n4js.tooling.organizeImports.DIUtility.isSingleton;
+import static org.eclipse.n4js.tooling.organizeImports.DIUtility.resolveBinders;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ArrLit;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ArrayElement;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._CallExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ExprStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ObjLit;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyGetterDecl;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyNameValuePair;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ReturnStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._StringLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._StringLiteralForSTE;
+import static org.eclipse.n4js.transpiler.utils.TranspilerUtils.orContainingExportDeclaration;
+import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.objectType;
+import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.symbolObjectType;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.head;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.last;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.map;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.n4js.AnnotationDefinition;
+import org.eclipse.n4js.N4JSLanguageConstants;
+import org.eclipse.n4js.n4JS.ArrayLiteral;
+import org.eclipse.n4js.n4JS.Expression;
+import org.eclipse.n4js.n4JS.N4ClassDeclaration;
+import org.eclipse.n4js.n4JS.PropertyAssignment;
+import org.eclipse.n4js.n4JS.Statement;
+import org.eclipse.n4js.tooling.organizeImports.DIUtility;
+import org.eclipse.n4js.transpiler.Transformation;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal;
+import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef;
+import org.eclipse.n4js.ts.typeRefs.TypeRef;
+import org.eclipse.n4js.ts.types.TAnnotationTypeRefArgument;
+import org.eclipse.n4js.ts.types.TClass;
+import org.eclipse.n4js.ts.types.TField;
+import org.eclipse.n4js.ts.types.TFormalParameter;
+import org.eclipse.n4js.ts.types.TMethod;
+import org.eclipse.n4js.ts.types.TN4Classifier;
+import org.eclipse.n4js.ts.types.Type;
+import org.eclipse.n4js.utils.DeclMergingHelper;
+import org.eclipse.xtext.xbase.lib.Pair;
+
+import com.google.inject.Inject;
+
+/**
+ * Generates DI meta information for the injector. Information is attached to the compiled type in the
+ * {@link N4JSLanguageConstants#DI_SYMBOL_KEY DI_SYMBOL_KEY} property.
+ *
+ * TODO the DI code below makes heavy use of TModule (probably) without true need; refactor this
+ */
+public class DependencyInjectionTransformation extends Transformation {
+
+ @Inject
+ private DeclMergingHelper declMergingHelper;
+
+ @Override
+ public void assertPreConditions() {
+ // true
+ }
+
+ @Override
+ public void assertPostConditions() {
+ // true
+ }
+
+ @Override
+ public void analyze() {
+ // ignore
+ }
+
+ @Override
+ public void transform() {
+ for (N4ClassDeclaration classDecl : collectNodes(getState().im, N4ClassDeclaration.class, false)) {
+ TClass tClass = getState().info.getOriginalDefinedType(classDecl);
+ if (tClass != null) {
+ Statement codeForDI = generateCodeForDI(tClass, classDecl);
+ if (codeForDI != null) {
+ insertAfter(orContainingExportDeclaration(classDecl), codeForDI);
+ }
+ }
+ }
+ }
+
+ /**
+ * Generates hooks for DI mechanisms. Mainly N4Injector (part of n4js libraries) depends on those hooks. Note that
+ * those in many cases require proper types to be imported, which makes those hooks indirectly depended on behavior
+ * of the script transpiler policy that decides to remove "unused" imported symbols. That one has to leave imported
+ * symbols that are refereed to in code generated here.
+ *
+ * Also, note second parameter passed is name or alias of the super type. passing it from caller, even if we don't
+ * use it, is a shortcut to avoid computing it again here.
+ */
+ @SuppressWarnings("unchecked")
+ private Statement generateCodeForDI(TClass tClass, N4ClassDeclaration classDecl) {
+ List> propertiesForDI = createPropertiesForDI(tClass);
+ if (propertiesForDI.isEmpty()) {
+ return null;
+ }
+ // Object.defineProperty(C, Symbol.for(''), {value: { ... }}
+ SymbolTableEntry classSTE = findSymbolTableEntryForElement(classDecl, true);
+ SymbolTableEntryOriginal objectSTE = getSymbolTableEntryOriginal(objectType(getState().G), true);
+ SymbolTableEntryOriginal definePropertySTE = getSymbolTableEntryForMember(objectType(getState().G),
+ "defineProperty", false, true, true);
+ SymbolTableEntryOriginal symbolObjectSTE = getSymbolTableEntryOriginal(symbolObjectType(getState().G), true);
+ SymbolTableEntryOriginal forSTE = getSymbolTableEntryForMember(symbolObjectType(getState().G), "for", false,
+ true, true);
+ return _ExprStmnt(_CallExpr(
+ _PropertyAccessExpr(objectSTE, definePropertySTE),
+ _IdentRef(classSTE),
+ _CallExpr(_PropertyAccessExpr(symbolObjectSTE, forSTE),
+ _StringLiteral(N4JSLanguageConstants.DI_SYMBOL_KEY)),
+ _ObjLit(Pair.of("value", _ObjLit(propertiesForDI.toArray(new Pair[0]))))));
+ }
+
+ private List> createPropertiesForDI(TClass tClass) {
+ List> result = new ArrayList<>();
+ if (isBinder(tClass)) {
+ result.add(Pair.of("bindings", (Expression) generateBindingPairs(tClass)));
+ result.add(Pair.of("methodBindings", (Expression) generateMethodBindings(tClass)));
+ result.addAll(injectionPointsMetaInfo(tClass));
+ } else if (isDIComponent(tClass)) {
+ if (hasParentInjector(tClass)) {
+ SymbolTableEntryOriginal parentDIC_STE = getSymbolTableEntryOriginal(findParentDIC(tClass), true);
+ result.add(Pair.of("parent", (Expression) __NSSafe_IdentRef(parentDIC_STE)));
+ }
+ result.add(Pair.of("binders", (Expression) generateBinders(tClass)));
+ result.addAll(injectionPointsMetaInfo(tClass));
+ } else if (isInjectedClass(tClass, declMergingHelper)) {
+ result.addAll(injectionPointsMetaInfo(tClass));
+ }
+ return result;
+ }
+
+ /**
+ * Generate DI hooks for scopes, super type, injected ctor, injected fields
+ */
+ private List> injectionPointsMetaInfo(TClass tClass) {
+ ArrayLiteral fieldInjectionValue = fieldInjection(tClass);
+ List> result = new ArrayList<>();
+ if (isSingleton(tClass)) {
+ result.add(Pair.of("scope", _StringLiteral("Singleton")));
+ }
+ TMethod ownedCtor = tClass.getOwnedCtor();
+ if (ownedCtor != null && AnnotationDefinition.INJECT.hasAnnotation(ownedCtor)) {
+ result.add(Pair.of("injectCtorParams", methodInjectedParams(ownedCtor)));
+ }
+ if (!fieldInjectionValue.getElements().isEmpty()) {
+ result.add(Pair.of("fieldsInjectedTypes", fieldInjection(tClass)));
+ }
+ return result;
+ }
+
+ /**
+ * Generate injection info for method annotated with {@link AnnotationDefinition#INJECT}. If method has no method
+ * parameters returned value is empty string, otherwise description of parameters is returned.
+ */
+ private ArrayLiteral methodInjectedParams(TMethod tMethod) {
+ ArrayLiteral result = _ArrLit();
+ for (TFormalParameter fpar : tMethod.getFpars()) {
+ SymbolTableEntryOriginal fparSTE = getSymbolTableEntryOriginal(fpar, true);
+ List pAs = new ArrayList<>();
+ pAs.add(_PropertyNameValuePair("name", _StringLiteralForSTE(fparSTE)));
+ pAs.addAll(generateTypeInfo(fpar.getTypeRef()));
+ result.getElements().add(_ArrayElement(_ObjLit(pAs.toArray(new PropertyAssignment[0]))));
+ }
+ return result;
+ }
+
+ /**
+ * Generate injection info for fields annotated with {@link AnnotationDefinition#INJECT}.
+ */
+ private ArrayLiteral fieldInjection(TClass tClass) {
+ ArrayLiteral result = _ArrLit();
+ for (TField field : getOwnedInejctedFields(tClass)) {
+ SymbolTableEntryOriginal fieldSTE = getSymbolTableEntryOriginal(field, true);
+ List pAs = new ArrayList<>();
+ pAs.add(_PropertyNameValuePair("name", _StringLiteralForSTE(fieldSTE)));
+ pAs.addAll(generateTypeInfo(field.getTypeRef()));
+ result.getElements().add(_ArrayElement(_ObjLit(pAs.toArray(new PropertyAssignment[0]))));
+ }
+ return result;
+ }
+
+ /**
+ * Generate injection info from {@link AnnotationDefinition#BIND} annotations on the provided class.
+ */
+ @SuppressWarnings("unchecked")
+ private ArrayLiteral generateBindingPairs(TClass tClass) {
+ ArrayLiteral result = _ArrLit();
+ for (Pair binding : getBindingPairs(tClass)) {
+ SymbolTableEntryOriginal keySTE = getSymbolTableEntryOriginal(binding.getKey(), true);
+ SymbolTableEntryOriginal valueSTE = getSymbolTableEntryOriginal(binding.getValue(), true);
+ result.getElements().add(_ArrayElement(_ObjLit(
+ Pair.of("from", __NSSafe_IdentRef(keySTE)),
+ Pair.of("to", __NSSafe_IdentRef(valueSTE)))));
+ }
+ return result;
+ }
+
+ /**
+ * Generate injection info for methods annotated with {@link AnnotationDefinition#PROVIDES}. Returned information
+ * contains returned type, name and formal parameters of the method.
+ */
+ private ArrayLiteral generateMethodBindings(TClass tClass) {
+ ArrayLiteral result = _ArrLit();
+ for (TMethod method : getOwnedProviderMethods(tClass)) {
+ result.getElements().add(_ArrayElement(_ObjLit(
+ generateTypeInfo(method.getReturnTypeRef(), "to").get(0),
+ _PropertyNameValuePair("name", _StringLiteral(method.getName())),
+ _PropertyNameValuePair("args", methodInjectedParams(method)))));
+ }
+ return result;
+ }
+
+ private ArrayLiteral generateBinders(TClass tClass) {
+ ArrayLiteral result = _ArrLit();
+ for (Type binderType : resolveBinders(tClass)) {
+ SymbolTableEntryOriginal binderTypeSTE = getSymbolTableEntryOriginal(binderType, true);
+ result.getElements().add(_ArrayElement(
+ __NSSafe_IdentRef(binderTypeSTE)));
+ }
+ return result;
+ }
+
+ /**
+ * Generate type information for DI. Mainly FQN of the {@link TypeRef}, or composed information if given type is
+ * generic.
+ */
+ private List generateTypeInfo(TypeRef typeRef) {
+ return generateTypeInfo(typeRef, "type");
+ }
+
+ private List generateTypeInfo(TypeRef typeRef, String propertyName) {
+ if (!DIUtility.isProviderType(typeRef)) {
+ SymbolTableEntryOriginal declaredTypeSTE = getSymbolTableEntryOriginal(typeRef.getDeclaredType(), true);
+ return List.of(
+ _PropertyGetterDecl(propertyName, _ReturnStmnt(__NSSafe_IdentRef(declaredTypeSTE))));
+ } else if (typeRef instanceof ParameterizedTypeRef) {
+ // typeRef should be N4Provider, fqn needs to be obtained at runtime
+ SymbolTableEntryOriginal declaredTypeSTE = getSymbolTableEntryOriginal(typeRef.getDeclaredType(), true);
+ return List.of(
+ _PropertyGetterDecl(propertyName, _ReturnStmnt(__NSSafe_IdentRef(declaredTypeSTE))),
+ _PropertyNameValuePair("typeVar", _ObjLit(
+ toArray(generateTypeInfo(head(filter(typeRef.getDeclaredTypeArgs(), TypeRef.class))),
+ PropertyAssignment.class))));
+ } else {
+ String msg = typeRef == null ? ""
+ : (typeRef.getDeclaredType() == null ? "" : typeRef.getDeclaredType().getName());
+ throw new IllegalStateException("cannot generate type info for " + msg);
+ // note: at this point, the old transpiler did only log the error and returned something like:
+ // return #[];
+ }
+ }
+
+ /**
+ * Get list of {@link Pair}s of first and second argument of the {@link AnnotationDefinition#BIND} annotation.
+ */
+ private Iterable> getBindingPairs(TClass clazz) {
+ return map(AnnotationDefinition.BIND.getAllAnnotations(clazz), a -> Pair.of(
+ (TN4Classifier) (((TAnnotationTypeRefArgument) a.getArgs().get(0)).getTypeRef().getDeclaredType()),
+ (TN4Classifier) ((TAnnotationTypeRefArgument) last(a.getArgs())).getTypeRef().getDeclaredType()));
+ }
+
+ private Iterable getOwnedProviderMethods(TClass clazz) {
+ return filter(filter(clazz.getOwnedMembers(), TMethod.class),
+ m -> AnnotationDefinition.PROVIDES.hasAnnotation(m));
+ }
+
+ private Iterable getOwnedInejctedFields(TN4Classifier clazz) {
+ return filter(filter(clazz.getOwnedMembers(), TField.class), f -> AnnotationDefinition.INJECT.hasAnnotation(f));
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DependencyInjectionTransformation.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DependencyInjectionTransformation.xtend
deleted file mode 100644
index 249acf26c1..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DependencyInjectionTransformation.xtend
+++ /dev/null
@@ -1,268 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.transform
-
-import com.google.inject.Inject
-import org.eclipse.n4js.AnnotationDefinition
-import org.eclipse.n4js.N4JSLanguageConstants
-import org.eclipse.n4js.n4JS.ArrayLiteral
-import org.eclipse.n4js.n4JS.Expression
-import org.eclipse.n4js.n4JS.N4ClassDeclaration
-import org.eclipse.n4js.n4JS.PropertyAssignment
-import org.eclipse.n4js.n4JS.Statement
-import org.eclipse.n4js.transpiler.Transformation
-import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef
-import org.eclipse.n4js.ts.typeRefs.TypeRef
-import org.eclipse.n4js.ts.types.TAnnotationTypeRefArgument
-import org.eclipse.n4js.ts.types.TClass
-import org.eclipse.n4js.ts.types.TField
-import org.eclipse.n4js.ts.types.TMethod
-import org.eclipse.n4js.ts.types.TN4Classifier
-import org.eclipse.n4js.utils.DeclMergingHelper
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-import static extension org.eclipse.n4js.tooling.organizeImports.DIUtility.*
-import static extension org.eclipse.n4js.transpiler.utils.TranspilerUtils.*
-import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.*
-
-/**
- * Generates DI meta information for the injector.
- * Information is attached to the compiled type in the {@link N4JSLanguageConstants#DI_SYMBOL_KEY DI_SYMBOL_KEY}
- * property.
- *
- * TODO the DI code below makes heavy use of TModule (probably) without true need; refactor this
- */
-class DependencyInjectionTransformation extends Transformation {
-
- @Inject private DeclMergingHelper declMergingHelper;
-
- override assertPreConditions() {
- // true
- }
-
- override assertPostConditions() {
- // true
- }
-
- override analyze() {
- // ignore
- }
-
- override transform() {
- collectNodes(state.im, N4ClassDeclaration, false).forEach[classDecl|
- val tClass = state.info.getOriginalDefinedType(classDecl);
- if( tClass !== null ) {
- val codeForDI = generateCodeForDI(tClass, classDecl);
- if(codeForDI!==null) {
- insertAfter(classDecl.orContainingExportDeclaration, codeForDI);
- }
- }
- ];
- }
-
- /**
- * Generates hooks for DI mechanisms. Mainly N4Injector (part of n4js libraries)
- * depends on those hooks.
- * Note that those in many cases require proper types to be imported, which makes those
- * hooks indirectly depended on behavior of the script transpiler policy that decides to
- * remove "unused" imported symbols. That one has to leave imported symbols that are refereed to
- * in code generated here.
- *
- * Also, note second parameter passed is name or alias of the super type.
- * passing it from caller, even if we don't use it, is a shortcut to avoid computing it again here.
- */
- def private Statement generateCodeForDI(TClass tClass, N4ClassDeclaration classDecl) {
- val propertiesForDI = createPropertiesForDI(tClass, classDecl);
- if(propertiesForDI.empty) {
- return null;
- }
- // Object.defineProperty(C, Symbol.for(''), {value: { ... }}
- val classSTE = findSymbolTableEntryForElement(classDecl, true);
- val objectSTE = getSymbolTableEntryOriginal(state.G.objectType, true);
- val definePropertySTE = getSymbolTableEntryForMember(state.G.objectType, "defineProperty", false, true, true);
- val symbolObjectSTE = getSymbolTableEntryOriginal(state.G.symbolObjectType, true);
- val forSTE = getSymbolTableEntryForMember(state.G.symbolObjectType, "for", false, true, true);
- return _ExprStmnt(_CallExpr(
- _PropertyAccessExpr(objectSTE, definePropertySTE),
- _IdentRef(classSTE),
- _CallExpr(_PropertyAccessExpr(symbolObjectSTE, forSTE), _StringLiteral(N4JSLanguageConstants.DI_SYMBOL_KEY)),
- _ObjLit(
- "value" -> _ObjLit(
- propertiesForDI
- )
- )
- ));
- }
-
- def private Pair[] createPropertiesForDI(TClass tClass, N4ClassDeclaration classDecl) {
- val result = >newArrayList;
- if(isBinder(tClass)) {
- result += "bindings" -> generateBindingPairs(tClass) as Expression;
- result += "methodBindings" -> generateMethodBindings(tClass) as Expression;
- result += injectionPointsMetaInfo(tClass);
- } else if(isDIComponent(tClass)) {
- if(hasParentInjector(tClass)) {
- val parentDIC_STE = getSymbolTableEntryOriginal(findParentDIC(tClass), true);
- result += "parent" -> __NSSafe_IdentRef(parentDIC_STE) as Expression;
- }
- result += "binders" -> generateBinders(tClass) as Expression;
- result += injectionPointsMetaInfo(tClass);
- } else if(isInjectedClass(tClass, declMergingHelper)) {
- result += injectionPointsMetaInfo(tClass);
- }
- return result;
- }
-
- /**
- * Generate DI hooks for scopes, super type, injected ctor, injected fields
- */
- def private Pair[] injectionPointsMetaInfo(TClass tClass) {
- val fieldInjectionValue = fieldInjection(tClass);
- val result = >newArrayList;
- if(isSingleton(tClass)) {
- result += "scope" -> _StringLiteral("Singleton") as Expression;
- }
- val ownedCtor = tClass.ownedCtor;
- if(ownedCtor!==null && AnnotationDefinition.INJECT.hasAnnotation(ownedCtor)) {
- result += "injectCtorParams" -> ownedCtor.methodInjectedParams as Expression;
- }
- if(!fieldInjectionValue.elements.empty) {
- result += "fieldsInjectedTypes" -> fieldInjection(tClass) as Expression;
- }
- return result;
- }
-
- /**
- * Generate injection info for method annotated with {@link AnnotationDefinition#INJECT}.
- * If method has no method parameters returned value is empty string,
- * otherwise description of parameters is returned.
- */
- def private ArrayLiteral methodInjectedParams(TMethod tMethod) {
- val result = _ArrLit();
- for(fpar : tMethod.fpars) {
- val fparSTE = getSymbolTableEntryOriginal(fpar, true);
- result.elements += _ArrayElement(_ObjLit(
- #[ _PropertyNameValuePair("name", _StringLiteralForSTE(fparSTE)) ]
- + fpar.typeRef.generateTypeInfo
- ));
- }
- return result;
- }
-
- /**
- * Generate injection info for fields annotated with {@link AnnotationDefinition#INJECT}.
- */
- def private ArrayLiteral fieldInjection(TClass tClass) {
- val result = _ArrLit();
- for(field : getOwnedInejctedFields(tClass)) {
- val fieldSTE = getSymbolTableEntryOriginal(field, true);
- result.elements += _ArrayElement(_ObjLit(
- #[ _PropertyNameValuePair("name", _StringLiteralForSTE(fieldSTE)) ]
- + field.typeRef.generateTypeInfo
- ));
- }
- return result;
- }
-
- /**
- * Generate injection info from {@link AnnotationDefinition#BIND} annotations on the provided class.
- */
- private def ArrayLiteral generateBindingPairs(TClass tClass) {
- val result = _ArrLit();
- for(binding : getBindingPairs(tClass)) {
- val keySTE = getSymbolTableEntryOriginal(binding.key, true);
- val valueSTE = getSymbolTableEntryOriginal(binding.value, true);
- result.elements += _ArrayElement(_ObjLit(
- "from" -> __NSSafe_IdentRef(keySTE),
- "to" -> __NSSafe_IdentRef(valueSTE)
- ));
- }
- return result;
- }
-
- /**
- * Generate injection info for methods annotated with {@link AnnotationDefinition#PROVIDES}.
- * Returned information contains returned type, name and formal parameters of the method.
- */
- def private ArrayLiteral generateMethodBindings(TClass tClass) {
- val result = _ArrLit();
- for(method : getOwnedProviderMethods(tClass)) {
- result.elements += _ArrayElement(_ObjLit(
- method.returnTypeRef.generateTypeInfo("to").head,
- _PropertyNameValuePair("name", _StringLiteral(method.name)),
- _PropertyNameValuePair("args", method.methodInjectedParams)
- ));
- }
- return result;
- }
-
- def private ArrayLiteral generateBinders(TClass tClass) {
- val result = _ArrLit();
- for(binderType : resolveBinders(tClass)) {
- val binderTypeSTE = getSymbolTableEntryOriginal(binderType, true);
- result.elements += _ArrayElement(
- __NSSafe_IdentRef(binderTypeSTE)
- );
- }
- return result;
- }
-
- /**
- * Generate type information for DI. Mainly FQN of the {@link TypeRef}, or composed information
- * if given type is generic.
- */
- def private PropertyAssignment[] generateTypeInfo(TypeRef typeRef) {
- typeRef.generateTypeInfo("type")
- }
- def private PropertyAssignment[] generateTypeInfo(TypeRef typeRef, String propertyName) {
- if (!typeRef.providerType) {
- val declaredTypeSTE = getSymbolTableEntryOriginal(typeRef.declaredType, true);
- return #[
- _PropertyGetterDecl(propertyName, _ReturnStmnt(__NSSafe_IdentRef(declaredTypeSTE)))
- ];
- } else if (typeRef instanceof ParameterizedTypeRef) {
- // typeRef should be N4Provider, fqn needs to be obtained at runtime
- val declaredTypeSTE = getSymbolTableEntryOriginal(typeRef.declaredType, true);
- return #[
- _PropertyGetterDecl(propertyName, _ReturnStmnt(__NSSafe_IdentRef(declaredTypeSTE))),
- _PropertyNameValuePair("typeVar", _ObjLit(
- typeRef.declaredTypeArgs.filter(TypeRef).head.generateTypeInfo
- ))
- ];
- } else {
- throw new IllegalStateException("cannot generate type info for " + typeRef?.declaredType?.name);
-// note: at this point, the old transpiler did only log the error and returned something like:
-// return #[];
- }
- }
-
-
-
-
- /**
- * Get list of {@link Pair}s of first and second argument of the {@link AnnotationDefinition#BIND} annotation.
- */
- def private getBindingPairs(TClass clazz) {
- AnnotationDefinition.BIND.getAllAnnotations(clazz).map [
- ((args.head as TAnnotationTypeRefArgument).getTypeRef.declaredType) as TN4Classifier
- -> ((args.last as TAnnotationTypeRefArgument).getTypeRef.declaredType) as TN4Classifier
- ]
- }
-
- def private getOwnedProviderMethods(TClass clazz) {
- clazz.ownedMembers.filter(TMethod).filter[AnnotationDefinition.PROVIDES.hasAnnotation(it)];
- }
-
- def private getOwnedInejctedFields(TN4Classifier clazz) {
- clazz.ownedMembers.filter(TField).filter[AnnotationDefinition.INJECT.hasAnnotation(it)]
- }
-}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DestructuringTransformation.java b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DestructuringTransformation.java
new file mode 100644
index 0000000000..f004873997
--- /dev/null
+++ b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DestructuringTransformation.java
@@ -0,0 +1,478 @@
+/**
+ * Copyright (c) 2016 NumberFour AG.
+ * 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:
+ * NumberFour AG - Initial API and implementation
+ */
+package org.eclipse.n4js.transpiler.es.transform;
+
+import static com.google.common.collect.Iterables.toArray;
+import static org.eclipse.n4js.n4JS.DestructureUtils.containsDestructuringPattern;
+import static org.eclipse.n4js.n4JS.DestructureUtils.isRoot;
+import static org.eclipse.n4js.n4JS.DestructureUtils.isTopOfDestructuringAssignment;
+import static org.eclipse.n4js.n4JS.DestructureUtils.isTopOfDestructuringForStatement;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._AssignmentExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Block;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._CallExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ConditionalExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._EqualityExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ExprStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._FormalParameter;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._FunExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IdentRef;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._IndexAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._NumericLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Parenthesis;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyAccessExpr;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ReturnStmnt;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Snippet;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._StringLiteral;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableDeclaration;
+import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableStatement;
+import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.arrayType;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.head;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.map;
+import static org.eclipse.xtext.xbase.lib.IterableExtensions.toList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.n4js.generator.GeneratorOption;
+import org.eclipse.n4js.n4JS.ArrayLiteral;
+import org.eclipse.n4js.n4JS.AssignmentExpression;
+import org.eclipse.n4js.n4JS.Block;
+import org.eclipse.n4js.n4JS.CatchBlock;
+import org.eclipse.n4js.n4JS.DestructNode;
+import org.eclipse.n4js.n4JS.EqualityOperator;
+import org.eclipse.n4js.n4JS.Expression;
+import org.eclipse.n4js.n4JS.ForStatement;
+import org.eclipse.n4js.n4JS.FormalParameter;
+import org.eclipse.n4js.n4JS.FunctionExpression;
+import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor;
+import org.eclipse.n4js.n4JS.N4JSASTUtils;
+import org.eclipse.n4js.n4JS.ObjectLiteral;
+import org.eclipse.n4js.n4JS.ParameterizedCallExpression;
+import org.eclipse.n4js.n4JS.PropertyAssignment;
+import org.eclipse.n4js.n4JS.Script;
+import org.eclipse.n4js.n4JS.Statement;
+import org.eclipse.n4js.n4JS.VariableBinding;
+import org.eclipse.n4js.n4JS.VariableDeclaration;
+import org.eclipse.n4js.n4JS.VariableDeclarationOrBinding;
+import org.eclipse.n4js.n4JS.VariableEnvironmentElement;
+import org.eclipse.n4js.n4JS.VariableStatement;
+import org.eclipse.n4js.n4JS.VariableStatementKeyword;
+import org.eclipse.n4js.n4JS.WithStatement;
+import org.eclipse.n4js.transpiler.AbstractTranspiler;
+import org.eclipse.n4js.transpiler.Transformation;
+import org.eclipse.n4js.transpiler.TransformationDependency.ExcludesAfter;
+import org.eclipse.n4js.transpiler.TransformationDependency.Optional;
+import org.eclipse.n4js.transpiler.im.IdentifierRef_IM;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntry;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryIMOnly;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryInternal;
+import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal;
+import org.eclipse.n4js.ts.types.TypableElement;
+import org.eclipse.xtext.EcoreUtil2;
+import org.eclipse.xtext.xbase.lib.IterableExtensions;
+import org.eclipse.xtext.xbase.lib.Pair;
+
+import com.google.common.base.Strings;
+
+/**
+ * Transforms ES6 destructuring patterns into equivalent ES5 code. If the target engine supports ES6 destructuring
+ * patterns, this transformation can simply be deactivated.
+ *
+ * For details on destructuring patterns, see documentation of class {@link DestructNode}.
+ */
+@Optional(GeneratorOption.Destructuring)
+@ExcludesAfter(StaticPolyfillTransformation.class) // otherwise destructuring patterns from filling module won't be
+ // processed!
+public class DestructuringTransformation extends Transformation {
+
+ /** Counts destructuring patterns per variable environment. Used to avoid name clashes of helper variables. */
+ private final Map destructsPerScope = new HashMap<>();
+
+ @Override
+ public void assertPreConditions() {
+ // true
+ // (however, this transformation should run as early as possible)
+ }
+
+ @Override
+ public void assertPostConditions() {
+ // true
+ }
+
+ @Override
+ public void analyze() {
+ // ignore
+ }
+
+ @Override
+ public void transform() {
+
+ // compute nodes we have to transform before changing anything
+ List destructBindings = toList(filter(
+ collectNodes(getState().im, VariableStatement.class, true), vs -> containsDestructuringPattern(vs)));
+ List destructAssignments = toList(filter(
+ collectNodes(getState().im, AssignmentExpression.class, true),
+ ae -> isTopOfDestructuringAssignment(ae) && isRoot(ae)));
+ List destructForStmnts = toList(filter(collectNodes(getState().im, ForStatement.class, true),
+ fs -> containsDestructuringPattern(fs) || isTopOfDestructuringForStatement(fs)));
+
+ // now perform the changes
+ for (VariableStatement vs : destructBindings) {
+ transformDestructuringBindings(vs);
+ }
+ for (AssignmentExpression ae : destructAssignments) {
+ transformDestructuringAssignment(ae);
+ }
+ for (ForStatement fs : destructForStmnts) {
+ transformForStatementWithDestructuring(fs);
+ }
+ }
+
+ /**
+ * Transforms not a single destructuring binding but all destructuring bindings in the given variable statement.
+ */
+ private void transformDestructuringBindings(VariableStatement stmnt) {
+ List newVarDecls = computeVariableDeclarations(stmnt.getVarDeclsOrBindings());
+ stmnt.getVarDeclsOrBindings().clear();
+ stmnt.getVarDeclsOrBindings().addAll(newVarDecls);
+ }
+
+ /**
+ * Transforms (a single) destructuring assignment.
+ */
+ public void transformDestructuringAssignment(AssignmentExpression expr) {
+ // We pass the value of the expression to the function call. GHOLD-407
+ String fparName;
+ FunctionExpression helperFun;
+
+ fparName = "$destruct" + "Param0";
+ FormalParameter fpar = _FormalParameter(fparName);
+ helperFun = _FunExpr(false, null, fpar);
+
+ EList helperFunContents = helperFun.getBody().getStatements();
+ DestructNode rootNode = DestructNode.unify(expr);
+ List helperVars = new ArrayList<>();
+ List> simpleAssignments = new ArrayList<>();
+ traverse(helperVars, simpleAssignments, rootNode, expr.getRhs(), fparName);
+
+ helperFunContents.addAll(toList(map(helperVars, vd -> _VariableStatement(vd))));
+ helperFunContents.addAll(toList(map(simpleAssignments, sa -> _ExprStmnt(_AssignmentExpr(
+ __NSSafe_IdentRef(sa.getKey()), sa.getValue())))));
+
+ // the following return statement is required to make sure entire expression evaluates to the rhs of
+ // the assignment expression 'expr' (without evaluating rhs again!)
+ SymbolTableEntry firstHelperVarSTE = findSymbolTableEntryForElement(helperVars.get(0), false);
+ helperFunContents.add(_ReturnStmnt(_IdentRef(firstHelperVarSTE)));
+
+ // parentheses required because the expression might appear as a statement (to disambiguate from function
+ // declaration) replace(expr, callExpr);
+ ParameterizedCallExpression callExpr = _CallExpr(_Parenthesis(helperFun), expr.getRhs());
+ replace(expr, callExpr);
+ }
+
+ private void transformForStatementWithDestructuring(ForStatement stmnt) {
+
+ if (stmnt.isForPlain()) {
+
+ if (!stmnt.getVarDeclsOrBindings().isEmpty()) {
+ // something like: for( var [a,b]=[1,2] ;;) {}
+
+ // note: pretty much the same case as method #transformDestructuringBindings() above
+ List newVarDecls = computeVariableDeclarations(stmnt.getVarDeclsOrBindings());
+ stmnt.getVarDeclsOrBindings().clear();
+ stmnt.getVarDeclsOrBindings().addAll(newVarDecls);
+
+ } else {
+ // something like: for( [a,b]=[1,2] ;;) {}
+
+ // here, stmnt.initExpr is an ordinary destructuring assignment that was already handled by method
+ // #transformDestructuringAssignment(AssignmentExpression) above -> nothing to do here!
+ }
+
+ } else {
+ // now: for..in OR for..of
+
+ int depth = getNestingDepth(stmnt);
+
+ VariableDeclaration iterVar = _VariableDeclaration("$destructStep$" + depth);
+ SymbolTableEntryIMOnly iterVarSTE = createSymbolTableEntryIMOnly(iterVar);
+
+ boolean needDeclarations = false;
+ VariableStatementKeyword varStmtKeyword = VariableStatementKeyword.VAR;
+ List helperVars = new ArrayList<>();
+ List> simpleAssignments = new ArrayList<>();
+ if (!stmnt.getVarDeclsOrBindings().isEmpty()) {
+ // something like: for( var [a,b] of [ [1,2], [3,4] ] ) {}
+
+ if (AbstractTranspiler.DEBUG_PERFORM_ASSERTIONS) {
+ assertTrue("there should be exactly one VariableBinding in stmnt.varDeclsOrBindings",
+ stmnt.getVarDeclsOrBindings().size() == 1
+ && stmnt.getVarDeclsOrBindings().get(0) instanceof VariableBinding);
+ }
+
+ DestructNode rootNode = DestructNode.unify((VariableBinding) stmnt.getVarDeclsOrBindings().get(0));
+ // fparname = null since we do not generate any function.
+ traverse(helperVars, simpleAssignments, rootNode, _IdentRef(iterVarSTE), null);
+ needDeclarations = true;
+ varStmtKeyword = stmnt.getVarStmtKeyword();
+
+ } else if (stmnt.getInitExpr() instanceof ArrayLiteral || stmnt.getInitExpr() instanceof ObjectLiteral) {
+ // something like: for( [a,b] of [ [1,2], [3,4] ] ) {}
+
+ DestructNode rootNode = DestructNode.unify(stmnt);
+ // fparname = null since we do not generate any function.
+ traverse(helperVars, simpleAssignments, rootNode, _IdentRef(iterVarSTE), null);
+ needDeclarations = false;
+
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ if (!(stmnt.getStatement() instanceof Block)) {
+ stmnt.setStatement(_Block(stmnt.getStatement()));
+ }
+ Block body = (Block) stmnt.getStatement();
+
+ List toBeInserted = new ArrayList<>();
+ if (needDeclarations) {
+ toBeInserted.add(_VariableStatement(varStmtKeyword, toArray(map(simpleAssignments, sa -> {
+ VariableDeclaration varDecl = getVariableDeclarationFromSTE(sa.getKey());
+ varDecl.setExpression(sa.getValue());
+ return varDecl;
+ }), VariableDeclarationOrBinding.class)));
+ } else {
+ toBeInserted.add(_VariableStatement(toArray(helperVars, VariableDeclaration.class)));
+ toBeInserted.addAll(toList(map(simpleAssignments, sa -> _ExprStmnt(_AssignmentExpr(
+ __NSSafe_IdentRef(sa.getKey()), sa.getValue())))));
+ }
+
+ body.getStatements().addAll(0, toBeInserted);
+
+ // initExpr has been move to beginning of body (if any)
+ stmnt.setInitExpr(null);
+ // variable declarations have been moved to beginning of body (if any)
+ stmnt.getVarDeclsOrBindings().clear();
+ // only declared variable in the for statement is the iteration variable
+ stmnt.getVarDeclsOrBindings().add(iterVar);
+ }
+ }
+
+ /**
+ * Breaks down all VariableBindings in the given list into simple variable declarations and returns a list
+ * containing the variable declarations that were contained in the given list from the beginning plus those created
+ * when breaking down the variable bindings. The order of the elements in the given list is preserved!
+ */
+ private List computeVariableDeclarations(
+ List varDeclsOrBindings) {
+ List result = new ArrayList<>();
+ for (VariableDeclarationOrBinding vdeclOrBinding : varDeclsOrBindings) {
+ if (vdeclOrBinding instanceof VariableDeclaration) {
+ result.add((VariableDeclaration) vdeclOrBinding);
+ } else if (vdeclOrBinding instanceof VariableBinding) {
+ DestructNode rootNode = DestructNode.unify(vdeclOrBinding);
+ List helperVars = new ArrayList<>();
+ List> simpleAssignments = new ArrayList<>();
+ // fparname = null since we do not generate any function.
+ traverse(helperVars, simpleAssignments, rootNode, vdeclOrBinding.getExpression(), null);
+ result.addAll(toList(map(simpleAssignments, sa -> {
+ VariableDeclaration varDecl = getVariableDeclarationFromSTE(sa.getKey());
+ varDecl.setExpression(sa.getValue());
+ return varDecl;
+ })));
+ }
+ }
+ return result;
+ }
+
+ private void traverse(List helperVars,
+ List> simpleAssignments,
+ DestructNode rootNode, Expression value, String fparName) {
+
+ VariableEnvironmentElement scope = N4JSASTUtils.getScope(rootNode.astElement, false);
+ if (scope == null) {
+ scope = getState().im;
+ }
+ int n = destructsPerScope.merge(scope, 1, (i, j) -> i + j) - 1;
+ traverse(helperVars, simpleAssignments, rootNode.getNestedNodes(), value, fparName, Integer.toString(n));
+ }
+
+ /**
+ * Breaks down the destructuring pattern, represented by the given {@link DestructNode}s, into a number of simple
+ * assignments (without destructuring) that will be added to argument 'simpleAssignments'. The order of those simple
+ * assignments matters. Nested patterns as returned by {@link DestructNode#getNestedNodes()} are also broken down.
+ * fparName, if not null, is the parameter name of the enclosing function.
+ */
+ private void traverse(List helperVars,
+ List> simpleAssignments,
+ List nodes, Expression value, String fparName, String helperVarSuffix) {
+
+ int len = nodes.size();
+ boolean isPositionalPattern = IterableExtensions.exists(nodes, n -> n.isPositional());
+ boolean isRest = isPositionalPattern && len > 0 && nodes.get(len - 1).rest;
+
+ // STEP 1: create code to prepare the value to be destructured and to assign it to a helper variable
+
+ // creating a new helper variable
+ String currHelperVarName = "$destruct" + helperVarSuffix;
+ VariableDeclaration currHelperVarDecl = _VariableDeclaration(currHelperVarName);
+ helperVars.add(currHelperVarDecl);
+ SymbolTableEntry currHelperVarSTE = findSymbolTableEntryForElement(currHelperVarDecl, true);
+ if (AbstractTranspiler.DEBUG_PERFORM_ASSERTIONS) {
+ assertTrue("", getVariableDeclarationFromSTE(currHelperVarSTE) == currHelperVarDecl);
+ assertTrue("", currHelperVarSTE.getElementsOfThisName().contains(currHelperVarDecl));
+ }
+ var $sliceToArrayForDestructSTE = steFor_$sliceToArrayForDestruct();
+
+ if (isRest) {
+ // result.add(currHelperVar+" = function(arr){return Array.isArray(arr) ? arr :
+ // Array.from(arr);}("+value+")");
+ simpleAssignments.add(Pair.of(currHelperVarSTE, _CallExpr(
+ _Snippet("function(arr){return Array.isArray(arr) ? arr : Array.from(arr);}"),
+ value)));
+ } else {
+ Expression passValue;
+ if (Strings.isNullOrEmpty(fparName)) {
+ passValue = value;
+ } else {
+ // If the fparName is not empty, the generated function for destructuring has a parameter and we should
+ // use that parameter instead. GHOLD-407
+ SymbolTableEntryInternal fparSTE = getSymbolTableEntryInternal(fparName, true);
+ passValue = _IdentRef(fparSTE);
+ }
+ if (isPositionalPattern) {
+ // result.add(currHelperVar+" = $sliceToArrayForDestruct(("+value+"), "+len+")");
+ simpleAssignments.add(Pair.of(currHelperVarSTE, _CallExpr(
+ _IdentRef($sliceToArrayForDestructSTE),
+ _Parenthesis(passValue),
+ _NumericLiteral(len))));
+ } else {
+ // result.add(currHelperVar+" = ("+value+")");
+ simpleAssignments.add(Pair.of(currHelperVarSTE, _Parenthesis(
+ passValue)));
+ }
+ }
+
+ // STEP 2: create code to perform the actual destructuring
+
+ SymbolTableEntryOriginal sliceSTE = getSymbolTableEntryForMember(arrayType(getState().G), "slice", false, false,
+ true);
+
+ var nestedPatternsCounter = 0;
+ for (var i = 0; i < len; i++) {
+ DestructNode currNode = nodes.get(i);
+
+ // get the current element or property out of 'value' (i.e. the one that corresponds to 'currNode')
+ Expression currValueRaw;
+ if (isRest && i == len - 1) {
+ // currHelperVar.slice(i)
+ currValueRaw = _CallExpr(_PropertyAccessExpr(currHelperVarSTE, sliceSTE), _NumericLiteral(i));
+ } else if (isPositionalPattern) {
+ // currHelperVar[i]
+ currValueRaw = _IndexAccessExpr(currHelperVarSTE, _NumericLiteral(i));
+ } else {
+ // currHelperVar['propName']
+ currValueRaw = _IndexAccessExpr(currHelperVarSTE, _StringLiteral(currNode.propName));
+ }
+
+ Expression currValue;
+ if (currNode.defaultExpr != null) {
+ // currValueRaw+" == undefined ? ("+transformAST.doTransform(currNode.defaultExpr)+") : "+currValueRaw
+ currValue = _ConditionalExpr(
+ _EqualityExpr(
+ copy(currValueRaw), // must copy because used twice in this conditional expression!
+ EqualityOperator.SAME,
+ undefinedRef()),
+ _Parenthesis(
+ currNode.defaultExpr),
+ currValueRaw);
+ } else {
+ currValue = currValueRaw;
+ }
+
+ if (currNode.varRef != null || currNode.varDecl != null) {
+ // actual destructuring
+ // (assigning an element or property from 'value', i.e. the 'currValue', to the variable with name
+ // currNode.varName)
+ TypableElement varSource = currNode.varRef != null ? currNode.varRef : currNode.varDecl;
+ SymbolTableEntry varSTE = null;
+
+ if (varSource instanceof IdentifierRef_IM) {
+ varSTE = ((IdentifierRef_IM) varSource).getId_IM();
+ } else if (varSource instanceof VariableDeclaration) {
+ varSTE = findSymbolTableEntryForElement((VariableDeclaration) varSource, true);
+ }
+ simpleAssignments.add(Pair.of(varSTE, currValue));
+ } else if (currNode.getNestedNodes() != null && currNode.getNestedNodes().size() != 0) {
+ // nested destructuring
+ // (assigning the current value in 'currValue' to the nested destructuring pattern)
+ nestedPatternsCounter++;
+ // fparname = null since we do not generate any function
+ traverse(helperVars, simpleAssignments, currNode.getNestedNodes(), currValue, null,
+ helperVarSuffix + "$" + nestedPatternsCounter);
+ } else {
+ // padding entry (from elision)
+ // -> do nothing (but consume the current index 'i')
+ }
+ }
+ }
+
+ private static final int getNestingDepth(ForStatement stmnt) {
+ int d = 0;
+ EObject obj = stmnt;
+ while ((obj = obj.eContainer()) != null) {
+ if (obj instanceof ForStatement) {
+ d++;
+ }
+ }
+ return d;
+ }
+
+ /**
+ * Returns a list of statements that are the root statements of the next outer variable environment directly
+ * containing the given AST node.
+ */
+ static EList super Statement> getContainingVariableEnvironmentContent(EObject astNode) {
+ VariableEnvironmentElement vee = EcoreUtil2.getContainerOfType(astNode.eContainer(),
+ VariableEnvironmentElement.class);
+ if (vee == null) {
+ throw new IllegalArgumentException("given AST node does not have an outer variable environment");
+ }
+ if (vee instanceof Script) {
+ return ((Script) vee).getScriptElements();
+ }
+ if (vee instanceof FunctionOrFieldAccessor) {
+ return ((FunctionOrFieldAccessor) vee).getBody().getStatements();
+ }
+ if (vee instanceof CatchBlock) {
+ return ((CatchBlock) vee).getBlock().getStatements();
+ }
+ if (vee instanceof PropertyAssignment) {
+ return getContainingVariableEnvironmentContent(vee);
+ }
+ if (vee instanceof WithStatement) {
+ WithStatement ws = (WithStatement) vee;
+ if (!(ws.getStatement() instanceof Block)) {
+ ws.setStatement(_Block(ws.getStatement()));
+ }
+ return ((Block) ws.getStatement()).getStatements();
+ }
+ throw new IllegalArgumentException("given AST node does not have an outer variable environment");
+ }
+
+ private static VariableDeclaration getVariableDeclarationFromSTE(SymbolTableEntry ste) {
+ return head(filter(ste.getElementsOfThisName(), VariableDeclaration.class));
+ }
+}
diff --git a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DestructuringTransformation.xtend b/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DestructuringTransformation.xtend
deleted file mode 100644
index 20edd7c310..0000000000
--- a/plugins/org.eclipse.n4js.transpiler.es/src/org/eclipse/n4js/transpiler/es/transform/DestructuringTransformation.xtend
+++ /dev/null
@@ -1,406 +0,0 @@
-/**
- * Copyright (c) 2016 NumberFour AG.
- * 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:
- * NumberFour AG - Initial API and implementation
- */
-package org.eclipse.n4js.transpiler.es.transform
-
-import java.util.ArrayList
-import java.util.List
-import java.util.Map
-import org.eclipse.emf.common.util.EList
-import org.eclipse.emf.ecore.EObject
-import org.eclipse.n4js.n4JS.DestructNode
-import org.eclipse.n4js.n4JS.ArrayLiteral
-import org.eclipse.n4js.n4JS.AssignmentExpression
-import org.eclipse.n4js.n4JS.Block
-import org.eclipse.n4js.n4JS.CatchBlock
-import org.eclipse.n4js.n4JS.EqualityOperator
-import org.eclipse.n4js.n4JS.Expression
-import org.eclipse.n4js.n4JS.ForStatement
-import org.eclipse.n4js.n4JS.FunctionExpression
-import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor
-import org.eclipse.n4js.n4JS.N4JSASTUtils
-import org.eclipse.n4js.n4JS.ObjectLiteral
-import org.eclipse.n4js.n4JS.PropertyAssignment
-import org.eclipse.n4js.n4JS.Script
-import org.eclipse.n4js.n4JS.Statement
-import org.eclipse.n4js.n4JS.VariableBinding
-import org.eclipse.n4js.n4JS.VariableDeclaration
-import org.eclipse.n4js.n4JS.VariableDeclarationOrBinding
-import org.eclipse.n4js.n4JS.VariableEnvironmentElement
-import org.eclipse.n4js.n4JS.VariableStatement
-import org.eclipse.n4js.n4JS.VariableStatementKeyword
-import org.eclipse.n4js.n4JS.WithStatement
-import org.eclipse.n4js.transpiler.Transformation
-import org.eclipse.n4js.transpiler.TransformationDependency.ExcludesAfter
-import org.eclipse.n4js.transpiler.TransformationDependency.Optional
-import org.eclipse.n4js.transpiler.im.IdentifierRef_IM
-import org.eclipse.n4js.transpiler.im.SymbolTableEntry
-import org.eclipse.xtext.EcoreUtil2
-
-import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.*
-
-import static extension org.eclipse.n4js.n4JS.DestructureUtils.*
-import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.*
-import org.eclipse.n4js.transpiler.AbstractTranspiler
-
-/**
- * Transforms ES6 destructuring patterns into equivalent ES5 code. If the target engine supports ES6 destructuring
- * patterns, this transformation can simply be deactivated.
- *
- * For details on destructuring patterns, see documentation of class {@link DestructNode}.
- */
-@Optional(Destructuring)
-@ExcludesAfter(StaticPolyfillTransformation) // otherwise destructuring patterns from filling module won't be processed!
-class DestructuringTransformation extends Transformation {
-
- /** Counts destructuring patterns per variable environment. Used to avoid name clashes of helper variables. */
- private final Map destructsPerScope = newHashMap;
-
-
- override assertPreConditions() {
- // true
- // (however, this transformation should run as early as possible)
- }
- override assertPostConditions() {
- // true
- }
-
- override analyze() {
- // ignore
- }
-
- override transform() {
-
- // compute nodes we have to transform before changing anything
- val destructBindings = collectNodes(state.im, VariableStatement, true)
- .filter[containsDestructuringPattern].toList;
- val destructAssignments = collectNodes(state.im, AssignmentExpression, true)
- .filter[isTopOfDestructuringAssignment].filter[isRoot].toList;
- val destructForStmnts = collectNodes(state.im, ForStatement, true)
- .filter[containsDestructuringPattern || isTopOfDestructuringForStatement].toList;
-
- // now perform the changes
- destructBindings.forEach[transformDestructuringBindings];
- destructAssignments.forEach[transformDestructuringAssignment];
- destructForStmnts.forEach[transformForStatementWithDestructuring];
- }
-
- /**
- * Transforms not a single destructuring binding but all destructuring bindings in the given variable statement.
- */
- def private void transformDestructuringBindings(VariableStatement stmnt) {
- val newVarDecls = computeVariableDeclarations(stmnt.varDeclsOrBindings);
- stmnt.varDeclsOrBindings.clear();
- stmnt.varDeclsOrBindings += newVarDecls;
- }
-
- /**
- * Transforms (a single) destructuring assignment.
- */
- def public void transformDestructuringAssignment(AssignmentExpression expr) {
- // We pass the value of the expression to the function call. GHOLD-407
- var String fparName;
- var FunctionExpression helperFun;
-
- fparName = "$destruct" + "Param0";
- val fpar = _FormalParameter(fparName);
- helperFun = _FunExpr(false, null, fpar);
-
- val helperFunContents = helperFun.body.statements;
- val rootNode = DestructNode.unify(expr);
- val helperVars = newArrayList;
- val simpleAssignments = >newArrayList;
- traverse(helperVars, simpleAssignments, rootNode, expr.rhs, fparName);
-
- helperFunContents += helperVars.map[_VariableStatement(it)];
- helperFunContents += simpleAssignments.map[
- _ExprStmnt(_AssignmentExpr(
- __NSSafe_IdentRef(it.key),
- it.value
- ))
- ];
-
- // the following return statement is required to make sure entire expression evaluates to the rhs of
- // the assignment expression 'expr' (without evaluating rhs again!)
- val firstHelperVarSTE = findSymbolTableEntryForElement(helperVars.get(0), false);
- helperFunContents += _ReturnStmnt(_IdentRef(firstHelperVarSTE));
-
- val callExpr = _CallExpr(_Parenthesis(helperFun), expr.rhs) // parentheses required because the expression might appear as a statement (to disambiguate from function declaration)
- replace(expr, callExpr);
- }
-
- def private void transformForStatementWithDestructuring(ForStatement stmnt) {
-
- if(stmnt.forPlain) {
-
- if(!stmnt.varDeclsOrBindings.empty) {
- // something like: for( var [a,b]=[1,2] ;;) {}
-
- // note: pretty much the same case as method #transformDestructuringBindings() above
- val newVarDecls = computeVariableDeclarations(stmnt.varDeclsOrBindings);
- stmnt.varDeclsOrBindings.clear();
- stmnt.varDeclsOrBindings += newVarDecls;
-
- } else {
- // something like: for( [a,b]=[1,2] ;;) {}
-
- // here, stmnt.initExpr is an ordinary destructuring assignment that was already handled by method
- // #transformDestructuringAssignment(AssignmentExpression) above -> nothing to do here!
- }
-
- } else {
- // now: for..in OR for..of
-
- val depth = stmnt.nestingDepth;
-
- val iterVar = _VariableDeclaration("$destructStep$"+depth);
- val iterVarSTE = createSymbolTableEntryIMOnly(iterVar);
-
- var needDeclarations = false;
- var varStmtKeyword = VariableStatementKeyword.VAR;
- val helperVars = newArrayList;
- val simpleAssignments = >newArrayList;
- if(!stmnt.varDeclsOrBindings.empty) {
- // something like: for( var [a,b] of [ [1,2], [3,4] ] ) {}
-
- if (AbstractTranspiler.DEBUG_PERFORM_ASSERTIONS) {
- assertTrue("there should be exactly one VariableBinding in stmnt.varDeclsOrBindings",
- stmnt.varDeclsOrBindings.size===1 && stmnt.varDeclsOrBindings.get(0) instanceof VariableBinding);
- }
-
- val rootNode = DestructNode.unify(stmnt.varDeclsOrBindings.head as VariableBinding);
- traverse(helperVars, simpleAssignments, rootNode, _IdentRef(iterVarSTE), null); // fparname = null since we do not generate any function.
- needDeclarations = true;
- varStmtKeyword = stmnt.varStmtKeyword;
-
- } else if(stmnt.initExpr instanceof ArrayLiteral || stmnt.initExpr instanceof ObjectLiteral) {
- // something like: for( [a,b] of [ [1,2], [3,4] ] ) {}
-
- val rootNode = DestructNode.unify(stmnt);
- traverse(helperVars, simpleAssignments, rootNode, _IdentRef(iterVarSTE), null); // fparname = null since we do not generate any function.
- needDeclarations = false;
-
- } else {
- throw new IllegalArgumentException();
- }
-
- if(!(stmnt.statement instanceof Block)) {
- stmnt.statement = _Block(stmnt.statement);
- }
- val body = stmnt.statement as Block;
-
- val toBeInserted = newArrayList;
- if(needDeclarations) {
- toBeInserted += _VariableStatement(varStmtKeyword, simpleAssignments.map[
- val varDecl = key.getVariableDeclarationFromSTE;
- varDecl.expression = value;
- return varDecl;
- ]);
- } else {
- toBeInserted += _VariableStatement(helperVars);
- toBeInserted += simpleAssignments.map[
- _ExprStmnt(_AssignmentExpr(
- __NSSafe_IdentRef(it.key),
- it.value
- ))
- ];
- }
-
- body.statements.addAll(0, toBeInserted);
-
- stmnt.initExpr = null; // initExpr has been move to beginning of body (if any)
- stmnt.varDeclsOrBindings.clear(); // variable declarations have been moved to beginning of body (if any)
- stmnt.varDeclsOrBindings += iterVar; // only declared variable in the for statement is the iteration variable
- }
- }
-
- /**
- * Breaks down all VariableBindings in the given list into simple variable declarations and returns a list containing
- * the variable declarations that were contained in the given list from the beginning plus those created when
- * breaking down the variable bindings. The order of the elements in the given list is preserved!
- */
- def private List computeVariableDeclarations(List