/* * Copyright 2011 CoreMedia AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package net.jangaroo.jooc.ast; import net.jangaroo.utils.AS3Type; import net.jangaroo.jooc.JooSymbol; import net.jangaroo.jooc.Scope; import net.jangaroo.jooc.sym; import java.io.IOException; import java.util.LinkedList; import java.util.List; /** * @author Andreas Gawecki * @author Frank Wienberg */ public class FunctionExpr extends Expr { public static final String ARGUMENTS = "arguments"; public static final Ide ARGUMENTS_IDE = new Ide(new JooSymbol(ARGUMENTS)); //todo unify predefined type definitions, they are scattered all over public static final Type ANY_TYPE = new Type(new JooSymbol(sym.MUL, "", -1, -1, "", AS3Type.ANY.toString())); private JooSymbol symFunction; private Ide ide; private JooSymbol lParen; private TypeRelation optTypeRelation; private Parameters params; private JooSymbol rParen; private BlockStatement optBody; private List<Parameter> implicitParams = new LinkedList<Parameter>(); private FunctionDeclaration functionDeclaration; // null for function expressions private boolean thisDefined = false; private final Parameter argumentsParameter; private boolean argumentsUsedAsArray = false; private IdeDeclaration classDeclaration; public FunctionExpr(FunctionDeclaration functionDeclaration, JooSymbol symFunction, Ide ide, JooSymbol lParen, Parameters params, JooSymbol rParen, TypeRelation optTypeRelation, BlockStatement optBody) { this.functionDeclaration = functionDeclaration; this.ide = ide; this.optTypeRelation = optTypeRelation; this.symFunction = symFunction; this.lParen = lParen; this.params = params; this.rParen = rParen; this.optBody = optBody; // is there an single rest parameter called 'arguments'? if (params != null && params.getHead() != null && params.getTail() == null && params.getHead().isRest() && ARGUMENTS.equals(params.getHead().getName())) { argumentsParameter = params.getHead(); } else { // 'arguments' is implicitly defined inside a function! argumentsParameter = new Parameter(null, ARGUMENTS_IDE, null, null); implicitParams.add(argumentsParameter); } } @Override public List<? extends AstNode> getChildren() { return makeChildren(super.getChildren(), ide, params, optTypeRelation, optBody); } @Override public void visit(AstVisitor visitor) throws IOException { visitor.visitFunctionExpr(this); } public FunctionDeclaration getFunctionDeclaration() { return functionDeclaration; } public Parameters getParams() { return params; } public BlockStatement getBody() { return optBody; } @Override public JooSymbol getSymbol() { return symFunction; } public IdeDeclaration getClassDeclaration() { return classDeclaration; } @Override public void scope(Scope scope) { classDeclaration = scope.getClassDeclaration(); /* if (parentDeclaration == null) { AstNode declaration = scope.getDefiningNode(); if (declaration instanceof IdeDeclaration) { parentDeclaration = (IdeDeclaration) declaration; } } */ if (!thisDefined) { addImplicitParam(new Parameter(null, new Ide("this"), new TypeRelation(null, ANY_TYPE), null)); } withNewDeclarationScope(functionDeclaration == null ? this : functionDeclaration, scope, new Scoped() { public void run(final Scope scope) { //declare ide inside Function if not already scoped: if ((functionDeclaration == null || !functionDeclaration.isMethod()) && ide != null && ide.getScope() == null) { IdeDeclaration decl = new VariableDeclaration(null, ide, null, null); decl.scope(scope); } // declare implicitParams scope(implicitParams, scope); withNewDeclarationScope(FunctionExpr.this, scope, new Scoped() { public void run(final Scope scope) { if (params != null) { params.scope(scope); } if (optTypeRelation != null) { optTypeRelation.scope(scope); } if (optBody != null) { optBody.scope(scope); } } }); } }); } public void analyze(AstNode parentNode) { super.analyze(parentNode); if (params != null) { params.analyze(this); } if (optTypeRelation != null) { optTypeRelation.analyze(this); } if (optBody != null) { optBody.analyze(this); } } public void addImplicitParam(Parameter parameter) { implicitParams.add(parameter); thisDefined = thisDefined || parameter.getIde().getName().equals("this"); } public boolean hasBody() { return getBody() != null; } public Ide getIde() { return ide; } public JooSymbol getSymFunction() { return symFunction; } public JooSymbol getLParen() { return lParen; } public TypeRelation getOptTypeRelation() { return optTypeRelation; } public JooSymbol getRParen() { return rParen; } public JooSymbol getFunSymbol() { return symFunction; } boolean notifyThisUsed(Scope scope) { if (functionDeclaration == null || !functionDeclaration.isClassMember()) { FunctionDeclaration methodDeclaration = scope.getMethodDeclaration(); // if "this" is used inside non-static method, remember that: if (methodDeclaration != null && !methodDeclaration.isStatic()) { methodDeclaration.aliasThis(); return true; } } return false; } void notifyArgumentsUsed(IdeDeclaration argumentsIdeDeclaration) { if (argumentsIdeDeclaration == argumentsParameter) { argumentsUsedAsArray = true; } } public boolean isArgumentsUsedAsArray() { return argumentsUsedAsArray; } }