From 36a891cd511f157256cbe66bdf9895b1dca3b84e Mon Sep 17 00:00:00 2001 From: mmews Date: Thu, 14 Dec 2023 14:31:54 +0100 Subject: [PATCH] migrate bundle transpiler --- .../transpiler/SymbolTableManagement.java | 484 +++++++++ .../transpiler/SymbolTableManagement.xtend | 451 --------- .../transpiler/TranspilerBuilderBlocks.java | 954 ++++++++++++++++++ .../transpiler/TranspilerBuilderBlocks.xtend | 870 ---------------- .../n4js/transpiler/TranspilerComponent.java | 19 +- .../transpiler/TranspilerStateOperations.java | 653 ++++++++++++ .../TranspilerStateOperations.xtend | 571 ----------- .../transpiler/assistants/TypeAssistant.java | 228 +++++ .../transpiler/assistants/TypeAssistant.xtend | 204 ---- 9 files changed, 2328 insertions(+), 2106 deletions(-) create mode 100644 plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/SymbolTableManagement.java delete mode 100644 plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/SymbolTableManagement.xtend create mode 100644 plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerBuilderBlocks.java delete mode 100644 plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerBuilderBlocks.xtend create mode 100644 plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerStateOperations.java delete mode 100644 plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerStateOperations.xtend create mode 100644 plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/assistants/TypeAssistant.java delete mode 100644 plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/assistants/TypeAssistant.xtend diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/SymbolTableManagement.java b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/SymbolTableManagement.java new file mode 100644 index 0000000000..a67b88f9a0 --- /dev/null +++ b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/SymbolTableManagement.java @@ -0,0 +1,484 @@ +/** + * 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; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.n4js.n4JS.ImportSpecifier; +import org.eclipse.n4js.n4JS.NamedElement; +import org.eclipse.n4js.n4JS.NamedImportSpecifier; +import org.eclipse.n4js.n4JS.NamespaceImportSpecifier; +import org.eclipse.n4js.transpiler.im.ImFactory; +import org.eclipse.n4js.transpiler.im.ImPackage; +import org.eclipse.n4js.transpiler.im.ReferencingElement_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.transpiler.im.TypeReferenceNode_IM; +import org.eclipse.n4js.ts.types.IdentifiableElement; +import org.eclipse.n4js.ts.types.ModuleNamespaceVirtualType; +import org.eclipse.n4js.ts.types.NameAndAccess; +import org.eclipse.n4js.ts.types.TClassifier; +import org.eclipse.n4js.ts.types.TMember; +import org.eclipse.n4js.ts.types.TModule; + +/** + */ +public class SymbolTableManagement { + + /** + * Create a symbol table entry for a given original target (either a TModule element OR a variable in the original + * AST, in case of non-exported top-level variables, local variables, formal parameters, etc.). + */ + public static SymbolTableEntryOriginal createSymbolTableEntryOriginal(TranspilerState state, + IdentifiableElement originalTarget) { + if (originalTarget == null) { + throw new IllegalArgumentException("original target may not be null"); + } + SymbolTableEntryOriginal newEntry = ImFactory.eINSTANCE.createSymbolTableEntryOriginal(); + newEntry.setName(originalTarget.getName()); + newEntry.setOriginalTarget(originalTarget); + if (originalTarget instanceof NamedElement) { + newEntry.getElementsOfThisName().add((NamedElement) originalTarget); + } + + // if a namespace import exists for the module containing 'originalTarget', we use it for this new STE + newEntry.setImportSpecifier( + getExistingNamespaceImportSpecifierForModule(state, originalTarget.getContainingModule())); + + addOriginal(state, newEntry); + + return newEntry; + } + + private static NamespaceImportSpecifier getExistingNamespaceImportSpecifierForModule(TranspilerState state, + TModule module) { + if (module != null && state.steCache.mapImportedModule_2_STE.get(module) != null) { + ImportSpecifier importSpec = state.steCache.mapImportedModule_2_STE.get(module).getImportSpecifier(); + if (importSpec instanceof NamespaceImportSpecifier) { + return (NamespaceImportSpecifier) importSpec; + } + } + return null; + } + + /** add a {@link SymbolTableEntryOriginal} */ + static public void addOriginal(TranspilerState state, SymbolTableEntryOriginal steOriginal) { + addOriginal(state.steCache, steOriginal); + } + + /** + * NOTE: Internal usage in preparation step, please call + * {@link #addOriginal(TranspilerState,SymbolTableEntryOriginal)} + */ + static public void addOriginal(TranspilerState.STECache steCache, SymbolTableEntryOriginal steOriginal) { + + SymbolTableEntryOriginal old = steCache.mapOriginal.put(steOriginal.getOriginalTarget(), steOriginal); + if (old != null) + throw new IllegalStateException( + "It is not allowed to register more then one STEOriginal for the same original Target. Already had: " + + old); + IdentifiableElement originalTarget = steOriginal.getOriginalTarget(); + if (originalTarget instanceof ModuleNamespaceVirtualType) { + TModule namespaceModule = ((ModuleNamespaceVirtualType) originalTarget).getModule(); + if (namespaceModule != null) { + steCache.mapImportedModule_2_STE.put(namespaceModule, steOriginal); + } + } + steCache.im.getSymbolTable().getEntries().add(steOriginal); + inverseMap(steCache, steOriginal); + } + + static private void inverseMap(TranspilerState.STECache steManager, SymbolTableEntryOriginal steOriginal) { + // register elements of this name. + for (NamedElement ele : steOriginal.getElementsOfThisName()) { + steManager.mapNamedElement_2_STE.put(ele, steOriginal); + } + } + + /** + * Create a symbol table entry for an element in the intermediate model. This should only be used if the element in + * the IM does not have a corresponding original target (either a TModule element or an element in the + * original AST, in case of non-exported variables), for example because it was newly created by an AST + * transformation. + */ + public static SymbolTableEntryIMOnly createSymbolTableEntryIMOnly(TranspilerState state, NamedElement elementInIM) { + if (elementInIM == null) { + throw new IllegalArgumentException("element in intermediate model may not be null"); + } + if (elementInIM.getName() == null) { + throw new IllegalArgumentException( + "element in intermediate model may not be unnamed when creating a symbol table entry for it"); + } + SymbolTableEntryIMOnly newEntry = ImFactory.eINSTANCE.createSymbolTableEntryIMOnly(); + newEntry.setName(elementInIM.getName()); + newEntry.getElementsOfThisName().add(elementInIM); + addIMOnly(state, newEntry); + return newEntry; + } + + /** + * Create an internal symbol table entry. They are special and should be used only in rare exception cases. + * See {@link SymbolTableEntryInternal} for details. + */ + public static SymbolTableEntryInternal createSymbolTableEntryInternal(TranspilerState state, String name) { + if (name == null) { + throw new IllegalArgumentException("name may not be null"); + } + SymbolTableEntryInternal newEntry = ImFactory.eINSTANCE.createSymbolTableEntryInternal(); + newEntry.setName(name); + addInteral(state, newEntry); + return newEntry; + } + + /** add a {@link SymbolTableEntryInternal} */ + private static void addInteral(TranspilerState state, SymbolTableEntryInternal ste) { + SymbolTableEntryInternal old = state.steCache.mapInternal.put(ste.getName(), ste); + if (old != null) { + throw new IllegalStateException( + "It is not allowed to put the same SymbolTableEntryInternal twice into the Symboltable " + old); + } + state.im.getSymbolTable().getEntries().add(ste); + } + + /** + * Search an STE by original target and create it if not found. + */ + public static SymbolTableEntryOriginal getSymbolTableEntryOriginal(TranspilerState state, + IdentifiableElement originalTarget, boolean create) { + if (originalTarget == null) { + throw new IllegalArgumentException("original target may not be null"); + } + SymbolTableEntryOriginal existingEntry = getSteOriginal(state, originalTarget); + + if (existingEntry != null) { + return existingEntry; + } + if (create) { + return createSymbolTableEntryOriginal(state, originalTarget); + } + return null; + } + + /** + * Convenience method for {@link #getSymbolTableEntryOriginal(TranspilerState, IdentifiableElement, boolean)}, + * allowing to retrieve the member by name and access from its parent classifier. + */ + public static SymbolTableEntryOriginal getSymbolTableEntryForMember(TranspilerState state, TClassifier type, + String memberName, boolean writeAccess, boolean staticAccess, boolean create) { + if (type == null || memberName == null || memberName.isEmpty()) { + throw new IllegalArgumentException("type may not be null and memberName may not be null or empty"); + } + TMember m = type.findOwnedMember(memberName, writeAccess, staticAccess); + if (m == null) { + NameAndAccess nameAndAccess = new NameAndAccess(memberName, writeAccess, staticAccess); + throw new IllegalArgumentException("no such member found in given type: " + nameAndAccess); + } + return getSymbolTableEntryOriginal(state, m, create); + } + + /** + * Search an internal STE by name and create it if not found. + */ + public static SymbolTableEntryInternal getSymbolTableEntryInternal(TranspilerState state, String name, + boolean create) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("name may not be null or empty"); + } + + SymbolTableEntryInternal existingEntry = getSteInternal(state, name); + + if (existingEntry != null) { + return existingEntry; + } + if (create) { + return createSymbolTableEntryInternal(state, name); + } + return null; + } + + /** + * Will look up the STE for the given named element in the IM. If not found and create is set to + * true a {@code SymbolTableEntryIMOnly} is created, otherwise null is returned. + *

+ * WARNING: during look up it will find both {@link SymbolTableEntryOriginal}s and + * {@link SymbolTableEntryIMOnly}s, but when creating a new STE, it will always create a + * {@code SymbolTableEntryIMOnly} which is invalid if there exists an original target for the given + * elementInIM (then a {@link SymbolTableEntryOriginal} would have to be created)!. In such a case, + * this method must not be used.
+ * Most of the time, this won't be the case and it is safe to use this method, because all + * {@code SymbolTableEntryOriginal}s will be created up-front during the {@link PreparationStep}; in some special + * cases, however, a new element is introduced into the IM that actually has an original target (so far, static + * polyfills are the only case of this). + */ + public static SymbolTableEntry findSymbolTableEntryForElement(TranspilerState state, NamedElement elementInIM, + boolean create) { + if (elementInIM == null) { + throw new IllegalArgumentException("element in intermediate model may not be null"); + } + SymbolTableEntry existingEntry = byElementsOfThisName(state, elementInIM); + + if (existingEntry != null) { + return existingEntry; + } + if (create) { + return createSymbolTableEntryIMOnly(state, elementInIM); + } + return null; + } + + /** + * Search STE for the given name space import. + */ + public static SymbolTableEntryOriginal findSymbolTableEntryForNamespaceImport(TranspilerState state, + NamespaceImportSpecifier importspec) { + // 1. linear version: + // state.im.symbolTable.entries.filter(SymbolTableEntryOriginal) + // .filter[it.importSpecifier == importspec] + // .filter[it.originalTarget instanceof ModuleNamespaceVirtualType] + // .head + + // 2. parallel version: + // return state.im.symbolTable.entries.parallelStream() + // .filter[it instanceof SymbolTableEntryOriginal].map[ it as SymbolTableEntryOriginal] + // .filter[it.importSpecifier == importspec] + // .filter[it.originalTarget instanceof ModuleNamespaceVirtualType] + // .findAny().orElse(null); + + // 3. only the originals: + // Should be safe to use the cache. + for (SymbolTableEntryOriginal steo : state.steCache.mapOriginal.values()) { + if (steo.getImportSpecifier() == importspec + && steo.getOriginalTarget() instanceof ModuleNamespaceVirtualType) { + return steo; + } + } + + return null; + } + + // let's try to keep this at "package" visibility for now (but there'll probably be special cases when a + // transformation needs to rewire something special by calling this directly) + /* package */ static void rewireSymbolTable(TranspilerState state, EObject from, EObject to) { + + if (!requiresRewiringOfSymbolTable(from) && !requiresRewiringOfSymbolTable(to)) { + return; // nothing to rewire! + } + + if (from instanceof ReferencingElement_IM && to instanceof ReferencingElement_IM) { + // case 1 + EReference eRefThatMightPointToOriginal = ImPackage.eINSTANCE.getSymbolTableEntry_ReferencingElements(); + // TODO can be speed up + for (SymbolTableEntry ste : state.im.getSymbolTable().getEntries()) { + replaceInEReference(ste, eRefThatMightPointToOriginal, from, to); + } + + } else if (from instanceof ImportSpecifier && to instanceof ImportSpecifier) { + // case 2 + EReference eRefThatMightPointToOriginal = ImPackage.eINSTANCE.getSymbolTableEntryOriginal_ImportSpecifier(); + // TODO can be speed up + for (SymbolTableEntry ste : state.im.getSymbolTable().getEntries()) { + if (ste instanceof SymbolTableEntryOriginal) { + replaceInEReference(ste, eRefThatMightPointToOriginal, from, to); + } + } + + } else if (from instanceof NamedElement && to instanceof NamedElement) { + // case 3 // Most relevant case according to profiler + EReference eRefThatMightPointToOriginal = ImPackage.eINSTANCE.getSymbolTableEntry_ElementsOfThisName(); + // Slow version: + // state.im.symbolTable.entries_.forEach[ + // replaceInEReference(it, x, from, to); + // ]; + + SymbolTableEntry steFrom = byElementsOfThisName(state, (NamedElement) from); + if (steFrom != null) { + replaceInEReference(steFrom, eRefThatMightPointToOriginal, from, to); + // update STECache: + replacedElementOfThisName(state, steFrom, (NamedElement) from, (NamedElement) to); + } + } else { + throw new IllegalArgumentException("rewiring symbol table entries from type " + from.eClass().getName() + + " to type " + to.eClass().getName() + " is not supported yet"); + } + } + + private static boolean requiresRewiringOfSymbolTable(EObject obj) { + return obj instanceof ReferencingElement_IM || obj instanceof ImportSpecifier || obj instanceof NamedElement; + } + + private static void replaceInEReference(EObject obj, EReference eRef, T original, + TN replacement) { + // note: cannot use EcoreUtil#replace() here, because it throws exceptions if original is not in reference! + if (eRef.isMany()) { + @SuppressWarnings("unchecked") + EList l = (EList) obj.eGet(eRef); + for (int idx = 0; idx < l.size(); idx++) { + if (l.get(idx) == original) { + l.set(idx, replacement); + } + } + } else { + if (obj.eGet(eRef) == original) { + obj.eSet(eRef, replacement); + } + } + } + + /** add a {@link SymbolTableEntryIMOnly} */ + static public void addIMOnly(TranspilerState state, SymbolTableEntryIMOnly only) { + // assumption 1: freshly generated - always connected to a named element. (IDEBUG-777) + if (only.getElementsOfThisName().size() != 1) { + throw new IllegalArgumentException( + "got a STEImOnly with elmentsOfThisName != 1 : " + only.getElementsOfThisName().size()); + } + // assumption 2: there are no other things registered by this name. (IDEBUG-777) + SymbolTableEntry old = state.steCache.mapNamedElement_2_STE.put(only.getElementsOfThisName().get(0), only); + if (old != null) { + throw new IllegalStateException("tries to install STEImOnly but already had one for the NamedElmeent = " + + only.getElementsOfThisName().get(0)); + } + state.im.getSymbolTable().getEntries().add(only); + } + + /** lookup a {@link SymbolTableEntryIMOnly} associated to an {@link IdentifiableElement} */ + static public SymbolTableEntryOriginal getSteOriginal(TranspilerState state, IdentifiableElement element) { + return state.steCache.mapOriginal.get(element); + } + + /** lookup an {@link SymbolTableEntryInternal} based on a plain name ({@link String}) */ + static public SymbolTableEntryInternal getSteInternal(TranspilerState state, String name) { + return state.steCache.mapInternal.get(name); + } + + /** lookup a {@link SymbolTableEntry} based on a {@link NamedElement} contained in the IM */ + static public SymbolTableEntry byElementsOfThisName(TranspilerState state, NamedElement elementInIM) { + SymbolTableEntry lookup = state.steCache.mapNamedElement_2_STE.get(elementInIM); + if (lookup != null) { + if (lookup.getElementsOfThisName().contains(elementInIM)) { + return lookup; + } + throw new IllegalStateException( + "Did find STE by NamedElement which is not contained in the list STE.elementsOfThisName. elementInIM=" + + elementInIM + " found wrong STE=" + lookup); + } + + return null; + } + + /** + * Update data structure for NamedElements after the list of {@link SymbolTableEntry#getElementsOfThisName()} of + * {@code entry} has been modified + * + * @param entry + * the updated STE (wherein elmentsOfThisName has been modified to contain {@code to} instead of + * {@code from} + * @param from + * old NamedElement + * @param to + * new NamedElement + */ + static public void replacedElementOfThisName(TranspilerState state, SymbolTableEntry entry, NamedElement from, + NamedElement to) { + + // internal check: + SymbolTableEntry steRegisteredWithFrom = state.steCache.mapNamedElement_2_STE.get(from); + if (steRegisteredWithFrom != entry) + throw new IllegalArgumentException( + "This method must be called directly after the replacement and only once." + + "Expected from=" + from + " to be related to entry=" + entry + + " in mapNamedElement_2_STE but found: " + steRegisteredWithFrom); + // repair map: + state.steCache.mapNamedElement_2_STE.remove(from); + state.steCache.mapNamedElement_2_STE.put(to, entry); + } + + /***/ + static public SymbolTableEntryOriginal findSymbolTableEntryForNamedImport(TranspilerState state, + NamedImportSpecifier importspec) { + + for (SymbolTableEntryOriginal steo : state.steCache.mapOriginal.values()) { + if (steo.getImportSpecifier() == importspec) { + return steo; + } + } + return null; + } + + /** + * This method defaults to {@link #findSymbolTableEntryForNamedImport(TranspilerState, NamedImportSpecifier)}. + */ + static public Collection findSymbolTableEntriesForVersionedTypeImport( + TranspilerState state, NamedImportSpecifier importspec) { + return List.of(findSymbolTableEntryForNamedImport(state, importspec)); + } + + /** + * Records in property {@link TypeReferenceNode_IM#getRewiredReferences() rewiredReferences} that the given type + * reference node refers to the type represented by the given symbol table entry. + */ + static public void recordReferenceToType(TranspilerState state, TypeReferenceNode_IM typeRefNode, + SymbolTableEntryOriginal ste) { + + // 1) record the reference to the type represented by 'ste' itself + typeRefNode.addRewiredTarget(ste); + // 2) record the reference to the namespace iff the type represented by 'ste' was imported via a namespace + // import + ImportSpecifier importSpec = ste.getImportSpecifier(); + if (importSpec instanceof NamespaceImportSpecifier) { + ModuleNamespaceVirtualType namespaceType = state.info + .getOriginalDefinedType((NamespaceImportSpecifier) importSpec); + if (namespaceType != null) { + SymbolTableEntryOriginal namespaceSTE = getSymbolTableEntryOriginal(state, namespaceType, false); + if (namespaceSTE != null) { + typeRefNode.addRewiredTarget(namespaceSTE); + } + } + } + } + + /***/ + static public void rename(SymbolTableEntry entry, String name) { + if (entry instanceof SymbolTableEntryInternal) { + throw new UnsupportedOperationException("cannot rename internal STEs " + entry); + + } else if (entry instanceof SymbolTableEntryIMOnly) { + entry.setName(name); + + } else if (entry instanceof SymbolTableEntryOriginal) { + + entry.setName(name); + + // should do something like the following: + // (not possible at the moment, because NamedElement does not have a setter for property 'name') + // entry.elementsOfThisName.forEach[it.name=newName]; + + if (((SymbolTableEntryOriginal) entry).getImportSpecifier() != null) { + throw new UnsupportedOperationException( + "renaming of symbol table entries not tested yet for imported elements!"); + } + // should be something like the following: + // switch(impSpec) { + // NamedImportSpecifier: if(impSpec.alias!=null) impSpec.alias = newName + // NamespaceImportSpecifier: if(impSpec.alias!=null) impSpec.alias = newName + // } + } else { + throw new UnsupportedOperationException( + "Rename request for SymboltableEntries of unkown type : " + entry); + } + } + +} diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/SymbolTableManagement.xtend b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/SymbolTableManagement.xtend deleted file mode 100644 index 744a9e1ae8..0000000000 --- a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/SymbolTableManagement.xtend +++ /dev/null @@ -1,451 +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 - -import java.util.Collection -import org.eclipse.emf.common.util.EList -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.EReference -import org.eclipse.n4js.n4JS.ImportSpecifier -import org.eclipse.n4js.n4JS.NamedElement -import org.eclipse.n4js.n4JS.NamedImportSpecifier -import org.eclipse.n4js.n4JS.NamespaceImportSpecifier -import org.eclipse.n4js.transpiler.im.ImFactory -import org.eclipse.n4js.transpiler.im.ImPackage -import org.eclipse.n4js.transpiler.im.ReferencingElement_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.transpiler.im.TypeReferenceNode_IM -import org.eclipse.n4js.ts.types.IdentifiableElement -import org.eclipse.n4js.ts.types.ModuleNamespaceVirtualType -import org.eclipse.n4js.ts.types.NameAndAccess -import org.eclipse.n4js.ts.types.TClassifier -import org.eclipse.n4js.ts.types.TModule - -/** - */ -class SymbolTableManagement { - - /** - * Create a symbol table entry for a given original target (either a TModule element OR a variable in the original - * AST, in case of non-exported top-level variables, local variables, formal parameters, etc.). - */ - def public static SymbolTableEntryOriginal createSymbolTableEntryOriginal(TranspilerState state, IdentifiableElement originalTarget) { - if(originalTarget===null) { - throw new IllegalArgumentException("original target may not be null"); - } - val newEntry = ImFactory.eINSTANCE.createSymbolTableEntryOriginal; - newEntry.name = originalTarget.name; - newEntry.originalTarget = originalTarget; - if(originalTarget instanceof NamedElement) { - newEntry.elementsOfThisName += originalTarget as NamedElement; - } - - // if a namespace import exists for the module containing 'originalTarget', we use it for this new STE - newEntry.importSpecifier = getExistingNamespaceImportSpecifierForModule(state, originalTarget.containingModule); - - state.addOriginal(newEntry) - - return newEntry; - } - - def private static NamespaceImportSpecifier getExistingNamespaceImportSpecifierForModule(TranspilerState state, TModule module) { - if (module !== null) { - val importSpec = state.steCache.mapImportedModule_2_STE.get(module)?.importSpecifier; - if (importSpec instanceof NamespaceImportSpecifier) { - return importSpec; - } - } - return null; - } - - /** add a {@link SymbolTableEntryOriginal} */ - def static public void addOriginal(TranspilerState state, SymbolTableEntryOriginal steOriginal) { - addOriginal ( state.steCache, steOriginal ) ; - } - - /** NOTE: Internal usage in preparation step, please call {@link #addOriginal(TranspilerState,SymbolTableEntryOriginal)} */ - def static public void addOriginal(TranspilerState.STECache steCache, SymbolTableEntryOriginal steOriginal) { - - val SymbolTableEntryOriginal old = steCache.mapOriginal.put(steOriginal.getOriginalTarget(), steOriginal); - if (old !== null) - throw new IllegalStateException( - "It is not allowed to register more then one STEOriginal for the same original Target. Already had: " - + old); - val originalTarget = steOriginal.originalTarget; - if (originalTarget instanceof ModuleNamespaceVirtualType) { - val namespaceModule = originalTarget.module; - if (namespaceModule !== null) { - steCache.mapImportedModule_2_STE.put(namespaceModule, steOriginal); - } - } - steCache.im.getSymbolTable().getEntries().add(steOriginal); - steCache.inverseMap(steOriginal); - } - - - def static private void inverseMap(TranspilerState.STECache steManager, SymbolTableEntryOriginal steOriginal) { - // register elements of this name. - steOriginal.getElementsOfThisName().forEach[ele | steManager.mapNamedElement_2_STE.put(ele, steOriginal)]; - } - - - /** - * Create a symbol table entry for an element in the intermediate model. This should only be used if the element - * in the IM does not have a corresponding original target (either a TModule element or an element - * in the original AST, in case of non-exported variables), for example because it was newly created by an AST - * transformation. - */ - def public static SymbolTableEntryIMOnly createSymbolTableEntryIMOnly(TranspilerState state, NamedElement elementInIM) { - if(elementInIM===null) { - throw new IllegalArgumentException("element in intermediate model may not be null"); - } - if(elementInIM.name===null) { - throw new IllegalArgumentException("element in intermediate model may not be unnamed when creating a symbol table entry for it"); - } - val newEntry = ImFactory.eINSTANCE.createSymbolTableEntryIMOnly; - newEntry.name = elementInIM.name; - newEntry.elementsOfThisName += elementInIM; - state.addIMOnly( newEntry ); - return newEntry; - } - - /** - * Create an internal symbol table entry. They are special and should be used only in rare exception cases. - * See {@link SymbolTableEntryInternal} for details. - */ - def public static SymbolTableEntryInternal createSymbolTableEntryInternal(TranspilerState state, String name) { - if(name===null) { - throw new IllegalArgumentException("name may not be null"); - } - val newEntry = ImFactory.eINSTANCE.createSymbolTableEntryInternal; - newEntry.name = name; - state.addInteral( newEntry ); - return newEntry; - } - - - - /** add a {@link SymbolTableEntryInternal} */ - def private static void addInteral(TranspilerState state, SymbolTableEntryInternal ste) { - val SymbolTableEntryInternal old = state.steCache.mapInternal.put(ste.getName(), ste); - if (old !== null) - throw new IllegalStateException( - "It is not allowed to put the same SymbolTableEntryInternal twice into the Symboltable " + old); - state.im.getSymbolTable().getEntries().add(ste); - } - - - - /** - * Search an STE by original target and create it if not found. - */ - def public static SymbolTableEntryOriginal getSymbolTableEntryOriginal(TranspilerState state, IdentifiableElement originalTarget, boolean create) { - if(originalTarget===null) { - throw new IllegalArgumentException("original target may not be null"); - } - val existingEntry = state.getSteOriginal(originalTarget); - - if(existingEntry!==null) { - return existingEntry; - } - if(create) { - return createSymbolTableEntryOriginal(state, originalTarget); - } - return null; - } - - /** - * Convenience method for {@link #getSymbolTableEntryOriginal(TranspilerState, IdentifiableElement, boolean}, - * allowing to retrieve the member by name and access from its parent classifier. - */ - def public static SymbolTableEntryOriginal getSymbolTableEntryForMember(TranspilerState state, TClassifier type, - String memberName, boolean writeAccess, boolean staticAccess, boolean create) { - if(type===null || memberName===null || memberName.empty) { - throw new IllegalArgumentException("type may not be null and memberName may not be null or empty"); - } - val m = type.findOwnedMember(memberName, writeAccess, staticAccess); - if(m===null) { - val nameAndAccess = new NameAndAccess(memberName, writeAccess, staticAccess); - throw new IllegalArgumentException("no such member found in given type: " + nameAndAccess); - } - return getSymbolTableEntryOriginal(state, m, create); - } - - /** - * Search an internal STE by name and create it if not found. - */ - def public static SymbolTableEntryInternal getSymbolTableEntryInternal(TranspilerState state, String name, boolean create) { - if(name===null || name.empty) { - throw new IllegalArgumentException("name may not be null or empty"); - } - - val existingEntry = state.getSteInternal(name); - - if(existingEntry!==null) { - return existingEntry; - } - if(create) { - return createSymbolTableEntryInternal(state, name); - } - return null; - } - - /** - * Will look up the STE for the given named element in the IM. If not found and create is set to - * true a {@code SymbolTableEntryIMOnly} is created, otherwise null is returned. - *

- * WARNING: during look up it will find both {@link SymbolTableEntryOriginal}s and {@link SymbolTableEntryIMOnly}s, - * but when creating a new STE, it will always create a {@code SymbolTableEntryIMOnly} which is invalid if there - * exists an original target for the given elementInIM (then a {@link SymbolTableEntryOriginal} would - * have to be created)!. In such a case, this method must not be used.
- * Most of the time, this won't be the case and it is safe to use this method, because all - * {@code SymbolTableEntryOriginal}s will be created up-front during the {@link PreparationStep}; in some special - * cases, however, a new element is introduced into the IM that actually has an original target (so far, static - * polyfills are the only case of this). - */ - def public static SymbolTableEntry findSymbolTableEntryForElement(TranspilerState state, NamedElement elementInIM, boolean create) { - if(elementInIM===null) { - throw new IllegalArgumentException("element in intermediate model may not be null"); - } - val existingEntry = state.byElementsOfThisName(elementInIM); - - if(existingEntry!==null) { - return existingEntry; - } - if(create) { - return createSymbolTableEntryIMOnly(state, elementInIM); - } - return null; - } - - /** - * Search STE for the given name space import. - */ - def public static SymbolTableEntryOriginal findSymbolTableEntryForNamespaceImport(TranspilerState state, NamespaceImportSpecifier importspec) { - // 1. linear version: - // state.im.symbolTable.entries.filter(SymbolTableEntryOriginal) - // .filter[it.importSpecifier === importspec] - // .filter[it.originalTarget instanceof ModuleNamespaceVirtualType] - // .head - - // 2. parallel version: - // return state.im.symbolTable.entries.parallelStream() - // .filter[it instanceof SymbolTableEntryOriginal].map[ it as SymbolTableEntryOriginal] - // .filter[it.importSpecifier === importspec] - // .filter[it.originalTarget instanceof ModuleNamespaceVirtualType] - // .findAny().orElse(null); - - // 3. only the originals: - // Should be safe to use the cache. - return state.steCache.mapOriginal.values.parallelStream() - .filter[it.importSpecifier === importspec] - .filter[it.originalTarget instanceof ModuleNamespaceVirtualType] - .findAny().orElse(null); - } - - - - // let's try to keep this at "package" visibility for now (but there'll probably be special cases when a - // transformation needs to rewire something special by calling this directly) - def /*package*/ static void rewireSymbolTable(TranspilerState state, EObject from, EObject to) { - if(!from.requiresRewiringOfSymbolTable && !to.requiresRewiringOfSymbolTable) { - return; // nothing to rewire! - } - if(from instanceof ReferencingElement_IM && to instanceof ReferencingElement_IM) { - // case 1 - val eRefThatMightPointToOriginal = ImPackage.eINSTANCE.symbolTableEntry_ReferencingElements; - // TODO can be speed up - state.im.symbolTable.entries.parallelStream - .forEach[ - replaceInEReference(it, eRefThatMightPointToOriginal, from, to); - ]; - - } else if(from instanceof ImportSpecifier && to instanceof ImportSpecifier) { - // case 2 - val eRefThatMightPointToOriginal = ImPackage.eINSTANCE.symbolTableEntryOriginal_ImportSpecifier; - // TODO can be speed up - state.im.symbolTable.entries.parallelStream.filter[it instanceof SymbolTableEntryOriginal] - .forEach[ - replaceInEReference(it, eRefThatMightPointToOriginal, from, to); - ]; - - } else if(from instanceof NamedElement && to instanceof NamedElement) { - // case 3 // Most relevant case according to profiler - val eRefThatMightPointToOriginal = ImPackage.eINSTANCE.symbolTableEntry_ElementsOfThisName; - // Slow version: - // state.im.symbolTable.entries_.forEach[ - // replaceInEReference(it, x, from, to); - // ]; - - val steFrom = state.byElementsOfThisName(from as NamedElement); - if( steFrom !== null ) { - replaceInEReference(steFrom, eRefThatMightPointToOriginal, from , to ); - // update STECache: - state.replacedElementOfThisName( steFrom, from as NamedElement, to as NamedElement ) - } - } else { - throw new IllegalArgumentException("rewiring symbol table entries from type " + from.eClass.name + - " to type " + to.eClass.name + " is not supported yet"); - } - } - - def private static boolean requiresRewiringOfSymbolTable(EObject obj) { - return obj instanceof ReferencingElement_IM || obj instanceof ImportSpecifier || obj instanceof NamedElement; - } - - def private static void replaceInEReference(EObject obj, EReference eRef, T original, TN replacement) { - // note: cannot use EcoreUtil#replace() here, because it throws exceptions if original is not in reference! - if(eRef.many) { - val l = obj.eGet(eRef) as EList; - for(idx : 0.. findSymbolTableEntriesForVersionedTypeImport(TranspilerState state, NamedImportSpecifier importspec) { - return #[findSymbolTableEntryForNamedImport(state, importspec)]; - } - - - /** - * Records in property {@link TypeReferenceNode_IM#getRewiredReferences() rewiredReferences} that the given type reference node refers - * to the type represented by the given symbol table entry. - */ - def static public void recordReferenceToType(TranspilerState state, TypeReferenceNode_IM typeRefNode, SymbolTableEntryOriginal ste) { - // 1) record the reference to the type represented by 'ste' itself - typeRefNode.addRewiredTarget(ste); - // 2) record the reference to the namespace iff the type represented by 'ste' was imported via a namespace import - val importSpec = ste.importSpecifier; - if (importSpec instanceof NamespaceImportSpecifier) { - val namespaceType = state.info.getOriginalDefinedType(importSpec); - if (namespaceType !== null) { - val namespaceSTE = getSymbolTableEntryOriginal(state, namespaceType, false); - if (namespaceSTE !== null) { - typeRefNode.addRewiredTarget(namespaceSTE); - } - } - } - } - - - def static public void rename(TranspilerState state, SymbolTableEntry entry, String name) { - - if (entry instanceof SymbolTableEntryInternal) { - throw new UnsupportedOperationException("cannot rename internal STEs " + entry); - - } else if (entry instanceof SymbolTableEntryIMOnly) { - entry.setName(name); - - } else if (entry instanceof SymbolTableEntryOriginal) { - - entry.setName(name); - - // should do something like the following: - // (not possible at the moment, because NamedElement does not have a setter for property 'name') - // entry.elementsOfThisName.forEach[it.name=newName]; - - if (entry.getImportSpecifier() !== null) - throw new UnsupportedOperationException( - "renaming of symbol table entries not tested yet for imported elements!"); - // should be something like the following: - // switch(impSpec) { - // NamedImportSpecifier: if(impSpec.alias!==null) impSpec.alias = newName - // NamespaceImportSpecifier: if(impSpec.alias!==null) impSpec.alias = newName - // } - } else { - throw new UnsupportedOperationException( - "Rename request for SymboltableEntries of unkown type : " + entry); - } - - } -} diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerBuilderBlocks.java b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerBuilderBlocks.java new file mode 100644 index 0000000000..1840cbeb79 --- /dev/null +++ b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerBuilderBlocks.java @@ -0,0 +1,954 @@ +/** + * 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; + +import static com.google.common.collect.Iterables.toArray; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.exists; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.filterNull; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.isEmpty; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.map; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.reduce; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.toList; +import static org.eclipse.xtext.xbase.lib.ListExtensions.map; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.n4js.AnnotationDefinition; +import org.eclipse.n4js.n4JS.AdditiveExpression; +import org.eclipse.n4js.n4JS.AdditiveOperator; +import org.eclipse.n4js.n4JS.AnnotableN4MemberDeclaration; +import org.eclipse.n4js.n4JS.Annotation; +import org.eclipse.n4js.n4JS.AnnotationList; +import org.eclipse.n4js.n4JS.Argument; +import org.eclipse.n4js.n4JS.ArrayElement; +import org.eclipse.n4js.n4JS.ArrayLiteral; +import org.eclipse.n4js.n4JS.ArrayPadding; +import org.eclipse.n4js.n4JS.ArrowFunction; +import org.eclipse.n4js.n4JS.AssignmentExpression; +import org.eclipse.n4js.n4JS.AssignmentOperator; +import org.eclipse.n4js.n4JS.BinaryLogicalExpression; +import org.eclipse.n4js.n4JS.BinaryLogicalOperator; +import org.eclipse.n4js.n4JS.BindingProperty; +import org.eclipse.n4js.n4JS.Block; +import org.eclipse.n4js.n4JS.BooleanLiteral; +import org.eclipse.n4js.n4JS.CommaExpression; +import org.eclipse.n4js.n4JS.ConditionalExpression; +import org.eclipse.n4js.n4JS.DefaultImportSpecifier; +import org.eclipse.n4js.n4JS.EmptyStatement; +import org.eclipse.n4js.n4JS.EqualityExpression; +import org.eclipse.n4js.n4JS.EqualityOperator; +import org.eclipse.n4js.n4JS.ExportDeclaration; +import org.eclipse.n4js.n4JS.ExportableElement; +import org.eclipse.n4js.n4JS.Expression; +import org.eclipse.n4js.n4JS.ExpressionStatement; +import org.eclipse.n4js.n4JS.FormalParameter; +import org.eclipse.n4js.n4JS.FunctionDeclaration; +import org.eclipse.n4js.n4JS.FunctionExpression; +import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor; +import org.eclipse.n4js.n4JS.IfStatement; +import org.eclipse.n4js.n4JS.ImportDeclaration; +import org.eclipse.n4js.n4JS.ImportSpecifier; +import org.eclipse.n4js.n4JS.IndexedAccessExpression; +import org.eclipse.n4js.n4JS.IntLiteral; +import org.eclipse.n4js.n4JS.LiteralOrComputedPropertyName; +import org.eclipse.n4js.n4JS.N4ClassDeclaration; +import org.eclipse.n4js.n4JS.N4EnumDeclaration; +import org.eclipse.n4js.n4JS.N4EnumLiteral; +import org.eclipse.n4js.n4JS.N4FieldDeclaration; +import org.eclipse.n4js.n4JS.N4GetterDeclaration; +import org.eclipse.n4js.n4JS.N4InterfaceDeclaration; +import org.eclipse.n4js.n4JS.N4JSFactory; +import org.eclipse.n4js.n4JS.N4MemberAnnotationList; +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.N4TypeVariable; +import org.eclipse.n4js.n4JS.NamedImportSpecifier; +import org.eclipse.n4js.n4JS.NamespaceImportSpecifier; +import org.eclipse.n4js.n4JS.NewExpression; +import org.eclipse.n4js.n4JS.NullLiteral; +import org.eclipse.n4js.n4JS.NumericLiteral; +import org.eclipse.n4js.n4JS.ObjectBindingPattern; +import org.eclipse.n4js.n4JS.ObjectLiteral; +import org.eclipse.n4js.n4JS.ParameterizedCallExpression; +import org.eclipse.n4js.n4JS.ParenExpression; +import org.eclipse.n4js.n4JS.PropertyAssignment; +import org.eclipse.n4js.n4JS.PropertyAssignmentAnnotationList; +import org.eclipse.n4js.n4JS.PropertyGetterDeclaration; +import org.eclipse.n4js.n4JS.PropertyNameKind; +import org.eclipse.n4js.n4JS.PropertyNameOwner; +import org.eclipse.n4js.n4JS.PropertyNameValuePair; +import org.eclipse.n4js.n4JS.RelationalExpression; +import org.eclipse.n4js.n4JS.RelationalOperator; +import org.eclipse.n4js.n4JS.ReturnStatement; +import org.eclipse.n4js.n4JS.Statement; +import org.eclipse.n4js.n4JS.StringLiteral; +import org.eclipse.n4js.n4JS.SuperLiteral; +import org.eclipse.n4js.n4JS.ThisLiteral; +import org.eclipse.n4js.n4JS.ThrowStatement; +import org.eclipse.n4js.n4JS.UnaryExpression; +import org.eclipse.n4js.n4JS.UnaryOperator; +import org.eclipse.n4js.n4JS.VariableBinding; +import org.eclipse.n4js.n4JS.VariableDeclaration; +import org.eclipse.n4js.n4JS.VariableDeclarationOrBinding; +import org.eclipse.n4js.n4JS.VariableStatement; +import org.eclipse.n4js.n4JS.VariableStatementKeyword; +import org.eclipse.n4js.n4JS.YieldExpression; +import org.eclipse.n4js.postprocessing.CompileTimeExpressionProcessor; +import org.eclipse.n4js.postprocessing.ComputedNameProcessor; +import org.eclipse.n4js.transpiler.im.IdentifierRef_IM; +import org.eclipse.n4js.transpiler.im.ImFactory; +import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM; +import org.eclipse.n4js.transpiler.im.Snippet; +import org.eclipse.n4js.transpiler.im.StringLiteralForSTE; +import org.eclipse.n4js.transpiler.im.SymbolTableEntry; +import org.eclipse.n4js.transpiler.im.TypeReferenceNode_IM; +import org.eclipse.n4js.ts.typeRefs.TypeRef; +import org.eclipse.n4js.ts.types.TField; +import org.eclipse.n4js.ts.types.TGetter; +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.types.utils.TypeUtils; +import org.eclipse.xtext.xbase.lib.Pair; + +/** + * Builder methods for intermediate elements. + */ +@SuppressWarnings("javadoc") +public class TranspilerBuilderBlocks { + + // ############################################################################################ + // n4js.xcore + + public static ImportDeclaration _ImportDecl(ImportSpecifier... importSpecifiers) { + ImportDeclaration result = N4JSFactory.eINSTANCE.createImportDeclaration(); + result.setModule(null); // must always be null, because we are in the intermediate model + result.getImportSpecifiers().addAll(toList(filterNull(Arrays.asList(importSpecifiers)))); + result.setImportFrom(result.getImportSpecifiers().size() > 0); + return result; + } + + public static NamedImportSpecifier _NamedImportSpecifier(String importedElementName, String alias, + boolean usedInCode) { + + NamedImportSpecifier result = N4JSFactory.eINSTANCE.createNamedImportSpecifier(); + result.setImportedElement(null); // must always be null, because we are in the intermediate model + result.setImportedElementAsText(importedElementName); + result.setAlias(alias); + result.setFlaggedUsedInCode(usedInCode); + return result; + } + + public static NamedImportSpecifier _DefaultImportSpecifier(String importedElementName, boolean usedInCode) { + DefaultImportSpecifier result = N4JSFactory.eINSTANCE.createDefaultImportSpecifier(); + result.setImportedElement(null); // must always be null, because we are in the intermediate model + result.setImportedElementAsText(importedElementName); + result.setFlaggedUsedInCode(usedInCode); + return result; + } + + public static NamespaceImportSpecifier _NamespaceImportSpecifier(String namespaceName, boolean usedInCode) { + NamespaceImportSpecifier result = N4JSFactory.eINSTANCE.createNamespaceImportSpecifier(); + result.setAlias(namespaceName); + result.setFlaggedUsedInCode(usedInCode); + return result; + } + + public static VariableStatement _VariableStatement(VariableDeclaration... varDecls) { + return _VariableStatement(VariableStatementKeyword.VAR, varDecls); + } + + public static VariableStatement _VariableStatement(VariableStatementKeyword keyword, + VariableDeclarationOrBinding... varDecls) { + + VariableStatement result = N4JSFactory.eINSTANCE.createVariableStatement(); + result.setVarStmtKeyword(keyword); + result.getVarDeclsOrBindings().addAll(toList(filterNull(Arrays.asList(varDecls)))); + return result; + } + + public static VariableDeclaration _VariableDeclaration(String name) { + VariableDeclaration result = N4JSFactory.eINSTANCE.createVariableDeclaration(); + result.setName(name); + return result; + } + + public static VariableDeclaration _VariableDeclaration(String name, Expression exp) { + VariableDeclaration result = N4JSFactory.eINSTANCE.createVariableDeclaration(); + result.setName(name); + result.setExpression(exp); + return result; + } + + public static VariableBinding _VariableBinding(Iterable properties, Expression exp) { + ObjectBindingPattern pattern = N4JSFactory.eINSTANCE.createObjectBindingPattern(); + pattern.getProperties().addAll(toList(properties)); + VariableBinding result = N4JSFactory.eINSTANCE.createVariableBinding(); + result.setPattern(pattern); + result.setExpression(exp); + return result; + } + + public static ExportDeclaration _ExportDeclaration(ExportableElement exported) { + ExportDeclaration result = N4JSFactory.eINSTANCE.createExportDeclaration(); + result.setExportedElement(exported); + return result; + } + + public static ReturnStatement _ReturnStmnt() { + ReturnStatement result = N4JSFactory.eINSTANCE.createReturnStatement(); + return result; + } + + public static ReturnStatement _ReturnStmnt(Expression expr) { + ReturnStatement result = N4JSFactory.eINSTANCE.createReturnStatement(); + result.setExpression(expr); + return result; + } + + public static IfStatement _IfStmnt(Expression condition, Statement ifStmnt) { + return _IfStmnt(condition, ifStmnt, null); + } + + public static IfStatement _IfStmnt(Expression condition, Statement ifStmnt, Statement elseStmnt) { + IfStatement result = N4JSFactory.eINSTANCE.createIfStatement(); + result.setExpression(condition); + result.setIfStmt(ifStmnt); + result.setElseStmt(elseStmnt); + return result; + } + + public static ThrowStatement _ThrowStmnt(Expression expression) { + ThrowStatement result = N4JSFactory.eINSTANCE.createThrowStatement(); + result.setExpression(expression); + return result; + } + + public static ConditionalExpression _ConditionalExpr(Expression condition, Expression trueExpr, + Expression falseExpr) { + + ConditionalExpression result = N4JSFactory.eINSTANCE.createConditionalExpression(); + result.setExpression(condition); + result.setTrueExpression(trueExpr); + result.setFalseExpression(falseExpr); + return result; + } + + public static YieldExpression _YieldExpr(Expression expr) { + YieldExpression result = N4JSFactory.eINSTANCE.createYieldExpression(); + result.setExpression(expr); + return result; + } + + public static ParameterizedCallExpression _CallExpr() { + ParameterizedCallExpression result = N4JSFactory.eINSTANCE.createParameterizedCallExpression(); + return result; + } + + public static ParameterizedCallExpression _CallExpr(Expression target, Expression... arguments) { + return _CallExpr(target, false, arguments); + } + + public static ParameterizedCallExpression _CallExpr(Expression target, boolean optionalChaining, + Expression... arguments) { + ParameterizedCallExpression result = N4JSFactory.eINSTANCE.createParameterizedCallExpression(); + result.setTarget(target); + result.setOptionalChaining(optionalChaining); + result.getArguments().addAll(toList(map(filterNull(Arrays.asList(arguments)), arg -> _Argument(arg)))); + return result; + } + + public static Argument _Argument(Expression expression) { + return _Argument(false, expression); + } + + public static Argument _Argument(boolean spread, Expression expression) { + Argument result = N4JSFactory.eINSTANCE.createArgument(); + result.setSpread(spread); + result.setExpression(expression); + return result; + } + + public static ExpressionStatement _ExprStmnt(Expression expr) { + ExpressionStatement result = N4JSFactory.eINSTANCE.createExpressionStatement(); + result.setExpression(expr); + return result; + } + + public static AssignmentExpression _AssignmentExpr() { + AssignmentExpression result = N4JSFactory.eINSTANCE.createAssignmentExpression(); + result.setOp(AssignmentOperator.ASSIGN); + return result; + } + + public static AssignmentExpression _AssignmentExpr(Expression lhs, Expression rhs) { + AssignmentExpression result = N4JSFactory.eINSTANCE.createAssignmentExpression(); + result.setLhs(lhs); + result.setOp(AssignmentOperator.ASSIGN); + result.setRhs(rhs); + return result; + } + + public static ParameterizedPropertyAccessExpression_IM _PropertyAccessExpr() { + ParameterizedPropertyAccessExpression_IM result = ImFactory.eINSTANCE + .createParameterizedPropertyAccessExpression_IM(); + return result; + } + + public static ParameterizedPropertyAccessExpression_IM _PropertyAccessExpr(SymbolTableEntry target, + SymbolTableEntry... properties) { + return _PropertyAccessExpr(_IdentRef(target), properties); + } + + public static ParameterizedPropertyAccessExpression_IM _PropertyAccessExpr(Expression target, + SymbolTableEntry... properties) { + if (properties == null || exists(Arrays.asList(properties), p -> p == null)) { + throw new IllegalArgumentException("none of the properties may be null"); + } + var result = ImFactory.eINSTANCE.createParameterizedPropertyAccessExpression_IM(); + result.setTarget(target); + if (properties.length > 0) { + result.setRewiredTarget(properties[0]); + for (int idx = 1; idx < properties.length; idx++) { + ParameterizedPropertyAccessExpression_IM newResult = ImFactory.eINSTANCE + .createParameterizedPropertyAccessExpression_IM(); + newResult.setTarget(result); + newResult.setRewiredTarget(properties[idx]); + result = newResult; + } + } + return result; + } + + public static IndexedAccessExpression _IndexAccessExpr() { + return _IndexAccessExpr((Expression) null, null); + } + + public static IndexedAccessExpression _IndexAccessExpr(SymbolTableEntry target, Expression index) { + return _IndexAccessExpr(_IdentRef(target), index); + } + + public static IndexedAccessExpression _IndexAccessExpr(Expression target, Expression index) { + IndexedAccessExpression result = N4JSFactory.eINSTANCE.createIndexedAccessExpression(); + result.setTarget(target); + result.setIndex(index); + return result; + } + + public static NewExpression _NewExpr(Expression callee, Expression... arguments) { + NewExpression result = N4JSFactory.eINSTANCE.createNewExpression(); + result.setCallee(callee); + result.setWithArgs(!isEmpty(filterNull(Arrays.asList(arguments)))); + result.getArguments().addAll(toList(map(filterNull(Arrays.asList(arguments)), a -> _Argument(a)))); + return result; + } + + public static RelationalExpression _RelationalExpr(Expression lhs, RelationalOperator op, Expression rhs) { + RelationalExpression result = N4JSFactory.eINSTANCE.createRelationalExpression(); + result.setLhs(lhs); + result.setOp(op); + result.setRhs(rhs); + return result; + } + + public static EqualityExpression _EqualityExpr(Expression lhs, EqualityOperator op, Expression rhs) { + EqualityExpression result = N4JSFactory.eINSTANCE.createEqualityExpression(); + result.setLhs(lhs); + result.setOp(op); + result.setRhs(rhs); + return result; + } + + public static UnaryExpression _NOT(Expression expr) { + return _UnaryExpr(UnaryOperator.NOT, expr); + } + + public static Expression _OR(Expression... operands) { + return reduce(Arrays.asList(operands), (op1, op2) -> _BinaryLogicalExpr(op1, BinaryLogicalOperator.OR, op2)); + } + + public static Expression _AND(Expression... operands) { + return reduce(Arrays.asList(operands), (op1, op2) -> _BinaryLogicalExpr(op1, BinaryLogicalOperator.AND, op2)); + } + + public static UnaryExpression _Void0() { + return _Void(_NumericLiteral(0)); + } + + public static UnaryExpression _Void(Expression expr) { + return _UnaryExpr(UnaryOperator.VOID, expr); + } + + public static UnaryExpression _UnaryExpr(UnaryOperator op, Expression expr) { + UnaryExpression result = N4JSFactory.eINSTANCE.createUnaryExpression(); + result.setOp(op); + result.setExpression(expr); + return result; + } + + public static BinaryLogicalExpression _BinaryLogicalExpr(Expression lhs, BinaryLogicalOperator op, Expression rhs) { + BinaryLogicalExpression result = N4JSFactory.eINSTANCE.createBinaryLogicalExpression(); + result.setLhs(lhs); + result.setOp(op); + result.setRhs(rhs); + return result; + } + + public static CommaExpression _CommaExpression(Expression... expressions) { + CommaExpression result = N4JSFactory.eINSTANCE.createCommaExpression(); + result.getExprs().addAll(Arrays.asList(expressions)); + return result; + } + + public static AdditiveExpression _AdditiveExpression(Expression lhs, AdditiveOperator op, Expression rhs) { + AdditiveExpression result = N4JSFactory.eINSTANCE.createAdditiveExpression(); + result.setLhs(lhs); + result.setOp(op); + result.setRhs(rhs); + return result; + } + + public static AdditiveExpression _AdditiveExpression(AdditiveOperator op, Expression... operands) { + if (operands == null || exists(Arrays.asList(operands), e -> e == null)) { + throw new IllegalArgumentException("none of the operands may be null"); + } + if (operands.length < 2) { + throw new IllegalArgumentException("need at least two operands"); + } + var result = N4JSFactory.eINSTANCE.createAdditiveExpression(); + result.setLhs(operands[0]); + result.setOp(op); + result.setRhs(operands[1]); + for (int idx = 2; idx < operands.length; idx++) { + AdditiveExpression newResult = N4JSFactory.eINSTANCE.createAdditiveExpression(); + newResult.setLhs(result); + newResult.setOp(op); + newResult.setRhs(operands[idx]); + result = newResult; + } + return result; + } + + public static ObjectLiteral _ObjLit() { // required to resolve the ambiguity between the other two methods + return _ObjLit((PropertyAssignment[]) null); + } + + /** + * Convenience method for creating object literals that only contain {@link PropertyNameValuePair}s. It is legal to + * pass in one or more null values (they will be ignored). + */ + @SuppressWarnings("unchecked") + public static ObjectLiteral _ObjLit(Pair... nameValuePairs) { + return _ObjLit(toArray(map(filterNull(Arrays.asList(nameValuePairs)), + p -> _PropertyNameValuePair(p.getKey(), p.getValue())), PropertyNameValuePair.class)); + } + + public static ObjectLiteral _ObjLit(PropertyAssignment... pas) { + ObjectLiteral result = N4JSFactory.eINSTANCE.createObjectLiteral(); + if (pas != null) { + result.getPropertyAssignments().addAll(toList(filterNull(Arrays.asList(pas)))); + } + return result; + } + + public static PropertyNameValuePair _PropertyNameValuePair(String name, Expression value) { + return _PropertyNameValuePair(_LiteralOrComputedPropertyName(name), value); + } + + public static PropertyNameValuePair _PropertyNameValuePair(LiteralOrComputedPropertyName name, Expression value) { + PropertyNameValuePair result = N4JSFactory.eINSTANCE.createPropertyNameValuePair(); + result.setDeclaredName(name); + result.setExpression(value); + return result; + } + + public static PropertyGetterDeclaration _PropertyGetterDecl(String name, Statement... stmnts) { + PropertyGetterDeclaration result = N4JSFactory.eINSTANCE.createPropertyGetterDeclaration(); + result.setDeclaredName(_LiteralOrComputedPropertyName(name)); + result.setBody(_Block(stmnts)); + return result; + } + + public static ArrayLiteral _ArrLit() { // required to resolve the ambiguity between the other two methods + return _ArrLit((ArrayElement[]) null); + } + + public static ArrayLiteral _ArrLit(Expression... elements) { + return _ArrLit( + toArray(map(filterNull(Arrays.asList(elements)), e -> _ArrayElement(e)), ArrayElement.class)); + } + + public static ArrayLiteral _ArrLit(ArrayElement... elements) { + ArrayLiteral result = N4JSFactory.eINSTANCE.createArrayLiteral(); + if (elements != null) { + result.getElements().addAll(toList(filterNull(Arrays.asList(elements)))); + } + return result; + } + + public static ArrayElement _ArrayElement(Expression expression) { + return _ArrayElement(false, expression); + } + + public static ArrayElement _ArrayElement(boolean spread, Expression expression) { + ArrayElement result = N4JSFactory.eINSTANCE.createArrayElement(); + result.setSpread(spread); + result.setExpression(expression); + return result; + } + + public static ArrayPadding _ArrayPadding() { + ArrayPadding result = N4JSFactory.eINSTANCE.createArrayPadding(); + return result; + } + + public static FunctionDeclaration _FunDecl(String name, Statement... statements) { + return _FunDecl(name, new FormalParameter[0], statements); + } + + public static FunctionDeclaration _FunDecl(String name, FormalParameter[] fpars, Statement... statements) { + FunctionDeclaration result = N4JSFactory.eINSTANCE.createFunctionDeclaration(); + result.setName(name); + result.getFpars().addAll(Arrays.asList(fpars)); + result.setBody(_Block(statements)); + return result; + } + + public static FunctionExpression _FunExpr(boolean async, Statement... statements) { + return _FunExpr(async, null, new FormalParameter[0], statements); + } + + public static FunctionExpression _FunExpr(boolean async, String name, Statement... statements) { + return _FunExpr(async, name, new FormalParameter[0], statements); + } + + public static FunctionExpression _FunExpr(boolean async, String name, FormalParameter... formalParams) { + return _FunExpr(async, name, formalParams, new Statement[0]); + } + + public static FunctionExpression _FunExpr(boolean async, String name, FormalParameter[] fpars, + Statement... statements) { + if (statements != null && statements.length == 1 && statements[0] instanceof Block) { + // safe guard: in case complex EMF inheritance hierarchy causes wrong overload to be invoked + return _FunExprWithBlock(async, name, fpars, (Block) statements[0]); + } + FunctionExpression result = N4JSFactory.eINSTANCE.createFunctionExpression(); + result.setDeclaredAsync(async); + result.setName(name); + result.getFpars().addAll(Arrays.asList(fpars)); + result.setBody(_Block(statements)); + return result; + } + + public static FormalParameter _FormalParameter(String name) { + FormalParameter result = N4JSFactory.eINSTANCE.createFormalParameter(); + result.setName(name); + return result; + } + + public static FunctionExpression _FunExpr(boolean async, String name, FormalParameter[] fpars, Block block) { + return _FunExprWithBlock(async, name, fpars, block); + } + + private static FunctionExpression _FunExprWithBlock(boolean async, String name, FormalParameter[] fpars, + Block block) { + FunctionExpression result = N4JSFactory.eINSTANCE.createFunctionExpression(); + result.setDeclaredAsync(async); + result.setName(name); + result.getFpars().addAll(Arrays.asList(fpars)); + result.setBody(block); + return result; + } + + /** Creates a {@link ArrowFunction#isSingleExprImplicitReturn() single-expression arrow function}. */ + public static ArrowFunction _ArrowFunc(boolean async, FormalParameter[] fpars, Expression expression) { + ArrowFunction result = N4JSFactory.eINSTANCE.createArrowFunction(); + result.setDeclaredAsync(async); + result.getFpars().addAll(Arrays.asList(fpars)); + result.setBody(_Block(_ExprStmnt(expression))); + result.setHasBracesAroundBody(false); + return result; + } + + public static ArrowFunction _ArrowFunc(boolean async, FormalParameter[] fpars, Statement... statements) { + ArrowFunction result = N4JSFactory.eINSTANCE.createArrowFunction(); + result.setDeclaredAsync(async); + result.getFpars().addAll(Arrays.asList(fpars)); + result.setBody(_Block(statements)); + result.setHasBracesAroundBody(true); + return result; + } + + public static N4MemberDeclaration _N4MemberDecl(TMember template, Statement... statements) { + if (template instanceof TField && statements.length > 0) { + throw new IllegalArgumentException("fields cannot have statements"); + } + PropertyNameOwner result; + if (template instanceof TField) { + result = N4JSFactory.eINSTANCE.createN4FieldDeclaration(); + } else if (template instanceof TGetter) { + result = N4JSFactory.eINSTANCE.createN4GetterDeclaration(); + } else if (template instanceof TSetter) { + result = N4JSFactory.eINSTANCE.createN4SetterDeclaration(); + } else if (template instanceof TMethod) { + result = N4JSFactory.eINSTANCE.createN4MethodDeclaration(); + } else { + throw new IllegalArgumentException("unsupported subtype of TMember: " + template.eClass().getName()); + } + + AnnotableN4MemberDeclaration resultAsMD = (AnnotableN4MemberDeclaration) result; + + // basic properties + result.setDeclaredName(_LiteralOrComputedPropertyName(template.getName())); + // body + if (result instanceof FunctionOrFieldAccessor) { + ((FunctionOrFieldAccessor) result).setBody(_Block( + toArray(filterNull(Arrays.asList(statements)), Statement.class))); + } + // formal parameters + if (template instanceof TSetter) { + String fparName = "value"; + if (((TSetter) template).getFpar() != null) { + fparName = ((TSetter) template).getFpar().getName(); + } + ((N4SetterDeclaration) result).setFpar(_Fpar(fparName)); + } + if (template instanceof TMethod) { + TMethod tMethod = (TMethod) template; + ((N4MethodDeclaration) result).getFpars().addAll( + toList(map(tMethod.getFpars(), fpar -> _Fpar(fpar.getName())))); + ((N4MethodDeclaration) result).setDeclaredAsync(tMethod.isDeclaredAsync()); + } + // static / non-static + if (template.isStatic()) { + resultAsMD.getDeclaredModifiers().add(N4Modifier.STATIC); + } + // access modifiers + switch (template.getMemberAccessModifier()) { + case PUBLIC: + resultAsMD.getDeclaredModifiers().add(N4Modifier.PUBLIC); + break; + case PUBLIC_INTERNAL: + resultAsMD.getDeclaredModifiers().add(N4Modifier.PUBLIC); + getOrCreateMemberAnnotationList(resultAsMD).getAnnotations() + .add(_Annotation(AnnotationDefinition.INTERNAL)); + break; + case PROTECTED: + resultAsMD.getDeclaredModifiers().add(N4Modifier.PROTECTED); + break; + case PROTECTED_INTERNAL: + resultAsMD.getDeclaredModifiers().add(N4Modifier.PROTECTED); + getOrCreateMemberAnnotationList(resultAsMD).getAnnotations() + .add(_Annotation(AnnotationDefinition.INTERNAL)); + break; + case PROJECT: + resultAsMD.getDeclaredModifiers().add(N4Modifier.PROJECT); + break; + case PRIVATE: + resultAsMD.getDeclaredModifiers().add(N4Modifier.PRIVATE); + break; + case UNDEFINED: { + /* NOP */} + } + + return resultAsMD; + } + + private static N4MemberAnnotationList getOrCreateMemberAnnotationList(AnnotableN4MemberDeclaration memberDecl) { + N4MemberAnnotationList annList = memberDecl.getAnnotationList(); + if (annList == null) { + annList = N4JSFactory.eINSTANCE.createN4MemberAnnotationList(); + memberDecl.setAnnotationList(annList); + } + return annList; + } + + public static N4FieldDeclaration _N4FieldDecl(boolean isStatic, String declaredName, Expression initExpr) { + return _N4FieldDecl(isStatic, _LiteralOrComputedPropertyName(declaredName), initExpr); + } + + public static N4FieldDeclaration _N4FieldDecl(boolean isStatic, LiteralOrComputedPropertyName declaredName, + Expression initExpr) { + N4FieldDeclaration result = N4JSFactory.eINSTANCE.createN4FieldDeclaration(); + if (isStatic) { + result.getDeclaredModifiers().add(N4Modifier.STATIC); + } + result.setDeclaredName(declaredName); + result.setExpression(initExpr); + return result; + } + + public static N4GetterDeclaration _N4GetterDecl(LiteralOrComputedPropertyName declaredName, Block body) { + N4GetterDeclaration result = N4JSFactory.eINSTANCE.createN4GetterDeclaration(); + result.setDeclaredName(declaredName); + result.setBody(body); + return result; + } + + public static N4SetterDeclaration _N4SetterDecl(LiteralOrComputedPropertyName declaredName, FormalParameter fpar, + Block body) { + N4SetterDeclaration result = N4JSFactory.eINSTANCE.createN4SetterDeclaration(); + result.setDeclaredName(declaredName); + result.setFpar(fpar); + result.setBody(body); + return result; + } + + public static N4MethodDeclaration _N4MethodDecl(String name, Statement... statements) { + return _N4MethodDecl(name, new FormalParameter[0], statements); + } + + public static N4MethodDeclaration _N4MethodDecl(String name, FormalParameter[] fpars, Statement... statements) { + return _N4MethodDecl(false, _LiteralOrComputedPropertyName(name), fpars, + _Block(toArray(filterNull(Arrays.asList(statements)), Statement.class))); + } + + public static N4MethodDeclaration _N4MethodDecl(LiteralOrComputedPropertyName declaredName, Block body) { + return _N4MethodDecl(false, declaredName, new FormalParameter[0], body); + } + + public static N4MethodDeclaration _N4MethodDecl(boolean isStatic, LiteralOrComputedPropertyName declaredName, + FormalParameter[] fpars, Block body) { + N4MethodDeclaration result = N4JSFactory.eINSTANCE.createN4MethodDeclaration(); + if (isStatic) { + result.getDeclaredModifiers().add(N4Modifier.STATIC); + } + result.setDeclaredName(declaredName); + result.getFpars().addAll(Arrays.asList(fpars)); + result.setBody(body); + return result; + } + + public static FormalParameter _Fpar() { + return _Fpar(null, false, false); + } + + public static FormalParameter _Fpar(String name) { + return _Fpar(name, false, false); + } + + public static FormalParameter _Fpar(String name, boolean variadic) { + return _Fpar(name, variadic, false); + } + + public static FormalParameter _Fpar(String name, boolean variadic, boolean isSpecFpar) { + FormalParameter result = N4JSFactory.eINSTANCE.createFormalParameter(); + result.setName(name); + result.setVariadic(variadic); + if (isSpecFpar) { + result.getAnnotations().add(_Annotation(AnnotationDefinition.SPEC)); + } + return result; + } + + public static Annotation _Annotation(AnnotationDefinition annDef) { + Annotation result = N4JSFactory.eINSTANCE.createAnnotation(); + result.setName(annDef.name); + return result; + } + + public static AnnotationList _AnnotationList(List annDef) { + AnnotationList result = N4JSFactory.eINSTANCE.createAnnotationList(); + if (annDef != null) + result.getAnnotations().addAll(map(annDef, ad -> _Annotation(ad))); + return result; + } + + public static PropertyAssignmentAnnotationList _PropertyAssignmentAnnotationList(Annotation[] annotations) { + PropertyAssignmentAnnotationList result = N4JSFactory.eINSTANCE.createPropertyAssignmentAnnotationList(); + result.getAnnotations().addAll(Arrays.asList(annotations)); + return result; + } + + public static Block _Block(Statement... statements) { + Block result = N4JSFactory.eINSTANCE.createBlock(); + result.getStatements().addAll(toList(filterNull(Arrays.asList(statements)))); + return result; + } + + public static ParenExpression _Parenthesis(Expression expr) { + ParenExpression result = N4JSFactory.eINSTANCE.createParenExpression(); + result.setExpression(expr); + return result; + } + + public static ParameterizedCallExpression _ParameterizedCallExpression(Expression expr) { + ParameterizedCallExpression result = N4JSFactory.eINSTANCE.createParameterizedCallExpression(); + result.setTarget(expr); + return result; + } + + public static NullLiteral _NULL() { + return N4JSFactory.eINSTANCE.createNullLiteral(); + } + + public static BooleanLiteral _TRUE() { + return _BooleanLiteral(true); + } + + public static BooleanLiteral _FALSE() { + return _BooleanLiteral(false); + } + + public static BooleanLiteral _BooleanLiteral(boolean value) { + BooleanLiteral result = N4JSFactory.eINSTANCE.createBooleanLiteral(); + result.setTrue(value); + return result; + } + + public static NumericLiteral _NumericLiteral(int num) { + return _NumericLiteral(BigDecimal.valueOf(num)); + } + + public static NumericLiteral _NumericLiteral(BigDecimal num) { + NumericLiteral result = N4JSFactory.eINSTANCE.createNumericLiteral(); + result.setValue(num); + return result; + } + + public static StringLiteral _StringLiteral(String s, String rawValue) { + StringLiteral result = _StringLiteral(s); + result.setRawValue(rawValue); + return result; + } + + public static StringLiteral _StringLiteral(String s) { + StringLiteral result = N4JSFactory.eINSTANCE.createStringLiteral(); + result.setValue(s); + return result; + } + + public static StringLiteral _StringLiteralForSTE(SymbolTableEntry symbolTableEntry) { + return _StringLiteralForSTE(symbolTableEntry, false); + } + + public static StringLiteral _StringLiteralForSTE(SymbolTableEntry symbolTableEntry, boolean useExportedName) { + StringLiteralForSTE result = ImFactory.eINSTANCE.createStringLiteralForSTE(); + result.setEntry(symbolTableEntry); + result.setUseExportedName(useExportedName); + return result; + } + + public static IntLiteral _IntLiteral(int i) { + IntLiteral result = N4JSFactory.eINSTANCE.createIntLiteral(); + result.setValue(BigDecimal.valueOf(i)); + return result; + } + + public static ThisLiteral _ThisLiteral() { + ThisLiteral result = N4JSFactory.eINSTANCE.createThisLiteral(); + return result; + } + + public static SuperLiteral _SuperLiteral() { + SuperLiteral result = N4JSFactory.eINSTANCE.createSuperLiteral(); + return result; + } + + public static EmptyStatement _emptyStatement() { + return N4JSFactory.eINSTANCE.createEmptyStatement(); + } + + public static N4EnumDeclaration _EnumDeclaration(String name, List literals) { + N4EnumDeclaration result = N4JSFactory.eINSTANCE.createN4EnumDeclaration(); + result.setName(name); + result.getLiterals().addAll(literals); + return result; + } + + public static N4EnumLiteral _EnumLiteral(String name, String value) { + N4EnumLiteral result = N4JSFactory.eINSTANCE.createN4EnumLiteral(); + result.setName(name); + result.setValueExpression((value != null) ? _StringLiteral(value) : null); + return result; + } + + public static N4ClassDeclaration _N4ClassDeclaration(String name) { + N4ClassDeclaration result = N4JSFactory.eINSTANCE.createN4ClassDeclaration(); + result.setName(name); + return result; + } + + public static N4InterfaceDeclaration _N4InterfaceDeclaration(String name) { + N4InterfaceDeclaration result = N4JSFactory.eINSTANCE.createN4InterfaceDeclaration(); + result.setName(name); + return result; + } + + public static N4TypeVariable _N4TypeVariable(String name, boolean covariant, boolean contravariant) { + N4TypeVariable result = N4JSFactory.eINSTANCE.createN4TypeVariable(); + result.setName(name); + result.setDeclaredCovariant(covariant); + result.setDeclaredContravariant(contravariant); + return result; + } + + public static LiteralOrComputedPropertyName _LiteralOrComputedPropertyName(String name) { + LiteralOrComputedPropertyName result = N4JSFactory.eINSTANCE.createLiteralOrComputedPropertyName(); + result.setKind(PropertyNameKind.STRING); + result.setLiteralName(name); + return result; + } + + /** + * @param computedName + * the string representation of the computed property name. This should be the value that is usually + * computed and set by {@link CompileTimeExpressionProcessor} and {@link ComputedNameProcessor}. + */ + public static LiteralOrComputedPropertyName _LiteralOrComputedPropertyName(Expression nameExpr, + String computedName) { + LiteralOrComputedPropertyName result = N4JSFactory.eINSTANCE.createLiteralOrComputedPropertyName(); + result.setKind(PropertyNameKind.COMPUTED); + result.setExpression(nameExpr); + result.setComputedName(computedName); + return result; + } + + // ############################################################################################ + // IM.xcore + + public static TypeReferenceNode_IM _TypeReferenceNode(TranspilerState state, + TypeRef typeRef) { + TypeReferenceNode_IM result = ImFactory.eINSTANCE.createTypeReferenceNode_IM(); + if (typeRef != null) { + state.info.setOriginalProcessedTypeRef_internal(result, TypeUtils.copyIfContained(typeRef)); + } + return result; + } + + public static IdentifierRef_IM _IdentRef(SymbolTableEntry symbolTableEntry) { + if (symbolTableEntry == null) { + throw new IllegalArgumentException("when creating an IdentifierRef_IM: symbol table entry may not be null"); + } + IdentifierRef_IM result = ImFactory.eINSTANCE.createIdentifierRef_IM(); + result.setRewiredTarget(symbolTableEntry); + return result; + } + + public static SymbolTableEntry _SymbolTableEntry(@SuppressWarnings("unused") String name) { + throw new UnsupportedOperationException( + "do not manually create symbol table entries; use methods #createSymbolTableEntry() or #getSymbolTableEntry() instead"); + } + + public static ExpressionStatement _SnippetAsStmnt(String code) { + return _ExprStmnt(_Snippet(code)); + } + + public static Snippet _Snippet(String codeToEmit) { + Snippet result = ImFactory.eINSTANCE.createSnippet(); + result.setCodeToEmit(codeToEmit); + return result; + } +} diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerBuilderBlocks.xtend b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerBuilderBlocks.xtend deleted file mode 100644 index 561f6121a2..0000000000 --- a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerBuilderBlocks.xtend +++ /dev/null @@ -1,870 +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 - -import java.math.BigDecimal -import java.util.List -import org.eclipse.n4js.AnnotationDefinition -import org.eclipse.n4js.n4JS.AdditiveExpression -import org.eclipse.n4js.n4JS.AdditiveOperator -import org.eclipse.n4js.n4JS.AnnotableN4MemberDeclaration -import org.eclipse.n4js.n4JS.Annotation -import org.eclipse.n4js.n4JS.AnnotationList -import org.eclipse.n4js.n4JS.Argument -import org.eclipse.n4js.n4JS.ArrayElement -import org.eclipse.n4js.n4JS.ArrayLiteral -import org.eclipse.n4js.n4JS.ArrayPadding -import org.eclipse.n4js.n4JS.ArrowFunction -import org.eclipse.n4js.n4JS.AssignmentExpression -import org.eclipse.n4js.n4JS.AssignmentOperator -import org.eclipse.n4js.n4JS.BinaryLogicalExpression -import org.eclipse.n4js.n4JS.BinaryLogicalOperator -import org.eclipse.n4js.n4JS.BindingProperty -import org.eclipse.n4js.n4JS.Block -import org.eclipse.n4js.n4JS.BooleanLiteral -import org.eclipse.n4js.n4JS.CommaExpression -import org.eclipse.n4js.n4JS.ConditionalExpression -import org.eclipse.n4js.n4JS.EqualityExpression -import org.eclipse.n4js.n4JS.EqualityOperator -import org.eclipse.n4js.n4JS.ExportDeclaration -import org.eclipse.n4js.n4JS.ExportableElement -import org.eclipse.n4js.n4JS.Expression -import org.eclipse.n4js.n4JS.ExpressionStatement -import org.eclipse.n4js.n4JS.FormalParameter -import org.eclipse.n4js.n4JS.FunctionDeclaration -import org.eclipse.n4js.n4JS.FunctionExpression -import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor -import org.eclipse.n4js.n4JS.IfStatement -import org.eclipse.n4js.n4JS.ImportDeclaration -import org.eclipse.n4js.n4JS.ImportSpecifier -import org.eclipse.n4js.n4JS.IndexedAccessExpression -import org.eclipse.n4js.n4JS.IntLiteral -import org.eclipse.n4js.n4JS.LiteralOrComputedPropertyName -import org.eclipse.n4js.n4JS.N4EnumDeclaration -import org.eclipse.n4js.n4JS.N4EnumLiteral -import org.eclipse.n4js.n4JS.N4FieldDeclaration -import org.eclipse.n4js.n4JS.N4GetterDeclaration -import org.eclipse.n4js.n4JS.N4JSFactory -import org.eclipse.n4js.n4JS.N4MemberAnnotationList -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.NamedImportSpecifier -import org.eclipse.n4js.n4JS.NamespaceImportSpecifier -import org.eclipse.n4js.n4JS.NewExpression -import org.eclipse.n4js.n4JS.NullLiteral -import org.eclipse.n4js.n4JS.NumericLiteral -import org.eclipse.n4js.n4JS.ObjectLiteral -import org.eclipse.n4js.n4JS.ParameterizedCallExpression -import org.eclipse.n4js.n4JS.ParenExpression -import org.eclipse.n4js.n4JS.PropertyAssignment -import org.eclipse.n4js.n4JS.PropertyAssignmentAnnotationList -import org.eclipse.n4js.n4JS.PropertyGetterDeclaration -import org.eclipse.n4js.n4JS.PropertyNameKind -import org.eclipse.n4js.n4JS.PropertyNameValuePair -import org.eclipse.n4js.n4JS.RelationalExpression -import org.eclipse.n4js.n4JS.RelationalOperator -import org.eclipse.n4js.n4JS.ReturnStatement -import org.eclipse.n4js.n4JS.Statement -import org.eclipse.n4js.n4JS.StringLiteral -import org.eclipse.n4js.n4JS.SuperLiteral -import org.eclipse.n4js.n4JS.ThisLiteral -import org.eclipse.n4js.n4JS.ThrowStatement -import org.eclipse.n4js.n4JS.UnaryExpression -import org.eclipse.n4js.n4JS.UnaryOperator -import org.eclipse.n4js.n4JS.VariableBinding -import org.eclipse.n4js.n4JS.VariableDeclaration -import org.eclipse.n4js.n4JS.VariableDeclarationOrBinding -import org.eclipse.n4js.n4JS.VariableStatement -import org.eclipse.n4js.n4JS.VariableStatementKeyword -import org.eclipse.n4js.n4JS.YieldExpression -import org.eclipse.n4js.postprocessing.CompileTimeExpressionProcessor -import org.eclipse.n4js.postprocessing.ComputedNameProcessor -import org.eclipse.n4js.transpiler.im.IdentifierRef_IM -import org.eclipse.n4js.transpiler.im.ImFactory -import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM -import org.eclipse.n4js.transpiler.im.Snippet -import org.eclipse.n4js.transpiler.im.SymbolTableEntry -import org.eclipse.n4js.transpiler.im.TypeReferenceNode_IM -import org.eclipse.n4js.ts.typeRefs.TypeRef -import org.eclipse.n4js.ts.types.TField -import org.eclipse.n4js.ts.types.TGetter -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.types.utils.TypeUtils - -/** - * Builder methods for intermediate elements. - */ -public class TranspilerBuilderBlocks -{ - - // ############################################################################################ - // n4js.xcore - - public static def ImportDeclaration _ImportDecl(ImportSpecifier... importSpecifiers) { - val result = N4JSFactory.eINSTANCE.createImportDeclaration; - result.module = null; // must always be null, because we are in the intermediate model - result.importSpecifiers += importSpecifiers.filterNull; - result.importFrom = importSpecifiers.length > 0; - return result; - } - - public static def NamedImportSpecifier _NamedImportSpecifier(String importedElementName, String alias, boolean usedInCode) { - val result = N4JSFactory.eINSTANCE.createNamedImportSpecifier; - result.importedElement = null; // must always be null, because we are in the intermediate model - result.importedElementAsText = importedElementName; - result.alias = alias; - result.flaggedUsedInCode = usedInCode; - return result; - } - - public static def NamedImportSpecifier _DefaultImportSpecifier(String importedElementName, boolean usedInCode) { - val result = N4JSFactory.eINSTANCE.createDefaultImportSpecifier; - result.importedElement = null; // must always be null, because we are in the intermediate model - result.importedElementAsText = importedElementName; - result.flaggedUsedInCode = usedInCode; - return result; - } - - public static def NamespaceImportSpecifier _NamespaceImportSpecifier(String namespaceName, boolean usedInCode) { - val result = N4JSFactory.eINSTANCE.createNamespaceImportSpecifier; - result.alias = namespaceName; - result.flaggedUsedInCode = usedInCode; - return result; - } - - public static def VariableStatement _VariableStatement(VariableDeclaration... varDecls) { - return _VariableStatement(VariableStatementKeyword.VAR, varDecls); - } - - public static def VariableStatement _VariableStatement(VariableStatementKeyword keyword, VariableDeclarationOrBinding... varDecls) { - val result = N4JSFactory.eINSTANCE.createVariableStatement; - result.varStmtKeyword = keyword; - result.varDeclsOrBindings += varDecls.filterNull; - return result; - } - - public static def VariableDeclaration _VariableDeclaration(String name) { - val result = N4JSFactory.eINSTANCE.createVariableDeclaration; - result.name = name; - return result; - } - - public static def VariableDeclaration _VariableDeclaration(String name, Expression exp) { - val result = N4JSFactory.eINSTANCE.createVariableDeclaration; - result.name = name; - result.expression = exp; - return result; - } - - public static def VariableBinding _VariableBinding(Iterable properties, Expression exp) { - val pattern = N4JSFactory.eINSTANCE.createObjectBindingPattern; - pattern.properties += properties; - val result = N4JSFactory.eINSTANCE.createVariableBinding; - result.pattern = pattern; - result.expression = exp; - return result; - } - - public static def ExportDeclaration _ExportDeclaration(ExportableElement exported) { - val result = N4JSFactory.eINSTANCE.createExportDeclaration; - result.exportedElement = exported - return result; - } - - public static def ReturnStatement _ReturnStmnt() { - val result = N4JSFactory.eINSTANCE.createReturnStatement; - return result; - } - - public static def ReturnStatement _ReturnStmnt(Expression expr) { - val result = N4JSFactory.eINSTANCE.createReturnStatement; - result.expression = expr; - return result; - } - - public static def IfStatement _IfStmnt(Expression condition, Statement ifStmnt) { - return _IfStmnt(condition, ifStmnt, null); - } - - public static def IfStatement _IfStmnt(Expression condition, Statement ifStmnt, Statement elseStmnt) { - val result = N4JSFactory.eINSTANCE.createIfStatement; - result.expression = condition; - result.ifStmt = ifStmnt; - result.elseStmt = elseStmnt; - return result; - } - - public static def ThrowStatement _ThrowStmnt(Expression expression) { - val result = N4JSFactory.eINSTANCE.createThrowStatement; - result.expression = expression; - return result; - } - - public static def ConditionalExpression _ConditionalExpr(Expression condition, Expression trueExpr, Expression falseExpr) { - val result = N4JSFactory.eINSTANCE.createConditionalExpression; - result.expression = condition; - result.trueExpression = trueExpr; - result.falseExpression = falseExpr; - return result; - } - - public static def YieldExpression _YieldExpr(Expression expr) { - val result = N4JSFactory.eINSTANCE.createYieldExpression; - result.expression = expr; - return result; - } - - public static def ParameterizedCallExpression _CallExpr() { - val result = N4JSFactory.eINSTANCE.createParameterizedCallExpression; - return result; - } - - public static def ParameterizedCallExpression _CallExpr(Expression target, Expression... arguments) { - return _CallExpr(target, false, arguments); - } - - public static def ParameterizedCallExpression _CallExpr(Expression target, boolean optionalChaining, Expression... arguments) { - val result = N4JSFactory.eINSTANCE.createParameterizedCallExpression; - result.target = target; - result.optionalChaining = optionalChaining; - result.arguments += arguments.filterNull.map[_Argument]; - return result; - } - - public static def Argument _Argument(Expression expression) { - return _Argument(false, expression); - } - public static def Argument _Argument(boolean spread, Expression expression) { - val result = N4JSFactory.eINSTANCE.createArgument; - result.spread = spread; - result.expression = expression; - return result; - } - - public static def ExpressionStatement _ExprStmnt(Expression expr) { - val result = N4JSFactory.eINSTANCE.createExpressionStatement; - result.expression = expr; - return result; - } - - public static def AssignmentExpression _AssignmentExpr() { - val result = N4JSFactory.eINSTANCE.createAssignmentExpression; - result.op = AssignmentOperator.ASSIGN; - return result; - } - - public static def AssignmentExpression _AssignmentExpr(Expression lhs, Expression rhs) { - val result = N4JSFactory.eINSTANCE.createAssignmentExpression; - result.lhs = lhs; - result.op = AssignmentOperator.ASSIGN; - result.rhs = rhs; - return result; - } - - public static def ParameterizedPropertyAccessExpression_IM _PropertyAccessExpr() { - val result = ImFactory.eINSTANCE.createParameterizedPropertyAccessExpression_IM; - return result; - } - - public static def ParameterizedPropertyAccessExpression_IM _PropertyAccessExpr(SymbolTableEntry target, - SymbolTableEntry... properties) { - return _PropertyAccessExpr(_IdentRef(target), properties); - } - - public static def ParameterizedPropertyAccessExpression_IM _PropertyAccessExpr(Expression target, - SymbolTableEntry... properties) { - if(properties===null || properties.exists[it===null]) { - throw new IllegalArgumentException("none of the properties may be null") - } - var result = ImFactory.eINSTANCE.createParameterizedPropertyAccessExpression_IM; - result.target = target; - if(properties.length>0) { - result.rewiredTarget = properties.get(0); - for(idx : 1..null values (they will be ignored). - */ - public static def ObjectLiteral _ObjLit(Pair... nameValuePairs) { - return _ObjLit(nameValuePairs.filterNull.map[_PropertyNameValuePair(key,value)]); - } - - public static def ObjectLiteral _ObjLit(PropertyAssignment... pas) { - val result = N4JSFactory.eINSTANCE.createObjectLiteral; - if(pas!==null) { - result.propertyAssignments += pas.filterNull; - } - return result; - } - - public static def PropertyNameValuePair _PropertyNameValuePair(String name, Expression value) { - return _PropertyNameValuePair(_LiteralOrComputedPropertyName(name), value); - } - public static def PropertyNameValuePair _PropertyNameValuePair(LiteralOrComputedPropertyName name, Expression value) { - val result = N4JSFactory.eINSTANCE.createPropertyNameValuePair; - result.declaredName = name; - result.expression = value; - return result; - } - - public static def PropertyGetterDeclaration _PropertyGetterDecl(String name, Statement... stmnts) { - val result = N4JSFactory.eINSTANCE.createPropertyGetterDeclaration; - result.declaredName = _LiteralOrComputedPropertyName(name); - result.body = _Block(stmnts); - return result; - } - - public static def ArrayLiteral _ArrLit() { // required to resolve the ambiguity between the other two methods - return _ArrLit(null as ArrayElement[]); - } - - public static def ArrayLiteral _ArrLit(Expression... elements) { - return _ArrLit(elements.filterNull.map[_ArrayElement(it)]); - } - - public static def ArrayLiteral _ArrLit(ArrayElement... elements) { - val result = N4JSFactory.eINSTANCE.createArrayLiteral; - if(elements!==null) { - result.elements += elements.filterNull; - } - return result; - } - - public static def ArrayElement _ArrayElement(Expression expression) { - return _ArrayElement(false, expression); - } - public static def ArrayElement _ArrayElement(boolean spread, Expression expression) { - val result = N4JSFactory.eINSTANCE.createArrayElement; - result.spread = spread; - result.expression = expression; - return result; - } - public static def ArrayPadding _ArrayPadding() { - val result = N4JSFactory.eINSTANCE.createArrayPadding; - return result; - } - - public static def FunctionDeclaration _FunDecl(String name, Statement... statements) { - return _FunDecl(name, #[], statements); - } - - public static def FunctionDeclaration _FunDecl(String name, FormalParameter[] fpars, Statement... statements) { - val result = N4JSFactory.eINSTANCE.createFunctionDeclaration; - result.name = name; - result.fpars += fpars; - result.body = _Block(statements); - return result; - } - - public static def FunctionExpression _FunExpr(boolean async, Statement... statements) { - return _FunExpr(async, null, #[], statements); - } - - public static def FunctionExpression _FunExpr(boolean async, String name, Statement... statements) { - return _FunExpr(async, name, #[], statements); - } - - public static def FunctionExpression _FunExpr(boolean async, String name, FormalParameter... formalParams) { - return _FunExpr(async, name, formalParams, #[]); - } - - public static def FunctionExpression _FunExpr(boolean async, String name, FormalParameter[] fpars, Statement... statements) { - if(statements !== null && statements.length===1 && statements.get(0) instanceof Block) { - // safe guard: in case complex EMF inheritance hierarchy causes wrong overload to be invoked - return _FunExprWithBlock(async, name, fpars, statements.get(0) as Block); - } - val result = N4JSFactory.eINSTANCE.createFunctionExpression; - result.declaredAsync = async; - result.name = name; - result.fpars += fpars; - result.body = _Block(statements); - return result; - } - - public static def FormalParameter _FormalParameter(String name) { - val result = N4JSFactory.eINSTANCE.createFormalParameter; - result.name = name; - return result; - } - - public static def FunctionExpression _FunExpr(boolean async, String name, FormalParameter[] fpars, Block block) { - return _FunExprWithBlock(async, name, fpars, block); - } - private static def FunctionExpression _FunExprWithBlock(boolean async, String name, FormalParameter[] fpars, Block block) { - val result = N4JSFactory.eINSTANCE.createFunctionExpression; - result.declaredAsync = async; - result.name = name; - result.fpars += fpars; - result.body = block; - return result; - } - - /** Creates a {@link ArrowFunction#isSingleExprImplicitReturn() single-expression arrow function}. */ - public static def ArrowFunction _ArrowFunc(boolean async, FormalParameter[] fpars, Expression expression) { - val result = N4JSFactory.eINSTANCE.createArrowFunction; - result.declaredAsync = async; - result.fpars += fpars; - result.body = _Block(_ExprStmnt(expression)); - result.hasBracesAroundBody = false; - return result; - } - - public static def ArrowFunction _ArrowFunc(boolean async, FormalParameter[] fpars, Statement... statements) { - val result = N4JSFactory.eINSTANCE.createArrowFunction; - result.declaredAsync = async; - result.fpars += fpars; - result.body = _Block(statements); - result.hasBracesAroundBody = true; - return result; - } - - public static def N4MemberDeclaration _N4MemberDecl(TMember template, Statement... statements) { - if(template instanceof TField && !statements.empty) { - throw new IllegalArgumentException("fields cannot have statements"); - } - val result = switch(template) { - TField: N4JSFactory.eINSTANCE.createN4FieldDeclaration - TGetter: N4JSFactory.eINSTANCE.createN4GetterDeclaration - TSetter: N4JSFactory.eINSTANCE.createN4SetterDeclaration - TMethod: N4JSFactory.eINSTANCE.createN4MethodDeclaration - default: throw new IllegalArgumentException("unsupported subtype of TMember: " + template.eClass.name) - }; - // basic properties - result.declaredName = _LiteralOrComputedPropertyName(template.name); - // body - if(result instanceof FunctionOrFieldAccessor) { - result.body = _Block(statements.filterNull); - } - // formal parameters - if(template instanceof TSetter) { - val fparName = template.fpar?.name ?: "value"; - (result as N4SetterDeclaration).fpar = _Fpar(fparName); - } - if(template instanceof TMethod) { - (result as N4MethodDeclaration).fpars += template.fpars.map[_Fpar(name)]; - (result as N4MethodDeclaration).declaredAsync = template.declaredAsync; - } - // static / non-static - if(template.static) { - result.declaredModifiers += N4Modifier.STATIC; - } - // access modifiers - switch(template.memberAccessModifier) { - case PUBLIC: result.declaredModifiers += N4Modifier.PUBLIC - case PUBLIC_INTERNAL: { - result.declaredModifiers += N4Modifier.PUBLIC; - result.getOrCreateMemberAnnotationList.annotations += _Annotation(AnnotationDefinition.INTERNAL); - } - case PROTECTED: result.declaredModifiers += N4Modifier.PROTECTED - case PROTECTED_INTERNAL: { - result.declaredModifiers += N4Modifier.PROTECTED; - result.getOrCreateMemberAnnotationList.annotations += _Annotation(AnnotationDefinition.INTERNAL); - } - case PROJECT: result.declaredModifiers += N4Modifier.PROJECT - case PRIVATE: result.declaredModifiers += N4Modifier.PRIVATE - case UNDEFINED: {/* NOP */} - } - return result; - } - private static def N4MemberAnnotationList getOrCreateMemberAnnotationList(AnnotableN4MemberDeclaration memberDecl) { - var annList = memberDecl.annotationList; - if(annList===null) { - annList = N4JSFactory.eINSTANCE.createN4MemberAnnotationList; - memberDecl.annotationList = annList; - } - return annList; - } - - public static def N4FieldDeclaration _N4FieldDecl(boolean isStatic, String declaredName, Expression initExpr) { - return _N4FieldDecl(isStatic, _LiteralOrComputedPropertyName(declaredName), initExpr); - } - - public static def N4FieldDeclaration _N4FieldDecl(boolean isStatic, LiteralOrComputedPropertyName declaredName, Expression initExpr) { - val result = N4JSFactory.eINSTANCE.createN4FieldDeclaration; - if (isStatic) { - result.declaredModifiers += N4Modifier.STATIC; - } - result.declaredName = declaredName; - result.expression = initExpr; - return result; - } - - public static def N4GetterDeclaration _N4GetterDecl(LiteralOrComputedPropertyName declaredName, Block body) { - val result = N4JSFactory.eINSTANCE.createN4GetterDeclaration; - result.declaredName = declaredName; - result.body = body; - return result; - } - - public static def N4SetterDeclaration _N4SetterDecl(LiteralOrComputedPropertyName declaredName, FormalParameter fpar, Block body) { - val result = N4JSFactory.eINSTANCE.createN4SetterDeclaration; - result.declaredName = declaredName; - result.fpar = fpar; - result.body = body; - return result; - } - - public static def N4MethodDeclaration _N4MethodDecl(String name, Statement... statements) { - return _N4MethodDecl(name, #[], statements); - } - public static def N4MethodDeclaration _N4MethodDecl(String name, FormalParameter[] fpars, Statement... statements) { - return _N4MethodDecl(false, _LiteralOrComputedPropertyName(name), fpars, _Block(statements.filterNull)); - } - public static def N4MethodDeclaration _N4MethodDecl(LiteralOrComputedPropertyName declaredName, Block body) { - return _N4MethodDecl(false, declaredName, #[], body); - } - public static def N4MethodDeclaration _N4MethodDecl(boolean isStatic, LiteralOrComputedPropertyName declaredName, FormalParameter[] fpars, Block body) { - val result = N4JSFactory.eINSTANCE.createN4MethodDeclaration; - if (isStatic) { - result.declaredModifiers += N4Modifier.STATIC - } - result.declaredName = declaredName; - result.fpars += fpars; - result.body = body; - return result; - } - - public static def FormalParameter _Fpar() { - return _Fpar(null, false, false); - } - public static def FormalParameter _Fpar(String name) { - return _Fpar(name, false, false); - } - public static def FormalParameter _Fpar(String name, boolean variadic) { - return _Fpar(name, variadic, false); - } - public static def FormalParameter _Fpar(String name, boolean variadic, boolean isSpecFpar) { - val result = N4JSFactory.eINSTANCE.createFormalParameter; - result.name = name; - result.variadic = variadic; - if(isSpecFpar) { - result.annotations += _Annotation(AnnotationDefinition.SPEC); - } - return result; - } - - public static def Annotation _Annotation(AnnotationDefinition annDef) { - val result = N4JSFactory.eINSTANCE.createAnnotation; - result.name = annDef.name; - return result; - } - - public static def AnnotationList _AnnotationList(List annDef) { - val result = N4JSFactory.eINSTANCE.createAnnotationList; - if( annDef !== null ) - result.annotations += annDef.map[ _Annotation(it) ]; - return result; - } - - public static def PropertyAssignmentAnnotationList _PropertyAssignmentAnnotationList(Annotation[] annotations) { - val result = N4JSFactory.eINSTANCE.createPropertyAssignmentAnnotationList; - result.annotations += annotations; - return result; - } - - public static def Block _Block(Statement... statements) { - val result = N4JSFactory.eINSTANCE.createBlock; - result.statements += statements.filterNull; - return result; - } - - public static def ParenExpression _Parenthesis(Expression expr) { - val result = N4JSFactory.eINSTANCE.createParenExpression; - result.expression = expr; - return result; - } - - public static def ParameterizedCallExpression _ParameterizedCallExpression(Expression expr) { - val result = N4JSFactory.eINSTANCE.createParameterizedCallExpression; - result.target = expr; - return result; - } - - public static def NullLiteral _NULL() { - return N4JSFactory.eINSTANCE.createNullLiteral; - } - - public static def BooleanLiteral _TRUE() { - return _BooleanLiteral(true); - } - public static def BooleanLiteral _FALSE() { - return _BooleanLiteral(false); - } - public static def BooleanLiteral _BooleanLiteral(boolean value) { - val result = N4JSFactory.eINSTANCE.createBooleanLiteral; - result.^true = value; - return result; - } - - public static def NumericLiteral _NumericLiteral(int num) { - return _NumericLiteral(BigDecimal.valueOf(num)); - } - - public static def NumericLiteral _NumericLiteral(BigDecimal num) { - val result = N4JSFactory.eINSTANCE.createNumericLiteral; - result.value = num; - return result; - } - - public static def StringLiteral _StringLiteral(String s, String rawValue) { - val result = _StringLiteral(s); - result.rawValue = rawValue; - return result; - } - - public static def StringLiteral _StringLiteral(String s) { - val result = N4JSFactory.eINSTANCE.createStringLiteral; - result.value = s; - return result; - } - - public static def StringLiteral _StringLiteralForSTE(SymbolTableEntry symbolTableEntry) { - return _StringLiteralForSTE(symbolTableEntry, false); - } - - public static def StringLiteral _StringLiteralForSTE(SymbolTableEntry symbolTableEntry, boolean useExportedName) { - val result = ImFactory.eINSTANCE.createStringLiteralForSTE; - result.entry = symbolTableEntry; - result.useExportedName = useExportedName; - return result; - } - - public static def IntLiteral _IntLiteral(int i) { - val result = N4JSFactory.eINSTANCE.createIntLiteral; - result.value = BigDecimal.valueOf(i); - return result; - } - - public static def ThisLiteral _ThisLiteral() { - val result = N4JSFactory.eINSTANCE.createThisLiteral; - return result; - } - - public static def SuperLiteral _SuperLiteral() { - val result = N4JSFactory.eINSTANCE.createSuperLiteral; - return result; - } - - public static def _emptyStatement() { - return N4JSFactory.eINSTANCE.createEmptyStatement - } - - public static def N4EnumDeclaration _EnumDeclaration(String name, List literals) { - val result = N4JSFactory.eINSTANCE.createN4EnumDeclaration; - result.name = name; - result.literals += literals; - return result; - } - - public static def _EnumLiteral(String name, String value) { - val result = N4JSFactory.eINSTANCE.createN4EnumLiteral; - result.name = name; - result.valueExpression = if (value !== null) _StringLiteral(value); - return result; - } - - public static def _N4ClassDeclaration(String name){ - val result = N4JSFactory.eINSTANCE.createN4ClassDeclaration; - result.name = name; - return result; - } - - public static def _N4InterfaceDeclaration(String name){ - val result = N4JSFactory.eINSTANCE.createN4InterfaceDeclaration; - result.name = name; - return result; - } - - public static def _N4TypeVariable(String name, boolean covariant, boolean contravariant) { - val result = N4JSFactory.eINSTANCE.createN4TypeVariable; - result.name = name; - result.declaredCovariant = covariant; - result.declaredContravariant = contravariant; - return result; - } - - public static def _LiteralOrComputedPropertyName(String name) { - val result = N4JSFactory.eINSTANCE.createLiteralOrComputedPropertyName; - result.kind = PropertyNameKind.STRING; - result.literalName = name; - return result; - } - - /** - * @param computedName the string representation of the computed property name. This should be the value that - * is usually computed and set by {@link CompileTimeExpressionProcessor} and {@link ComputedNameProcessor}. - */ - public static def _LiteralOrComputedPropertyName(Expression nameExpr, String computedName) { - val result = N4JSFactory.eINSTANCE.createLiteralOrComputedPropertyName; - result.kind = PropertyNameKind.COMPUTED; - result.expression = nameExpr; - result.computedName = computedName; - return result; - } - - // ############################################################################################ - // IM.xcore - - public static def TypeReferenceNode_IM _TypeReferenceNode(TranspilerState state, TypeRef typeRef) { - val TypeReferenceNode_IM result = ImFactory.eINSTANCE.createTypeReferenceNode_IM(); - if (typeRef !== null) { - state.info.setOriginalProcessedTypeRef_internal(result, TypeUtils.copyIfContained(typeRef)); - } - return result; - } - - public static def IdentifierRef_IM _IdentRef(SymbolTableEntry symbolTableEntry) { - if(symbolTableEntry===null) { - throw new IllegalArgumentException("when creating an IdentifierRef_IM: symbol table entry may not be null"); - } - val result = ImFactory.eINSTANCE.createIdentifierRef_IM; - result.rewiredTarget = symbolTableEntry; - return result; - } - - public static def SymbolTableEntry _SymbolTableEntry(String name) { - throw new UnsupportedOperationException("do not manually create symbol table entries; use methods #createSymbolTableEntry() or #getSymbolTableEntry() instead"); - } - - public static def ExpressionStatement _SnippetAsStmnt(String code) { - return _ExprStmnt(_Snippet(code)); - } - - public static def Snippet _Snippet(String codeToEmit) { - val result = ImFactory.eINSTANCE.createSnippet; - result.codeToEmit = codeToEmit; - return result; - } -} diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerComponent.java b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerComponent.java index 85f9363a4f..746941abf3 100644 --- a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerComponent.java +++ b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerComponent.java @@ -12,6 +12,7 @@ import java.util.Collection; import java.util.List; +import java.util.function.Consumer; import org.eclipse.emf.ecore.EObject; import org.eclipse.n4js.n4JS.ArrowFunction; @@ -49,7 +50,6 @@ import org.eclipse.n4js.ts.types.TClassifier; import org.eclipse.n4js.ts.types.TModule; import org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions; -import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -130,7 +130,7 @@ protected void setTarget(ParameterizedPropertyAccessExpression_IM accExpr, Expre @SuppressWarnings("javadoc") protected void addArgument(ParameterizedCallExpression callExpr, int index, Expression newArgument) { - TranspilerStateOperations.addArgument(state, callExpr, index, newArgument); + TranspilerStateOperations.addArgument(callExpr, index, newArgument); } @SuppressWarnings("javadoc") @@ -207,19 +207,18 @@ protected void replaceAndRelocate(FormalParameter fPar_to_remove, VariableStatem @SuppressWarnings("javadoc") protected void wrapExistingExpression(T exprToWrap, Expression outerExpr_without_exprToWrap, - Procedure1 inserterFunction) { - TranspilerStateOperations.wrapExistingExpression(state, exprToWrap, outerExpr_without_exprToWrap, - inserterFunction); + Consumer inserterFunction) { + TranspilerStateOperations.wrapExistingExpression(exprToWrap, outerExpr_without_exprToWrap, inserterFunction); } - /** Delegates to {@link TranspilerStateOperations#insertBefore(TranspilerState, EObject, EObject...)}. */ + /** Delegates to {@link TranspilerStateOperations#insertBefore( EObject, EObject...)}. */ protected void insertBefore(EObject elementInIntermediateModel, EObject... newElements) { - TranspilerStateOperations.insertBefore(state, elementInIntermediateModel, newElements); + TranspilerStateOperations.insertBefore(elementInIntermediateModel, newElements); } - /** Delegates to {@link TranspilerStateOperations#insertAfter(TranspilerState, EObject, EObject...)}. */ + /** Delegates to {@link TranspilerStateOperations#insertAfter( EObject, EObject...)}. */ protected void insertAfter(EObject elementInIntermediateModel, EObject... newElements) { - TranspilerStateOperations.insertAfter(state, elementInIntermediateModel, newElements); + TranspilerStateOperations.insertAfter(elementInIntermediateModel, newElements); } /** Delegates to {@link TranspilerStateOperations#copy(TranspilerState, EObject)}. */ @@ -307,7 +306,7 @@ protected void recordReferenceToType(TypeReferenceNode_IM typeRefNode, Symbol @SuppressWarnings("javadoc") protected void rename(SymbolTableEntry entry, String newName) { - TranspilerStateOperations.rename(state, entry, newName); + TranspilerStateOperations.rename(entry, newName); } @SuppressWarnings("javadoc") diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerStateOperations.java b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerStateOperations.java new file mode 100644 index 0000000000..d7c090b1f2 --- /dev/null +++ b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerStateOperations.java @@ -0,0 +1,653 @@ +/** + * 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; + +import static org.eclipse.n4js.transpiler.SymbolTableManagement.findSymbolTableEntryForElement; +import static org.eclipse.n4js.transpiler.SymbolTableManagement.getSymbolTableEntryOriginal; +import static org.eclipse.n4js.transpiler.SymbolTableManagement.rewireSymbolTable; +import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._Argument; +import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ImportDecl; +import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._NamedImportSpecifier; +import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._NamespaceImportSpecifier; +import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._ReturnStmnt; +import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableDeclaration; +import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._VariableStatement; +import static org.eclipse.n4js.utils.Strings.join; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.map; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.toList; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.util.EObjectContainmentEList; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.n4js.n4JS.ArrowFunction; +import org.eclipse.n4js.n4JS.Block; +import org.eclipse.n4js.n4JS.EmptyStatement; +import org.eclipse.n4js.n4JS.ExportDeclaration; +import org.eclipse.n4js.n4JS.Expression; +import org.eclipse.n4js.n4JS.ExpressionStatement; +import org.eclipse.n4js.n4JS.FormalParameter; +import org.eclipse.n4js.n4JS.FunctionDeclaration; +import org.eclipse.n4js.n4JS.FunctionExpression; +import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor; +import org.eclipse.n4js.n4JS.ImportDeclaration; +import org.eclipse.n4js.n4JS.ImportSpecifier; +import org.eclipse.n4js.n4JS.N4ClassDeclaration; +import org.eclipse.n4js.n4JS.N4EnumDeclaration; +import org.eclipse.n4js.n4JS.N4InterfaceDeclaration; +import org.eclipse.n4js.n4JS.N4MemberDeclaration; +import org.eclipse.n4js.n4JS.NamedImportSpecifier; +import org.eclipse.n4js.n4JS.NamespaceImportSpecifier; +import org.eclipse.n4js.n4JS.ParameterizedCallExpression; +import org.eclipse.n4js.n4JS.ReturnStatement; +import org.eclipse.n4js.n4JS.Script; +import org.eclipse.n4js.n4JS.ScriptElement; +import org.eclipse.n4js.n4JS.Statement; +import org.eclipse.n4js.n4JS.VariableBinding; +import org.eclipse.n4js.n4JS.VariableDeclaration; +import org.eclipse.n4js.n4JS.VariableEnvironmentElement; +import org.eclipse.n4js.n4JS.VariableStatement; +import org.eclipse.n4js.n4JS.VariableStatementKeyword; +import org.eclipse.n4js.transpiler.im.ImPackage; +import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM; +import org.eclipse.n4js.transpiler.im.ReferencingElement_IM; +import org.eclipse.n4js.transpiler.im.SymbolTableEntry; +import org.eclipse.n4js.transpiler.im.SymbolTableEntryIMOnly; +import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal; +import org.eclipse.n4js.transpiler.utils.TranspilerUtils; +import org.eclipse.n4js.ts.types.IdentifiableElement; +import org.eclipse.n4js.ts.types.ModuleNamespaceVirtualType; +import org.eclipse.n4js.ts.types.TModule; +import org.eclipse.n4js.ts.types.TypesFactory; +import org.eclipse.xtext.xbase.lib.Pair; + +import com.google.common.collect.Lists; + +/** + * Methods of this class provide elementary operations on a transpiler state, mainly on the intermediate model. The + * intermediate model should only be changed through the operations defined by this class. + *

+ * Main clients are AST transformations, but they should not invoke these operations directly, but instead use the + * delegation methods in {@link Transformation}. + */ +public class TranspilerStateOperations { + + /** + * Creates a new namespace import for the given module and adds it to the intermediate model of the given transpiler + * state. The returned symbol table entry can be used to create references to the namespace, e.g. by passing it to + * {@link TranspilerBuilderBlocks#_IdentRef(SymbolTableEntry)}. The newly created import can be obtained by calling + * {@link SymbolTableEntryOriginal#getImportSpecifier()} on the returned symbol table entry. + *

+ * IMPORTANT: this method does not check if an import for the given module exists already or if the given namespace + * name is unique (i.e. does not avoid name clashes!). + */ + public static SymbolTableEntryOriginal addNamespaceImport(TranspilerState state, TModule moduleToImport, + String namespaceName) { + + // 1) create import declaration & specifier + NamespaceImportSpecifier importSpec = _NamespaceImportSpecifier(namespaceName, true); + ImportDeclaration importDecl = _ImportDecl(importSpec); + // 2) create a temporary type to use as original target + ModuleNamespaceVirtualType typeForNamespace = TypesFactory.eINSTANCE.createModuleNamespaceVirtualType(); + typeForNamespace.setName(namespaceName); + state.resource.addTemporaryType(typeForNamespace); // make sure our temporary type is contained in a resource + // 3) create a symbol table entry + SymbolTableEntryOriginal steForNamespace = getSymbolTableEntryOriginal(state, typeForNamespace, true); + steForNamespace.setImportSpecifier(importSpec); + // 4) add import to intermediate model + EList scriptElements = state.im.getScriptElements(); + if (scriptElements.isEmpty()) { + scriptElements.add(importDecl); + } else { + insertBefore(scriptElements.get(0), importDecl); + } + // 5) update info registry + state.info.setImportedModule(importDecl, moduleToImport); + return steForNamespace; + } + + /** + * Creates a new named import for the given element and adds it to the intermediate model of the given transpiler + * state. The returned symbol table entry can be used to create references to the imported element, e.g. by passing + * it to {@link TranspilerBuilderBlocks#_IdentRef(SymbolTableEntry)}. The newly created import can be obtained by + * calling {@link SymbolTableEntryOriginal#getImportSpecifier()} on the returned symbol table entry. + *

+ * If a named import already exists for the given element, nothing will be changed in the intermediate model and its + * symbol table entry will be returned as described above. If the given element is of type + * {@link ModuleNamespaceVirtualType} an exception will be thrown (because only namespace imports can be created for + * those types). + *

+ * IMPORTANT: this method does not check if the given namespace name is unique (i.e. does not avoid name clashes!). + */ + public static SymbolTableEntryOriginal addNamedImport(TranspilerState state, IdentifiableElement elementToImport, + String aliasOrNull) { + SymbolTableEntryOriginal steOfElementToImport = getSymbolTableEntryOriginal(state, elementToImport, true); + addNamedImport(state, steOfElementToImport, aliasOrNull); + return steOfElementToImport; + } + + /** + * Creates a new named import for the given STE and adds it to the intermediate model of the given transpiler state. + * The passed-in symbol table entry can be used to create references to the imported element, e.g. by passing it to + * {@link TranspilerBuilderBlocks#_IdentRef(SymbolTableEntry)}. The newly created import can be obtained by calling + * {@link SymbolTableEntryOriginal#getImportSpecifier()} on the passed-in symbol table entry. + *

+ * If a named import already exists for the given element, nothing will be changed in the intermediate model. If the + * original target of the given symbol table entry is of type {@link ModuleNamespaceVirtualType} an exception will + * be thrown (because only namespace imports can be created for those types). + *

+ * IMPORTANT: this method does not check if the given namespace name is unique (i.e. does not avoid name clashes!). + */ + public static void addNamedImport(TranspilerState state, SymbolTableEntryOriginal steOfElementToImport, + String aliasOrNull) { + // check for valid type of element to be imported (i.e. the original target) + IdentifiableElement originalTarget = steOfElementToImport.getOriginalTarget(); + if (originalTarget instanceof ModuleNamespaceVirtualType) { + throw new IllegalArgumentException("cannot create named import for a ModuleNamespaceVirtualType"); + } + // check for existing import + ImportSpecifier existingImportSpec = steOfElementToImport.getImportSpecifier(); + if (existingImportSpec != null) { + // import already exists, nothing to be done + return; + } + + // 1) create import declaration & specifier + NamedImportSpecifier importSpec = _NamedImportSpecifier(steOfElementToImport.getExportedName(), aliasOrNull, + true); + ImportDeclaration importDecl = _ImportDecl(importSpec); + // 2) add import to intermediate model + EList scriptElements = state.im.getScriptElements(); + if (scriptElements.isEmpty()) { + scriptElements.add(importDecl); + } else { + insertBefore(scriptElements.get(0), importDecl); + } + // 3) link symbol table entry to its newly created import specifier + steOfElementToImport.setImportSpecifier(importSpec); + // 4) update info registry + TModule moduleOfOriginalTarget = originalTarget.getContainingModule(); + state.info.setImportedModule(importDecl, moduleOfOriginalTarget); + } + + /** + * Adds an "empty" import to the intermediate model, i.e. an import of the form: + * + *

+	 * import "<moduleSpecifier>";
+	 * 
+ */ + public static void addEmptyImport(TranspilerState state, String moduleSpecifier) { + // 1) create import declaration + ImportDeclaration importDecl = _ImportDecl(); + importDecl.setModuleSpecifierAsText(moduleSpecifier); + // 2) add import to intermediate model + EList scriptElements = state.im.getScriptElements(); + if (scriptElements.isEmpty()) { + scriptElements.add(importDecl); + } else { + insertBefore(scriptElements.get(0), importDecl); + } + } + + /** + * Returns the symbol table entry to a temporary variable with the given name, intended for use at the location of + * "nodeInIM" in the intermediate model. If no such variable exists yet, a new variable statement and declaration + * will be created. + *

+ * When newly created, the temporary declarations will be added to the body of the closest ancestor + * function/accessor (or on the top level if no such ancestor exists), even if a temporary variable of the same name + * already exists in an outer variable environment (i.e. an outer function/accessor or on top level if inside a + * function/accessor). + */ + public static SymbolTableEntryIMOnly addOrGetTemporaryVariable(TranspilerState state, String name, + EObject nodeInIM) { + FunctionOrFieldAccessor contextFunctionOrAccessor = getContextFunctionOrAccessor(nodeInIM); + VariableEnvironmentElement context = contextFunctionOrAccessor != null ? contextFunctionOrAccessor : state.im; + SymbolTableEntryIMOnly tempVarSTE = state.temporaryVariables.get(Pair.of(context, name)); + if (tempVarSTE != null) { + return tempVarSTE; + } + // need to create a new temporary variable below context + VariableStatement tempVarStmnt = addOrGetTemporaryVariableStatement(state, context); + VariableDeclaration tempVarDecl = _VariableDeclaration(name); + tempVarStmnt.getVarDeclsOrBindings().add(tempVarDecl); + SymbolTableEntryIMOnly tempVarSTENew = (SymbolTableEntryIMOnly) findSymbolTableEntryForElement(state, + tempVarDecl, true); + state.temporaryVariables.put(Pair.of(context, name), tempVarSTENew); + return tempVarSTENew; + } + + private static FunctionOrFieldAccessor getContextFunctionOrAccessor(EObject nodeInIM) { + if (nodeInIM == null) { + return null; + } + if (nodeInIM instanceof FunctionOrFieldAccessor) { + return (FunctionOrFieldAccessor) nodeInIM; + } + EObject parent = nodeInIM.eContainer(); + if (parent instanceof FormalParameter + && parent.eContainer() instanceof FunctionOrFieldAccessor + && ((FormalParameter) parent).getInitializer() == nodeInIM) { + // special case: since the expression of a default parameter cannot access a function"s local variables, + // the directly containing function of a default parameter is not a valid context function for temporary + // variables used in the default parameter"s initializer expression. + EObject parentOfContainingFunctionOrAccessor = parent.eContainer().eContainer(); + return getContextFunctionOrAccessor(parentOfContainingFunctionOrAccessor); + } + return getContextFunctionOrAccessor(parent); + } + + /** If context is absent, then the temporary variable statement will be created on the top level. */ + private static VariableStatement addOrGetTemporaryVariableStatement(TranspilerState state, + VariableEnvironmentElement context) { + VariableStatement tempVarStmnt = state.temporaryVariableStatements.get(context); + if (tempVarStmnt != null) { + return tempVarStmnt; + } + // need to create a new temporary variable statement + VariableStatement tempVarStmntNew = _VariableStatement(VariableStatementKeyword.LET); + state.temporaryVariableStatements.put(context, tempVarStmntNew); + if (context instanceof FunctionOrFieldAccessor) { + // add to body of function/accessor + if (context instanceof ArrowFunction) { + ArrowFunction af = (ArrowFunction) context; + if (!af.isHasBracesAroundBody()) { + // to allow for declarations inside the body, we have to turn single-expression arrow functions into + // ordinary arrow functions + if (af.isSingleExprImplicitReturn()) { + ExpressionStatement singleExprStmnt = (ExpressionStatement) af.getBody().getStatements().get(0); // we + // know + // this, + // because + // #isSingleExprImplicitReturn() + // returned + // true + replace(state, singleExprStmnt, _ReturnStmnt(singleExprStmnt.getExpression())); + } + af.setHasBracesAroundBody(true); + } + } + ((FunctionOrFieldAccessor) context).getBody().getStatements().add(0, tempVarStmntNew); + } else if (context instanceof Script) { + Script script = (Script) context; + // add on top level before the first non-empty, non-import statement + Iterator iter = script.getScriptElements().iterator(); + ScriptElement elem; + do { + elem = (iter.hasNext()) ? iter.next() : null; + } while (elem instanceof EmptyStatement || elem instanceof ImportDeclaration); + if (elem != null) { + insertBefore(elem, tempVarStmntNew); + } else { + script.getScriptElements().add(tempVarStmntNew); + } + } + return tempVarStmntNew; + } + + /***/ + public static void setTarget(TranspilerState state, ParameterizedCallExpression callExpr, Expression newTarget) { + Expression oldTarget = callExpr.getTarget(); + if (oldTarget != null) { + replaceWithoutRewire(state, oldTarget, newTarget); + } else { + callExpr.setTarget(newTarget); + } + } + + /***/ + public static void setTarget(TranspilerState state, ParameterizedPropertyAccessExpression_IM accExpr, + Expression newTarget) { + Expression oldTarget = accExpr.getTarget(); + if (oldTarget != null) { + replaceWithoutRewire(state, oldTarget, newTarget); + } else { + accExpr.setTarget(newTarget); + } + } + + /***/ + public static void addArgument(ParameterizedCallExpression callExpr, int index, Expression newArgument) { + callExpr.getArguments().add(index, _Argument(newArgument)); + } + + /***/ + public static void removeAll(TranspilerState state, Iterable elementsInIM) { + for (EObject elementInIM : Lists.newArrayList(elementsInIM)) { + remove(state, elementInIM); + } + } + + /***/ + public static void remove(TranspilerState state, EObject elementInIM) { + replaceWithoutRewire(state, elementInIM); // i.e. replace with nothing (will update tracer) + if (elementInIM instanceof ReferencingElement_IM) { + ((ReferencingElement_IM) elementInIM).setRewiredTarget(null); // important here: will remove elementInIM + // from its symbol table entry"s + // "referencingElements" list! + // note: this update of the symbol table is incomplete; elementInIM may be the root of an entire subtree + // of the IM, so we would have to iterate over all successors + } + } + + /** + * Removes the export-container (ExportDeclaration) by creating a new VariableStatement {@code varStmt}, moving all + * content from {@code exVarStmnt} into it and replacing the ExportDeclaration with the newly created + * {@code varStmt} + */ + public static void removeExport(TranspilerState state, VariableStatement exVarStmnt) { + + if (!TranspilerUtils.isIntermediateModelElement(exVarStmnt)) { + throw new IllegalArgumentException("not an element in the intermediate model: " + exVarStmnt); + } + + ExportDeclaration exportDecl = (ExportDeclaration) exVarStmnt.eContainer(); + + replaceWithoutRewire(state, exportDecl, exVarStmnt); + } + + /***/ + public static void replace(TranspilerState state, Statement stmnt, ReturnStatement returnStmnt) { + replaceWithoutRewire(state, stmnt, returnStmnt); + } + + /***/ + public static void replace(TranspilerState state, N4ClassDeclaration classDecl, FunctionDeclaration funDecl) { + replaceWithoutRewire(state, classDecl, funDecl); + rewireSymbolTable(state, classDecl, funDecl); + } + + /** + * Replace an interface declaration by a variable declaration. The variable declaration will be wrapped in a newly + * created [Exported]VariableStatement. + */ + public static void replace(TranspilerState state, N4InterfaceDeclaration ifcDecl, VariableDeclaration varDecl) { + VariableStatement varStmnt = _VariableStatement(VariableStatementKeyword.CONST, varDecl); + replaceWithoutRewire(state, ifcDecl, varStmnt); + rewireSymbolTable(state, ifcDecl, varDecl); + } + + /***/ + public static void replace(TranspilerState state, N4EnumDeclaration enumDecl, N4ClassDeclaration classDecl) { + replaceWithoutRewire(state, enumDecl, classDecl); + rewireSymbolTable(state, enumDecl, classDecl); + } + + /***/ + public static void replace(TranspilerState state, FunctionDeclaration funDecl, VariableDeclaration varDecl) { + VariableStatement varStmnt = _VariableStatement(varDecl); + replaceWithoutRewire(state, funDecl, varStmnt); + rewireSymbolTable(state, funDecl, varDecl); + // need to rewire the local arguments variable, to enable renaming: + Expression varValue = varDecl.getExpression(); + if (varValue instanceof FunctionExpression) { + rewireSymbolTable(state, funDecl.getImplicitArgumentsVariable(), + ((FunctionExpression) varValue).getImplicitArgumentsVariable()); + } else { + throw new IllegalArgumentException( + "when replacing a function declaration by a variable declaration, " + + "we expect the variable to be initialized with a function expression"); + } + } + + /***/ + public static void replace(TranspilerState state, FunctionDeclaration functionDecl, ExpressionStatement stmt) { + replaceWithoutRewire(state, functionDecl, stmt); + } + + /***/ + public static void replace(TranspilerState state, N4MemberDeclaration memberDecl, N4MemberDeclaration replacement) { + replaceWithoutRewire(state, memberDecl, replacement); + rewireSymbolTable(state, memberDecl, replacement); + } + + /***/ + public static void replace(TranspilerState state, VariableStatement varStmnt, Statement... newStmnts) { + replaceWithoutRewire(state, varStmnt, newStmnts); + } + + /***/ + public static void replace(TranspilerState state, VariableBinding varBinding, VariableDeclaration... varDecls) { + replaceWithoutRewire(state, varBinding, varDecls); + } + + /***/ + public static void replace(TranspilerState state, Expression exprOld, Expression exprNew) { + replaceWithoutRewire(state, exprOld, exprNew); + } + + /***/ + public static void replace(TranspilerState state, ArrowFunction exprOld, ParameterizedCallExpression exprNew, + FunctionExpression rewireTarget) { + replaceWithoutRewire(state, exprOld, exprNew); + rewireSymbolTable(state, exprOld, rewireTarget); + } + + /** Replace formal parameter with a variableStmt. Rewire the fpar to the VariableDeclaration. Relocate the Stmt */ + public static void replaceAndRelocate(TranspilerState state, FormalParameter fPar_to_remove, + VariableStatement varStmnt, + VariableDeclaration varDecl_wireTo, Block newContainer) { + if (varDecl_wireTo.eContainer() != varStmnt) { + throw new IllegalArgumentException("varDecl must be contained in varStmnt"); + } + replaceAndRelocateWithoutRewire_internal(state, fPar_to_remove, varStmnt, newContainer.getStatements(), 0); + + rewireSymbolTable(state, fPar_to_remove, varDecl_wireTo); + } + + /***/ + public static void wrapExistingExpression(T exprToWrap, + Expression outerExpr_without_exprToWrap, Consumer inserterFunction) { + + insertOrReplace_internal(exprToWrap, List.of(outerExpr_without_exprToWrap), true, false); + inserterFunction.accept(exprToWrap); + } + + /* + * append( pos < 0 or > current size ), prepend(pos==0) or insert at {@code pos} the object {@code insertThis} to + * {@code newContainer}. Also delete {@code removeThis} from the IM. Does not rewire. But keeps trace. + */ + private static void replaceAndRelocateWithoutRewire_internal(TranspilerState state, EObject removeThis, + EObject insertThis, + EList newContainer, int pos) { + + EReference eRefRemove = checkedContainmentFeature(removeThis); + + if (insertThis.eContainer() != null) + throw new IllegalArgumentException("The new element must not be contained anywhere." + + " insertThis=" + insertThis + " is currently contained in " + insertThis.eContainer()); + + if (newContainer instanceof EObjectContainmentEList) { + @SuppressWarnings("unchecked") + EObjectContainmentEList newContainerCasted = (EObjectContainmentEList) newContainer; + + //// Tracing: + state.tracer.copyTrace(removeThis, insertThis); + state.tracer.discardIntermediateModelNode(removeThis); + + //////////////////////////////////// + //// remove: + if (eRefRemove.getUpperBound() == 1) { // single-value + if (eRefRemove.isUnsettable()) + removeThis.eContainer().eUnset(eRefRemove); + else + removeThis.eContainer().eSet(eRefRemove, null); + } else { // multivalue + @SuppressWarnings("unchecked") + List l = (List) removeThis.eContainer().eGet(eRefRemove); // c.f. type check above + int idx = l.indexOf(removeThis); + l.remove(idx); + } + + //////////////////////////////////// + //// insert: + int idx = pos; // insert + + if (pos < 0 || pos >= newContainer.size()) { + // append + idx = newContainer.size(); + } + newContainerCasted.add(idx, insertThis); + + } else { + throw new IllegalArgumentException( + "designated new container-list must be a subtype of type EObjectContainmentList"); + } + } + + /** + * {@code elementInIntermediateModel} is going away (ie, should be garbage-collected) and therefore we clear all + * references to it from tracing. The {@code replacements} IM nodes take over whatever AST node was previously + * traced-back-to via the element going away. + */ + private static void replaceWithoutRewire(TranspilerState state, EObject elementInIntermediateModel, + EObject... replacements) { + + state.tracer.copyTrace(elementInIntermediateModel, replacements); + state.tracer.discardIntermediateModelNode(elementInIntermediateModel); + insertOrReplace_internal(elementInIntermediateModel, Arrays.asList(replacements), true, false); + } + + /***/ + public static void insertBefore(EObject elementInIntermediateModel, EObject... newElements) { + insertOrReplace_internal(elementInIntermediateModel, Arrays.asList(newElements), false, false); + } + + /***/ + public static void insertAfter(EObject elementInIntermediateModel, EObject... newElements) { + insertOrReplace_internal(elementInIntermediateModel, Arrays.asList(newElements), false, true); + } + + private static void insertOrReplace_internal(EObject elementInIntermediateModel, + List newElements, boolean replace, boolean after) { + if (newElements.isEmpty() && !replace) { + return; // nothing to be inserted + } + EReference eRef = checkedContainmentFeature(elementInIntermediateModel); + EClass eRefType = eRef.getEReferenceType(); + List replElemsOfWrongType = toList( + filter(newElements, elem -> !eRefType.isSuperTypeOf(elem.eClass()))); + if (!replElemsOfWrongType.isEmpty()) { + throw new IllegalArgumentException("one or more elements are of wrong type, expected: " + + eRef.getEReferenceType().getName() + ", actual: " + + join(", ", map(replElemsOfWrongType, eobj -> eobj.eClass().getName()))); + } + if (eRef.getUpperBound() == 1) { + // single valued + if (newElements.size() > 1) { + throw new IllegalArgumentException( + "the single-valued reference " + eRef.getName() + " in class " + eRef.getEContainingClass() + + " is not able to hold " + newElements.size() + " elements."); + } + if (newElements.size() == 1) { + if (!replace) { + throw new IllegalArgumentException( + "Cannot insert another element into a single-valued containment reference " + eRef.getName() + + " in class " + eRef.getEContainingClass()); + } + elementInIntermediateModel.eContainer().eSet(eRef, newElements.get(0)); + } else { + if (!replace) { + throw new IllegalArgumentException("Inserting zero elements with replace==false is pointless."); + } + // no element, so remove + if (eRef.isUnsettable()) { + elementInIntermediateModel.eContainer().eUnset(eRef); + } else { + elementInIntermediateModel.eContainer().eSet(eRef, null); + } + } + + } else { + // multi-valued + @SuppressWarnings("unchecked") + // c.f. type check above + List l = (List) elementInIntermediateModel.eContainer().eGet(eRef); + int idx = l.indexOf(elementInIntermediateModel); + if (replace) { + l.remove(idx); + } else { + // note: before/after only applicable if !replace + if (after) { + idx++; // always safe to increment, because we know there exists an element at index "idx" + } + } + l.addAll(idx, newElements); + } + } + + /** + * Retrieves the EReference of the Container. Throws Exceptions if a) not part of the IM or b) not contained + * anywhere + */ + private static EReference checkedContainmentFeature(EObject elementInIntermediateModel) { + if (!TranspilerUtils.isIntermediateModelElement(elementInIntermediateModel)) { + throw new IllegalArgumentException( + "not an element in the intermediate model: " + elementInIntermediateModel); + } + EReference eRef = elementInIntermediateModel.eContainmentFeature(); + if (eRef == null) { + throw new IllegalArgumentException("element is not contained anywhere"); + } + return eRef; + } + + /** + * Rename the given symbol table entry and all named elements in the intermediate model that are using this name. + * During AST transformations in the transpiler, the "name" property of a symbol table entry should never be changed + * directly, but this operation should be used instead. + *

+ * WARNING: renaming is currently only implemented partially and used only in a single, very specific use + * case; if renaming is required in the future, then the implementation of this method has to be complemented! + */ + public static void rename(SymbolTableEntry entry, String newName) { + SymbolTableManagement.rename(entry, newName); + } + + /** + * Copy a subtree of the intermediate model. + */ + @SuppressWarnings("unchecked") + public static T copy(TranspilerState state, T elementInIM) { + // create a copy with a special copier to take care of reference ReferencingElement_IM#rewiredTarget + IM2IMCopier copier = new IM2IMCopier(); + EObject result = copier.copy(elementInIM); + copier.copyReferences(); + // copy tracing information + state.tracer.copyTrace(elementInIM, result); // note: copying trace for all nodes would be more fine grained + return (T) result; + } + + private static final class IM2IMCopier extends EcoreUtil.Copier { + private static final EReference eRef__ReferencingElement_IM__rewiredTarget = ImPackage.eINSTANCE + .getReferencingElement_IM_RewiredTarget(); + + @Override + protected void copyReference(EReference eReference, EObject eObject, EObject copyEObject) { + if (eReference == eRef__ReferencingElement_IM__rewiredTarget) { + ((ReferencingElement_IM) copyEObject) + .setRewiredTarget(((ReferencingElement_IM) eObject).getRewiredTarget()); + } else { + super.copyReference(eReference, eObject, copyEObject); + } + } + + } +} diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerStateOperations.xtend b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerStateOperations.xtend deleted file mode 100644 index 3da26775c5..0000000000 --- a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/TranspilerStateOperations.xtend +++ /dev/null @@ -1,571 +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 - -import com.google.common.collect.Lists -import java.util.List -import org.eclipse.emf.common.util.EList -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.EReference -import org.eclipse.emf.ecore.util.EObjectContainmentEList -import org.eclipse.emf.ecore.util.EcoreUtil -import org.eclipse.n4js.n4JS.ArrowFunction -import org.eclipse.n4js.n4JS.Block -import org.eclipse.n4js.n4JS.EmptyStatement -import org.eclipse.n4js.n4JS.ExportDeclaration -import org.eclipse.n4js.n4JS.Expression -import org.eclipse.n4js.n4JS.ExpressionStatement -import org.eclipse.n4js.n4JS.FormalParameter -import org.eclipse.n4js.n4JS.FunctionDeclaration -import org.eclipse.n4js.n4JS.FunctionExpression -import org.eclipse.n4js.n4JS.FunctionOrFieldAccessor -import org.eclipse.n4js.n4JS.ImportDeclaration -import org.eclipse.n4js.n4JS.N4ClassDeclaration -import org.eclipse.n4js.n4JS.N4EnumDeclaration -import org.eclipse.n4js.n4JS.N4InterfaceDeclaration -import org.eclipse.n4js.n4JS.N4MemberDeclaration -import org.eclipse.n4js.n4JS.ParameterizedCallExpression -import org.eclipse.n4js.n4JS.ReturnStatement -import org.eclipse.n4js.n4JS.Script -import org.eclipse.n4js.n4JS.ScriptElement -import org.eclipse.n4js.n4JS.Statement -import org.eclipse.n4js.n4JS.VariableBinding -import org.eclipse.n4js.n4JS.VariableDeclaration -import org.eclipse.n4js.n4JS.VariableEnvironmentElement -import org.eclipse.n4js.n4JS.VariableStatement -import org.eclipse.n4js.n4JS.VariableStatementKeyword -import org.eclipse.n4js.transpiler.im.ImPackage -import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM -import org.eclipse.n4js.transpiler.im.ReferencingElement_IM -import org.eclipse.n4js.transpiler.im.SymbolTableEntry -import org.eclipse.n4js.transpiler.im.SymbolTableEntryIMOnly -import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal -import org.eclipse.n4js.transpiler.utils.TranspilerUtils -import org.eclipse.n4js.ts.types.IdentifiableElement -import org.eclipse.n4js.ts.types.ModuleNamespaceVirtualType -import org.eclipse.n4js.ts.types.TModule -import org.eclipse.n4js.ts.types.TypesFactory - -import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.* - -import static extension org.eclipse.n4js.transpiler.SymbolTableManagement.* - -/** - * Methods of this class provide elementary operations on a transpiler state, mainly on the intermediate model. The - * intermediate model should only be changed through the operations defined by this class. - *

- * Main clients are AST transformations, but they should not invoke these operations directly, but instead use the - * delegation methods in {@link Transformation}. - */ -class TranspilerStateOperations { - - /** - * Creates a new namespace import for the given module and adds it to the intermediate model of the given transpiler - * state. The returned symbol table entry can be used to create references to the namespace, e.g. by passing it to - * {@link TranspilerBuilderBlocks#_IdentRef(SymbolTableEntry)}. The newly created import can be obtained by calling - * {@link SymbolTableEntryOriginal#getImportSpecifier()} on the returned symbol table entry. - *

- * IMPORTANT: this method does not check if an import for the given module exists already or if the given namespace - * name is unique (i.e. does not avoid name clashes!). - */ - def public static SymbolTableEntryOriginal addNamespaceImport(TranspilerState state, TModule moduleToImport, - String namespaceName) { - - // 1) create import declaration & specifier - val importSpec = _NamespaceImportSpecifier(namespaceName, true); - val importDecl = _ImportDecl(importSpec); - // 2) create a temporary type to use as original target - val typeForNamespace = TypesFactory.eINSTANCE.createModuleNamespaceVirtualType(); - typeForNamespace.name = namespaceName; - state.resource.addTemporaryType(typeForNamespace); // make sure our temporary type is contained in a resource - // 3) create a symbol table entry - val steForNamespace = getSymbolTableEntryOriginal(state, typeForNamespace, true); - steForNamespace.importSpecifier = importSpec; - // 4) add import to intermediate model - val scriptElements = state.im.scriptElements; - if(scriptElements.empty) { - scriptElements.add(importDecl); - } else { - insertBefore(state, scriptElements.get(0), importDecl); - } - // 5) update info registry - state.info.setImportedModule(importDecl, moduleToImport); - return steForNamespace; - } - - /** - * Creates a new named import for the given element and adds it to the intermediate model of the given transpiler - * state. The returned symbol table entry can be used to create references to the imported element, e.g. by passing - * it to {@link TranspilerBuilderBlocks#_IdentRef(SymbolTableEntry)}. The newly created import can be obtained by - * calling {@link SymbolTableEntryOriginal#getImportSpecifier()} on the returned symbol table entry. - *

- * If a named import already exists for the given element, nothing will be changed in the intermediate model and its - * symbol table entry will be returned as described above. If the given element is of type - * {@link ModuleNamespaceVirtualType} an exception will be thrown (because only namespace imports can be created for - * those types). - *

- * IMPORTANT: this method does not check if the given namespace name is unique (i.e. does not avoid name clashes!). - */ - def public static SymbolTableEntryOriginal addNamedImport(TranspilerState state, IdentifiableElement elementToImport, String aliasOrNull) { - val steOfElementToImport = getSymbolTableEntryOriginal(state, elementToImport, true); - addNamedImport(state, steOfElementToImport, aliasOrNull); - return steOfElementToImport; - } - - /** - * Creates a new named import for the given STE and adds it to the intermediate model of the given transpiler - * state. The passed-in symbol table entry can be used to create references to the imported element, e.g. by passing - * it to {@link TranspilerBuilderBlocks#_IdentRef(SymbolTableEntry)}. The newly created import can be obtained by - * calling {@link SymbolTableEntryOriginal#getImportSpecifier()} on the passed-in symbol table entry. - *

- * If a named import already exists for the given element, nothing will be changed in the intermediate model. If the - * original target of the given symbol table entry is of type {@link ModuleNamespaceVirtualType} an exception will - * be thrown (because only namespace imports can be created for those types). - *

- * IMPORTANT: this method does not check if the given namespace name is unique (i.e. does not avoid name clashes!). - */ - def public static void addNamedImport(TranspilerState state, SymbolTableEntryOriginal steOfElementToImport, String aliasOrNull) { - // check for valid type of element to be imported (i.e. the original target) - val originalTarget = steOfElementToImport.originalTarget; - if(originalTarget instanceof ModuleNamespaceVirtualType) { - throw new IllegalArgumentException("cannot create named import for a ModuleNamespaceVirtualType"); - } - // check for existing import - val existingImportSpec = steOfElementToImport.importSpecifier; - if(existingImportSpec!==null) { - // import already exists, nothing to be done - return; - } - - // 1) create import declaration & specifier - val importSpec = _NamedImportSpecifier(steOfElementToImport.exportedName, aliasOrNull, true); - val importDecl = _ImportDecl(importSpec); - // 2) add import to intermediate model - val scriptElements = state.im.scriptElements; - if(scriptElements.empty) { - scriptElements.add(importDecl); - } else { - insertBefore(state, scriptElements.get(0), importDecl); - } - // 3) link symbol table entry to its newly created import specifier - steOfElementToImport.importSpecifier = importSpec; - // 4) update info registry - val moduleOfOriginalTarget = originalTarget.containingModule; - state.info.setImportedModule(importDecl, moduleOfOriginalTarget); - } - - /** - * Adds an "empty" import to the intermediate model, i.e. an import of the form: - *

-	 * import "<moduleSpecifier>";
-	 * 
- */ - def public static void addEmptyImport(TranspilerState state, String moduleSpecifier) { - // 1) create import declaration - val importDecl = _ImportDecl() => [ - moduleSpecifierAsText = moduleSpecifier - ]; - // 2) add import to intermediate model - val scriptElements = state.im.scriptElements; - if(scriptElements.empty) { - scriptElements.add(importDecl); - } else { - insertBefore(state, scriptElements.get(0), importDecl); - } - } - - /** - * Returns the symbol table entry to a temporary variable with the given name, intended for use at the location - * of 'nodeInIM' in the intermediate model. If no such variable exists yet, a new variable statement and declaration - * will be created. - *

- * When newly created, the temporary declarations will be added to the body of the closest ancestor function/accessor - * (or on the top level if no such ancestor exists), even if a temporary variable of the same name already exists in - * an outer variable environment (i.e. an outer function/accessor or on top level if inside a function/accessor). - */ - def public static SymbolTableEntryIMOnly addOrGetTemporaryVariable(TranspilerState state, String name, EObject nodeInIM) { - val contextFunctionOrAccessor = getContextFunctionOrAccessor(nodeInIM); - val context = contextFunctionOrAccessor ?: state.im; - val tempVarSTE = state.temporaryVariables.get(context -> name); - if (tempVarSTE !== null) { - return tempVarSTE; - } - // need to create a new temporary variable below context - val tempVarStmnt = state.addOrGetTemporaryVariableStatement(context); - val tempVarDecl = _VariableDeclaration(name); - tempVarStmnt.varDeclsOrBindings += tempVarDecl; - val tempVarSTENew = state.findSymbolTableEntryForElement(tempVarDecl, true) as SymbolTableEntryIMOnly; - state.temporaryVariables.put(context -> name, tempVarSTENew); - return tempVarSTENew; - } - - def private static FunctionOrFieldAccessor getContextFunctionOrAccessor(EObject nodeInIM) { - if (nodeInIM === null) { - return null; - } - if (nodeInIM instanceof FunctionOrFieldAccessor) { - return nodeInIM; - } - val parent = nodeInIM.eContainer(); - if (parent instanceof FormalParameter - && parent.eContainer() instanceof FunctionOrFieldAccessor - && (parent as FormalParameter).initializer === nodeInIM) { - // special case: since the expression of a default parameter cannot access a function's local variables, - // the directly containing function of a default parameter is not a valid context function for temporary - // variables used in the default parameter's initializer expression. - val parentOfContainingFunctionOrAccessor = parent.eContainer().eContainer(); - return getContextFunctionOrAccessor(parentOfContainingFunctionOrAccessor); - } - return getContextFunctionOrAccessor(parent); - } - - /** If context is absent, then the temporary variable statement will be created on the top level. */ - def private static VariableStatement addOrGetTemporaryVariableStatement(TranspilerState state, VariableEnvironmentElement context) { - val tempVarStmnt = state.temporaryVariableStatements.get(context); - if (tempVarStmnt !== null) { - return tempVarStmnt; - } - // need to create a new temporary variable statement - val tempVarStmntNew = _VariableStatement(VariableStatementKeyword.LET); - state.temporaryVariableStatements.put(context, tempVarStmntNew); - if (context instanceof FunctionOrFieldAccessor) { - // add to body of function/accessor - if (context instanceof ArrowFunction) { - if (!context.hasBracesAroundBody) { - // to allow for declarations inside the body, we have to turn single-expression arrow functions into ordinary arrow functions - if (context.isSingleExprImplicitReturn) { - val singleExprStmnt = context.body.statements.head as ExpressionStatement; // we know this, because #isSingleExprImplicitReturn() returned true - state.replace(singleExprStmnt, _ReturnStmnt(singleExprStmnt.expression)); - } - context.hasBracesAroundBody = true; - } - } - context.body.statements.add(0, tempVarStmntNew); - } else if (context instanceof Script) { - // add on top level before the first non-empty, non-import statement - val iter = context.scriptElements.iterator; - var ScriptElement elem; - do { - elem = if (iter.hasNext()) iter.next() else null; - } while(elem instanceof EmptyStatement || elem instanceof ImportDeclaration); - if (elem !== null) { - state.insertBefore(elem, tempVarStmntNew); - } else { - context.scriptElements += tempVarStmntNew; - } - } - return tempVarStmntNew; - } - - def public static void setTarget(TranspilerState state, ParameterizedCallExpression callExpr, Expression newTarget) { - val oldTarget = callExpr.target; - if(oldTarget!==null) { - state.replaceWithoutRewire(oldTarget, newTarget); - } else { - callExpr.target = newTarget; - } - } - - def public static void setTarget(TranspilerState state, ParameterizedPropertyAccessExpression_IM accExpr, Expression newTarget) { - val oldTarget = accExpr.target; - if(oldTarget!==null) { - state.replaceWithoutRewire(oldTarget, newTarget); - } else { - accExpr.target = newTarget; - } - } - - def public static void addArgument(TranspilerState state, ParameterizedCallExpression callExpr, int index, Expression newArgument) { - callExpr.arguments.add(index, _Argument(newArgument)); - } - - def public static void removeAll(TranspilerState state, Iterable elementsInIM) { - for (EObject elementInIM : Lists.newArrayList(elementsInIM)) { - remove(state, elementInIM); - } - } - - def public static void remove(TranspilerState state, EObject elementInIM) { - state.replaceWithoutRewire(elementInIM) // i.e. replace with nothing (will update tracer) - if(elementInIM instanceof ReferencingElement_IM) { - elementInIM.rewiredTarget = null; // important here: will remove elementInIM from its symbol table entry's 'referencingElements' list! - // note: this update of the symbol table is incomplete; elementInIM may be the root of an entire subtree - // of the IM, so we would have to iterate over all successors - } - } - - /** - * Removes the export-container (ExportDeclaration) by creating a new VariableStatement {@code varStmt}, moving all content from {@code exVarStmnt} - * into it and replacing the ExportDeclaration with the newly created {@code varStmt} - * @return newly created {@code varStmt} (already part of the intermediate model). - */ - def public static void removeExport(TranspilerState state, VariableStatement exVarStmnt) { - - if(!TranspilerUtils.isIntermediateModelElement(exVarStmnt)) { - throw new IllegalArgumentException("not an element in the intermediate model: " + exVarStmnt); - } - - val exportDecl = exVarStmnt.eContainer as ExportDeclaration - - state.replaceWithoutRewire(exportDecl,exVarStmnt); - } - - - def public static void replace(TranspilerState state, Statement stmnt, ReturnStatement returnStmnt) { - state.replaceWithoutRewire(stmnt, returnStmnt); - } - - def public static void replace(TranspilerState state, N4ClassDeclaration classDecl, FunctionDeclaration funDecl) { - state.replaceWithoutRewire(classDecl, funDecl); - state.rewireSymbolTable(classDecl, funDecl); - } - - /** - * Replace an interface declaration by a variable declaration. The variable declaration will be wrapped in a - * newly created [Exported]VariableStatement. - */ - def public static void replace(TranspilerState state, N4InterfaceDeclaration ifcDecl, VariableDeclaration varDecl) { - val varStmnt = _VariableStatement(VariableStatementKeyword.CONST, varDecl); - state.replaceWithoutRewire(ifcDecl, varStmnt); - state.rewireSymbolTable(ifcDecl, varDecl); - } - - def public static void replace(TranspilerState state, N4EnumDeclaration enumDecl, N4ClassDeclaration classDecl) { - state.replaceWithoutRewire(enumDecl, classDecl); - state.rewireSymbolTable(enumDecl, classDecl); - } - - def public static void replace(TranspilerState state, FunctionDeclaration funDecl, VariableDeclaration varDecl) { - val varStmnt = _VariableStatement(varDecl); - state.replaceWithoutRewire(funDecl, varStmnt); - state.rewireSymbolTable(funDecl,varDecl); - // need to rewire the local arguments variable, to enable renaming: - val varValue = varDecl.expression; - if(varValue instanceof FunctionExpression) { - state.rewireSymbolTable(funDecl.implicitArgumentsVariable, varValue.implicitArgumentsVariable); - } else { - throw new IllegalArgumentException( - "when replacing a function declaration by a variable declaration, " + - "we expect the variable to be initialized with a function expression"); - } - } - - def public static void replace(TranspilerState state, FunctionDeclaration functionDecl, ExpressionStatement stmt) { - state.replaceWithoutRewire(functionDecl, stmt); - } - - def public static void replace(TranspilerState state, N4MemberDeclaration memberDecl, N4MemberDeclaration replacement) { - state.replaceWithoutRewire(memberDecl, replacement); - state.rewireSymbolTable(memberDecl, replacement); - } - - def public static void replace(TranspilerState state, VariableStatement varStmnt, Statement... newStmnts) { - state.replaceWithoutRewire(varStmnt, newStmnts); - } - - def public static void replace(TranspilerState state, VariableBinding varBinding, VariableDeclaration... varDecls) { - state.replaceWithoutRewire(varBinding, varDecls); - } - - def public static void replace(TranspilerState state, Expression exprOld, Expression exprNew) { - state.replaceWithoutRewire(exprOld, exprNew); - } - - def public static void replace(TranspilerState state, ArrowFunction exprOld, ParameterizedCallExpression exprNew, FunctionExpression rewireTarget) { - state.replaceWithoutRewire(exprOld, exprNew); - state.rewireSymbolTable(exprOld, rewireTarget); - } - - - /** Replace formal parameter with a variableStmt. Rewire the fpar to the VariableDeclaration. Relocate the Stmt */ - def public static void replaceAndRelocate(TranspilerState state, FormalParameter fPar_to_remove, VariableStatement varStmnt, - VariableDeclaration varDecl_wireTo, Block newContainer ) { - if(varDecl_wireTo.eContainer!==varStmnt) { - throw new IllegalArgumentException("varDecl must be contained in varStmnt"); - } - state.replaceAndRelocateWithoutRewire_internal(fPar_to_remove, varStmnt, newContainer.statements, 0); - - state.rewireSymbolTable(fPar_to_remove, varDecl_wireTo); - } - - def public static void wrapExistingExpression(TranspilerState state, - T exprToWrap, Expression outerExpr_without_exprToWrap, (T)=>void inserterFunction - ) { - state.insertOrReplace_internal(exprToWrap, #[outerExpr_without_exprToWrap], true, false); - inserterFunction.apply(exprToWrap) - } - - /* append( pos < 0 or > current size ), prepend(pos==0) or insert at {@code pos} the object {@code insertThis} - * to {@code newContainer}. Also delete {@code removeThis} from the IM. Does not rewire. But keeps trace.*/ - def private static void replaceAndRelocateWithoutRewire_internal(TranspilerState state, EObject removeThis, EObject insertThis, - EList newContainer, int pos ) { - - - val eRefRemove = checkedContainmentFeature(removeThis); - - if( insertThis.eContainer !== null ) throw new IllegalArgumentException("The new element must not be contained anywhere."+ - " insertThis="+insertThis+" is currently contained in "+insertThis.eContainer); - - if( newContainer instanceof EObjectContainmentEList ){ - val newContainerCasted = newContainer as EObjectContainmentEList; - - //// Tracing: - state.tracer.copyTrace(removeThis, insertThis); - state.tracer.discardIntermediateModelNode(removeThis); - - //////////////////////////////////// - //// remove: - if( eRefRemove.upperBound == 1 ) { // single-value - if( eRefRemove.isUnsettable ) - removeThis.eContainer.eUnset(eRefRemove) - else - removeThis.eContainer.eSet(eRefRemove,null) - } else { // multivalue - val l = removeThis.eContainer.eGet(eRefRemove) as List; // c.f. type check above - var idx = l.indexOf(removeThis); - l.remove(idx); - } - - //////////////////////////////////// - //// insert: - val idx = if( pos < 0 || pos >= newContainer.size ) { - // append - newContainer.size - } else { - // insert - pos - }; - newContainerCasted.add(idx,insertThis) - - } else { - throw new IllegalArgumentException("designated new container-list must be a subtype of type EObjectContainmentList") - } - } - - /** - * {@code elementInIntermediateModel} is going away (ie, should be garbage-collected) and therefore we clear all - * references to it from tracing. The {@code replacements} IM nodes take over whatever AST node was previously - * traced-back-to via the element going away. - */ - def private static void replaceWithoutRewire(TranspilerState state, EObject elementInIntermediateModel, EObject... replacements) { - state.tracer.copyTrace(elementInIntermediateModel, replacements); - state.tracer.discardIntermediateModelNode(elementInIntermediateModel); - state.insertOrReplace_internal(elementInIntermediateModel, replacements, true, false); - } - - - def public static void insertBefore(TranspilerState state, EObject elementInIntermediateModel, EObject... newElements) { - state.insertOrReplace_internal(elementInIntermediateModel, newElements, false, false); - } - - def public static void insertAfter(TranspilerState state, EObject elementInIntermediateModel, EObject... newElements) { - state.insertOrReplace_internal(elementInIntermediateModel, newElements, false, true); - } - - def private static void insertOrReplace_internal(TranspilerState state, EObject elementInIntermediateModel, - EObject[] newElements, boolean replace, boolean after - ) { - if(newElements.empty && ! replace) { - return; // nothing to be inserted - } - val eRef = checkedContainmentFeature(elementInIntermediateModel); - val eRefType = eRef.getEReferenceType; - val replElemsOfWrongType = newElements.filter[!eRefType.isSuperTypeOf(it.eClass)]; - if(!replElemsOfWrongType.empty) { - throw new IllegalArgumentException("one or more elements are of wrong type, expected: " - + eRef.EReferenceType.name + ", actual: " + replElemsOfWrongType.map[eClass.name].join(', ')); - } - if( eRef.upperBound == 1 ) { - // single valued - if( newElements.length > 1 ) - throw new IllegalArgumentException("the single-valued reference "+eRef.name+" in class "+eRef.EContainingClass - + " is not able to hold "+newElements.length +" elements."); - if( newElements.length == 1 ) - { - if( !replace ) - throw new IllegalArgumentException("Cannot insert another element into a single-valued containment reference "+eRef.name+" in class "+eRef.EContainingClass ); - elementInIntermediateModel.eContainer.eSet(eRef,newElements.get(0)); - } else { - if( !replace ) - throw new IllegalArgumentException("Inserting zero elements with replace==false is pointless."); - // no element, so remove - if( eRef.isUnsettable ) - elementInIntermediateModel.eContainer.eUnset(eRef) - else - elementInIntermediateModel.eContainer.eSet(eRef,null) - } - - } else { - // multi-valued - val l = elementInIntermediateModel.eContainer.eGet(eRef) as List; // c.f. type check above - var idx = l.indexOf(elementInIntermediateModel); - if(replace) { - l.remove(idx); - } else { - // note: before/after only applicable if !replace - if(after) { - idx++; // always safe to increment, because we know there exists an element at index 'idx' - } - } - l.addAll(idx, newElements); - } - } - - /** Retrieves the EReference of the Container. Throws Exceptions if a) not part of the IM or b) not contained anywhere*/ - def private static EReference checkedContainmentFeature(EObject elementInIntermediateModel) { - if(!TranspilerUtils.isIntermediateModelElement(elementInIntermediateModel)) { - throw new IllegalArgumentException("not an element in the intermediate model: " + elementInIntermediateModel); - } - val eRef = elementInIntermediateModel.eContainmentFeature; - if(eRef===null) { - throw new IllegalArgumentException("element is not contained anywhere"); - } - return eRef; - } - - /** - * Rename the given symbol table entry and all named elements in the intermediate model that are using this name. - * During AST transformations in the transpiler, the 'name' property of a symbol table entry should never be changed - * directly, but this operation should be used instead. - *

- * WARNING: renaming is currently only implemented partially and used only in a single, very specific use - * case; if renaming is required in the future, then the implementation of this method has to be complemented! - */ - def public static void rename(TranspilerState state, SymbolTableEntry entry, String newName) { - SymbolTableManagement.rename(state, entry, newName ); - } - - /** - * Copy a subtree of the intermediate model. - */ - def public static T copy(TranspilerState state, T elementInIM) { - // create a copy with a special copier to take care of reference ReferencingElement_IM#rewiredTarget - val copier = new IM2IMCopier(); - val result = copier.copy(elementInIM); - copier.copyReferences(); - // copy tracing information - state.tracer.copyTrace(elementInIM, result); // note: copying trace for all nodes would be more fine grained - return result as T; - } - - private static final class IM2IMCopier extends EcoreUtil.Copier { - private static final EReference eRef__ReferencingElement_IM__rewiredTarget = ImPackage.eINSTANCE.referencingElement_IM_RewiredTarget; - - override protected copyReference(EReference eReference, EObject eObject, EObject copyEObject) { - if(eReference===eRef__ReferencingElement_IM__rewiredTarget) { - (copyEObject as ReferencingElement_IM).rewiredTarget = (eObject as ReferencingElement_IM).rewiredTarget; - } else { - super.copyReference(eReference, eObject, copyEObject); - } - } - - } -} diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/assistants/TypeAssistant.java b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/assistants/TypeAssistant.java new file mode 100644 index 0000000000..2170c6c66e --- /dev/null +++ b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/assistants/TypeAssistant.java @@ -0,0 +1,228 @@ +/** + * 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.assistants; + +import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks._PropertyAccessExpr; +import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.n4ObjectType; +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.filterNull; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.flatten; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.forall; +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.n4JS.AnnotationList; +import org.eclipse.n4js.n4JS.ExportDeclaration; +import org.eclipse.n4js.n4JS.FunctionDefinition; +import org.eclipse.n4js.n4JS.N4ClassDeclaration; +import org.eclipse.n4js.n4JS.N4ClassifierDeclaration; +import org.eclipse.n4js.n4JS.N4InterfaceDeclaration; +import org.eclipse.n4js.n4JS.Script; +import org.eclipse.n4js.n4JS.TypeDefiningElement; +import org.eclipse.n4js.n4JS.TypeReferenceNode; +import org.eclipse.n4js.transpiler.AbstractTranspiler; +import org.eclipse.n4js.transpiler.InformationRegistry; +import org.eclipse.n4js.transpiler.TransformationAssistant; +import org.eclipse.n4js.transpiler.TranspilerState; +import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM; +import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal; +import org.eclipse.n4js.transpiler.utils.ConcreteMembersOrderedForTranspiler; +import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef; +import org.eclipse.n4js.ts.typeRefs.TypeRef; +import org.eclipse.n4js.ts.types.TClass; +import org.eclipse.n4js.ts.types.TClassifier; +import org.eclipse.n4js.ts.types.TFunction; +import org.eclipse.n4js.ts.types.TInterface; +import org.eclipse.n4js.ts.types.Type; +import org.eclipse.n4js.utils.N4JSLanguageUtils; +import org.eclipse.n4js.validation.JavaScriptVariantHelper; + +import com.google.inject.Inject; + +/** + */ +public class TypeAssistant extends TransformationAssistant { + + @Inject + private JavaScriptVariantHelper jsVariantHelper; + + /** + * Some assertions related to {@link N4ClassifierDeclaration}s that apply to several transformations and are + * therefore factored out into this helper method. + */ + public void assertClassifierPreConditions() { + if (AbstractTranspiler.DEBUG_PERFORM_ASSERTIONS) { + List allClassifierDecls = collectNodes(getState().im, + N4ClassifierDeclaration.class, false); + assertTrue("all classifier declarations must have an original defined type", + forall(allClassifierDecls, cd -> getState().info.getOriginalDefinedType(cd) != null)); + + assertTrue("all class declarations must have a superClassRef pointing to a TClass (if non-null)", + forall(filterNull(map(filter(allClassifierDecls, N4ClassDeclaration.class), + cd -> cd.getSuperClassRef())), + scr -> { + TypeRef tRef = getState().info.getOriginalProcessedTypeRef(scr); + Type originalDeclType = tRef == null ? null : tRef.getDeclaredType(); + return originalDeclType instanceof TClass; + })); + + assertTrue( + "all classifier declarations must have all implementedOrExtendedInterfaceRefs pointing to a TInterface", + forall(flatten(map(allClassifierDecls, + cd -> cd.getImplementedOrExtendedInterfaceRefs())), + ir -> { + TypeRef tRef = getState().info.getOriginalProcessedTypeRef(ir); + Type originalDeclType = tRef == null ? null : tRef.getDeclaredType(); + return originalDeclType instanceof TInterface; + })); + + } + } + + /** + * Same as {@link InformationRegistry#getOriginalProcessedTypeRef(TypeReferenceNode)}. + */ + public TypeRef getOriginalOrContainedTypeRef(TypeReferenceNode typeRefNodeInIM) { + TypeRef originalTypeRef = getState().info.getOriginalProcessedTypeRef(typeRefNodeInIM); + if (originalTypeRef != null) { + return originalTypeRef; + } + // note: typeRefNodeInIM.getTypeRefInAST() will always be 'null', so no point in using that + return null; + } + + /***/ + // keep aligned to following method! + public SymbolTableEntryOriginal getOriginalDeclaredTypeSTE(TypeReferenceNode typeRefNodeInIM) { + TypeRef typeRef = getOriginalOrContainedTypeRef(typeRefNodeInIM); + Type declType = typeRef == null ? null : typeRef.getDeclaredType(); + if (declType != null) { + return getSymbolTableEntryOriginal(declType, true); + } + return null; + } + + /***/ + // keep aligned to previous method! + public Type getOriginalDeclaredType(TypeReferenceNode typeRefNodeInIM) { + TypeRef typeRef = getOriginalOrContainedTypeRef(typeRefNodeInIM); + return typeRef == null ? null : typeRef.getDeclaredType(); + } + + /** + * Returns symbol table entry for super class of given class declaration. + */ + public SymbolTableEntryOriginal getSuperClassSTE(N4ClassDeclaration classDecl) { + TypeReferenceNode superClassRef = classDecl == null ? null : classDecl.getSuperClassRef(); + if (superClassRef != null) { + SymbolTableEntryOriginal superClassSTE = getOriginalDeclaredTypeSTE(superClassRef); + if (superClassSTE != null) { + return superClassSTE; + } + } + return getSymbolTableEntryOriginal(n4ObjectType(getState().G), true); + } + + /** + * Returns super interfaces (i.e. implemented or extended interfaces) of given classifier. + */ + public List getSuperInterfacesSTEs(N4ClassifierDeclaration classifierDecl) { + List> superIfcRefNodes; + + if (classifierDecl instanceof N4ClassDeclaration) { + superIfcRefNodes = ((N4ClassDeclaration) classifierDecl).getImplementedInterfaceRefs(); + } else if (classifierDecl instanceof N4InterfaceDeclaration) { + superIfcRefNodes = ((N4InterfaceDeclaration) classifierDecl).getSuperInterfaceRefs(); + } else { + throw new IllegalStateException("unsupported subclass of N4ClassifierDeclaration: " + + (classifierDecl == null ? null : classifierDecl.getName())); + } + return toList(filterNull(map(superIfcRefNodes, si -> getOriginalDeclaredTypeSTE(si)))); + } + + /** + * Tells if the given classifier is declared on top level. + */ + public boolean isTopLevel(TypeDefiningElement typeDef) { + EObject parent = typeDef.eContainer(); + while (parent instanceof ExportDeclaration || parent instanceof AnnotationList) { + parent = parent.eContainer(); + } + return parent instanceof Script; + } + + /** + * Tells if the given type is defined in an N4JSD file. + *

+ * WARNING: for interfaces it is not enough to check {@link TInterface#isExternal()}, for this purpose, because + * structural interfaces in N4JSD files need not be declared external! + */ + public boolean inN4JSD(Type type) { + return jsVariantHelper.isExternalMode(type); + } + + /** + * For a member name that represents a symbol, such as #iterator, this method will return a property + * access expression that will evaluate to the corresponding symbol, e.g. Symbol.iterator. + */ + public ParameterizedPropertyAccessExpression_IM getMemberNameAsSymbol(String memberName) { + if (!memberName.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX)) { + throw new IllegalArgumentException("given member name does not denote a symbol"); + } + return _PropertyAccessExpr( + getSymbolTableEntryOriginal(symbolObjectType(getState().G), true), + getSymbolTableEntryInternal(memberName.substring(1), true)); + } + + /** + * Returns an instance of {@link ConcreteMembersOrderedForTranspiler} for the given classifier, using a cached + * instance if available. + */ + public ConcreteMembersOrderedForTranspiler getOrCreateCMOFT(TClassifier classifier) { + ConcreteMembersOrderedForTranspiler cachedCMOFT = getState().info.getCachedCMOFT(classifier); + if (cachedCMOFT != null) { + return cachedCMOFT; + } else { + ConcreteMembersOrderedForTranspiler newCMOFT = ConcreteMembersOrderedForTranspiler.create(getState(), + classifier); + getState().info.cacheCMOFT(classifier, newCMOFT); + return newCMOFT; + } + } + + /** + * From a given {@link FunctionDefinition} of the IM, this methods returns the {@link TypeRef} of the return type. + */ + public TypeRef getReturnTypeRef(TranspilerState state, FunctionDefinition funDef) { + EObject astNode = state.tracer.getOriginalASTNode(funDef); + if (astNode instanceof FunctionDefinition) { + TFunction tFunction = ((FunctionDefinition) astNode).getDefinedFunction(); + if (tFunction != null) { + TypeRef outerReturnTypeRef = tFunction.getReturnTypeRef(); + if (outerReturnTypeRef == null) { + // If you get an exception here: a transformation might have created an async and/or generator + // FunctionDefinition without the expected Promise<...> / [Async]Generator<...> return type + // (therefore the above call to method #hasExpectedSpecialReturnType() returned false); + // automatically deriving the outer from an inner return type is not supported for + // FunctionDefinitions created by transformations! + throw new IllegalStateException( + "unable to obtain outer return type of function from TModule"); + } + return outerReturnTypeRef; + } + } + return null; + } +} diff --git a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/assistants/TypeAssistant.xtend b/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/assistants/TypeAssistant.xtend deleted file mode 100644 index 236867c783..0000000000 --- a/plugins/org.eclipse.n4js.transpiler/src/org/eclipse/n4js/transpiler/assistants/TypeAssistant.xtend +++ /dev/null @@ -1,204 +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.assistants - -import com.google.inject.Inject -import java.util.List -import org.eclipse.n4js.n4JS.AnnotationList -import org.eclipse.n4js.n4JS.ExportDeclaration -import org.eclipse.n4js.n4JS.FunctionDefinition -import org.eclipse.n4js.n4JS.N4ClassDeclaration -import org.eclipse.n4js.n4JS.N4ClassifierDeclaration -import org.eclipse.n4js.n4JS.N4InterfaceDeclaration -import org.eclipse.n4js.n4JS.Script -import org.eclipse.n4js.n4JS.TypeDefiningElement -import org.eclipse.n4js.n4JS.TypeReferenceNode -import org.eclipse.n4js.transpiler.AbstractTranspiler -import org.eclipse.n4js.transpiler.InformationRegistry -import org.eclipse.n4js.transpiler.TransformationAssistant -import org.eclipse.n4js.transpiler.TranspilerState -import org.eclipse.n4js.transpiler.im.ParameterizedPropertyAccessExpression_IM -import org.eclipse.n4js.transpiler.im.SymbolTableEntryOriginal -import org.eclipse.n4js.transpiler.utils.ConcreteMembersOrderedForTranspiler -import org.eclipse.n4js.ts.typeRefs.TypeRef -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.Type -import org.eclipse.n4js.utils.N4JSLanguageUtils -import org.eclipse.n4js.validation.JavaScriptVariantHelper - -import static org.eclipse.n4js.transpiler.TranspilerBuilderBlocks.* - -import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.* - -/** - */ -class TypeAssistant extends TransformationAssistant { - - @Inject private JavaScriptVariantHelper jsVariantHelper; - - /** - * Some assertions related to {@link N4ClassifierDeclaration}s that apply to several transformations and are - * therefore factored out into this helper method. - */ - def public void assertClassifierPreConditions() { - if (AbstractTranspiler.DEBUG_PERFORM_ASSERTIONS) { - val allClassifierDecls = collectNodes(state.im, N4ClassifierDeclaration, false); - assertTrue("all classifier declarations must have an original defined type", - allClassifierDecls.forall[state.info.getOriginalDefinedType(it)!==null]); - assertTrue("all class declarations must have a superClassRef pointing to a TClass (if non-null)", - allClassifierDecls.filter(N4ClassDeclaration).map[superClassRef].filterNull - .forall[ - val originalDeclType = state.info.getOriginalProcessedTypeRef(it)?.declaredType; - return originalDeclType instanceof TClass; - ]); - assertTrue("all classifier declarations must have all implementedOrExtendedInterfaceRefs pointing to a TInterface", - allClassifierDecls.map[implementedOrExtendedInterfaceRefs].flatten - .forall[ - val originalDeclType = state.info.getOriginalProcessedTypeRef(it)?.declaredType; - return originalDeclType instanceof TInterface; - ]); - - } - } - - /** - * Same as {@link InformationRegistry#getOriginalProcessedTypeRef(TypeReferenceNode)}. - */ - def public TypeRef getOriginalOrContainedTypeRef(TypeReferenceNode typeRefNodeInIM) { - val originalTypeRef = state.info.getOriginalProcessedTypeRef(typeRefNodeInIM); - if (originalTypeRef !== null) { - return originalTypeRef; - } - // note: typeRefNodeInIM.getTypeRefInAST() will always be 'null', so no point in using that - return null; - } - - // keep aligned to following method! - def public SymbolTableEntryOriginal getOriginalDeclaredTypeSTE(TypeReferenceNode typeRefNodeInIM) { - val typeRef = getOriginalOrContainedTypeRef(typeRefNodeInIM); - val declType = typeRef?.declaredType; - if (declType !== null) { - return getSymbolTableEntryOriginal(declType, true); - } - return null; - } - - // keep aligned to previous method! - def public Type getOriginalDeclaredType(TypeReferenceNode typeRefNodeInIM) { - val typeRef = getOriginalOrContainedTypeRef(typeRefNodeInIM); - return typeRef?.declaredType; - } - - /** - * Returns symbol table entry for super class of given class declaration. - */ - def public SymbolTableEntryOriginal getSuperClassSTE(N4ClassDeclaration classDecl) { - val superClassRef = classDecl?.superClassRef; - if (superClassRef !== null) { - val superClassSTE = getOriginalDeclaredTypeSTE(superClassRef); - if(superClassSTE !== null) { - return superClassSTE; - } - } - return getSymbolTableEntryOriginal(state.G.n4ObjectType, true); - } - - /** - * Returns super interfaces (i.e. implemented or extended interfaces) of given classifier. - */ - def public List getSuperInterfacesSTEs(N4ClassifierDeclaration classifierDecl) { - val superIfcRefNodes = switch(classifierDecl) { - N4ClassDeclaration: classifierDecl.implementedInterfaceRefs - N4InterfaceDeclaration: classifierDecl.superInterfaceRefs - default: throw new IllegalStateException("unsupported subclass of N4ClassifierDeclaration: " + classifierDecl?.name) - } - return superIfcRefNodes - .map[getOriginalDeclaredTypeSTE(it)] - .filterNull - .toList; - } - - /** - * Tells if the given classifier is declared on top level. - */ - def public boolean isTopLevel(TypeDefiningElement typeDef) { - var parent = typeDef.eContainer; - while(parent instanceof ExportDeclaration || parent instanceof AnnotationList) { - parent = parent.eContainer; - } - return parent instanceof Script; - } - - /** - * Tells if the given type is defined in an N4JSD file. - *

- * WARNING: for interfaces it is not enough to check {@link TInterface#isExternal()}, for this purpose, - * because structural interfaces in N4JSD files need not be declared external! - */ - def public boolean inN4JSD(Type type) { - return jsVariantHelper.isExternalMode(type); - } - - /** - * For a member name that represents a symbol, such as #iterator, this method will return a property - * access expression that will evaluate to the corresponding symbol, e.g. Symbol.iterator. - */ - def public ParameterizedPropertyAccessExpression_IM getMemberNameAsSymbol(String memberName) { - if(!memberName.startsWith(N4JSLanguageUtils.SYMBOL_IDENTIFIER_PREFIX)) { - throw new IllegalArgumentException("given member name does not denote a symbol"); - } - return _PropertyAccessExpr( - getSymbolTableEntryOriginal(state.G.symbolObjectType, true), - getSymbolTableEntryInternal(memberName.substring(1), true) - ); - } - - /** - * Returns an instance of {@link ConcreteMembersOrderedForTranspiler} for the given classifier, using a cached - * instance if available. - */ - def public ConcreteMembersOrderedForTranspiler getOrCreateCMOFT(TClassifier classifier) { - val cachedCMOFT = state.info.getCachedCMOFT(classifier); - if(cachedCMOFT!==null) { - return cachedCMOFT; - } else { - val newCMOFT = ConcreteMembersOrderedForTranspiler.create(state, classifier); - state.info.cacheCMOFT(classifier, newCMOFT); - return newCMOFT; - } - } - - /** - * From a given {@link FunctionDefinition} of the IM, this methods returns the {@link TypeRef} of the return type. - */ - def public TypeRef getReturnTypeRef(TranspilerState state, FunctionDefinition funDef) { - val astNode = state.tracer.getOriginalASTNode(funDef); - if (astNode instanceof FunctionDefinition) { - val tFunction = astNode.getDefinedFunction(); - if (tFunction !== null) { - val outerReturnTypeRef = tFunction.getReturnTypeRef(); - if (outerReturnTypeRef === null) { - // If you get an exception here: a transformation might have created an async and/or generator - // FunctionDefinition without the expected Promise<...> / [Async]Generator<...> return type - // (therefore the above call to method #hasExpectedSpecialReturnType() returned false); - // automatically deriving the outer from an inner return type is not supported for - // FunctionDefinitions created by transformations! - throw new IllegalStateException( - "unable to obtain outer return type of function from TModule"); - } - return outerReturnTypeRef; - } - } - return null; - } -}