From 100998f0db8ec47db66f9f98096613fa40d25061 Mon Sep 17 00:00:00 2001 From: mmews Date: Wed, 21 Feb 2024 14:41:28 +0100 Subject: [PATCH] migrate migrate --- .../n4js/postprocessing/ASTProcessor.xtend | 4 +- .../postprocessing/AbstractPolyProcessor.java | 334 ++++++++++++++++++ .../AbstractPolyProcessor.xtend | 289 --------------- .../CompileTimeExpressionProcessor.java | 72 ++++ .../CompileTimeExpressionProcessor.xtend | 70 ---- .../postprocessing/ComputedNameProcessor.java | 127 +++++++ .../ComputedNameProcessor.xtend | 131 ------- 7 files changed, 535 insertions(+), 492 deletions(-) create mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.java delete mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.xtend create mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/CompileTimeExpressionProcessor.java delete mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/CompileTimeExpressionProcessor.xtend create mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ComputedNameProcessor.java delete mode 100644 plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ComputedNameProcessor.xtend diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ASTProcessor.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ASTProcessor.xtend index e214de2616..212c8656d2 100644 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ASTProcessor.xtend +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ASTProcessor.xtend @@ -146,10 +146,10 @@ public class ASTProcessor extends AbstractProcessor { def private void processAST(RuleEnvironment G, Script script, ASTMetaInfoCache cache) { // phase 0: process compile-time expressions & computed property names (order is important) for(node : script.eAllContents.filter(Expression).toIterable) { - compileTimeExpressionProcessor.evaluateCompileTimeExpression(G, node, cache, 0); + compileTimeExpressionProcessor.evaluateCompileTimeExpression(G, node, cache); } for(node : script.eAllContents.filter(LiteralOrComputedPropertyName).toIterable) { - computedNameProcessor.processComputedPropertyName(G, node, cache, 0); + computedNameProcessor.processComputedPropertyName( node, cache ); } // phase 1: main processing diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.java new file mode 100644 index 0000000000..35aae015b0 --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.java @@ -0,0 +1,334 @@ +/** + * 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.postprocessing; + +import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.newRuleEnvironment; +import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.wrap; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.exists; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.map; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.toSet; +import static org.eclipse.xtext.xbase.lib.IteratorExtensions.exists; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.n4js.n4JS.Argument; +import org.eclipse.n4js.n4JS.ArrayElement; +import org.eclipse.n4js.n4JS.ArrayLiteral; +import org.eclipse.n4js.n4JS.ArrowFunction; +import org.eclipse.n4js.n4JS.ConditionalExpression; +import org.eclipse.n4js.n4JS.Expression; +import org.eclipse.n4js.n4JS.FunctionDefinition; +import org.eclipse.n4js.n4JS.FunctionExpression; +import org.eclipse.n4js.n4JS.ObjectLiteral; +import org.eclipse.n4js.n4JS.ParameterizedCallExpression; +import org.eclipse.n4js.n4JS.PropertyAssignment; +import org.eclipse.n4js.n4JS.PropertyAssignmentAnnotationList; +import org.eclipse.n4js.n4JS.PropertyGetterDeclaration; +import org.eclipse.n4js.n4JS.PropertyMethodDeclaration; +import org.eclipse.n4js.n4JS.PropertyNameValuePair; +import org.eclipse.n4js.n4JS.PropertySetterDeclaration; +import org.eclipse.n4js.n4JS.PropertySpread; +import org.eclipse.n4js.ts.typeRefs.FunctionTypeExprOrRef; +import org.eclipse.n4js.ts.typeRefs.TypeRef; +import org.eclipse.n4js.ts.types.InferenceVariable; +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.ts.types.TypeVariable; +import org.eclipse.n4js.types.utils.TypeUtils; +import org.eclipse.n4js.typesystem.N4JSTypeSystem; +import org.eclipse.n4js.typesystem.constraints.InferenceContext; +import org.eclipse.n4js.typesystem.utils.RuleEnvironment; +import org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions; +import org.eclipse.n4js.typesystem.utils.TypeSystemHelper; +import org.eclipse.n4js.typesystem.utils.TypeSystemHelper.Callable; +import org.eclipse.n4js.utils.N4JSLanguageUtils; + +import com.google.inject.Inject; + +/** + * Base for all poly processors. Contains some utility and convenience methods. + */ +abstract class AbstractPolyProcessor extends AbstractProcessor { + + @Inject + private N4JSTypeSystem ts; + @Inject + private TypeSystemHelper tsh; + + /** + * Convenience method for {@link #isPoly(Expression)} and {@link #isPoly(PropertyAssignment)}, accepting any type of + * EObject. + */ + boolean isPoly(EObject obj) { + if (obj instanceof Expression) { + return isPoly(obj); + } + if (obj instanceof PropertyAssignment) { + return isPoly(obj); + } + return false; + } + + /** + * Tells whether the given expression is a poly expression, i.e. requires constraint-based type inference. + */ + boolean isPoly(Expression obj) { + if (obj instanceof ParameterizedCallExpression) { + ParameterizedCallExpression pce = (ParameterizedCallExpression) obj; + // NOTE: in next line, we do not propagate the cancel indicator; however, this is not required, because + // all we do with the newly created rule environment is to type a backward(!) reference, so we can be + // sure that no significant processing will be triggered by the type judgment invocation below + RuleEnvironment G = newRuleEnvironment(pce); + TypeRef targetTypeRef = ts.type(G, pce.getTarget()); // this is a backward reference (because we type obj's + // child) + Callable callable = tsh.getCallableTypeRef(G, targetTypeRef); + if (callable != null && callable.getSignatureTypeRef().isPresent()) { + FunctionTypeExprOrRef signatureTypeRef = callable.getSignatureTypeRef().get(); + return N4JSLanguageUtils.isPoly(signatureTypeRef, pce); + } else { + return false; + } + } + if (obj instanceof FunctionExpression) { + FunctionExpression fe = (FunctionExpression) obj; + return exists(fe.getFpars(), fpar -> fpar.getDeclaredTypeRefInAST() == null) + // type of 1 or more fpars is undeclared + || fe.getDeclaredReturnTypeRefInAST() == null; // return type is undeclared + // note: if the FunctionExpression is generic, this does *not* make it poly! + } + if (obj instanceof ArrayLiteral) { + return true; + } + if (obj instanceof ObjectLiteral) { + return exists(((ObjectLiteral) obj).getPropertyAssignments(), pa -> isPoly(pa)); + } + if (obj instanceof ConditionalExpression) { + ConditionalExpression ce = (ConditionalExpression) obj; + boolean trueIsPoly = isPoly(ce.getTrueExpression()); + boolean trueAllowsPoly = allowsPoly(ce.getTrueExpression()); + boolean falseAllowsPoly = allowsPoly(ce.getFalseExpression()); + boolean falseIsPoly = isPoly(ce.getFalseExpression()); + + return (trueIsPoly && falseAllowsPoly) || (falseIsPoly && trueAllowsPoly); + } + + return false; + + } + + boolean allowsPoly(Expression obj) { + if (obj == null) { + return false; + } + RuleEnvironment G = RuleEnvironmentExtensions.newRuleEnvironment(obj); + return N4JSLanguageUtils.isUndefinedLiteral(G, obj) || N4JSLanguageUtils.isNullLiteral(G, obj); + } + + /** + * True iff the given PropertyAssignment is a poly "expression", i.e. requires constraint-based type inference. + */ + private boolean isPoly(PropertyAssignment pa) { + if (pa instanceof PropertyNameValuePair) { + PropertyNameValuePair pnvp = (PropertyNameValuePair) pa; + // FIXME requiring pa.expression!=null is inconsistent! + return pnvp.getExpression() != null && pnvp.getDeclaredTypeRefInAST() == null; + } + if (pa instanceof PropertyGetterDeclaration) { + PropertyGetterDeclaration pgd = (PropertyGetterDeclaration) pa; + return pgd.getDeclaredTypeRefInAST() == null; + } + if (pa instanceof PropertySetterDeclaration) { + PropertySetterDeclaration psd = (PropertySetterDeclaration) pa; + return psd.getDeclaredTypeRefInAST() == null; + } + if (pa instanceof PropertyMethodDeclaration) { + return false; + } + if (pa instanceof PropertySpread) { + return false; // TODO GH-1337 add support for spread operator + } + if (pa instanceof PropertyAssignmentAnnotationList) { + return false; + } + + throw new IllegalArgumentException("unsupported subclass of PropertyAssignment: " + pa.eClass().getName()); + } + + /** + * Convenience method for {@link #isRootPoly(Expression)}, accepting any type of EObject. + */ + boolean isRootPoly(EObject obj) { + return (obj instanceof Expression) ? isRootPoly(obj) : false; + } + + /** + * Tells whether the given expression is a root poly expression, i.e. it + *
    + *
  1. is a {@link #isPoly(Expression) poly expression}, and + *
  2. represents the root of a tree of nested poly expressions which have to be inferred together within a single + * constraint system (this tree may have depth 0, i.e. consist only of the given expression). + *
+ */ + boolean isRootPoly(Expression obj) { + if (isPoly(obj)) { + EObject p = getParentPolyCandidate(obj); + return p == null || !isPoly(p); + } + return false; + } + + /** + * Given a poly expression, returns the parent expression that might be the parent poly expression. If the + * given expression is not poly, the return value is undefined. + */ + private EObject getParentPolyCandidate(Expression poly) { + EObject directParent = poly == null ? null : poly.eContainer(); + EObject grandParent = directParent == null ? null : directParent.eContainer(); + + if (directParent instanceof Argument) { + if (grandParent instanceof ParameterizedCallExpression && toSet(map( + ((ParameterizedCallExpression) grandParent).getArguments(), a -> a.getExpression())) + .contains(poly)) { + // TODO what about the target expression? i.e.: || directParent.target==poly) { + return grandParent; + } + } else if (directParent instanceof FunctionExpression) { + return null; // function expressions never have nested poly expressions (expression in the body are + // detached) + } else if (directParent instanceof ArrayElement) { + if (((Argument) directParent).getExpression() == poly) { + return directParent.eContainer();// return the ArrayLiteral as parent (not the ArrayElement) + } + } else if (directParent instanceof PropertyNameValuePair) { + if (((Argument) directParent).getExpression() == poly) { + return directParent;// return the PropertyNameValuePair as parent (not the ObjectLiteral) + } + } else if (directParent instanceof ConditionalExpression) { + return directParent; + } else if (directParent instanceof PropertyGetterDeclaration) { + return null;// getters never have nested poly expressions + } else if (directParent instanceof PropertySetterDeclaration) { + return null; // setters never have nested poly expressions + } else if (directParent instanceof PropertySpread) { + return null;// TODO GH-1337 add support for spread operator + } + + return null; + } + + // ------------------------------------------------------------------------------------------------------------------------------ + + /** + * Returns the type of a nested poly expression. The final type is returned, i.e. not the one created when preparing + * the constraint system that may contain inference variables. + *

+ * Because final types are created and stored in the typing cache in the onSuccess/onFailure lambdas and those + * lambdas of nested poly expressions are registered before those of outer expression, we can here simply read the + * nested poly expression's type from the cache. + */ + protected TypeRef getFinalResultTypeOfNestedPolyExpression(Expression nestedPolyExpression) { + return ASTMetaInfoUtils.getTypeFailSafe(nestedPolyExpression); + } + + protected TypeRef subst(TypeRef typeRef, RuleEnvironment G, + Map substitutions) { + + return subst(typeRef, G, substitutions, false); + } + + protected TypeRef subst(TypeRef typeRef, RuleEnvironment G, + Map substitutions, boolean reverse) { + + RuleEnvironment Gx = wrap(G); + for (Entry e : substitutions.entrySet()) { + if (reverse) { + Gx.put(e.getValue(), TypeUtils.createTypeRef(e.getKey())); + } else { + Gx.put(e.getKey(), TypeUtils.createTypeRef(e.getValue())); + } + } + + TypeRef typeRefSubst = ts.substTypeVariables(Gx, typeRef); + if (typeRefSubst == null) { + throw new IllegalArgumentException("substitution failed"); + } + return typeRefSubst; + } + + protected TypeRef applySolution(TypeRef typeRef, RuleEnvironment G, Map solution) { + if (typeRef == null || solution == null || solution.isEmpty()) { + return typeRef; // note: returning 'null' if typeRef==null (broken AST, etc.) + } + RuleEnvironment Gx = wrap(G); + for (Entry e : solution.entrySet()) { + Gx.put(e.getKey(), e.getValue()); + } + TypeRef typeRefSubst = ts.substTypeVariables(Gx, typeRef); + if (typeRefSubst == null) { + throw new IllegalArgumentException("substitution failed"); + } + return typeRefSubst; + } + + protected Map createPseudoSolution(InferenceContext infCtx, + TypeRef defaultTypeRef) { + + Map pseudoSolution = new HashMap<>(); + for (InferenceVariable iv : infCtx.getInferenceVariables()) { + pseudoSolution.put(iv, defaultTypeRef); // map all inference variables to the default + } + return pseudoSolution; + } + + // FIXME move to a better place + protected boolean isReturningValue(FunctionDefinition fun) { + return (fun.getBody() != null && exists(fun.getBody().getAllReturnStatements(), s -> s.getExpression() != null)) + || ((fun instanceof ArrowFunction) ? ((ArrowFunction) fun).isSingleExprImplicitReturn() : false); + // TODO except call to void function!! + } + + protected TypeRef getTypeOfMember(TMember m) { + if (m instanceof TField) { + return ((TField) m).getTypeRef(); + } else if (m instanceof TGetter) { + return ((TGetter) m).getTypeRef(); + } else if (m instanceof TSetter) { + return ((TSetter) m).getFpar().getTypeRef(); + } else if (m instanceof TMethod) { + throw new IllegalArgumentException("this method should not be used for TMethod"); + } + String clsName = m == null ? null : m.eClass() == null ? null : m.eClass().getName(); + throw new IllegalArgumentException("unknown subtype of TMember: " + clsName); + } + + protected void setTypeOfMember(TMember m, TypeRef type) { + if (m instanceof TField) { + ((TField) m).setTypeRef(type); + } else if (m instanceof TGetter) { + ((TGetter) m).setTypeRef(type); + } else if (m instanceof TSetter) { + TSetter tst = (TSetter) m; + if (tst.getFpar() != null) { + tst.getFpar().setTypeRef(type); + } + } else if (m instanceof TMethod) { + throw new IllegalArgumentException("this method should not be used for TMethod"); + } + String clsName = m == null ? null : m.eClass() == null ? null : m.eClass().getName(); + throw new IllegalArgumentException("unknown subtype of TMember: " + clsName); + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.xtend deleted file mode 100644 index 8d3b99d5f3..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/AbstractPolyProcessor.xtend +++ /dev/null @@ -1,289 +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.postprocessing - -import com.google.inject.Inject -import java.util.Map -import org.eclipse.emf.ecore.EObject -import org.eclipse.n4js.n4JS.Argument -import org.eclipse.n4js.n4JS.ArrayElement -import org.eclipse.n4js.n4JS.ArrayLiteral -import org.eclipse.n4js.n4JS.ArrowFunction -import org.eclipse.n4js.n4JS.ConditionalExpression -import org.eclipse.n4js.n4JS.Expression -import org.eclipse.n4js.n4JS.FunctionDefinition -import org.eclipse.n4js.n4JS.FunctionExpression -import org.eclipse.n4js.n4JS.ObjectLiteral -import org.eclipse.n4js.n4JS.ParameterizedCallExpression -import org.eclipse.n4js.n4JS.PropertyAssignment -import org.eclipse.n4js.n4JS.PropertyAssignmentAnnotationList -import org.eclipse.n4js.n4JS.PropertyGetterDeclaration -import org.eclipse.n4js.n4JS.PropertyMethodDeclaration -import org.eclipse.n4js.n4JS.PropertyNameValuePair -import org.eclipse.n4js.n4JS.PropertySetterDeclaration -import org.eclipse.n4js.n4JS.PropertySpread -import org.eclipse.n4js.ts.typeRefs.TypeRef -import org.eclipse.n4js.ts.types.InferenceVariable -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.ts.types.TypeVariable -import org.eclipse.n4js.types.utils.TypeUtils -import org.eclipse.n4js.typesystem.N4JSTypeSystem -import org.eclipse.n4js.typesystem.constraints.InferenceContext -import org.eclipse.n4js.typesystem.utils.RuleEnvironment -import org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions -import org.eclipse.n4js.typesystem.utils.TypeSystemHelper -import org.eclipse.n4js.utils.N4JSLanguageUtils - -import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.* - -/** - * Base for all poly processors. Contains some utility and convenience methods. - */ -package abstract class AbstractPolyProcessor extends AbstractProcessor { - - @Inject - private N4JSTypeSystem ts; - @Inject - private TypeSystemHelper tsh; - - /** - * Convenience method for {@link #isPoly(Expression)} and {@link #isPoly(PropertyAssignment)}, accepting any type of - * EObject. - */ - def boolean isPoly(EObject obj) { - return switch (obj) { - Expression: obj.isPoly - PropertyAssignment: obj.isPoly - default: false - } - } - - /** - * Tells whether the given expression is a poly expression, i.e. requires constraint-based type inference. - */ - def boolean isPoly(Expression obj) { - return switch (obj) { - ParameterizedCallExpression: { - // NOTE: in next line, we do not propagate the cancel indicator; however, this is not required, because - // all we do with the newly created rule environment is to type a backward(!) reference, so we can be - // sure that no significant processing will be triggered by the type judgment invocation below - val G = obj.newRuleEnvironment; - val TypeRef targetTypeRef = ts.type(G, obj.target); // this is a backward reference (because we type obj's child) - val callable = tsh.getCallableTypeRef(G, targetTypeRef); - if (callable !== null && callable.signatureTypeRef.present) { - val signatureTypeRef = callable.signatureTypeRef.get(); - N4JSLanguageUtils.isPoly(signatureTypeRef, obj) - } else { - false - } - } - FunctionExpression: - obj.fpars.exists[declaredTypeRefInAST === null] // type of 1 or more fpars is undeclared - || obj.declaredReturnTypeRefInAST === null // return type is undeclared - // note: if the FunctionExpression is generic, this does *not* make it poly! - ArrayLiteral: - true - ObjectLiteral: - obj.propertyAssignments.exists[isPoly] - ConditionalExpression: { - val boolean trueIsPoly = isPoly(obj.trueExpression); - val boolean trueAllowsPoly = allowsPoly(obj.trueExpression); - val boolean falseAllowsPoly = allowsPoly(obj.falseExpression); - val boolean falseIsPoly = isPoly(obj.falseExpression); - - (trueIsPoly && falseAllowsPoly) || (falseIsPoly && trueAllowsPoly) - } - default: - false - } - } - - def boolean allowsPoly(Expression obj) { - if (obj === null) { - return false; - } - val RuleEnvironment G = RuleEnvironmentExtensions.newRuleEnvironment(obj); - return N4JSLanguageUtils.isUndefinedLiteral(G, obj) || N4JSLanguageUtils.isNullLiteral(G, obj); - } - - /** - * Tells whether the given PropertyAssignment is a poly "expression", i.e. requires constraint-based type inference. - */ - def private boolean isPoly(PropertyAssignment pa) { - switch (pa) { - PropertyNameValuePair: - pa.expression !== null && pa.declaredTypeRefInAST === null // FIXME requiring pa.expression!==null is inconsistent! - PropertyGetterDeclaration: - pa.declaredTypeRefInAST === null - PropertySetterDeclaration: - pa.declaredTypeRefInAST === null - PropertyMethodDeclaration: - false - PropertySpread: - false // TODO GH-1337 add support for spread operator - PropertyAssignmentAnnotationList: - false - default: - throw new IllegalArgumentException("unsupported subclass of PropertyAssignment: " + pa.eClass.name) - } - } - - /** - * Convenience method for {@link #isRootPoly(Expression)}, accepting any type of EObject. - */ - def boolean isRootPoly(EObject obj) { - if (obj instanceof Expression) obj.isRootPoly else false - } - - /** - * Tells whether the given expression is a root poly expression, i.e. it - *

    - *
  1. is a {@link #isPoly(Expression) poly expression}, and - *
  2. represents the root of a tree of nested poly expressions which have to be inferred together within a single - * constraint system (this tree may have depth 0, i.e. consist only of the given expression). - *
- */ - def boolean isRootPoly(Expression obj) { - if (isPoly(obj)) { - val p = getParentPolyCandidate(obj); - return p === null || !isPoly(p); - } - return false; - } - - /** - * Given a poly expression, returns the parent expression that might be the parent poly expression. - * If the given expression is not poly, the return value is undefined. - */ - def private EObject getParentPolyCandidate(Expression poly) { - val directParent = poly?.eContainer; - val grandParent = directParent?.eContainer; - return switch (directParent) { - Argument case grandParent instanceof ParameterizedCallExpression && - (grandParent as ParameterizedCallExpression).arguments.map[expression].contains(poly): // TODO what about the target expression? i.e.: || directParent.target===poly - grandParent - FunctionExpression: - null // function expressions never have nested poly expressions (expression in the body are detached) - ArrayElement case directParent.expression === poly: - directParent.eContainer as ArrayLiteral // return the ArrayLiteral as parent (not the ArrayElement) - PropertyNameValuePair case directParent.expression === poly: - directParent // return the PropertyNameValuePair as parent (not the ObjectLiteral) - ConditionalExpression: - directParent - PropertyGetterDeclaration: - null // getters never have nested poly expressions - PropertySetterDeclaration: - null // setters never have nested poly expressions - PropertySpread: - null // TODO GH-1337 add support for spread operator - } - } - - - // ------------------------------------------------------------------------------------------------------------------------------ - - - /** - * Returns the type of a nested poly expression. The final type is returned, i.e. not the one created when preparing - * the constraint system that may contain inference variables. - *

- * Because final types are created and stored in the typing cache in the onSuccess/onFailure lambdas and those - * lambdas of nested poly expressions are registered before those of outer expression, we can here simply read the - * nested poly expression's type from the cache. - */ - def protected TypeRef getFinalResultTypeOfNestedPolyExpression(Expression nestedPolyExpression) { - return ASTMetaInfoUtils.getTypeFailSafe(nestedPolyExpression); - } - - def protected TypeRef subst(TypeRef typeRef, RuleEnvironment G, - Map substitutions) { - - subst(typeRef, G, substitutions, false) - } - - def protected TypeRef subst(TypeRef typeRef, RuleEnvironment G, - Map substitutions, boolean reverse) { - - val Gx = G.wrap; - substitutions.entrySet.forEach [ e | - if (reverse) - Gx.put(e.value, TypeUtils.createTypeRef(e.key)) - else - Gx.put(e.key, TypeUtils.createTypeRef(e.value)) - ]; - val typeRefSubst = ts.substTypeVariables(Gx, typeRef); - if (typeRefSubst === null) - throw new IllegalArgumentException("substitution failed"); - return typeRefSubst; - } - - def protected TypeRef applySolution(TypeRef typeRef, RuleEnvironment G, Map solution) { - if (typeRef === null || solution === null || solution.empty) { - return typeRef; // note: returning 'null' if typeRef==null (broken AST, etc.) - } - val Gx = G.wrap; - solution.entrySet.forEach[e|Gx.put(e.key, e.value)]; - val typeRefSubst = ts.substTypeVariables(Gx, typeRef); - if (typeRefSubst === null) - throw new IllegalArgumentException("substitution failed"); - return typeRefSubst; - } - - def protected Map createPseudoSolution(InferenceContext infCtx, - TypeRef defaultTypeRef) { - - val pseudoSolution = newHashMap; - for (iv : infCtx.getInferenceVariables) { - pseudoSolution.put(iv, defaultTypeRef); // map all inference variables to the default - } - return pseudoSolution; - } - - // FIXME move to a better place - def protected boolean isReturningValue(FunctionDefinition fun) { - return (fun.body !== null && fun.body.allReturnStatements.exists[expression !== null]) || - (if (fun instanceof ArrowFunction) fun.singleExprImplicitReturn else false); // TODO except call to void function!! - } - - def protected TypeRef getTypeOfMember(TMember m) { - switch (m) { - TField: - m.typeRef - TGetter: - m.typeRef - TSetter: - m?.fpar.typeRef - TMethod: - throw new IllegalArgumentException("this method should not be used for TMethod") - default: - throw new IllegalArgumentException("unknown subtype of TMember: " + m?.eClass?.name) - } - } - - def protected void setTypeOfMember(TMember m, TypeRef type) { - switch (m) { - TField: - m.typeRef = type - TGetter: - m.typeRef = type - TSetter: - if (m.fpar !== null) m.fpar.typeRef = type - TMethod: - throw new IllegalArgumentException("this method should not be used for TMethod") - default: - throw new IllegalArgumentException("unknown subtype of TMember: " + m?.eClass?.name) - } - } -} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/CompileTimeExpressionProcessor.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/CompileTimeExpressionProcessor.java new file mode 100644 index 0000000000..8aae96c70f --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/CompileTimeExpressionProcessor.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2017 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ +package org.eclipse.n4js.postprocessing; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.n4js.compileTime.CompileTimeEvaluator; +import org.eclipse.n4js.compileTime.CompileTimeValue; +import org.eclipse.n4js.n4JS.Expression; +import org.eclipse.n4js.n4JS.N4FieldDeclaration; +import org.eclipse.n4js.n4JS.VariableDeclaration; +import org.eclipse.n4js.ts.types.TConstableElement; +import org.eclipse.n4js.typesystem.utils.RuleEnvironment; +import org.eclipse.n4js.utils.EcoreUtilN4; +import org.eclipse.n4js.utils.N4JSLanguageUtils; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +/** + * Processing of compile-time expressions. + *

+ * All expressions for which {@link N4JSLanguageUtils#isProcessedAsCompileTimeExpression(Expression)} returns + * true are evaluated and the resulting {@link CompileTimeValue} is stored in the cache. In some cases, the + * value is also stored in the TModule. + */ +@Singleton +public class CompileTimeExpressionProcessor { + + @Inject + private CompileTimeEvaluator compileTimeEvaluator; + + /** + * If the given AST node is an expression that is directly processed as a compile-time expression (cf. + * {@link N4JSLanguageUtils#isProcessedAsCompileTimeExpression(Expression)}, this method will evaluate the + * expression and store the evaluation result in the given cache; otherwise, this method will do nothing. + */ + public void evaluateCompileTimeExpression(RuleEnvironment G, Expression astNode, ASTMetaInfoCache cache) { + if (N4JSLanguageUtils.isProcessedAsCompileTimeExpression(astNode)) { + CompileTimeValue value = compileTimeEvaluator.evaluateCompileTimeExpression(G, astNode); + cache.storeCompileTimeValue(astNode, value); + + // in some cases, we have to store the compile-time value in the TModule: + EObject parent = astNode.eContainer(); + if (parent instanceof VariableDeclaration) { + VariableDeclaration vd = (VariableDeclaration) parent; + if (vd.isDirectlyExported()) { + storeValueInTModule(vd.getDefinedVariable(), value); + } + } else if (parent instanceof N4FieldDeclaration) { + N4FieldDeclaration fd = (N4FieldDeclaration) parent; + storeValueInTModule(fd.getDefinedField(), value); + } + } + } + + private void storeValueInTModule(TConstableElement elem, CompileTimeValue value) { + if (elem != null && elem.isConst()) { + String valueStr = CompileTimeValue.serialize(value); + EcoreUtilN4.doWithDeliver(false, () -> { + elem.setCompileTimeValue(valueStr); + }, elem); + } + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/CompileTimeExpressionProcessor.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/CompileTimeExpressionProcessor.xtend deleted file mode 100644 index 131327ad52..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/CompileTimeExpressionProcessor.xtend +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2017 NumberFour AG. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * NumberFour AG - Initial API and implementation - */ -package org.eclipse.n4js.postprocessing - -import com.google.inject.Inject -import com.google.inject.Singleton -import org.eclipse.n4js.compileTime.CompileTimeEvaluator -import org.eclipse.n4js.compileTime.CompileTimeValue -import org.eclipse.n4js.n4JS.Expression -import org.eclipse.n4js.n4JS.N4FieldDeclaration -import org.eclipse.n4js.n4JS.VariableDeclaration -import org.eclipse.n4js.ts.types.TConstableElement -import org.eclipse.n4js.typesystem.utils.RuleEnvironment -import org.eclipse.n4js.utils.EcoreUtilN4 -import org.eclipse.n4js.utils.N4JSLanguageUtils - -/** - * Processing of compile-time expressions. - *

- * All expressions for which {@link N4JSLanguageUtils#isProcessedAsCompileTimeExpression(Expression)} returns - * true are evaluated and the resulting {@link CompileTimeValue} is stored in the cache. In some cases, - * the value is also stored in the TModule. - */ -@Singleton -class CompileTimeExpressionProcessor { - - @Inject - private CompileTimeEvaluator compileTimeEvaluator; - - /** - * If the given AST node is an expression that is directly processed as a compile-time expression (cf. - * {@link N4JSLanguageUtils#isProcessedAsCompileTimeExpression(Expression)}, this method will evaluate the - * expression and store the evaluation result in the given cache; otherwise, this method will do nothing. - */ - def public void evaluateCompileTimeExpression(RuleEnvironment G, Expression astNode, ASTMetaInfoCache cache, - int indentLevel) { - - if (N4JSLanguageUtils.isProcessedAsCompileTimeExpression(astNode)) { - val value = compileTimeEvaluator.evaluateCompileTimeExpression(G, astNode); - cache.storeCompileTimeValue(astNode, value); - - // in some cases, we have to store the compile-time value in the TModule: - val parent = astNode.eContainer; - if (parent instanceof VariableDeclaration) { - if (parent.directlyExported) { - storeValueInTModule(G, parent.definedVariable, value); - } - } else if (parent instanceof N4FieldDeclaration) { - storeValueInTModule(G, parent.definedField, value); - } - } - } - - def private void storeValueInTModule(RuleEnvironment G, TConstableElement elem, CompileTimeValue value) { - if (elem !== null && elem.const) { - val valueStr = CompileTimeValue.serialize(value); - EcoreUtilN4.doWithDeliver(false, [ - elem.compileTimeValue = valueStr; - ], elem); - } - } -} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ComputedNameProcessor.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ComputedNameProcessor.java new file mode 100644 index 0000000000..b8429a6d55 --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ComputedNameProcessor.java @@ -0,0 +1,127 @@ +/** + * Copyright (c) 2017 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ +package org.eclipse.n4js.postprocessing; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.n4js.compileTime.CompileTimeValue; +import org.eclipse.n4js.n4JS.LiteralOrComputedPropertyName; +import org.eclipse.n4js.n4JS.N4FieldDeclaration; +import org.eclipse.n4js.n4JS.N4GetterDeclaration; +import org.eclipse.n4js.n4JS.N4JSASTUtils; +import org.eclipse.n4js.n4JS.N4SetterDeclaration; +import org.eclipse.n4js.n4JS.PropertyGetterDeclaration; +import org.eclipse.n4js.n4JS.PropertyNameValuePair; +import org.eclipse.n4js.n4JS.PropertySetterDeclaration; +import org.eclipse.n4js.n4JS.PropertySpread; +import org.eclipse.n4js.n4JS.TypeDefiningElement; +import org.eclipse.n4js.ts.types.IdentifiableElement; +import org.eclipse.n4js.ts.types.SyntaxRelatedTElement; +import org.eclipse.n4js.utils.EcoreUtilN4; +import org.eclipse.n4js.utils.N4JSLanguageUtils; + +import com.google.inject.Singleton; + +/** + * Processing of {@link LiteralOrComputedPropertyName}s that have a computed property name, mainly setting property + * {@link LiteralOrComputedPropertyName#getComputedName() 'computedName'}. + *

+ * For details, see + * {@link ComputedNameProcessor#processComputedPropertyName( LiteralOrComputedPropertyName, ASTMetaInfoCache )}. + */ +@Singleton +public class ComputedNameProcessor { + + /** + * If the given 'nameDecl' has a computed property name, this method will + *

    + *
  1. obtain its expression's compile-time value from the cache (the actual evaluation of the expression happened + * in {@link CompileTimeExpressionProcessor}), + *
  2. store this name in 'nameDecl' (for later use), and + *
  3. store this name in the corresponding TModule element (if such a TModule element exists). + *
+ *

+ * In case the compile-time value of the expression is invalid (i.e. the expression is not a valid compile-time + * expression) no actual name will be stored in 'nameDecl' and the corresponding TModule element will be removed + * from the TModule, entirely (if such a TModule element exists). + */ + public void processComputedPropertyName(LiteralOrComputedPropertyName nameDecl, ASTMetaInfoCache cache) { + if (nameDecl.hasComputedPropertyName()) { + // obtain compile-time value of expression + CompileTimeValue value = cache.getCompileTimeValue(nameDecl.getExpression()); + // derive a property name from the value + String name = N4JSLanguageUtils.derivePropertyNameFromCompileTimeValue(value); + if (name != null) { + // cache the computed name in the LiteralOrComputedPropertyName AST node + EcoreUtilN4.doWithDeliver(false, () -> { + nameDecl.setComputedName(name); + nameDecl.setComputedSymbol(value instanceof CompileTimeValue.ValueSymbol); + }, nameDecl); + // set the computed name in the types model element + EObject owner = nameDecl.eContainer(); + EObject typeElem = N4JSASTUtils.getCorrespondingTypeModelElement(owner); + if (typeElem instanceof IdentifiableElement) { + EcoreUtilN4.doWithDeliver(false, () -> { + ((IdentifiableElement) typeElem).setName(name); + }, typeElem); + } + } else { + // invalid name expression (i.e. not a constant expression) + // -> remove the types model element from the TModule + // (note: we have to do this for consistency with how the types builder handles elements that are + // unnamed (usually due to a broken AST): in those cases, the types builder does not create a TModule + // element) + EObject owner = nameDecl.eContainer(); + discardTypeModelElement(owner); + } + } + } + + /** + * Discards the types model element corresponding to the given AST node. Throws exception if given AST node does not + * have a corresponding types model element. + */ + private void discardTypeModelElement(EObject astNode) { + EObject elem = N4JSASTUtils.getCorrespondingTypeModelElement(astNode); + + EcoreUtilN4.doWithDeliver(false, () -> { + if (astNode instanceof TypeDefiningElement) { + ((TypeDefiningElement) astNode).setDefinedType(null); + } else if (astNode instanceof N4FieldDeclaration) { + ((N4FieldDeclaration) astNode).setDefinedField(null); + } else if (astNode instanceof N4GetterDeclaration) { + ((N4GetterDeclaration) astNode).setDefinedGetter(null); + } else if (astNode instanceof N4SetterDeclaration) { + ((N4SetterDeclaration) astNode).setDefinedSetter(null); + } else if (astNode instanceof PropertyNameValuePair) { + ((PropertyNameValuePair) astNode).setDefinedField(null); + } else if (astNode instanceof PropertyGetterDeclaration) { + ((PropertyGetterDeclaration) astNode).setDefinedGetter(null); + } else if (astNode instanceof PropertySetterDeclaration) { + ((PropertySetterDeclaration) astNode).setDefinedSetter(null); + // note: PropertyMethodDeclaration is a TypeDefiningElement (handled above) + } else if (astNode instanceof PropertySpread) { + // nothing to discard in this case + } else { + throw new UnsupportedOperationException("switch case missing for: " + astNode); + } + }, astNode); + + if (elem instanceof SyntaxRelatedTElement) { + ((SyntaxRelatedTElement) elem).setAstElement(null); + } + if (elem != null) { + EcoreUtilN4.doWithDeliver(false, () -> { + EcoreUtil.remove(elem); + }, elem.eContainer()); + } + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ComputedNameProcessor.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ComputedNameProcessor.xtend deleted file mode 100644 index 4bb8100700..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/postprocessing/ComputedNameProcessor.xtend +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright (c) 2017 NumberFour AG. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * NumberFour AG - Initial API and implementation - */ -package org.eclipse.n4js.postprocessing - -import com.google.inject.Singleton -import org.eclipse.emf.ecore.EObject -import org.eclipse.n4js.compileTime.CompileTimeValue -import org.eclipse.n4js.n4JS.LiteralOrComputedPropertyName -import org.eclipse.n4js.n4JS.N4FieldDeclaration -import org.eclipse.n4js.n4JS.N4GetterDeclaration -import org.eclipse.n4js.n4JS.N4JSASTUtils -import org.eclipse.n4js.n4JS.N4SetterDeclaration -import org.eclipse.n4js.n4JS.PropertyGetterDeclaration -import org.eclipse.n4js.n4JS.PropertyNameValuePair -import org.eclipse.n4js.n4JS.PropertySetterDeclaration -import org.eclipse.n4js.n4JS.PropertySpread -import org.eclipse.n4js.n4JS.TypeDefiningElement -import org.eclipse.n4js.ts.types.IdentifiableElement -import org.eclipse.n4js.ts.types.SyntaxRelatedTElement -import org.eclipse.n4js.typesystem.utils.RuleEnvironment -import org.eclipse.n4js.utils.EcoreUtilN4 -import org.eclipse.n4js.utils.N4JSLanguageUtils -import org.eclipse.xtext.EcoreUtil2 - -/** - * Processing of {@link LiteralOrComputedPropertyName}s that have a computed property name, mainly setting property - * {@link LiteralOrComputedPropertyName#getComputedName() 'computedName'}. - *

- * For details, see {@link ComputedNameProcessor#processComputedPropertyName(RuleEnvironment, LiteralOrComputedPropertyName, ASTMetaInfoCache, int)}. - */ -@Singleton -class ComputedNameProcessor { - - /** - * If the given 'nameDecl' has a computed property name, this method will - *

    - *
  1. obtain its expression's compile-time value from the cache (the actual evaluation of the expression happened - * in {@link CompileTimeExpressionProcessor}), - *
  2. derive the actual property name from that value (cf. {@link #getPropertyNameFromExpression(RuleEnvironment, Expression, ASTMetaInfoCache)}), - *
  3. store this name in 'nameDecl' (for later use), and - *
  4. store this name in the corresponding TModule element (if such a TModule element exists). - *
- *

- * In case the compile-time value of the expression is invalid (i.e. the expression is not a valid compile-time - * expression) no actual name will be stored in 'nameDecl' and the corresponding TModule element will be removed - * from the TModule, entirely (if such a TModule element exists). - */ - def public void processComputedPropertyName(RuleEnvironment G, LiteralOrComputedPropertyName nameDecl, - ASTMetaInfoCache cache, int indentLevel) { - - if (nameDecl.hasComputedPropertyName) { - // obtain compile-time value of expression - val value = cache.getCompileTimeValue(nameDecl.expression); - // derive a property name from the value - val name = N4JSLanguageUtils.derivePropertyNameFromCompileTimeValue(value); - if (name !== null) { - // cache the computed name in the LiteralOrComputedPropertyName AST node - EcoreUtilN4.doWithDeliver(false, [ - nameDecl.computedName = name; - nameDecl.computedSymbol = value instanceof CompileTimeValue.ValueSymbol; - ], nameDecl); - // set the computed name in the types model element - val owner = nameDecl.eContainer; - val typeElem = N4JSASTUtils.getCorrespondingTypeModelElement(owner); - if (typeElem instanceof IdentifiableElement) { - EcoreUtilN4.doWithDeliver(false, [ - typeElem.name = name; - ], typeElem); - } - } else { - // invalid name expression (i.e. not a constant expression) - // -> remove the types model element from the TModule - // (note: we have to do this for consistency with how the types builder handles elements that are - // unnamed (usually due to a broken AST): in those cases, the types builder does not create a TModule - // element) - val owner = nameDecl.eContainer; - discardTypeModelElement(owner); - } - } - } - - /** - * Discards the types model element corresponding to the given AST node. Throws exception if given AST node does not - * have a corresponding types model element. - */ - def private void discardTypeModelElement(EObject astNode) { - val elem = N4JSASTUtils.getCorrespondingTypeModelElement(astNode); - - EcoreUtilN4.doWithDeliver(false, [ - switch (astNode) { - TypeDefiningElement: - astNode.definedType = null - N4FieldDeclaration: - astNode.definedField = null - N4GetterDeclaration: - astNode.definedGetter = null - N4SetterDeclaration: - astNode.definedSetter = null - PropertyNameValuePair: - astNode.definedField = null - PropertyGetterDeclaration: - astNode.definedGetter = null - PropertySetterDeclaration: - astNode.definedSetter = null - // note: PropertyMethodDeclaration is a TypeDefiningElement (handled above) - PropertySpread: { - // nothing to discard in this case - } - default: - throw new UnsupportedOperationException("switch case missing for: " + astNode) - }; - ], astNode); - - if (elem instanceof SyntaxRelatedTElement) { - elem.astElement = null; - } - if (elem !== null) { - EcoreUtilN4.doWithDeliver(false, [ - EcoreUtil2.remove(elem); - ], elem.eContainer); - } - } -}