diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFormalParameterTypesBuilder.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFormalParameterTypesBuilder.java new file mode 100644 index 0000000000..04f90594d5 --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFormalParameterTypesBuilder.java @@ -0,0 +1,97 @@ +/** + * 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.typesbuilder; + +import org.eclipse.n4js.n4JS.FormalParameter; +import org.eclipse.n4js.scoping.builtin.BuiltInTypeScope; +import org.eclipse.n4js.ts.typeRefs.TypeRef; +import org.eclipse.n4js.ts.types.TFormalParameter; +import org.eclipse.n4js.ts.types.TFunction; +import org.eclipse.n4js.ts.types.TypesFactory; +import org.eclipse.n4js.types.utils.TypeUtils; + +import com.google.inject.Inject; + +class N4JSFormalParameterTypesBuilder { + + @Inject + N4JSTypesBuilderHelper _n4JSTypesBuilderHelper; + + boolean relinkFormalParameter(FormalParameter astFormalParameter, TFunction functionType, + BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase, int idx) { + TFormalParameter formalParameterType = functionType.getFpars().get(idx); + _n4JSTypesBuilderHelper.ensureEqualName(astFormalParameter, formalParameterType); + + formalParameterType.setAstElement(astFormalParameter); + astFormalParameter.setDefinedVariable(formalParameterType); + setFormalParameterType(formalParameterType, astFormalParameter, null, builtInTypeScope, preLinkingPhase); + + return true; + } + + TFormalParameter createFormalParameter(FormalParameter n4FormalParameter, BuiltInTypeScope builtInTypeScope, + boolean preLinkingPhase) { + return createFormalParameter(n4FormalParameter, null, builtInTypeScope, preLinkingPhase); + } + + /** + * Creates a TFormalParameter for the given FormalParameter from the AST. + * + * @param defaultTypeRef + * will be used in case there is no declared type for the formal parameter; this may be null + * and in this case any will be the formal parameter's actual type. + */ + TFormalParameter createFormalParameter(FormalParameter astFormalParameter, TypeRef defaultTypeRef, + BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase) { + // note: we also build an fpar if astFormalParameter.name===null (otherwise the AST and types model + // would have different number of formal parameters in case of a broken AST, messing up indices, etc.) + TFormalParameter formalParameterType = TypesFactory.eINSTANCE.createTFormalParameter(); + formalParameterType.setName(astFormalParameter.getName()); + formalParameterType.setVariadic(astFormalParameter.isVariadic()); + formalParameterType.setAstInitializer(null); + formalParameterType.setHasInitializerAssignment(astFormalParameter.isHasInitializerAssignment()); + setFormalParameterType(formalParameterType, astFormalParameter, defaultTypeRef, builtInTypeScope, + preLinkingPhase); + + _n4JSTypesBuilderHelper.copyAnnotations(formalParameterType, astFormalParameter, preLinkingPhase); + + formalParameterType.setAstElement(astFormalParameter); + astFormalParameter.setDefinedVariable(formalParameterType); + + return formalParameterType; + } + + /** + * @param formalParameterType + * the type system related parameter type to be set + * @param astFormalParameter + * the AST related parameter which is to be copied to the former + */ + private void setFormalParameterType(TFormalParameter formalParameterType, FormalParameter astFormalParameter, + TypeRef defaultTypeRef, BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase) { + if (!preLinkingPhase) { + TypeRef copy = TypeUtils.copyWithProxies(astFormalParameter.getDeclaredTypeRefInAST()); + formalParameterType.setTypeRef(copy != null ? copy + : getDefaultParameterType(defaultTypeRef, astFormalParameter, builtInTypeScope)); + } + } + + private TypeRef getDefaultParameterType(TypeRef defaultTypeRef, + FormalParameter astFormalParameter, BuiltInTypeScope builtInTypeScope) { + if (astFormalParameter.getInitializer() != null) { + return TypeUtils.createDeferredTypeRef(); + } else if (defaultTypeRef == null) { + return builtInTypeScope.getAnyTypeRef(); + } else { + return TypeUtils.copy(defaultTypeRef); + } + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFormalParameterTypesBuilder.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFormalParameterTypesBuilder.xtend deleted file mode 100644 index 939b88c359..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFormalParameterTypesBuilder.xtend +++ /dev/null @@ -1,88 +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.typesbuilder - -import com.google.inject.Inject -import org.eclipse.n4js.n4JS.FormalParameter -import org.eclipse.n4js.scoping.builtin.BuiltInTypeScope -import org.eclipse.n4js.ts.typeRefs.TypeRef -import org.eclipse.n4js.ts.types.TFormalParameter -import org.eclipse.n4js.ts.types.TFunction -import org.eclipse.n4js.ts.types.TypesFactory -import org.eclipse.n4js.types.utils.TypeUtils - -package class N4JSFormalParameterTypesBuilder { - - @Inject extension N4JSTypesBuilderHelper - - def package boolean relinkFormalParameter(FormalParameter astFormalParameter, TFunction functionType, BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase, int idx) { - val formalParameterType = functionType.fpars.get(idx); - ensureEqualName(astFormalParameter, formalParameterType); - - formalParameterType.astElement = astFormalParameter; - astFormalParameter.definedVariable = formalParameterType; - setFormalParameterType(formalParameterType, astFormalParameter, null, builtInTypeScope, preLinkingPhase); - - return true; - } - - def package TFormalParameter createFormalParameter(FormalParameter n4FormalParameter, BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase) { - return createFormalParameter(n4FormalParameter, null, builtInTypeScope, preLinkingPhase); - } - - /** - * Creates a TFormalParameter for the given FormalParameter from the AST. - * - * @param defaultTypeRef will be used in case there is no declared type for the formal parameter; - * this may be null and in this case any will be - * the formal parameter's actual type. - */ - def package TFormalParameter createFormalParameter(FormalParameter astFormalParameter, TypeRef defaultTypeRef, BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase) { - // note: we also build an fpar if astFormalParameter.name===null (otherwise the AST and types model - // would have different number of formal parameters in case of a broken AST, messing up indices, etc.) - val formalParameterType = TypesFactory::eINSTANCE.createTFormalParameter(); - formalParameterType.name = astFormalParameter.name; - formalParameterType.variadic = astFormalParameter.variadic; - formalParameterType.astInitializer = null; - formalParameterType.hasInitializerAssignment = astFormalParameter.hasInitializerAssignment; - setFormalParameterType(formalParameterType, astFormalParameter, defaultTypeRef, builtInTypeScope, preLinkingPhase) - - copyAnnotations(formalParameterType, astFormalParameter, preLinkingPhase) - - formalParameterType.astElement = astFormalParameter; - astFormalParameter.definedVariable = formalParameterType; - - return formalParameterType; - } - - /** - * @param formalParameterType the type system related parameter type to be set - * @param astFormalParameter the AST related parameter which is to be copied to the former - */ - def private void setFormalParameterType(TFormalParameter formalParameterType, FormalParameter astFormalParameter, - TypeRef defaultTypeRef, BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase - ) { - if (!preLinkingPhase) - formalParameterType.typeRef = TypeUtils.copyWithProxies(astFormalParameter.declaredTypeRefInAST) ?: getDefaultParameterType(defaultTypeRef, astFormalParameter, builtInTypeScope) - } - - def private TypeRef getDefaultParameterType(TypeRef defaultTypeRef, - FormalParameter astFormalParameter, BuiltInTypeScope builtInTypeScope - ) { - if (astFormalParameter.initializer !== null) { - TypeUtils.createDeferredTypeRef - } else if (defaultTypeRef === null) { - builtInTypeScope.anyTypeRef - } else { - TypeUtils.copy(defaultTypeRef) - } - } -} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFunctionDefinitionTypesBuilder.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFunctionDefinitionTypesBuilder.java new file mode 100644 index 0000000000..6c00548e43 --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFunctionDefinitionTypesBuilder.java @@ -0,0 +1,213 @@ +/** + * 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.typesbuilder; + +import static org.eclipse.xtext.xbase.lib.IterableExtensions.filterNull; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.map; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.toList; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.n4js.n4JS.FunctionDeclaration; +import org.eclipse.n4js.n4JS.FunctionDefinition; +import org.eclipse.n4js.n4JS.FunctionExpression; +import org.eclipse.n4js.n4JS.N4JSPackage; +import org.eclipse.n4js.scoping.builtin.BuiltInTypeScope; +import org.eclipse.n4js.ts.typeRefs.TypeRef; +import org.eclipse.n4js.ts.types.AbstractNamespace; +import org.eclipse.n4js.ts.types.TFunction; +import org.eclipse.n4js.ts.types.TypesFactory; +import org.eclipse.n4js.types.utils.TypeUtils; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +/** + * Type builder for function declaration or expression builder. + */ +// TODO we temporarily create a BuiltInTypeScope in order to get primitive types. This may be changed by passing in this +// scope, one this method is called by the typesystem +@Singleton +public class N4JSFunctionDefinitionTypesBuilder extends AbstractFunctionDefinitionTypesBuilder { + + @Inject + N4JSFormalParameterTypesBuilder _n4JSFormalParameterTypesBuilder; + @Inject + N4JSTypeVariableTypesBuilder _n4JSTypeVariableTypesBuilder; + @Inject + N4JSVariableStatementTypesBuilder _n4JSVariableStatementTypesBuilder; + @Inject + N4JSTypesBuilderHelper _n4JSTypesBuilderHelper; + + boolean relinkTFunction(FunctionDeclaration functionDecl, AbstractNamespace target, boolean preLinkingPhase, + int idx) { + EObject functionDefinedType = (EObject) functionDecl + .eGet(N4JSPackage.eINSTANCE.getTypeDefiningElement_DefinedType(), false); + if (functionDefinedType != null && !functionDefinedType.eIsProxy()) { + throw new IllegalStateException("TFunction already created for FunctionDeclaration"); + } + + if (functionDecl.getName() == null) { + return false; + } + + TFunction functionType = target.getFunctions().get(idx); + _n4JSTypesBuilderHelper.ensureEqualName(functionDecl, functionType); + + relinkFormalParameters(functionType, functionDecl, preLinkingPhase); + functionType.setAstElement(functionDecl); + functionDecl.setDefinedType(functionType); + + return true; + } + + /** + * Creates TFunction for the given function declaration and adds it to the modules top level types (as function + * declarations are only allowed on top level). + * + * @param functionDecl + * declaration for which the TFunction is created, must not be linked to a TFunction yet (i.e. its + * defined type must be null). + * @param target + * the module to which the newly created TFunction is added + */ + void createTFunction(FunctionDeclaration functionDecl, AbstractNamespace target, boolean preLinkingPhase) { + EObject functionDefinedType = (EObject) functionDecl + .eGet(N4JSPackage.eINSTANCE.getTypeDefiningElement_DefinedType(), false); + if (functionDefinedType != null && !functionDefinedType.eIsProxy()) { + throw new IllegalStateException("TFunction already created for FunctionDeclaration"); + } + + if (functionDecl.getName() == null) { + return; + } + + BuiltInTypeScope builtInTypeScope = BuiltInTypeScope.get(functionDecl.eResource().getResourceSet()); + TFunction functionType = createAndLinkTFunction(functionDecl); + _n4JSVariableStatementTypesBuilder.createImplicitArgumentsVariable(functionDecl, target, builtInTypeScope, + preLinkingPhase); + + addFormalParameters(functionType, functionDecl, builtInTypeScope, preLinkingPhase); + _n4JSTypesBuilderHelper.setTypeAccessModifier(functionType, functionDecl); + _n4JSTypesBuilderHelper.setProvidedByRuntime(functionType, functionDecl, preLinkingPhase); + setReturnType(functionType, functionDecl, builtInTypeScope, preLinkingPhase); + _n4JSTypeVariableTypesBuilder.addTypeParameters(functionType, functionDecl, preLinkingPhase); + _n4JSTypesBuilderHelper.setDeclaredThisTypeFromAnnotation(functionType, functionDecl, preLinkingPhase); + _n4JSTypesBuilderHelper.copyAnnotations(functionType, functionDecl, preLinkingPhase); + functionType.setDeclaredAsync(functionDecl.isAsync());// TODO change to declaredAsync once the annotation is + // gone + functionType.setDeclaredGenerator(functionDecl.isGenerator()); + + // set container + target.getFunctions().add(functionType); + } + + /** + * Creates TFunction for the given function expression and adds it to the module. Note that this method applies only + * to expressions that define a function, not to function type expressions that merely define a function type (the + * latter are represented in the AST and TModule by a node of type FunctionTypeExpression from + * Types.xcore). + *

+ * Creating a TFunction for a function expression becomes a bit tricky when type inference has to be used to infer + * the types of one or more formal parameters and/or the return value. These are the steps involved: + *

    + *
  1. method {@link #createTFunction(FunctionExpression,AbstractNamespace,boolean)} creates an initial TFunction in + * which the type of every fpar may(!) be a ComputedTypeRef and the return type may(!) be a ComputedTypeRef. + * ComputedTypeRefs are only used if there is no declared type available. + *
  2. when the first(!) of these ComputedTypeRefs is resolved (and only for the first!), then method + * resolveTFunction(ComputedTypeRef,TFunction,FunctionExpression,BuiltInTypeScope) is invoked. This method will + * handle the resolution of all ComputedTypeRefs of the given TFunction in one step (to avoid unnecessary repeated + * inference of the expected type; note: caching does not help here, because we call judgment 'expectedTypeIn' and + * not 'type'). + *
+ */ + void createTFunction(FunctionExpression functionExpr, AbstractNamespace target, boolean preLinkingPhase) { + EObject functionDefinedType = (EObject) functionExpr + .eGet(N4JSPackage.eINSTANCE.getTypeDefiningElement_DefinedType(), false); + if (functionDefinedType != null && !functionDefinedType.eIsProxy()) { + throw new IllegalStateException("TFunction already created for FunctionExpression"); + } + + BuiltInTypeScope builtInTypeScope = BuiltInTypeScope.get(functionExpr.eResource().getResourceSet()); + TFunction functionType = createAndLinkTFunction(functionExpr); + _n4JSVariableStatementTypesBuilder.createImplicitArgumentsVariable(functionExpr, target, builtInTypeScope, + preLinkingPhase); + + addFormalParametersWithInferredType(functionType, functionExpr, builtInTypeScope, preLinkingPhase); + setReturnTypeWithInferredType(functionType, functionExpr, preLinkingPhase); + _n4JSTypeVariableTypesBuilder.addTypeParameters(functionType, functionExpr, preLinkingPhase); + _n4JSTypesBuilderHelper.setDeclaredThisTypeFromAnnotation(functionType, functionExpr, preLinkingPhase); + + _n4JSTypesBuilderHelper.copyAnnotations(functionType, functionExpr, preLinkingPhase); + + // set container + target.getContainingModule().getInternalTypes().add(functionType); + } + + /** + * Same as + * {@link AbstractFunctionDefinitionTypesBuilder#addFormalParameters(TFunction,FunctionDefinition,BuiltInTypeScope,boolean)}, + * but uses a ComputedTypeRef as the fpar's type if the type has to be inferred. + */ + private void addFormalParametersWithInferredType(TFunction functionType, FunctionExpression functionExpr, + BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase) { + + functionType.getFpars().addAll( + toList(filterNull(map(functionExpr.getFpars(), + fpar -> _n4JSFormalParameterTypesBuilder.createFormalParameter(fpar, + TypeUtils.createDeferredTypeRef(), + // TypeUtils.createComputedTypeRef([resolveAllComputedTypeRefsInTFunction(functionType,functionExpr,builtInTypeScope)]), + builtInTypeScope, + preLinkingPhase))))); + } + + /** + * Same as + * {@link AbstractFunctionDefinitionTypesBuilder#setReturnType(TFunction,FunctionDefinition,BuiltInTypeScope,boolean)}, + * but uses a ComputedTypeRef as the return type if the type has to be inferred. + */ + private void setReturnTypeWithInferredType(TFunction functionType, FunctionExpression functionExpr, + boolean preLinkingPhase) { + if (!preLinkingPhase) { + /* + * TODO IDE-1579 this branch skips makePromiseIfAsync. Question: could this result in 'void' as inferred + * return type (for an async method)? + */ + TypeRef copy = TypeUtils.copyWithProxies(functionExpr.getDeclaredReturnTypeRefInAST()); + functionType.setReturnTypeRef(copy != null ? copy : TypeUtils.createDeferredTypeRef()); + // note: handling of the return type of async functions not done here, see + // TypeProcessor#handleAsyncFunctionDeclaration() + } + } + + private TFunction createAndLinkTFunction(FunctionDefinition functionDef) { + TFunction functionType = this.createTFunction(); + if (functionDef instanceof FunctionDeclaration) { + functionType.setExternal(((FunctionDeclaration) functionDef).isExternal()); + } + functionType.setName(functionDef.getName()); // maybe null in case of function expression + functionType.setDeclaredAsync(functionDef.isAsync()); // TODO change to declaredAsync when annotation is removed + functionType.setDeclaredGenerator(functionDef.isGenerator());// TODO change to declaredAsync when annotation is + // removed + + // link + functionType.setAstElement(functionDef); + functionDef.setDefinedType(functionType); + + return functionType; + } + + /** + * Creates a new plain instance of {@link TFunction}. + */ + private TFunction createTFunction() { + return TypesFactory.eINSTANCE.createTFunction(); + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFunctionDefinitionTypesBuilder.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFunctionDefinitionTypesBuilder.xtend deleted file mode 100644 index a6462a9606..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesbuilder/N4JSFunctionDefinitionTypesBuilder.xtend +++ /dev/null @@ -1,184 +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.typesbuilder - -import com.google.inject.Inject -import com.google.inject.Singleton -import org.eclipse.emf.ecore.EObject -import org.eclipse.n4js.n4JS.FunctionDeclaration -import org.eclipse.n4js.n4JS.FunctionDefinition -import org.eclipse.n4js.n4JS.FunctionExpression -import org.eclipse.n4js.n4JS.N4JSPackage -import org.eclipse.n4js.scoping.builtin.BuiltInTypeScope -import org.eclipse.n4js.ts.types.AbstractNamespace -import org.eclipse.n4js.ts.types.TFunction -import org.eclipse.n4js.ts.types.TypesFactory -import org.eclipse.n4js.types.utils.TypeUtils - -/** - * Type builder for function declaration or expression builder. - */ -// TODO we temporarily create a BuiltInTypeScope in order to get primitive types. This may be changed by passing in this scope, one this method is called by the typesystem -@Singleton -public class N4JSFunctionDefinitionTypesBuilder extends AbstractFunctionDefinitionTypesBuilder { - - @Inject extension N4JSFormalParameterTypesBuilder - @Inject extension N4JSTypeVariableTypesBuilder - @Inject extension N4JSVariableStatementTypesBuilder - @Inject extension N4JSTypesBuilderHelper - - def package boolean relinkTFunction(FunctionDeclaration functionDecl, AbstractNamespace target, boolean preLinkingPhase, int idx) { - val functionDefinedType = functionDecl.eGet(N4JSPackage.eINSTANCE.typeDefiningElement_DefinedType, false) as EObject; - if (functionDefinedType !== null && ! functionDefinedType.eIsProxy) { - throw new IllegalStateException("TFunction already created for FunctionDeclaration"); - } - - if (functionDecl.name === null) { - return false; - } - - val TFunction functionType = target.functions.get(idx) - ensureEqualName(functionDecl, functionType); - - functionType.relinkFormalParameters(functionDecl, preLinkingPhase) - functionType.astElement = functionDecl - functionDecl.definedType = functionType - - return true; - } - - /** - * Creates TFunction for the given function declaration and adds it to the modules top level types - * (as function declarations are only allowed on top level). - * - * @param functionDecl declaration for which the TFunction is created, must not be linked to a TFunction yet (i.e. its defined type must be null). - * @param target the module to which the newly created TFunction is added - */ - def package void createTFunction(FunctionDeclaration functionDecl, AbstractNamespace target, boolean preLinkingPhase) { - val functionDefinedType = functionDecl.eGet(N4JSPackage.eINSTANCE.typeDefiningElement_DefinedType, false) as EObject; - if (functionDefinedType !== null && ! functionDefinedType.eIsProxy) { - throw new IllegalStateException("TFunction already created for FunctionDeclaration"); - } - - if (functionDecl.name === null) { - return; - } - - val builtInTypeScope = BuiltInTypeScope.get(functionDecl.eResource.resourceSet) - val functionType = functionDecl.createAndLinkTFunction(preLinkingPhase) - functionDecl.createImplicitArgumentsVariable(target, builtInTypeScope, preLinkingPhase); - - functionType.addFormalParameters(functionDecl, builtInTypeScope, preLinkingPhase) - functionType.setTypeAccessModifier(functionDecl) - functionType.setProvidedByRuntime(functionDecl, preLinkingPhase) - functionType.setReturnType(functionDecl, builtInTypeScope, preLinkingPhase) - functionType.addTypeParameters(functionDecl, preLinkingPhase) - functionType.setDeclaredThisTypeFromAnnotation(functionDecl, preLinkingPhase) - functionType.copyAnnotations(functionDecl, preLinkingPhase) - functionType.declaredAsync = functionDecl.async // TODO change to declaredAsync once the annotation is gone - functionType.declaredGenerator = functionDecl.generator - - // set container - target.functions += functionType - } - - /** - * Creates TFunction for the given function expression and adds it to the module. Note that this method applies - * only to expressions that define a function, not to function type expressions that merely define a function - * type (the latter are represented in the AST and TModule by a node of type FunctionTypeExpression - * from Types.xcore). - *

- * Creating a TFunction for a function expression becomes a bit tricky when type inference has to be used to - * infer the types of one or more formal parameters and/or the return value. These are the steps involved: - *

    - *
  1. method {@link #createTFunction(FunctionExpression,TModule,boolean)} creates an initial TFunction in - * which the type of every fpar may(!) be a ComputedTypeRef and the return type may(!) be a ComputedTypeRef. - * ComputedTypeRefs are only used if there is no declared type available. - *
  2. when the first(!) of these ComputedTypeRefs is resolved (and only for the first!), then method - * {@link #resolveTFunction(ComputedTypeRef,TFunction,FunctionExpression,BuiltInTypeScope)} - * is invoked. This method will handle the resolution of all ComputedTypeRefs of the given TFunction in - * one step (to avoid unnecessary repeated inference of the expected type; note: caching does not help - * here, because we call judgment 'expectedTypeIn' and not 'type'). - *
- */ - def package void createTFunction(FunctionExpression functionExpr, AbstractNamespace target, boolean preLinkingPhase) { - val functionDefinedType = functionExpr.eGet(N4JSPackage.eINSTANCE.typeDefiningElement_DefinedType, false) as EObject; - if (functionDefinedType !== null && ! functionDefinedType.eIsProxy) { - throw new IllegalStateException("TFunction already created for FunctionExpression"); - } - - val builtInTypeScope = BuiltInTypeScope.get(functionExpr.eResource.resourceSet) - val functionType = functionExpr.createAndLinkTFunction(preLinkingPhase) - functionExpr.createImplicitArgumentsVariable(target, builtInTypeScope, preLinkingPhase); - - functionType.addFormalParametersWithInferredType(functionExpr, builtInTypeScope, preLinkingPhase) - functionType.setReturnTypeWithInferredType(functionExpr, builtInTypeScope, preLinkingPhase) - functionType.addTypeParameters(functionExpr, preLinkingPhase) - functionType.setDeclaredThisTypeFromAnnotation(functionExpr, preLinkingPhase) - - functionType.copyAnnotations(functionExpr, preLinkingPhase) - - // set container - target.containingModule.internalTypes += functionType - } - - /** - * Same as {@link AbstractFunctionDefinitionTypesBuilder#addFormalParameters(TFunction,FunctionDefinition,BuiltInTypeScope,boolean)}, - * but uses a ComputedTypeRef as the fpar's type if the type has to be inferred. - */ - def private void addFormalParametersWithInferredType(TFunction functionType, FunctionExpression functionExpr, - BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase) { - functionType.fpars.addAll( - functionExpr.fpars.map[it.createFormalParameter( - TypeUtils.createDeferredTypeRef, //TypeUtils.createComputedTypeRef([resolveAllComputedTypeRefsInTFunction(functionType,functionExpr,builtInTypeScope)]), - builtInTypeScope, - preLinkingPhase - )].filterNull); - } - - /** - * Same as {@link AbstractFunctionDefinitionTypesBuilder#setReturnType(TFunction,FunctionDefinition,BuiltInTypeScope,boolean)}, - * but uses a ComputedTypeRef as the return type if the type has to be inferred. - */ - def private void setReturnTypeWithInferredType(TFunction functionType, FunctionExpression functionExpr, - BuiltInTypeScope builtInTypeScope, boolean preLinkingPhase) { - if (!preLinkingPhase) - /* - * TODO IDE-1579 this branch skips makePromiseIfAsync. - * Question: could this result in 'void' as inferred return type (for an async method)? - */ - functionType.returnTypeRef = TypeUtils.copyWithProxies(functionExpr.declaredReturnTypeRefInAST) ?: TypeUtils.createDeferredTypeRef - // note: handling of the return type of async functions not done here, see TypeProcessor#handleAsyncFunctionDeclaration() - } - - def private TFunction createAndLinkTFunction(FunctionDefinition functionDef, boolean preLinkingPhase) { - val functionType = this.createTFunction(functionDef); - if(functionDef instanceof FunctionDeclaration) { - functionType.external = functionDef.external; - } - functionType.name = functionDef.name; // maybe null in case of function expression - functionType.declaredAsync = functionDef.isAsync // TODO change to declaredAsync when annotation is removed - functionType.declaredGenerator = functionDef.generator // TODO change to declaredAsync when annotation is removed - - // link - functionType.astElement = functionDef - functionDef.definedType = functionType - - return functionType - } - - /** - * Creates a new plain instance of {@link TFunction}. - */ - def private TFunction createTFunction(FunctionDefinition functionDef) { - return TypesFactory::eINSTANCE.createTFunction(); - } -}