| /* |
| * |
| * 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 static org.apache.royale.abc.ABCConstants.*; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Vector; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Future; |
| |
| import org.apache.royale.abc.ABCConstants; |
| import org.apache.royale.abc.ABCEmitter; |
| import org.apache.royale.abc.instructionlist.InstructionList; |
| import org.apache.royale.abc.semantics.MethodBodyInfo; |
| import org.apache.royale.abc.semantics.MethodInfo; |
| import org.apache.royale.abc.semantics.Name; |
| import org.apache.royale.abc.semantics.Namespace; |
| import org.apache.royale.abc.semantics.PooledValue; |
| import org.apache.royale.abc.visitors.IMethodBodyVisitor; |
| import org.apache.royale.abc.visitors.IMethodVisitor; |
| import org.apache.royale.abc.visitors.IScriptVisitor; |
| import org.apache.royale.abc.visitors.IVisitor; |
| import org.apache.royale.compiler.embedding.IEmbedData; |
| import org.apache.royale.compiler.exceptions.BURMAbortException; |
| import org.apache.royale.compiler.exceptions.CodegenInterruptedException; |
| import org.apache.royale.compiler.exceptions.MissingBuiltinException; |
| import org.apache.royale.compiler.internal.definitions.FunctionDefinition; |
| import org.apache.royale.compiler.internal.definitions.ParameterDefinition; |
| import org.apache.royale.compiler.internal.definitions.TypeDefinitionBase; |
| import org.apache.royale.compiler.internal.embedding.EmbedData; |
| import org.apache.royale.compiler.internal.semantics.SemanticUtils; |
| import org.apache.royale.compiler.internal.tree.as.FunctionNode; |
| import org.apache.royale.compiler.internal.units.EmbedCompilationUnitFactory; |
| import org.apache.royale.compiler.internal.units.requests.ABCBytesRequestResult; |
| import org.apache.royale.compiler.problems.CodegenInternalProblem; |
| import org.apache.royale.compiler.problems.ICompilerProblem; |
| import org.apache.royale.compiler.problems.MissingBuiltinProblem; |
| import org.apache.royale.compiler.problems.NonConstantParamInitializerProblem; |
| 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.IFileNodeAccumulator; |
| import org.apache.royale.compiler.tree.as.IFunctionNode; |
| import org.apache.royale.compiler.tree.as.IParameterNode; |
| import org.apache.royale.compiler.units.requests.IABCBytesRequestResult; |
| import com.google.common.util.concurrent.Futures; |
| import org.apache.royale.utils.ASTUtil; |
| |
| /** |
| * ABCGenerator is the public interface to the code generator. |
| */ |
| public class ABCGenerator implements ICodeGenerator |
| { |
| private static boolean DEFINITION_NORMALIZATION_DISABLED = false; |
| |
| @Override |
| public ABCBytesRequestResult generate (String synthetic_name_prefix, IASNode root_node, ICompilerProject project) throws InterruptedException |
| { |
| return generate(null, false, synthetic_name_prefix, root_node, project, DEFINITION_NORMALIZATION_DISABLED, Collections.<String, String>emptyMap()); |
| } |
| |
| @Override |
| public ABCBytesRequestResult generate(ExecutorService executorService, boolean useParallelCodegen, |
| String synthetic_name_prefix, IASNode root_node, |
| ICompilerProject project, boolean inInvisibleCompilationUnit, |
| Map<String, String> encodedDebugFiles) |
| throws InterruptedException |
| { |
| // Set up the global lexical scope. |
| final GlobalLexicalScope global_scope = new GlobalLexicalScope( |
| project, this, synthetic_name_prefix, inInvisibleCompilationUnit, useParallelCodegen, encodedDebugFiles); |
| |
| final ABCEmitter emitter = (ABCEmitter)global_scope.getEmitter(); |
| |
| // CG targets the latest version - these ABCs can be postprocessed to downgrade to previous versions |
| emitter.visit(ABCConstants.VERSION_ABC_MAJOR_FP10, ABCConstants.VERSION_ABC_MINOR_FP10); |
| |
| IScriptVisitor sv = emitter.visitScript(); |
| sv.visit(); |
| MethodInfo init_method = new MethodInfo(); |
| sv.visitInit(init_method); |
| |
| MethodBodyInfo init_body = new MethodBodyInfo(); |
| init_body.setMethodInfo(init_method); |
| |
| IMethodVisitor mv = emitter.visitMethod(init_method); |
| mv.visit(); |
| IMethodBodyVisitor mbv = mv.visitBody(init_body); |
| mbv.visit(); |
| |
| global_scope.traitsVisitor = sv.visitTraits(); |
| global_scope.setMethodInfo(init_method); |
| global_scope.methodBodyVisitor = mbv; |
| global_scope.setInitialControlFlowRegionNode(root_node); |
| |
| // Process global directives. |
| GlobalDirectiveProcessor top_level_processor = new GlobalDirectiveProcessor(executorService, useParallelCodegen, global_scope, emitter); |
| boolean fatal_error_encountered = false; |
| try |
| { |
| top_level_processor.traverse(root_node); |
| } |
| catch (MissingBuiltinException e) |
| { |
| global_scope.addProblem(new MissingBuiltinProblem(root_node, e.getBuiltinName())); |
| fatal_error_encountered = true; |
| } |
| catch (CodegenInterruptedException e) |
| { |
| // Unwrap the InterruptedException and rethrow it. |
| throw e.getException(); |
| } |
| |
| top_level_processor.finish(); |
| |
| byte[] generatedBytes = IABCBytesRequestResult.ZEROBYTES; |
| |
| if ( !fatal_error_encountered ) |
| { |
| // Initialize the init script. |
| InstructionList script_init_insns = new InstructionList(); |
| |
| script_init_insns.addInstruction(OP_getlocal0); |
| script_init_insns.addInstruction(OP_pushscope); |
| |
| script_init_insns.addAll(global_scope.getInitInstructions()); |
| script_init_insns.addAll(top_level_processor.directiveInsns); |
| |
| if ( script_init_insns.canFallThrough() || script_init_insns.hasPendingLabels() ) |
| script_init_insns.addInstruction(OP_returnvoid); |
| |
| // Allocate temps beginning with register 1, |
| // register 0 is reserved for "this" global. |
| global_scope.initializeTempRegisters(1); |
| |
| // Make any vistEnd method calls |
| // that were deferred. |
| // callVisitEnds must be called on the same thread |
| // that original called ABCGenerator.generate ( this method ). |
| global_scope.callVisitEnds(); |
| |
| mbv.visitInstructionList(script_init_insns); |
| mbv.visitEnd(); |
| mv.visitEnd(); |
| global_scope.traitsVisitor.visitEnd(); |
| sv.visitEnd(); |
| |
| try |
| { |
| generatedBytes = emitter.emit(); |
| } |
| catch ( Exception e ) |
| { |
| global_scope.addProblem(new CodegenInternalProblem(root_node, e)); |
| } |
| } |
| |
| Set<EmbedData> embeds = global_scope.getEmbeds(); |
| EmbedCompilationUnitFactory.collectEmbedDatas(project, (IFileNodeAccumulator)root_node, embeds, global_scope.getProblems()); |
| |
| Set<IEmbedData> iembeds = new HashSet<IEmbedData>(); |
| for (EmbedData embed : embeds) |
| iembeds.add(embed); |
| ICompilerProblem[] problemsArray = global_scope.getProblems().toArray(IABCBytesRequestResult.ZEROPROBLEMS); |
| return new ABCBytesRequestResult(generatedBytes, problemsArray, iembeds); |
| } |
| |
| /** |
| * Translate an AST into ABC instructions. |
| * @param subtree - the CM subtree. |
| * @param goal_state - the desired goal state. |
| * One of the nonterminal states in CmcEmitter, |
| * or 0 if you're feeling lucky and are willing |
| * to accept whatever instruction sequence the |
| * BURM decides is optimal. |
| * @param scope - the active lexical scope. |
| * @return a list of ABC instructions. |
| */ |
| public InstructionList generateInstructions (IASNode subtree, int goal_state, LexicalScope scope) |
| { |
| return generateInstructions(subtree, goal_state, scope, null); |
| } |
| |
| /** |
| * Translate an AST into ABC instructions. |
| * @param subtree - the CM subtree. |
| * @param goal_state - the desired goal state. |
| * One of the nonterminal states in CmcEmitter, |
| * or 0 if you're feeling lucky and are willing |
| * to accept whatever instruction sequence the |
| * BURM decides is optimal. |
| * @param scope - the active lexical scope. |
| * @param instance_init_insns - a list of instance |
| * initialization instructions collected outside |
| * a constructor body that must be included in the |
| * constructor. |
| * @post if instance_init_insns is not null then the |
| * method will have been processed as and marked |
| * as a constructor. |
| * @return a list of ABC instructions. |
| */ |
| public InstructionList generateInstructions(IASNode subtree, int goal_state, LexicalScope scope, InstructionList instance_init_insns) |
| { |
| CmcEmitter burm = new CmcEmitter(); |
| burm.reducer = new ABCGeneratingReducer(); |
| burm.reducer.setCurrentscope(scope); |
| burm.reducer.setInstanceInitializers(instance_init_insns); |
| |
| try { |
| burm.burm(subtree, goal_state); |
| return ((InstructionList)burm.getResult()); |
| } |
| catch ( Exception cant_reduce) { |
| handleBurmError(burm, subtree, cant_reduce, scope); |
| return new InstructionList(); |
| } |
| } |
| |
| |
| /** |
| * Generate code for a function declaration, and put its initialization code |
| * on the relevant instruction list. |
| * <p> |
| * Post condition: if instance_init_insns is not null then the method will |
| * have been processed as and marked as a constructor. |
| * |
| * @param func the function declaration node. |
| * @param enclosing_scope the lexical scope in which the function was |
| * defined. |
| * @param instance_init_insns a list of instance initialization instructions |
| * collected outside a constructor body that must be included in the |
| * constructor. |
| * @return {@link MethodInfo} created for the function. |
| */ |
| public MethodInfo generateFunction (FunctionNode func, LexicalScope enclosing_scope, InstructionList instance_init_insns, Name alternate_name) |
| { |
| MethodInfo mi = createMethodInfo(enclosing_scope, func, alternate_name); |
| if (mi.isNative()) |
| { |
| // previous versions didn't add the default argument values to |
| // native methods, but that led to broken signatures -JT |
| mi = createMethodInfoWithOptionalDefaultArgumentValues(enclosing_scope, func, true, alternate_name); |
| generateNativeMethod(func, mi, enclosing_scope); |
| } |
| else |
| { |
| generateMethodBodyForFunction(mi, func, enclosing_scope, instance_init_insns); |
| } |
| |
| func.discardFunctionBody(); |
| return mi; |
| } |
| |
| /** |
| * Generate code for a function declaration, using a background thread |
| * provided by the specified {@link ExecutorService}. |
| * |
| * @param executorService {@link ExecutorService} used to do work in other |
| * threads. |
| * @param func the function declaration node. |
| * @param enclosing_scope the lexical scope in which the function was |
| * defined. |
| * @return {@link GenerateFunctionInParallelResult} which can be used to |
| * wait for code generation of the specified function to complete and to |
| * extract the {@link MethodInfo} created for the specified function. The |
| * {@link MethodInfo} may be extracted immediately after this method |
| * completes ( you don't have to wait for code generation of the specified |
| * function complete ). |
| */ |
| public GenerateFunctionInParallelResult generateFunctionInParallel (ExecutorService executorService, FunctionNode func, LexicalScope enclosing_scope) |
| { |
| MethodInfo mi = createMethodInfo(enclosing_scope, func, null); |
| if (mi.isNative()) |
| { |
| // previous versions didn't add the default argument values to |
| // native methods, but that led to broken signatures -JT |
| mi = createMethodInfoWithOptionalDefaultArgumentValues(enclosing_scope, func, true, null); |
| generateNativeMethod(func, mi, enclosing_scope); |
| return new GenerateFunctionInParallelResult(Futures.immediateFuture(null), mi, Collections.<IVisitor>emptyList()); |
| } |
| GenerateFunctionRunnable runnable = new GenerateFunctionRunnable(mi, func, enclosing_scope); |
| Future<?> future = executorService.submit(runnable); |
| return new GenerateFunctionInParallelResult(future, mi, runnable.getDeferredVisitEndsList()); |
| } |
| |
| /** |
| * Helper method used by <code>generateFunction()</code>. |
| * @param func - the function declaration node. |
| * @param mi - the MethodInfo describing the signature |
| * @param enclosing_scope - the lexical scope |
| * in which the handler method is autogenerated. |
| */ |
| void generateNativeMethod(FunctionNode func, MethodInfo mi, |
| LexicalScope enclosing_scope) |
| { |
| enclosing_scope.getMethodBodySemanticChecker().checkNativeMethod(func); |
| |
| // don't need to create a new scope, so just use the enclosing scope |
| // to get a handle to the emitter |
| IMethodVisitor mv = enclosing_scope.getEmitter().visitMethod(mi); |
| |
| // Just visit the method info. Do NOT generate a body |
| // for native methods |
| mv.visit(); |
| |
| // For non native methods, the return type is set by the burm, |
| // but for native types, as the burm isn't run, we need to set |
| // the return type here. |
| String returnType = func.getReturnType(); |
| int lastDotIndex = returnType.lastIndexOf('.'); |
| if (lastDotIndex != -1) |
| { |
| Namespace ns = new Namespace(CONSTANT_PackageNs, returnType.substring(0, lastDotIndex)); |
| String baseName = returnType.substring(lastDotIndex + 1); |
| mi.setReturnType(new Name(ns, baseName)); |
| } |
| else |
| { |
| mi.setReturnType(new Name(returnType)); |
| } |
| |
| mv.visitEnd(); |
| } |
| |
| /** |
| * Helper method used by <code>generateFunction()</code> (and also by |
| * <code>generateEventHandler()</code> in MXMLDocumentDirectiveProcessor). |
| * @param mi - the MethodInfo describing the signature |
| * @param node - the FunctionNode or MXMLEventSpecifierNode. |
| * @param enclosing_scope - the lexical scope |
| * in which the handler method is autogenerated. |
| * @param instance_init_insns - a list of instance |
| * initialization instructions collected outside |
| * a constructor body that must be included in the |
| * constructor. |
| * @post if instance_init_insns is not null then the |
| * method will have been processed as and marked |
| * as a constructor. |
| */ |
| public void generateMethodBodyForFunction(MethodInfo mi, IASNode node, |
| LexicalScope enclosing_scope, |
| InstructionList instance_init_insns) |
| { |
| List<IVisitor> deferredVisitEnds = new LinkedList<IVisitor>(); |
| generateMethodBodyForFunction(deferredVisitEnds, mi, node, enclosing_scope, instance_init_insns); |
| for (IVisitor v : deferredVisitEnds) |
| v.visitEnd(); |
| } |
| |
| private void generateMethodBodyForFunction(List<IVisitor> deferredVisitEnds, |
| MethodInfo mi, IASNode node, |
| LexicalScope enclosing_scope, |
| InstructionList instance_init_insns) |
| { |
| assert node != null; |
| final boolean is_constructor = SemanticUtils.isInConstructor(node); |
| // Set up a lexical scope for this function. |
| LexicalScope function_scope = enclosing_scope.pushFrame(); |
| |
| // If instance_init_insns is not null, then the new |
| // scope needs to assume ownership of the initializers' |
| // data, such as hasnext2 instruction initializers. |
| if ( instance_init_insns != null ) |
| { |
| function_scope.transferInitializerData(); |
| } |
| |
| IMethodVisitor mv = function_scope.getEmitter().visitMethod(mi); |
| mv.visit(); |
| |
| MethodBodyInfo mbi = new MethodBodyInfo(); |
| mbi.setMethodInfo(mi); |
| |
| IMethodBodyVisitor mbv = mv.visitBody(mbi); |
| mbv.visit(); |
| |
| function_scope.methodBodyVisitor = mbv; |
| function_scope.traitsVisitor = mbv.visitTraits(); |
| function_scope.setMethodInfo(mi); |
| |
| if ( is_constructor ) |
| function_scope.getMethodBodySemanticChecker().enterConstructor(); |
| |
| InstructionList insns = null; |
| |
| if (node instanceof IFunctionNode) |
| function_scope.setInitialControlFlowRegionNode(((IFunctionNode)node).getScopedNode()); |
| else |
| function_scope.setInitialControlFlowRegionNode((IASNode)node); |
| // If we are passed a single node, generate its instructions |
| insns = generateInstructions((IASNode)node, CmcEmitter.__function_NT, function_scope, instance_init_insns); |
| |
| if ( is_constructor ) |
| function_scope.getMethodBodySemanticChecker().leaveConstructor(); |
| |
| assert insns != null; |
| |
| |
| // Make any vistEnd method calls |
| // that were deferred. |
| // callVisitEnds must be called on the same thread |
| // that started code generation. |
| function_scope.addVisitEndsToList(deferredVisitEnds); |
| |
| mbv.visitInstructionList(insns); |
| deferredVisitEnds.add(function_scope.traitsVisitor); |
| deferredVisitEnds.add(mbv); |
| deferredVisitEnds.add(mv); |
| |
| // Perform semantic checks that require the control flow graph. |
| function_scope.getMethodBodySemanticChecker().checkControlFlow( node, mi, mbi ); |
| } |
| |
| /** |
| * Helper method used by mxml databinding codegen to emit an anonymous |
| * function used by an mxml data binding destination function. Example: |
| * <p> |
| * If the expression node is a.b.c, this method will generate a funtion |
| * whose source would look something like this: |
| * |
| * <pre> |
| * function (arg:*):void { a.b.c = arg; } |
| * </pre> |
| * |
| * @param mi - the MethodInfo describing the signature |
| * @param setterExpression {@link IExpressionNode} that is the destination |
| * expression of a mxml data binding. |
| * @param enclosing_scope {@link LexicalScope} for the class initializer |
| * that encloses the function being generated. |
| */ |
| public void generateMXMLDataBindingSetterFunction (MethodInfo mi, IExpressionNode setterExpression, LexicalScope enclosing_scope) |
| { |
| IMethodVisitor methodVisitor = enclosing_scope.getEmitter().visitMethod(mi); |
| methodVisitor.visit(); |
| MethodBodyInfo methodBodyInfo = new MethodBodyInfo(); |
| methodBodyInfo.setMethodInfo(mi); |
| IMethodBodyVisitor methodBodyVisitor = methodVisitor.visitBody(methodBodyInfo); |
| methodBodyVisitor.visit(); |
| |
| // Set up a lexical scope for this function. |
| LexicalScope function_scope = enclosing_scope.pushFrame(); |
| |
| function_scope.methodBodyVisitor = methodBodyVisitor; |
| function_scope.traitsVisitor = methodBodyVisitor.visitTraits(); |
| function_scope.setMethodInfo(mi); |
| |
| |
| InstructionList functionBody; |
| if (setterExpression instanceof InstructionListNode) |
| functionBody = ((InstructionListNode)setterExpression).getInstructions(); |
| else |
| functionBody = generateInstructions(setterExpression, CmcEmitter.__mxml_data_binding_setter_expression_NT, function_scope, null); |
| |
| functionBody.addInstruction(OP_returnvoid); |
| |
| methodBodyVisitor.visitInstructionList(functionBody); |
| methodBodyVisitor.visitEnd(); |
| methodVisitor.visitEnd(); |
| } |
| |
| /** |
| * Helper method used by databinding codegen to emit an anonymous function |
| * based on a list of {@link IExpressionNode}'s. This method emits a |
| * function that contains code that evaluates each expression in the list |
| * and adds the expressions together with {@link ABCConstants#OP_add}. |
| * |
| * @param mi - the MethodInfo describing the signature |
| * @param nodes - a {@link List} of {@link IExpressionNode}'s to be |
| * codegen'd. |
| * @param enclosing_scope {@link LexicalScope} for the class initializer |
| * that encloses the function being generated. |
| */ |
| public void generateMXMLDataBindingGetterFunction (MethodInfo mi, List<IExpressionNode> nodes, |
| LexicalScope enclosing_scope) |
| { |
| IMethodVisitor methodVisitor = enclosing_scope.getEmitter().visitMethod(mi); |
| methodVisitor.visit(); |
| MethodBodyInfo methodBodyInfo = new MethodBodyInfo(); |
| methodBodyInfo.setMethodInfo(mi); |
| IMethodBodyVisitor methodBodyVisitor = methodVisitor.visitBody(methodBodyInfo); |
| methodBodyVisitor.visit(); |
| |
| // Set up a lexical scope for this function. |
| LexicalScope function_scope = enclosing_scope.pushFrame(); |
| |
| function_scope.methodBodyVisitor = methodBodyVisitor; |
| function_scope.traitsVisitor = methodBodyVisitor.visitTraits(); |
| function_scope.setMethodInfo(mi); |
| |
| InstructionList functionBody = null; |
| // for a list of nodes, generate all their instructions and add the results together. |
| // typically we are doing this to concatenate strings |
| for (IExpressionNode expressionNode : nodes) |
| { |
| InstructionList instructionsForExpression = generateInstructions(expressionNode, CmcEmitter.__expression_NT, function_scope, null); |
| if (functionBody == null) |
| { |
| // First one in the list makes a new IL and puts |
| // instructions into it |
| functionBody = instructionsForExpression; |
| } |
| else |
| { |
| // successive children generate into the same IL, then add the results |
| functionBody.addAll(instructionsForExpression); |
| functionBody.addInstruction(OP_add); |
| } |
| } |
| |
| functionBody.addInstruction(OP_returnvalue); |
| |
| methodBodyVisitor.visitInstructionList(functionBody); |
| function_scope.traitsVisitor.visitEnd(); |
| methodBodyVisitor.visitEnd(); |
| methodVisitor.visitEnd(); |
| } |
| /** |
| * Creates a MethodInfo specifying the signature of a method |
| * declared by a FunctionNode. |
| * @param func - A FunctionNode representing a method declaration. |
| * @return The MethodInfo specifying the signature of the method. |
| */ |
| @Override |
| public MethodInfo createMethodInfo (LexicalScope scope, FunctionNode func, Name alternate_name) |
| { |
| return createMethodInfoWithOptionalDefaultArgumentValues(scope, func, false, alternate_name); |
| } |
| |
| /** |
| ** |
| * Creates a MethodInfo specifying the signature of a method |
| * declared by a FunctionNode, and adds in the information for any |
| * default argument values. |
| * |
| * @param func - A FunctionNode representing a method declaration. |
| * @return The MethodInfo specifying the signature of the method. |
| * |
| * Will generate a compiler problem is the default value is bad |
| */ |
| @Override |
| public MethodInfo createMethodInfoWithDefaultArgumentValues (LexicalScope scope, FunctionNode func) |
| { |
| return createMethodInfoWithOptionalDefaultArgumentValues(scope, func, true, null); |
| } |
| |
| private static MethodInfo createMethodInfoWithOptionalDefaultArgumentValues(LexicalScope scope, FunctionNode func, |
| boolean addDefalutValues, Name alternate_name) |
| { |
| MethodInfo mi = new MethodInfo(); |
| mi.setMethodName(alternate_name != null ? alternate_name.getBaseName() : func.getName()); |
| |
| FunctionDefinition funcDef = func.getDefinition(); |
| // Marshal the function's arguments. |
| ParameterDefinition[] args = funcDef.getParameters(); |
| List<String> param_names = new ArrayList<String>(); |
| |
| ICompilerProject project = scope.getProject(); |
| if ( args.length > 0 ) |
| { |
| Vector<Name> method_args = new Vector<Name>(); |
| for ( ParameterDefinition arg: args) |
| { |
| TypeDefinitionBase arg_type = (TypeDefinitionBase)arg.resolveType(project); |
| Name type_name = arg_type != null ? arg_type.getMName(project) : null; |
| |
| if ( arg.isRest() ) |
| { |
| mi.setFlags( (byte)(mi.getFlags() | ABCConstants.NEED_REST) ); |
| } |
| else |
| { |
| method_args.add(type_name); |
| param_names.add(arg.getBaseName()); |
| } |
| |
| // If appropriate, tell the MethodInfo about the default parameter value |
| if (addDefalutValues && arg.hasDefaultValue()) |
| { |
| Object initValue = arg.resolveInitialValue(project); |
| |
| // init value might resolve to null, if the source code is bad |
| if (initValue == null) |
| { |
| IParameterNode paramNode = arg.getNode(); |
| scope.addProblem(new NonConstantParamInitializerProblem(paramNode.getAssignedValueNode())); |
| // re-write non-constant expression to undefined, so resulting ABC will pass the verifier. |
| initValue = ABCConstants.UNDEFINED_VALUE; |
| } |
| mi.addDefaultValue(new PooledValue(initValue)); |
| } |
| } |
| mi.setParamTypes(method_args); |
| mi.setParamNames(param_names); |
| } |
| |
| // check for native modifier |
| if ( func.getDefinition().isNative() ) |
| { |
| mi.setFlags( (byte)(mi.getFlags() | ABCConstants.NATIVE) ); |
| } |
| |
| // The return type will be set by the BURM. |
| |
| return mi; |
| } |
| |
| /** |
| * Helper method to expose the constant folding code to clients outside of the burm, such |
| * as org.apache.royale.compiler.internal.as.definitions.ConstantDefinition. |
| * @param subtree the tree to generate a constant value for |
| * @param project the project to use to evaluate the tree |
| * @return the constant value for the subtree, or null if a constant value can't be determined |
| */ |
| @Override |
| public IConstantValue generateConstantValue (IASNode subtree, ICompilerProject project) |
| { |
| IConstantValue result = null; |
| |
| LexicalScope scope = new GlobalLexicalScope(project, this); |
| |
| if ( subtree != null ) |
| { |
| try |
| { |
| Object value = reduceSubtree(subtree, scope, CmcEmitter.__constant_value_NT); |
| result = new ConstantValue(value, scope.getProblems()); |
| } |
| catch ( Exception cant_reduce) |
| { |
| // Can't generate a constant value, just return null |
| // Do not add a problem for this exception as it could be a non-constant |
| // initializer like: var foo = new Date(); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Reduce an AST to its equivalent ABC structures. |
| * @param subtree - the root of the AST subtree. |
| * May be null, in which case this routine returns null. |
| * @param scope - the active LexicalScope. |
| * @param goal - the BURM's goal state. One of the CmcEmitter.__foo_NT constants. |
| * @return the result of reducing the subtree to the desired goal state, |
| * or null if the input subtree was null. |
| * @throws Exception from the BURM if the computation didn't succeed or was interrupted. |
| */ |
| public Object reduceSubtree (IASNode subtree, LexicalScope scope, int goal) |
| throws Exception |
| { |
| CmcEmitter burm = new CmcEmitter(); |
| burm.reducer = new ABCGeneratingReducer(); |
| burm.reducer.setCurrentscope(scope); |
| |
| burm.burm(subtree, CmcEmitter.__constant_value_NT); |
| return burm.getResult(); |
| } |
| |
| /** |
| * Handle an error from a BURM: emit diagnostics and bump the error count. |
| * @param n - the subtree that was to be reduced. |
| * @param ex - the exception. |
| */ |
| private void handleBurmError(CmcEmitter burm, IASNode n, Exception ex, LexicalScope scope) |
| { |
| if ( ex instanceof CodegenInterruptedException ) |
| { |
| // If the exception is an InterruptedException, do nothing, as not |
| // a real error. The incremental flow kicked in and interrupted |
| // the current work, so just throw away the current work and carry |
| // on our merry way. |
| // No problem should be reported in this case. |
| scope.getProblems().clear(); |
| return; |
| } |
| else if ( ! ( ex instanceof BURMAbortException ) ) |
| { |
| scope.addProblem( new CodegenInternalProblem(n, ex) ); |
| } |
| DumpBURMState.dump(burm, n); |
| } |
| |
| /** |
| * Implementation of {@link Runnable} that is used to code generate a |
| * function on a background thread. |
| */ |
| private class GenerateFunctionRunnable implements Runnable |
| { |
| |
| GenerateFunctionRunnable(MethodInfo methodInfo, FunctionNode func, LexicalScope enclosing_scope) |
| { |
| this.methodInfo = methodInfo; |
| this.functionNode = func; |
| this.enclosingScope = enclosing_scope; |
| this.deferredVisitEnds = new LinkedList<IVisitor>(); |
| } |
| |
| private final MethodInfo methodInfo; |
| private final FunctionNode functionNode; |
| private final LexicalScope enclosingScope; |
| private final List<IVisitor> deferredVisitEnds; |
| |
| @Override |
| public void run() |
| { |
| assert !methodInfo.isNative() : "Native methods should be handled in the main thread and not be dispatched to a background thread!"; |
| functionNode.parseFunctionBody(enclosingScope.getProblems()); |
| ASTUtil.processFunctionNode(functionNode, enclosingScope.getProject()); |
| generateMethodBodyForFunction(deferredVisitEnds, methodInfo, functionNode, enclosingScope, null); |
| } |
| |
| public List<IVisitor> getDeferredVisitEndsList() |
| { |
| return deferredVisitEnds; |
| } |
| } |
| |
| /** |
| * Get an ICodeGeneratorFactory that will always return the same ABCGenerator instance |
| */ |
| public static ICodeGeneratorFactory getABCGeneratorFactory() |
| { |
| return new ICodeGeneratorFactory() |
| { |
| public ICodeGenerator get () |
| { |
| return new ABCGenerator(); |
| } |
| }; |
| } |
| |
| /** |
| * Represents the result of {@link #generateConstantValue}(}. |
| * <p> |
| * In addition to producing the constant value itself, |
| * the constant reduction process can also produce compiler problems. |
| */ |
| public static final class ConstantValue implements IConstantValue |
| { |
| public ConstantValue(Object value, Collection<ICompilerProblem> problems) |
| { |
| this.value = value; |
| this.problems = problems; |
| } |
| |
| private final Object value; |
| private final Collection<ICompilerProblem> problems; |
| |
| @Override |
| public Object getValue() |
| { |
| return value; |
| } |
| |
| @Override |
| public Collection<ICompilerProblem> getProblems() |
| { |
| return problems; |
| } |
| } |
| } |