/* * Copyright 2008 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.jooc.JooSymbol; import net.jangaroo.jooc.Jooc; import net.jangaroo.jooc.Scope; import net.jangaroo.jooc.SyntacticKeywords; import java.io.IOException; import java.util.List; /** * @author Andreas Gawecki * @author Frank Wienberg */ public class FunctionDeclaration extends TypedIdeDeclaration { private FunctionExpr fun; private JooSymbol symGetOrSet; private JooSymbol optSymSemicolon; private boolean isConstructor = false; private boolean isDeclaredInInterface = false; private boolean containsSuperConstructorCall = false; private boolean thisAliased; private static final int DEFAULT_ALLOWED_METHOD_MODIFIERS = // NOSONAR there is no simpler way to tell it; we need all these flags MODIFIER_OVERRIDE | MODIFIER_ABSTRACT | MODIFIER_VIRTUAL | MODIFIER_FINAL | MODIFIERS_SCOPE | MODIFIER_STATIC | MODIFIER_NATIVE; public FunctionDeclaration(List<JooSymbol> modifiers, JooSymbol symFunction, JooSymbol symGetOrSet, Ide ide, JooSymbol lParen, Parameters params, JooSymbol rParen, TypeRelation optTypeRelation, BlockStatement optBody, JooSymbol optSymSemicolon) { super(modifiers.toArray(new JooSymbol[modifiers.size()]), ide, null); //todo pass Function type as typeRelation this.fun = new FunctionExpr(this, symFunction, ide, lParen, params, rParen, optTypeRelation, optBody); this.symGetOrSet = symGetOrSet; this.optSymSemicolon = optSymSemicolon; if (isGetterOrSetter() && !(isGetter() || isSetter())) { throw Jooc.error(symGetOrSet, "Expected 'get' or 'set'."); } } @Override public List<? extends AstNode> getChildren() { return makeChildren(fun); // do not call super.getChildren(), as fun already contains ide! } @Override public void visit(AstVisitor visitor) throws IOException { visitor.visitFunctionDeclaration(this); } public int getModifiers() { int modifiers = super.getModifiers(); if (isDeclaredInInterface) { modifiers |= MODIFIER_PUBLIC; } return modifiers; } public boolean overrides() { return (getModifiers() & MODIFIER_OVERRIDE) != 0; } @Override public boolean isMethod() { return isClassMember(); } public boolean isGetterOrSetter() { return symGetOrSet != null; } public boolean isGetter() { return isGetterOrSetter() && SyntacticKeywords.GET.equals(symGetOrSet.getText()); } public boolean isSetter() { return isGetterOrSetter() && SyntacticKeywords.SET.equals(symGetOrSet.getText()); } public final boolean isConstructor() { return isConstructor; } public FunctionExpr getFun() { return fun; } public JooSymbol getSymGetOrSet() { return symGetOrSet; } public JooSymbol getOptSymSemicolon() { return optSymSemicolon; } public boolean containsSuperConstructorCall() { return isContainsSuperConstructorCall(); } public void setContainsSuperConstructorCall(boolean containsSuperConstructorCallStatement) { this.containsSuperConstructorCall = containsSuperConstructorCallStatement; } public boolean isAbstract() { return getClassDeclaration() != null && getClassDeclaration().isInterface() || super.isAbstract(); } public Parameters getParams() { return fun.getParams(); } public boolean hasBody() { return fun.hasBody(); } public BlockStatement getBody() { return fun.getBody(); } @Override public void scope(Scope scope) { final ClassDeclaration classDeclaration = scope.getClassDeclaration(); // todo: temporarily resetting the ide field looks weird Ide oldIde = getIde(); if (classDeclaration != null && getIde().getName().equals(classDeclaration.getName())) { setConstructor(true); classDeclaration.setConstructor(this); setIde(null); // do NOT declare constructor ide in scope, as it would override the class, is not inherited, etc.! } isDeclaredInInterface = classDeclaration != null && classDeclaration.isInterface(); super.scope(scope); setIde(oldIde); //todo check correct override usage if (overrides() && isAbstract()) { throw Jooc.error(this, "overriding methods are not allowed to be declared abstract"); } if (isAbstract()) { if (classDeclaration == null) { throw Jooc.error(this, "package-scoped function " + getName() + " must not be abstract."); } if (!classDeclaration.isAbstract()) { throw Jooc.error(this, classDeclaration.getName() + "is not declared abstract"); } if (hasBody()) { throw Jooc.error(this, "abstract method must not be implemented"); } } if (isNative() && hasBody()) { throw Jooc.error(this, "native method must not be implemented"); } if (!isAbstract() && !isNative() && !hasBody()) { throw Jooc.error(this, "method must either be implemented or declared abstract or native"); } //TODO:check whether abstract method does not actually override if (!isStatic()) { ClassDeclaration currentClass = scope.getClassDeclaration(); if (classDeclaration != null) { // otherwise we are in a global function - todo parse them as function declaration // declare this and super final Type thisType = currentClass.getThisType(); final Parameter thisParam = new Parameter(null, new Ide("this"), new TypeRelation(null, thisType), null); fun.addImplicitParam(thisParam); final Type superType = currentClass.getSuperType(); if (superType != null) { fun.addImplicitParam(new Parameter(null, new Ide("super"), new TypeRelation(null, superType), null)); } } } fun.scope(scope); if (containsSuperConstructorCall()) { // must be contained at top level BlockStatement block = getBody(); block.checkSuperConstructorCall(); } } public void analyze(AstNode parentNode) { super.analyze(parentNode); // computes modifiers fun.analyze(this); if (isPublicApi()) { // This method will be rendered into the public API stubs. Parameters params = fun.getParams(); while (params != null) { Parameter parameter = params.getHead(); if (isClassMember() && isPublicApi() && parameter.getOptInitializer() != null) { parameter.getOptInitializer().addPublicApiDependencies(); } addPublicApiDependencyOn(parameter.getOptTypeRelation()); params = params.getTail(); } addPublicApiDependencyOn(fun.getOptTypeRelation()); } } void aliasThis() { if (!thisAliased) { thisAliased = true; } } public boolean isThisAliased() { return thisAliased; } @Override protected int getAllowedModifiers() { return isConstructor() ? MODIFIERS_SCOPE | MODIFIER_NATIVE : DEFAULT_ALLOWED_METHOD_MODIFIERS; } @Override public void handleDuplicateDeclaration(Scope scope, AstNode oldNode) { if (isGetterOrSetter() && oldNode instanceof FunctionDeclaration) { FunctionDeclaration other = (FunctionDeclaration) oldNode; if (other.isGetterOrSetter() && isGetter() != other.isGetter()) { // found counterpart for this getter or setter: // do not trigger warning or error! return; } } super.handleDuplicateDeclaration(scope, oldNode); } @Override public JooSymbol getSymbol() { return fun.getSymbol(); } @Override public IdeDeclaration resolveDeclaration() { // todo this looks quirky, try not to define constructor within scope? return isConstructor() ? getClassDeclaration() : super.resolveDeclaration(); } public void setConstructor(boolean constructor) { isConstructor = constructor; } public boolean isContainsSuperConstructorCall() { return containsSuperConstructorCall; } }