| package flash.tools.debugger.expression; |
| |
| header |
| { |
| /* |
| |
| 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. |
| |
| AS3DebuggerBURM.java is a Bottom-Up Rewrite Machine for ActionScript 3.0. |
| |
| A BURM recognizes patterns of nodes within an tree (in this case, an |
| abstract syntax tree for AS 3.0 expressions) and performs rule-based reduction |
| on them (in this case producing DebuggerValues). |
| |
| AS3DebuggerBURM.java is generated from AS3DebuggerBURM.jbg using JBurg, an open-source |
| Java implementation of a Bottom-Up Rewrite Generator. |
| |
| The most important entry point of this class is burm(), |
| which is called by the code generator. |
| */ |
| |
| import static org.apache.flex.abc.ABCConstants.*; |
| import static org.apache.flex.compiler.tree.ASTNodeID.*; |
| |
| import org.apache.flex.compiler.constants.IASLanguageConstants; |
| import org.apache.flex.compiler.exceptions.BURMAbortException; |
| import org.apache.flex.compiler.problems.*; |
| import org.apache.flex.compiler.internal.semantics.SemanticUtils; |
| import org.apache.flex.compiler.tree.ASTNodeID; |
| import org.apache.flex.compiler.tree.as.IASNode; |
| import org.apache.flex.compiler.tree.as.ITryNode; |
| } |
| |
| INodeType IASNode; |
| OpcodeType ASTNodeID; |
| |
| /* |
| * The I-node type is IASNode, and it has its own adapter. |
| */ |
| INodeAdapter org.apache.flex.compiler.internal.as.codegen.IASNodeAdapter; |
| |
| // Generate Java output. |
| Language java; |
| |
| // Return type of all the reductions |
| ReturnType Object; |
| |
| /** |
| * This is a name to use in an expression context. |
| * |
| * TODO: What type should this be? |
| */ |
| ReturnType name = Object; |
| |
| ReturnType decl_name = Object; |
| |
| /** |
| * Qualified name - this acts the same as a name above, but is built a little differently. |
| */ |
| ReturnType qualifiedName = Object; |
| ReturnType qualifiedNamePart = Object; |
| |
| /** |
| * This is a name used in a type annotation context, such as 'Foo' in: |
| * var x : Foo; |
| * It is a Binding, as type annotations must always be resolved. |
| * A type_name also allows '*', which can't happen in a general expression context. |
| */ |
| ReturnType type_name = Object; |
| |
| /** |
| * This is a name used in a new context, such as 'Foo' in: |
| * new Foo(); |
| * We can do additional analysis when the new expression resolves to a Type, which |
| * is why we have a special name for the new expression. |
| */ |
| ReturnType new_type_name = Object; |
| |
| ReturnType dottedNamePart = String; |
| |
| ReturnType non_resolving_identifier = String; |
| |
| ReturnType runtime_name_expression = Object; |
| ReturnType multinameL = Object; |
| ReturnType e4x_literal = String; |
| |
| ReturnType integer_constant = Integer; |
| ReturnType uint_constant = Long; |
| ReturnType double_constant = Double; |
| ReturnType string_constant = String; |
| ReturnType boolean_constant = Boolean; |
| ReturnType float_constant = Float; |
| |
| ReturnType numeric_constant = Number; |
| |
| ReturnType constant_value = Object; |
| |
| /** |
| * Value to use to indicate a cost that is not feasible |
| */ |
| JBurg.Constant ERROR_TRAP = 268435456; |
| |
| { |
| final static boolean NEED_VALUE = true; |
| final static boolean DISCARD_VALUE = false; |
| |
| |
| /** |
| * The reducer has all the implementation |
| * logic for the rewrite; the BURM constructed |
| * by this specification drives that logic. |
| */ |
| AS3DebuggerReducer reducer; |
| |
| |
| /* |
| * ** Cost functions ** |
| * Cost functions are Java code that returns an int value. |
| * The value is used to compute the cost of a particular |
| * candidate reduction. The BURM searches for the lowest |
| * total cost sequence of reductions to transform the input |
| * AST into a value, so low values mean "good cost," higher |
| * values mean "less desirable." The total cost of a reduction |
| * is always strictly less than Integer.MAX_VALUE; a cost of |
| * Integer.MAX_VALUE tells the pattern matcher that the |
| * reduction is not feasible. |
| */ |
| |
| |
| /** |
| * @return "feasible" if the reducer can reduce this to a dotted name. |
| */ |
| int isDottedName(IASNode iNode) |
| { |
| return reducer.isDottedName(iNode); |
| } |
| |
| /** |
| * @return "feasible" if the reducer can reduce this to a package name. |
| */ |
| int isPackageName(IASNode iNode) |
| { |
| return reducer.isPackageName(iNode); |
| } |
| |
| /** |
| * @return "feasible" if this node's qualifier is a compile-time constant. |
| */ |
| int qualifierIsCompileTimeConstant(IASNode iNode) |
| { |
| return reducer.qualifierIsCompileTimeConstant(iNode); |
| } |
| |
| /** |
| * @return "feasible" if this node can be resolved to a compile-time constant. |
| */ |
| int isCompileTimeConstant(IASNode iNode) |
| { |
| return reducer.isCompileTimeConstant(iNode); |
| } |
| |
| /** |
| * @return "feasible" if this function call node can be resolved to a compile-time constant function. |
| */ |
| int isCompileTimeConstantFunction(IASNode iNode) |
| { |
| return reducer.isCompileTimeConstantFunction(iNode); |
| } |
| |
| /** |
| * @return "feasible" if this binary node has at least one expression that is |
| * of type "float". |
| */ |
| int isFloatBinOp(IASNode iNode) |
| { |
| return reducer.isFloatBinOp(iNode); |
| } |
| |
| /** |
| * @return "feasible" if this node is for 'new Array()'. |
| */ |
| int isEmptyArrayConstructor(IASNode iNode) |
| { |
| return reducer.isEmptyArrayConstructor(iNode); |
| } |
| |
| /** |
| * @return "feasible" if this node is for 'new Object()'. |
| */ |
| int isEmptyObjectConstructor(IASNode iNode) |
| { |
| return reducer.isEmptyObjectConstructor(iNode); |
| } |
| |
| /** |
| * @return "feasible" if this node resolves to a type definition. |
| */ |
| int isKnownType(IASNode iNode) |
| { |
| return reducer.isKnownType(iNode); |
| } |
| |
| /** |
| * @return "feasible" if the type parameter resolves to a type definition. |
| */ |
| int parameterTypeIsConstant(IASNode iNode) |
| { |
| return reducer.parameterTypeIsConstant(iNode); |
| } |
| |
| /** |
| * recordError is a convenience method for error reductions; |
| * it adds a problem to the current set of problems and |
| */ |
| Object recordError(ICompilerProblem problem) |
| { |
| return new Object(); |
| } |
| |
| int isIntLiteral(IASNode iNode) |
| { |
| return reducer.isIntLiteral(iNode); |
| } |
| |
| int isUintLiteral(IASNode iNode) |
| { |
| return reducer.isUintLiteral(iNode); |
| } |
| |
| int isDoubleLiteral(IASNode iNode) |
| { |
| return reducer.isDoubleLiteral(iNode); |
| } |
| |
| int isFloatLiteral(IASNode iNode) |
| { |
| return reducer.isDoubleLiteral(iNode); |
| } |
| |
| } |
| |
| /* |
| * Error recovery routine: deduce what we can from the problem |
| * tree, then abort this BURM with an exception that the caller |
| * can catch and ignore. |
| */ |
| DefaultErrorHandler |
| { |
| BURMAbortException.abort(); |
| } |
| |
| /* |
| * Patterns and rules are stored in their own, shareable file. |
| */ |
| JBurg.include "CmcPatterns.jbg" |
| |
| |
| /* |
| * This pattern is used by the debugger to call special functions |
| * like $obj() when handling the ‘print #ObjectRefNumber’ feature. |
| * I don’t think I would have a rule like this in the compiler as |
| * calling foo() where foo is just an identifier is probably likely |
| * and “foo” has to be resolved from an identifier to, in most cases, |
| * a MemberAccessExpression, but I don’t think folks print function |
| * calls in the debugger. And even if they do, there is a chance it |
| * will work anyway. |
| * |
| * If the debugger is missing the ability to print something, it may be |
| * that a new Pattern and Rule need to be added. First, set a breakpoint |
| * in DebuggerExpressionEvaluator at the call to burm.burm(). Examine |
| * the node to make sure it was parsed correctly into a tree of nodes. |
| * |
| * If the node tree is correct, step into the burm() call. You should |
| * see the code call label() and return a different tree of instances. |
| * For lots of things, there are custom classes like: |
| * JBurgAnnotation_FunctionCallID_2_n |
| * which represents a FunctionCallID in the AST. If the top node is an |
| * instance of JBurgAnnotationGeneral, that means that the BURM did not |
| * expect a node of that type. This may be true throughout the tree |
| * but I’ve seen that ContainerID, which represents parenthesis around |
| * parameters to a function also becomes a JBurgAnnotationGeneral because |
| * the BURM theoretically never examines that node in the tree, it examines |
| * its children. |
| * |
| * If a pattern is missing, add the pattern. A Pattern seems to be a node ID |
| * with a parameter list of the children node ID’s or other Patterns. The |
| * names you use in the parameter list will be used in the reduction function |
| * later. |
| * |
| * Then add a Rule for it in AS3DebuggerRules.jbg. |
| * |
| * It seems like the BURM |
| * operates on “levels” of constructs, the higher order construct being an |
| * “expression”. Expressions are composed of other expressions or lower-level |
| * constructs like name or constants (like uint_constant) or literals (like |
| * boolean_literal. It seems like, even if the string you want the debugger |
| * to print is just a string or number, it may get “reduced” by the BURM into |
| * higher-level things first. |
| * |
| * So, I think most new Rules for complex patterns will be an expression, |
| * but if it doesn’t recognize a constant of some sort you may need to |
| * add that. Anyway, you add an expression like the one in this change list |
| * and then assign a value (or cost) for the rule. Lower numbers |
| * (but greater than 0) win. The actual cost is computed by factoring in the |
| * cost of reducing the children. |
| * |
| * On the next line below the rule and its cost, add, for most things, a |
| * JBurg.reduction method call that you will write to do the reduction. Pass |
| * in the node (__p) and any of the parameters from the Pattern. |
| * |
| * Next, in AS3DebuggerReducer.java, add that reduction method and fill |
| * in the method body. Try to look for similar reductions and copy what |
| * they do. For the debugger, you eventually want to return a DebuggerValue. |
| * DebuggerValues seem to be what expressions reduce to and you’ll find they |
| * get passed into the reduction method as well. |
| * |
| */ |
| // $obj() |
| Pattern functionCallSpecial |
| FunctionCallID(IdentifierID(void) specialName, ContainerID(expression args*)); |
| |
| |
| JBurg.include "AS3DebuggerRules.jbg" |