/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.flex.compiler.internal.as.codegen; import java.util.Iterator; import org.apache.flex.abc.ABCConstants; import org.apache.flex.abc.instructionlist.InstructionList; import org.apache.flex.abc.semantics.ClassInfo; import org.apache.flex.abc.semantics.InstanceInfo; import org.apache.flex.abc.semantics.MethodInfo; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.visitors.IABCVisitor; import org.apache.flex.abc.visitors.IClassVisitor; import org.apache.flex.abc.visitors.IMethodVisitor; import org.apache.flex.abc.visitors.ITraitVisitor; import org.apache.flex.abc.visitors.ITraitsVisitor; import static org.apache.flex.abc.ABCConstants.*; import org.apache.flex.compiler.common.ASModifier; import org.apache.flex.compiler.common.IMetaInfo; import org.apache.flex.compiler.common.ModifiersSet; import org.apache.flex.compiler.constants.INamespaceConstants; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.IInterfaceDefinition; import org.apache.flex.compiler.definitions.INamespaceDefinition; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.internal.definitions.AmbiguousDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.definitions.DefinitionBase; import org.apache.flex.compiler.internal.definitions.FunctionDefinition; import org.apache.flex.compiler.internal.definitions.InterfaceDefinition; import org.apache.flex.compiler.internal.semantics.SemanticUtils; import org.apache.flex.compiler.internal.tree.as.ExpressionNodeBase; import org.apache.flex.compiler.internal.tree.as.FunctionNode; import org.apache.flex.compiler.internal.tree.as.ImportNode; import org.apache.flex.compiler.internal.tree.as.InterfaceNode; import org.apache.flex.compiler.internal.tree.as.NamespaceIdentifierNode; import org.apache.flex.compiler.internal.tree.as.VariableNode; import org.apache.flex.compiler.problems.AmbiguousReferenceProblem; import org.apache.flex.compiler.problems.CannotExtendClassProblem; import org.apache.flex.compiler.problems.ConstructorInInterfaceProblem; import org.apache.flex.compiler.problems.DuplicateInterfaceDefinitionProblem; import org.apache.flex.compiler.problems.FinalOutsideClassProblem; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.InterfaceBindablePropertyProblem; import org.apache.flex.compiler.problems.InterfaceMethodWithBodyProblem; import org.apache.flex.compiler.problems.InvalidOverrideProblem; import org.apache.flex.compiler.problems.NamespaceInInterfaceProblem; import org.apache.flex.compiler.problems.NativeUsedInInterfaceProblem; import org.apache.flex.compiler.problems.StaticOutsideClassProblem; import org.apache.flex.compiler.problems.UnknownInterfaceProblem; import org.apache.flex.compiler.problems.BadAccessInterfaceMemberProblem; import org.apache.flex.compiler.problems.InterfaceNamespaceAttributeProblem; import org.apache.flex.compiler.problems.VarInInterfaceProblem; import org.apache.flex.compiler.problems.VirtualOutsideClassProblem; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.IExpressionNode; import org.apache.flex.compiler.tree.as.IIdentifierNode; import org.apache.flex.compiler.tree.as.INamespaceDecorationNode; /** * The InterfaceDirectiveProcessor translates an InterfaceNode AST * and children into an interface declaration and definition in the * ABC and the init script, respectively. */ public class InterfaceDirectiveProcessor extends DirectiveProcessor { /** * The interface's syntax node. */ InterfaceNode interfaceNode; /** * The interface's lexical scope. */ LexicalScope interfaceScope; /** * The active ABC emitter. */ IABCVisitor emitter; /** * The name of the interface. */ Name interfaceName; /** The interface' instance traits */ ITraitsVisitor itraits; /** The AET visitor implementing this interface. */ IClassVisitor cv; /** The interface' AET ClassInfo. */ ClassInfo cinfo = new ClassInfo(); /** The interface' AET InstanceInfo. */ InstanceInfo iinfo = new InstanceInfo(); /** * Create an InterfaceDirectiveProcessor and set up the basic AET structures. * @param in - the InterfaceNode. * @param enclosing_scope - the lexical scope that encloses this interface * declaration. Either the global scope or a package scope. * @param emitter - the active ABC emitter. */ InterfaceDirectiveProcessor(InterfaceNode in, LexicalScope enclosing_scope, IABCVisitor emitter) { super(enclosing_scope.getProblems()); this.interfaceNode = in; this.emitter = emitter; this.interfaceScope = enclosing_scope.pushFrame(); // Create the class level structures. InterfaceDefinition interfDef = interfaceNode.getDefinition(); this.interfaceName = interfDef.getMName(interfaceScope.getProject()); iinfo.name = this.interfaceName; // Check for a duplicate interface name. switch ( SemanticUtils.getMultiDefinitionType(interfaceNode.getDefinition(), interfaceScope.getProject())) { case AMBIGUOUS: this.interfaceScope.addProblem(new DuplicateInterfaceDefinitionProblem(in, this.interfaceName.getBaseName())); break; case NONE: break; default: assert false; // I don't think interfaces can have other type of multiple definitions } if (this.interfaceName != null) { SemanticUtils.checkScopedToDefaultNamespaceProblem(this.interfaceScope, in, interfDef, this.interfaceName.getBaseName()); } // Check for circular definition by iterating over all parent interfaces. Iterator<IInterfaceDefinition> ifaces = interfDef.interfaceIterator(interfaceScope.getProject(), false, interfaceScope.getProblems()); while (ifaces.hasNext()) { ifaces.next(); } // check that args are valid // checkArguments(interfDef, interfaceScope.getProject(), interfaceScope.getProblems()); // Interfaces can't have a superclass. iinfo.superName = null; // Add implmented interfaces. IExpressionNode[] raw_interfaces = this.interfaceNode.getExtendedInterfaceNodes(); iinfo.interfaceNames = new Name[raw_interfaces.length]; for ( int i = 0; i < raw_interfaces.length; i++) { IExpressionNode extendedInterface = raw_interfaces[i]; IDefinition extendedDefinition = extendedInterface.resolve(interfaceScope.getProject()); if ( extendedDefinition instanceof IInterfaceDefinition ) { Name interfaceName = ((DefinitionBase)extendedDefinition).getMName(interfaceScope.getProject()); iinfo.interfaceNames[i] = interfaceName; } else if ( extendedDefinition instanceof ClassDefinition ) { this.interfaceScope.addProblem(new CannotExtendClassProblem(extendedInterface, extendedDefinition.getBaseName())); } else if ( extendedDefinition instanceof AmbiguousDefinition ) { if ( extendedInterface instanceof IIdentifierNode ) { this.interfaceScope.addProblem(new AmbiguousReferenceProblem(extendedInterface, ((IIdentifierNode)extendedInterface).getName())); } else { // Parser let something weird through. this.interfaceScope.addProblem(new AmbiguousReferenceProblem(extendedInterface, "")); } } else if ( extendedDefinition != null ) { this.interfaceScope.addProblem(new UnknownInterfaceProblem(extendedInterface, extendedDefinition.getBaseName())); } else { if ( extendedInterface instanceof IIdentifierNode ) { this.interfaceScope.addProblem(new UnknownInterfaceProblem(extendedInterface, ((IIdentifierNode)extendedInterface).getName())); } else { // Parser let something weird through. this.interfaceScope.addProblem(new UnknownInterfaceProblem(extendedInterface, "")); } } // Report a problem if the interface is deprecated // and the reference to it is not within a deprecated API. if ( extendedDefinition != null && extendedDefinition.isDeprecated()) { if (!SemanticUtils.hasDeprecatedAncestor(extendedInterface)) { ICompilerProblem problem = SemanticUtils.createDeprecationProblem(extendedDefinition, extendedInterface); this.interfaceScope.addProblem(problem); } } } // Set the flags corresponding to 'final' and 'dynamic'. if (interfDef.isFinal()) iinfo.flags |= ABCConstants.CLASS_FLAG_final; if (!interfDef.isDynamic()) iinfo.flags |= ABCConstants.CLASS_FLAG_sealed; iinfo.flags |= ABCConstants.CLASS_FLAG_interface; this.cv = emitter.visitClass(iinfo, cinfo); cv.visit(); this.itraits = cv.visitInstanceTraits(); // Define the interface in the init script. InstructionList setup_insns = this.interfaceScope.getGlobalScope().getInitInstructions(); setup_insns.addInstruction(OP_getscopeobject, 0); // Interfaces don't have a base class. setup_insns.addInstruction(OP_pushnull); setup_insns.addInstruction(OP_newclass, cinfo); setup_insns.addInstruction(OP_initproperty, interfaceName); ITraitVisitor tv = this.interfaceScope.getGlobalScope().traitsVisitor.visitClassTrait(TRAIT_Class, interfaceName, 0, cinfo); this.interfaceScope.processMetadata(tv, interfDef.getAllMetaTags()); tv.visitEnd(); } /** * Finish processing an interface declaration. */ void finishInterfaceDefinition() { this.itraits.visitEnd(); this.cv.visitEnd(); } /** * Translate a VaribleNode AST into ABC. * Interfaces can't have vars in them, so just emit a diagnostic. * @param var - the variable's AST. */ @Override void declareVariable(VariableNode var) { interfaceScope.addProblem(new VarInInterfaceProblem(var.getNameExpressionNode())); } /** * Declare a function. */ @Override void declareFunction(FunctionNode func) { func.parseFunctionBody(interfaceScope.getProblems()); functionSemanticChecks(func); FunctionDefinition func_def = func.getDefinition(); final String interfaceBaseName = interfaceNode.getShortName(); // ignore a constructor in an interface. // functionSemanticChecks() will already have issued a diagnostic. if (func_def.getBaseName().equals(interfaceBaseName)) { return; } // make the method info, including any default vaules of parameters. Will do semantic checks // on the default values, too. MethodInfo mi = interfaceScope.getGenerator().createMethodInfoWithDefaultArgumentValues(this.interfaceScope, func); ICompilerProject project = interfaceScope.getProject(); ExpressionNodeBase return_type_expr = (ExpressionNodeBase)func.getReturnTypeNode(); if ( return_type_expr != null ) { Name return_type_name = return_type_expr.getMName(project); mi.setReturnType(return_type_name); } IMethodVisitor mv = this.emitter.visitMethod(mi); mv.visit(); mv.visitEnd(); Name funcName = func_def.getMName(project); ITraitVisitor tv = itraits.visitMethodTrait(functionTraitKind(func, TRAIT_Method), funcName, 0, mi); IMetaInfo[] metaTags = func_def.getAllMetaTags(); if (metaTags != null && metaTags.length > 0) { interfaceScope.processMetadata(tv, metaTags); } } /** * This method performs the semantic analysis of a function declared in a class. * @param func the FunctionNode to semantically analyze */ void functionSemanticChecks(FunctionNode func) { final FunctionDefinition func_def = func.getDefinition(); final String interfaceBaseName = interfaceNode.getShortName(); // Check for constructor and log a problem. If it is a constructor, // don't bother doing anymore checking if (func_def.getBaseName().equals(interfaceBaseName)) { interfaceScope.addProblem(new ConstructorInInterfaceProblem(func)); return; } // check the modifiers verifyFunctionModifiers(func); // Make sure the function doesn't have a namespace verifyFunctionNamespace(func, func_def); // Warn if there is no return type SemanticUtils.checkReturnValueHasNoTypeDeclaration(interfaceScope, func, func_def); // Interface methods can't have a body if( func.hasBody() ) { interfaceScope.addProblem(new InterfaceMethodWithBodyProblem(SemanticUtils.getFunctionProblemNode(func))); } ICompilerProject project = interfaceScope.getProject(); // Do some semantic checking on the function interfaceScope.getMethodBodySemanticChecker().checkFunctionDecl(func); // Ensure the return type is defined. IDefinition return_type = func_def.resolveReturnType(project); if ( !SemanticUtils.isType(return_type) ) { interfaceScope.getMethodBodySemanticChecker().addTypeProblem(func.getReturnTypeNode(), return_type, func_def.getReturnTypeAsDisplayString(), true); } Name funcName = func_def.getMName(project); if (funcName != null) { interfaceScope.getMethodBodySemanticChecker().checkInterfaceFunctionForConflictingDefinitions(func, func_def); } } /** * Verify that interface function does not have a namespace on it. If it does, print the appropriate error * Note that "appropriate" means what the old compiler did: internal, public, private are special cased. * * @param func is the function node do be analyzed * @param func_def is the definition for func */ private void verifyFunctionNamespace(FunctionNode func, FunctionDefinition func_def) { INamespaceDecorationNode nsNode = func.getActualNamespaceNode(); // if we have no "actual" node, then there is no namespace in front of our function if (nsNode != null) { boolean isLanguateNS = false; // We need a special check for "internal", because tree building has already munged innternal to // make it just look like the default. But here we need to know what it really is if (INamespaceConstants.internal_.equals(nsNode.getName())) { isLanguateNS = true; } else { // If it's not "internal", then we can use our normal check to find out what it is INamespaceReference ns_ref = func_def.getNamespaceReference(); if ( ns_ref instanceof INamespaceDefinition.ILanguageNamespaceDefinition ) { isLanguateNS = true; } } // generate the appropriate error if (isLanguateNS) { interfaceScope.addProblem(new BadAccessInterfaceMemberProblem(func)); } else { interfaceScope.addProblem(new InterfaceNamespaceAttributeProblem(func)); } } } /** * Validate that the modifiers used on a function are allowed */ protected void verifyFunctionModifiers(FunctionNode f) { ModifiersSet modifiersSet = f.getModifiers(); if (modifiersSet == null) return; ASModifier[] modifiers = modifiersSet.getAllModifiers(); IExpressionNode site = f.getNameExpressionNode(); for (ASModifier modifier : modifiers) { if( modifier == ASModifier.STATIC ) { this.interfaceScope.addProblem(new StaticOutsideClassProblem(site)); } else if ( modifier == ASModifier.OVERRIDE ) { interfaceScope.addProblem(new InvalidOverrideProblem(site)); } else if( modifier == ASModifier.FINAL ) { interfaceScope.addProblem(new FinalOutsideClassProblem(site)); } else if( modifier == ASModifier.NATIVE ) { interfaceScope.addProblem(new NativeUsedInInterfaceProblem(site)); } else if( modifier == ASModifier.VIRTUAL ) { interfaceScope.addProblem(new VirtualOutsideClassProblem(site)); } else if ( modifier == ASModifier.DYNAMIC ) { // Allow this and continue. } } interfaceScope.getMethodBodySemanticChecker().checkForDuplicateModifiers(f); } /** * Process a namespace identifier. */ @Override void processNamespaceIdentifierDirective(NamespaceIdentifierNode ns) { interfaceScope.addProblem(new NamespaceInInterfaceProblem(ns)); } /** * Process an import directive. */ @Override void processImportDirective(ImportNode imp) { // Run the BURM, but for the purpose of semantic checking not code generation. interfaceScope.getGenerator().generateInstructions(imp, CmcEmitter.__statement_NT, this.interfaceScope); } /** * */ @Override void processDirective(IASNode n) { switch ( n.getNodeID() ) { case NamespaceID: interfaceScope.addProblem(new NamespaceInInterfaceProblem(n)); break; default: super.processDirective(n); } } @Override void declareBindableVariable(VariableNode varNode) { interfaceScope.addProblem(new InterfaceBindablePropertyProblem(varNode)); } }