| /* |
| * |
| * 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 static org.apache.flex.abc.ABCConstants.OP_add; |
| import static org.apache.flex.abc.ABCConstants.OP_getlocal0; |
| import static org.apache.flex.abc.ABCConstants.OP_pushscope; |
| import static org.apache.flex.abc.ABCConstants.OP_returnvalue; |
| import static org.apache.flex.abc.ABCConstants.OP_returnvoid; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Vector; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Future; |
| |
| import org.apache.flex.abc.ABCConstants; |
| import org.apache.flex.abc.instructionlist.InstructionList; |
| import org.apache.flex.abc.semantics.MethodBodyInfo; |
| import org.apache.flex.abc.semantics.MethodInfo; |
| import org.apache.flex.abc.semantics.Name; |
| import org.apache.flex.abc.visitors.IMethodBodyVisitor; |
| import org.apache.flex.abc.visitors.IMethodVisitor; |
| import org.apache.flex.abc.visitors.IScriptVisitor; |
| import org.apache.flex.abc.visitors.IVisitor; |
| import org.apache.flex.compiler.definitions.references.IReference; |
| import org.apache.flex.compiler.exceptions.BURMAbortException; |
| import org.apache.flex.compiler.exceptions.CodegenInterruptedException; |
| import org.apache.flex.compiler.exceptions.MissingBuiltinException; |
| import org.apache.flex.compiler.internal.as.codegen.ICodeGenerator.IConstantValue; |
| import org.apache.flex.compiler.internal.definitions.FunctionDefinition; |
| import org.apache.flex.compiler.internal.definitions.ParameterDefinition; |
| import org.apache.flex.compiler.internal.definitions.TypeDefinitionBase; |
| import org.apache.flex.compiler.internal.embedding.EmbedData; |
| import org.apache.flex.compiler.internal.scopes.ASScope; |
| import org.apache.flex.compiler.internal.testing.NodesToXMLStringFormatter; |
| import org.apache.flex.compiler.internal.tree.as.FunctionNode; |
| import org.apache.flex.compiler.internal.tree.mxml.MXMLFileNode; |
| import org.apache.flex.compiler.internal.units.requests.ABCBytesRequestResult; |
| import org.apache.flex.compiler.problems.CodegenInternalProblem; |
| import org.apache.flex.compiler.problems.ICompilerProblem; |
| import org.apache.flex.compiler.problems.MissingBuiltinProblem; |
| 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.units.ICompilationUnit; |
| import org.apache.flex.compiler.units.ICompilationUnit.Operation; |
| import org.apache.flex.compiler.units.requests.IABCBytesRequestResult; |
| |
| import com.google.common.util.concurrent.Futures; |
| |
| /** |
| * ABCGenerator is the public interface to the code generator. |
| */ |
| |
| /** |
| * JSGenerator is modeled after from ABCGenerator and called by |
| * JSCompilationUnit. JSGenerator owns the JSSharedData singleton. Ideally |
| * JSGenerator and ABCGenerator should be derived from the same base class, i.e. |
| * Generator. Some of the code in JSGenerator and ABCGenerator could be shared |
| * if Generator used a burm factory in generateInstructions(). ABCGenerator |
| * creates and uses a CmcEmitter, while JSGenerator uses a CmcJSEmitter. This |
| * implementation is part of FalconJS. For more details on FalconJS see |
| * org.apache.flex.compiler.JSDriver |
| */ |
| public class JSGenerator implements ICodeGenerator |
| { |
| public ICompilationUnit m_compilationUnit = null; |
| private Boolean m_needsSecondPass = false; |
| private CmcJSEmitter m_cmcJSEmitter = null; |
| private JSGeneratingReducer m_burm = null; |
| private ICompilationUnit.Operation m_buildPhase = Operation.GET_ABC_BYTES; |
| private JSEmitter m_emitter = null; |
| |
| public JSGenerator() |
| { |
| m_cmcJSEmitter = JSSharedData.backend.createCmcJSEmitter(); |
| m_cmcJSEmitter.reducer = JSSharedData.backend.createReducer(); |
| m_burm = m_cmcJSEmitter.reducer; |
| } |
| |
| // If JSEmitter.needsSecondPass() returns true, JSGenerator.generate() will return null during scanning, |
| // which will result in JSCompilationUnit::handleSemanticProblemsRequest not caching any abcBytes for |
| // handleABCBytesRequest. The net result is that JSGenerator.generate() will be called again in handleABCBytesRequest. |
| // This mechanic will ensure selective two-pass compilation. |
| public Boolean needsSecondPass() |
| { |
| return m_needsSecondPass; |
| } |
| |
| /* |
| * There are currently a lot of problems with the DependencyGraph: - |
| * ImportNode::resolveRefs() has not been implemented. - import and |
| * expression dependencies are not being recognized. If |
| * useOwnDependencyGraph() returns true we use our own (old) DependencyGraph |
| * implementation that used to drive the while loop in |
| * JSGenerator.generate() using nextJS. - JSDriver does a second pass - |
| * JSGlobalDirectiveProcessor::processImportDirective() registers imported |
| * classes. |
| */ |
| public static Boolean useOwnDependencyGraph() |
| { |
| // return true; |
| return false; |
| } |
| |
| /** |
| * Generate an ABC file equivalent to the input syntax tree. |
| * |
| * @param synthetic_name_prefix Prefix to prepend to all synthetic names |
| * @param root_node the root of the syntax tree. |
| * @param project {@link ICompilerProject} whose symbol table is used to |
| * resolve references to definitions. |
| * @throws InterruptedException |
| */ |
| @SuppressWarnings("nls") |
| public ABCBytesRequestResult generate(String synthetic_name_prefix, IASNode root_node, ICompilerProject project) throws InterruptedException |
| { |
| return generate(null, false, synthetic_name_prefix, root_node, project, false, 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 |
| { |
| m_needsSecondPass = false; |
| if (root_node instanceof MXMLFileNode) |
| m_emitter = new JSMXMLEmitter(JSSharedData.instance, m_buildPhase, project, this); |
| else |
| m_emitter = JSSharedData.backend.createEmitter(m_buildPhase, project, this); |
| m_emitter.visit(ABCConstants.VERSION_ABC_MAJOR_FP10, ABCConstants.VERSION_ABC_MINOR_FP10); |
| |
| IScriptVisitor sv = m_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 = m_emitter.visitMethod(init_method); |
| IMethodBodyVisitor mbv = mv.visitBody(init_body); |
| mbv.visit(); |
| |
| // Set up the global lexical scope. |
| final LexicalScope global_scope = new GlobalLexicalScope(project, this, synthetic_name_prefix, m_emitter); |
| global_scope.traitsVisitor = sv.visitTraits(); |
| global_scope.setMethodInfo(init_method); |
| global_scope.methodBodyVisitor = mbv; |
| |
| // Process global directives. |
| GlobalDirectiveProcessor top_level_processor = JSSharedData.backend.createGlobalDirectiveProcessor(this, global_scope, m_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(); |
| } |
| |
| 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); |
| |
| mbv.visitInstructionList(script_init_insns); |
| mbv.visitEnd(); |
| mv.visitEnd(); |
| sv.visitEnd(); |
| |
| try |
| { |
| generatedBytes = m_emitter.emit(); |
| } |
| catch (Throwable cant_generate) |
| { |
| global_scope.addProblem(new CodegenInternalProblem(root_node, cant_generate)); |
| } |
| } |
| |
| ICompilerProblem[] problemsArray = global_scope.getProblems().toArray(IABCBytesRequestResult.ZEROPROBLEMS); |
| return new ABCBytesRequestResult(generatedBytes, problemsArray, Collections.<EmbedData> emptySet()); |
| } |
| |
| /** |
| * Translate an AST into ABC instructions. |
| * |
| * @param subtree - the CM subtree. |
| * @param goal_state - the desired goal state. One of the nonterminal states |
| * in CmcJSEmitter, 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 CmcJSEmitter, 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) |
| { |
| m_burm.setCurrentscope(scope); |
| m_burm.setInstanceInitializers(instance_init_insns); |
| m_burm.setAprioriinstructions(instance_init_insns); |
| m_burm.setFunctionNode(subtree); |
| |
| InstructionList list = new InstructionList(); |
| |
| try |
| { |
| m_cmcJSEmitter.burm(subtree, goal_state); |
| |
| // TODO: cmcJSEmitter.getResult() now returns a String, which needs to be wrapped into an InstructionList |
| // return ((InstructionList)cmcJSEmitter.getResult()); |
| list.addInstruction(JSSharedData.OP_JS, m_cmcJSEmitter.getResult()); |
| |
| // If JSEmitter.needsSecondPass() returns true, JSGenerator.generate() will return null during scanning, |
| // which will result in JSCompilationUnit::handleSemanticProblemsRequest not caching any abcBytes for |
| // handleABCBytesRequest. The net result is that JSGenerator.generate() will be called again in handleABCBytesRequest. |
| // This mechanic will ensure selective two-pass compilation. |
| if (m_burm.needsSecondPass()) |
| { |
| m_needsSecondPass = true; |
| } |
| } |
| catch (Exception cant_reduce) |
| { |
| handleBurmError(m_cmcJSEmitter, subtree, cant_reduce, scope); |
| } |
| |
| return list; |
| } |
| |
| /** |
| * Generate code for a function declaration, and put its initialization code |
| * on the relevant instruction list. |
| * |
| * @param func - the function declaration node. |
| * @param enclosing_scope - the lexical scope in which the function was |
| * defined. |
| * @param a_priori_insns - instructions generated by an enclosing subsystem |
| * that should be included in the function (e.g., a constructor needs a |
| * priori instructions to initialize instance vars). |
| */ |
| public MethodInfo generateFunction(FunctionNode func, LexicalScope enclosing_scope, InstructionList a_priori_insns) |
| { |
| m_burm.setCurrentscope(enclosing_scope); |
| MethodInfo mi = createMethodInfo(m_burm, m_emitter, enclosing_scope, func); |
| if (mi.isNative()) |
| { |
| generateNativeMethod(func, mi, enclosing_scope); |
| } |
| else |
| { |
| /* |
| * Now done in JSEmitter: // support for class inits. // If this is |
| * a static function and the owner class has a class init then call |
| * __static_init() final IDefinition fdef = func.getDefinition(); |
| * if( fdef.isStatic() && fdef.getParent() != null && |
| * fdef.getParent() instanceof ClassDefinition ) { final IDefinition |
| * cdef = fdef.getParent(); final String fullName = |
| * JSGeneratingReducer |
| * .createFullNameFromDefinition(enclosing_scope.getProject(),cdef); |
| * if( JSSharedData.instance.hasClassInit(fullName) ) { final String |
| * callInit = fullName + "." + JSSharedData.STATIC_INIT + "();\n"; |
| * if( a_priori_insns == null ) a_priori_insns = new |
| * InstructionList(); a_priori_insns.addInstruction( |
| * JSSharedData.OP_JS, callInit ); } } |
| */ |
| m_burm.startFunction(func); |
| generateMethodBodyForFunction(mi, func, enclosing_scope, a_priori_insns); |
| m_burm.endFunction(func); |
| } |
| |
| return mi; |
| } |
| |
| public GenerateFunctionInParallelResult generateFunctionInParallel (ExecutorService executorService, FunctionNode func, LexicalScope enclosing_scope) |
| { |
| /** AJH commented out for now |
| MethodInfo mi = createMethodInfo(enclosing_scope, func); |
| if (mi.isNative()) |
| { |
| 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()); |
| */ |
| System.out.println("unhandled call to generateFunctionInParallel"); |
| return null; |
| } |
| |
| /** |
| * 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. |
| */ |
| static 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(); |
| |
| // func.getReturnType() returns a short name string. |
| // But we need a real name. ctors don't have return types. |
| if (!func.isConstructor()) |
| { |
| final ASScope scope = (ASScope)JSGeneratingReducer.getScopeFromNode(func); |
| final FunctionDefinition fdef = func.getDefinition(); |
| final IReference ref = fdef.getReturnTypeReference(); |
| final Name returnTypeName = ref.getMName(enclosing_scope.getProject(), scope); |
| mi.setReturnType(returnTypeName); |
| } |
| |
| // 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(); |
| // 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. may be null |
| * when generating method bodies for purely synthetic functions, such as |
| * theIEventDispatcher methods that [Bindable] introduces. |
| * @param enclosing_scope - the lexical scope in which the handler method is |
| * autogenerated. |
| * @param a_priori_insns - instructions generated by an enclosing subsystem |
| * that should be included in the function (e.g., a constructor needs a |
| * priori instructions to initialize instance vars). |
| */ |
| public void generateMethodBodyForFunction(MethodInfo mi, IASNode node, |
| LexicalScope enclosing_scope, |
| InstructionList a_priori_insns) |
| { |
| generateMethodBody(mi, node, enclosing_scope, a_priori_insns, CmcEmitter.__function_NT, null); |
| } |
| |
| /** |
| * Helper methods used by databinding codegen to emit anonymous functions |
| * based on an expression node. |
| * |
| * @param mi - the MethodInfo describing the signature |
| * @param node - the expression node whose code will start the function. may |
| * be null when generating method bodies for purely synthetic functions, |
| * such as theIEventDispatcher methods that [Bindable] introduces. |
| * @param enclosing_scope |
| * @param insns_to_append - typically some massaging of the TOS and a return |
| * function |
| */ |
| public MethodInfo generateFunctionFromExpression(MethodInfo mi, IASNode node, |
| LexicalScope enclosing_scope, |
| InstructionList insns_to_append) |
| { |
| return generateMethodBody(mi, node, enclosing_scope, null, CmcEmitter.__expression_NT, insns_to_append); |
| } |
| |
| /** |
| * Helper methods used by databinding codegen to emit anonymous functions |
| * based on an expression node. |
| * |
| * @param mi - the MethodInfo describing the signature |
| * @param nodes - a list of expression nodes whose code will start the |
| * function. May have the following values: null when generating method |
| * bodies for purely synthetic functions, such as theIEventDispatcher |
| * methods that [Bindable] introduces. an IASNode for an expression that |
| * will be code-gen'd a List of IASNodes. This is a special case where we |
| * code-gen the sum of all the expressions |
| * @param enclosing_scope |
| * @param insns_to_append - typically some massaging of the TOS and a return |
| * function |
| */ |
| |
| public MethodInfo generateFunctionFromExpressions(MethodInfo mi, List<? extends IASNode> nodes, |
| LexicalScope enclosing_scope, |
| InstructionList insns_to_append) |
| { |
| return generateMethodBody(mi, nodes, enclosing_scope, null, CmcEmitter.__expression_NT, insns_to_append); |
| } |
| |
| /** |
| * General method body maker. see the public documentation, above, for more |
| * into |
| */ |
| MethodInfo generateMethodBody(MethodInfo mi, Object node, |
| LexicalScope enclosing_scope, |
| InstructionList a_priori_insns, |
| int goal_state, |
| InstructionList insns_to_append) |
| { |
| // Set up a lexical scope for this function. |
| LexicalScope function_scope = enclosing_scope.pushFrame(); |
| |
| 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); |
| |
| System.out.println(mi.getMethodName()); |
| if (mi.getMethodName().contains("loginInternal")) |
| { |
| System.out.println("got it"); |
| } |
| |
| InstructionList insns = null; |
| if (node == null) |
| { |
| // node may be null when generating method bodies for purely synthetic functions, such as the |
| // IEventDispatcher methods that [Bindable] introduces. |
| insns = new InstructionList(); |
| } |
| else if (node instanceof IASNode) |
| { |
| // If we are passed a single node, generate its instructions |
| insns = generateInstructions((IASNode)node, goal_state, function_scope, a_priori_insns); |
| } |
| else if (node instanceof List<?>) |
| { |
| List<?> nodes = (List<?>)node; |
| |
| // for a list of nodes, generate all their instructions and add the results together. |
| // typically we are doing this to concatenate strings |
| for (int nodeIndex = 0; nodeIndex < nodes.size(); ++nodeIndex) |
| { |
| IASNode n = (IASNode)nodes.get(nodeIndex); |
| if (nodeIndex == 0) |
| { |
| // First one in the list makes a new IL and puts |
| // instructions into it |
| insns = generateInstructions(n, goal_state, function_scope, a_priori_insns); |
| } |
| else |
| { |
| // successive children generate into the same IS, then add the results |
| insns.addAll(generateInstructions(n, goal_state, function_scope, a_priori_insns)); |
| insns.addInstruction(OP_add); |
| } |
| } |
| } |
| else |
| { |
| assert false; // Illegal type passed as node parameter |
| } |
| assert insns != null; |
| |
| // If caller passed in instructions to get after the BURM-generated stuff, |
| // add them to the instruction stream |
| if (insns_to_append != null) |
| { |
| insns.addAll(insns_to_append); |
| } |
| |
| mbv.visitInstructionList(insns); |
| |
| if (function_scope.needsActivation()) |
| { |
| mi.setFlags((byte)(mi.getFlags() | ABCConstants.NEED_ACTIVATION)); |
| } |
| |
| mbv.visitEnd(); |
| mv.visitEnd(); |
| |
| return mi; |
| } |
| |
| /** |
| * 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) |
| { |
| System.out.println("unhandled call to generateMXMLDataBindingSetterFunction"); |
| /* AJH commented out for now |
| 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) |
| { |
| System.out.println("unhandled call to generateMXMLDataBindingSetterFunction"); |
| /* AJH commented out for now |
| 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. |
| */ |
| public static MethodInfo createMethodInfo(JSGeneratingReducer burm, JSEmitter emitter, LexicalScope scope, FunctionNode func) |
| { |
| MethodInfo mi = new MethodInfo(); |
| // FIXME: FunctionNode.getQualifiedName() has |
| // preconditions that need to be understood! |
| mi.setMethodName(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 = 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)); |
| param_names.add(arg.getBaseName()); |
| } |
| else |
| { |
| method_args.add(type_name); |
| param_names.add(arg.getBaseName()); |
| } |
| } |
| 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. |
| |
| // Falcon's IMethodVisitor only records a fraction of the FunctionDefinition. |
| // For that reason we are registering every MethodInfo with its corresponding FunctionDefinition at the JSEmitter. |
| emitter.visitFunctionDefinition(mi, funcDef); |
| |
| return mi; |
| } |
| |
| // called by JSInterfaceDirectiveProcessor |
| public MethodInfo createMethodInfo(LexicalScope scope, FunctionNode func) |
| { |
| return JSGenerator.createMethodInfo(m_burm, m_emitter, scope, func); |
| } |
| |
| /** |
| ** |
| * 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 JSGenerator.createMethodInfo(m_burm, m_emitter, scope, func); |
| } |
| |
| /** |
| * Helper method to expose the constant folding code to clients outside of |
| * the burm, such as |
| * org.apache.flex.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 |
| */ |
| public IConstantValue generateConstantValue(IASNode subtree, ICompilerProject project) |
| { |
| Object result = null; |
| |
| LexicalScope scope = new GlobalLexicalScope(project, null, GlobalLexicalScope.EMPTY_NAME_PREFIX, m_emitter); |
| |
| if (subtree != null) |
| { |
| try |
| { |
| result = reduceSubtree(subtree, scope, CmcJSEmitter.__constant_value_NT); |
| } |
| catch (Exception cant_reduce) |
| { |
| // Can't generate a constant value, just return null |
| } |
| } |
| |
| return new ConstantValue(result, null); |
| } |
| |
| /** |
| * 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 |
| { |
| CmcJSEmitter burm = m_cmcJSEmitter; |
| burm.reducer = this.m_burm; |
| 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. |
| */ |
| @SuppressWarnings("nls") |
| private static void handleBurmError(CmcJSEmitter 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)); |
| } |
| |
| java.io.PrintWriter dumper; |
| |
| String dump_dir = System.getenv("JBURG_DUMP_DIR"); |
| if (dump_dir != null) |
| { |
| try |
| { |
| String dump_file = dump_dir + "/failedBurm-" + Integer.toString(dumpCount++) + ".xml"; |
| dumper = new java.io.PrintWriter(new java.io.FileWriter(dump_file)); |
| dumper.println("<?xml version=\"1.0\"?>"); |
| dumper.println("<BurmDump date=\"" + new Date().toString() + "\">"); |
| burm.dump(dumper); |
| dumper.println("<AST>"); |
| dumper.println(new NodesToXMLStringFormatter(n).toString()); |
| dumper.println("</AST>"); |
| dumper.println("</BurmDump>"); |
| dumper.flush(); |
| dumper.close(); |
| } |
| catch (Exception e) |
| { |
| JSSharedData.instance.stderr("Unable to dump due to: " + e.toString()); |
| try |
| { |
| JSSharedData.instance.stderr(new NodesToXMLStringFormatter(n).toString()); |
| } |
| catch (Exception cantformat) |
| { |
| // Probably an error in the AST itself, diagnosed above. |
| } |
| } |
| } |
| } |
| |
| /** |
| * Number of diagnostic dumps emitted by this compiler; used to generate |
| * unique dump file names. |
| */ |
| static int dumpCount = 0; |
| |
| public String toString() |
| { |
| return "JSGenerator: " + m_compilationUnit.toString(); |
| } |
| |
| public void setBuildPhase(ICompilationUnit.Operation op) |
| { |
| m_buildPhase = op; |
| m_burm.setBuildPhase(op); |
| } |
| |
| public JSGeneratingReducer getReducer() |
| { |
| return m_burm; |
| } |
| |
| |
| /** |
| * Get an ICodeGeneratorFactory that will always return the same ABCGenerator instance |
| */ |
| public static ICodeGeneratorFactory getABCGeneratorFactory() |
| { |
| return new ICodeGeneratorFactory() |
| { |
| public ICodeGenerator get () |
| { |
| return new JSGenerator(); |
| } |
| }; |
| } |
| |
| /** |
| * 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; |
| } |
| } |
| } |