| /* |
| * |
| * 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.royale.compiler.internal.as.codegen; |
| |
| import java.util.Iterator; |
| |
| import org.apache.royale.abc.ABCConstants; |
| import org.apache.royale.abc.instructionlist.InstructionList; |
| import org.apache.royale.abc.semantics.ClassInfo; |
| import org.apache.royale.abc.semantics.InstanceInfo; |
| import org.apache.royale.abc.semantics.MethodInfo; |
| import org.apache.royale.abc.semantics.Name; |
| import org.apache.royale.abc.visitors.IABCVisitor; |
| import org.apache.royale.abc.visitors.IClassVisitor; |
| import org.apache.royale.abc.visitors.IMethodVisitor; |
| import org.apache.royale.abc.visitors.ITraitVisitor; |
| import org.apache.royale.abc.visitors.ITraitsVisitor; |
| |
| import static org.apache.royale.abc.ABCConstants.*; |
| |
| import org.apache.royale.compiler.common.ASModifier; |
| import org.apache.royale.compiler.common.IMetaInfo; |
| import org.apache.royale.compiler.common.ModifiersSet; |
| import org.apache.royale.compiler.constants.INamespaceConstants; |
| import org.apache.royale.compiler.definitions.IDefinition; |
| import org.apache.royale.compiler.definitions.IInterfaceDefinition; |
| import org.apache.royale.compiler.definitions.INamespaceDefinition; |
| import org.apache.royale.compiler.definitions.references.INamespaceReference; |
| import org.apache.royale.compiler.internal.definitions.AmbiguousDefinition; |
| import org.apache.royale.compiler.internal.definitions.ClassDefinition; |
| import org.apache.royale.compiler.internal.definitions.DefinitionBase; |
| import org.apache.royale.compiler.internal.definitions.FunctionDefinition; |
| import org.apache.royale.compiler.internal.definitions.InterfaceDefinition; |
| import org.apache.royale.compiler.internal.semantics.SemanticUtils; |
| import org.apache.royale.compiler.internal.tree.as.ExpressionNodeBase; |
| import org.apache.royale.compiler.internal.tree.as.FunctionNode; |
| import org.apache.royale.compiler.internal.tree.as.ImportNode; |
| import org.apache.royale.compiler.internal.tree.as.InterfaceNode; |
| import org.apache.royale.compiler.internal.tree.as.NamespaceIdentifierNode; |
| import org.apache.royale.compiler.internal.tree.as.VariableNode; |
| import org.apache.royale.compiler.problems.AmbiguousReferenceProblem; |
| import org.apache.royale.compiler.problems.CannotExtendClassProblem; |
| import org.apache.royale.compiler.problems.ConstructorInInterfaceProblem; |
| import org.apache.royale.compiler.problems.DuplicateInterfaceDefinitionProblem; |
| import org.apache.royale.compiler.problems.FinalOutsideClassProblem; |
| import org.apache.royale.compiler.problems.ICompilerProblem; |
| import org.apache.royale.compiler.problems.InterfaceBindablePropertyProblem; |
| import org.apache.royale.compiler.problems.InterfaceMethodWithBodyProblem; |
| import org.apache.royale.compiler.problems.InvalidOverrideProblem; |
| import org.apache.royale.compiler.problems.NamespaceInInterfaceProblem; |
| import org.apache.royale.compiler.problems.NativeUsedInInterfaceProblem; |
| import org.apache.royale.compiler.problems.StaticOutsideClassProblem; |
| import org.apache.royale.compiler.problems.UnknownInterfaceProblem; |
| import org.apache.royale.compiler.problems.BadAccessInterfaceMemberProblem; |
| import org.apache.royale.compiler.problems.InterfaceNamespaceAttributeProblem; |
| import org.apache.royale.compiler.problems.VarInInterfaceProblem; |
| import org.apache.royale.compiler.problems.VirtualOutsideClassProblem; |
| import org.apache.royale.compiler.projects.ICompilerProject; |
| import org.apache.royale.compiler.tree.as.IASNode; |
| import org.apache.royale.compiler.tree.as.IExpressionNode; |
| import org.apache.royale.compiler.tree.as.IIdentifierNode; |
| import org.apache.royale.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)); |
| } |
| } |