| /* |
| * |
| * 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.*; |
| |
| import java.util.ArrayList; |
| import java.util.Calendar; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.text.SimpleDateFormat; |
| import java.util.TreeSet; |
| import java.util.Vector; |
| |
| import org.apache.flex.abc.ABCConstants; |
| import org.apache.flex.abc.instructionlist.InstructionList; |
| import org.apache.flex.abc.semantics.ECMASupport; |
| import org.apache.flex.abc.semantics.Instruction; |
| 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.semantics.Namespace; |
| import org.apache.flex.abc.semantics.Nsset; |
| import org.apache.flex.abc.semantics.PooledValue; |
| import org.apache.flex.abc.visitors.IMethodBodyVisitor; |
| import org.apache.flex.abc.visitors.IMethodVisitor; |
| import org.apache.flex.abc.visitors.ITraitsVisitor; |
| import org.apache.flex.compiler.common.ASModifier; |
| import org.apache.flex.compiler.common.DependencyType; |
| import org.apache.flex.compiler.constants.IASLanguageConstants; |
| import org.apache.flex.compiler.constants.IASLanguageConstants.BuiltinType; |
| import org.apache.flex.compiler.definitions.IAccessorDefinition; |
| import org.apache.flex.compiler.definitions.IAppliedVectorDefinition; |
| import org.apache.flex.compiler.definitions.IClassDefinition; |
| import org.apache.flex.compiler.definitions.IDefinition; |
| import org.apache.flex.compiler.definitions.IFunctionDefinition; |
| import org.apache.flex.compiler.definitions.IGetterDefinition; |
| import org.apache.flex.compiler.definitions.IInterfaceDefinition; |
| import org.apache.flex.compiler.definitions.INamespaceDefinition; |
| import org.apache.flex.compiler.definitions.IParameterDefinition; |
| import org.apache.flex.compiler.definitions.ISetterDefinition; |
| import org.apache.flex.compiler.definitions.ITypeDefinition; |
| import org.apache.flex.compiler.definitions.IVariableDefinition; |
| import org.apache.flex.compiler.definitions.metadata.IMetaTag; |
| import org.apache.flex.compiler.definitions.references.INamespaceReference; |
| import org.apache.flex.compiler.definitions.references.IReference; |
| import org.apache.flex.compiler.exceptions.DuplicateLabelException; |
| import org.apache.flex.compiler.filespecs.IFileSpecification; |
| import org.apache.flex.compiler.internal.definitions.AmbiguousDefinition; |
| import org.apache.flex.compiler.internal.definitions.AppliedVectorDefinition; |
| import org.apache.flex.compiler.internal.definitions.ClassDefinition; |
| import org.apache.flex.compiler.internal.definitions.DefinitionBase; |
| import org.apache.flex.compiler.internal.definitions.FunctionDefinition; |
| import org.apache.flex.compiler.internal.definitions.GetterDefinition; |
| import org.apache.flex.compiler.internal.definitions.InterfaceDefinition; |
| import org.apache.flex.compiler.internal.definitions.MemberedDefinition; |
| import org.apache.flex.compiler.internal.definitions.NamespaceDefinition; |
| import org.apache.flex.compiler.internal.definitions.PackageDefinition; |
| import org.apache.flex.compiler.internal.definitions.ParameterDefinition; |
| import org.apache.flex.compiler.internal.definitions.SetterDefinition; |
| import org.apache.flex.compiler.internal.definitions.TypeDefinitionBase; |
| import org.apache.flex.compiler.internal.definitions.VariableDefinition; |
| import org.apache.flex.compiler.internal.legacy.ASDefinitionFilter; |
| import org.apache.flex.compiler.internal.legacy.ASDefinitionFilter.AccessValue; |
| import org.apache.flex.compiler.internal.legacy.ASDefinitionFilter.ClassificationValue; |
| import org.apache.flex.compiler.internal.legacy.ASDefinitionFilter.SearchScopeValue; |
| import org.apache.flex.compiler.internal.legacy.ASScopeUtils; |
| import org.apache.flex.compiler.internal.legacy.MemberedDefinitionUtils; |
| import org.apache.flex.compiler.internal.projects.CompilerProject; |
| import org.apache.flex.compiler.internal.scopes.ASFileScope; |
| import org.apache.flex.compiler.internal.scopes.ASProjectScope; |
| import org.apache.flex.compiler.internal.scopes.ASScope; |
| import org.apache.flex.compiler.internal.scopes.ASScopeBase; |
| import org.apache.flex.compiler.internal.scopes.CatchScope; |
| import org.apache.flex.compiler.internal.semantics.SemanticUtils; |
| import org.apache.flex.compiler.internal.tree.as.BaseDefinitionNode; |
| import org.apache.flex.compiler.internal.tree.as.BaseVariableNode; |
| import org.apache.flex.compiler.internal.tree.as.ExpressionNodeBase; |
| import org.apache.flex.compiler.internal.tree.as.ForLoopNode; |
| import org.apache.flex.compiler.internal.tree.as.FunctionCallNode; |
| import org.apache.flex.compiler.internal.tree.as.FunctionNode; |
| import org.apache.flex.compiler.internal.tree.as.FunctionObjectNode; |
| import org.apache.flex.compiler.internal.tree.as.IdentifierNode; |
| import org.apache.flex.compiler.internal.tree.as.LabeledStatementNode; |
| import org.apache.flex.compiler.internal.tree.as.MemberAccessExpressionNode; |
| import org.apache.flex.compiler.internal.tree.as.NamespaceNode; |
| import org.apache.flex.compiler.internal.tree.as.NodeBase; |
| import org.apache.flex.compiler.internal.tree.as.ParameterNode; |
| import org.apache.flex.compiler.internal.tree.as.SwitchNode; |
| import org.apache.flex.compiler.internal.tree.as.VariableExpressionNode; |
| import org.apache.flex.compiler.internal.tree.as.VariableNode; |
| import org.apache.flex.compiler.problems.CodegenInternalProblem; |
| import org.apache.flex.compiler.problems.CodegenProblem; |
| import org.apache.flex.compiler.problems.CompilerProblem; |
| import org.apache.flex.compiler.problems.DuplicateLabelProblem; |
| import org.apache.flex.compiler.problems.DuplicateNamespaceDefinitionProblem; |
| import org.apache.flex.compiler.problems.ICompilerProblem; |
| import org.apache.flex.compiler.problems.InvalidLvalueProblem; |
| import org.apache.flex.compiler.problems.InvalidOverrideProblem; |
| import org.apache.flex.compiler.problems.PackageCannotBeUsedAsValueProblem; |
| import org.apache.flex.compiler.problems.UnknownNamespaceProblem; |
| import org.apache.flex.compiler.problems.VoidTypeProblem; |
| import org.apache.flex.compiler.projects.ICompilerProject; |
| import org.apache.flex.compiler.scopes.IASScope; |
| import org.apache.flex.compiler.tree.ASTNodeID; |
| import org.apache.flex.compiler.tree.as.IASNode; |
| import org.apache.flex.compiler.tree.as.IDynamicAccessNode; |
| import org.apache.flex.compiler.tree.as.IBinaryOperatorNode; |
| import org.apache.flex.compiler.tree.as.ICatchNode; |
| import org.apache.flex.compiler.tree.as.IClassNode; |
| import org.apache.flex.compiler.tree.as.IExpressionNode; |
| import org.apache.flex.compiler.tree.as.IForLoopNode; |
| import org.apache.flex.compiler.tree.as.IFunctionCallNode; |
| import org.apache.flex.compiler.tree.as.IFunctionNode; |
| import org.apache.flex.compiler.tree.as.IIdentifierNode; |
| import org.apache.flex.compiler.tree.as.IImportNode; |
| import org.apache.flex.compiler.tree.as.ILiteralNode; |
| import org.apache.flex.compiler.tree.as.IMemberAccessExpressionNode; |
| import org.apache.flex.compiler.tree.as.INumericLiteralNode; |
| import org.apache.flex.compiler.tree.as.IObjectLiteralValuePairNode; |
| import org.apache.flex.compiler.tree.as.IParameterNode; |
| import org.apache.flex.compiler.tree.as.IRegExpLiteralNode; |
| import org.apache.flex.compiler.tree.as.IScopedNode; |
| import org.apache.flex.compiler.tree.as.ITryNode; |
| import org.apache.flex.compiler.tree.as.IUnaryOperatorNode; |
| import org.apache.flex.compiler.tree.as.IVariableNode; |
| import org.apache.flex.compiler.tree.as.IWhileLoopNode; |
| import org.apache.flex.compiler.tree.as.IWithNode; |
| import org.apache.flex.compiler.tree.mxml.IMXMLEventSpecifierNode; |
| import org.apache.flex.compiler.units.ICompilationUnit; |
| import org.apache.flex.compiler.units.ICompilationUnit.Operation; |
| |
| /** |
| * JSGeneratingReducer is modeled after from ABCGeneratingReducer and called by |
| * the generated CmcJSEmitter (see cmc-js.jbg). This is the "meat" of FalconJS. |
| * This implementation is part of FalconJS. For more details on FalconJS see |
| * org.apache.flex.compiler.JSDriver |
| */ |
| @SuppressWarnings("nls") |
| public class JSGeneratingReducer |
| { |
| /** |
| * A very high cost, used to trap errors but otherwise well outside normal |
| * bounds. |
| */ |
| static final int ERROR_TRAP = 268435456; |
| |
| private static final Name NAME_Vector = new Name(IASLanguageConstants.Vector); |
| |
| /** |
| * A struct for the decoded pieces of a catch block. |
| */ |
| public class CatchPrototype |
| { |
| Name catchType; |
| Name catchVarName; |
| String catchBody; |
| } |
| |
| /** |
| * ConditionalFragment holds the disparate elements of a conditional |
| * statement fragment. |
| */ |
| public class ConditionalFragment |
| { |
| IASNode site; |
| String condition; |
| String statement; |
| |
| // private Label statementLabel = null; |
| |
| /** |
| * Construct a ConditionalFragment with a single statement. |
| */ |
| ConditionalFragment(IASNode site, String condition, String statement) |
| { |
| this.site = site; |
| this.condition = condition; |
| this.statement = statement; |
| } |
| |
| /** |
| * Construct a ConditionalFragment with a list of statements, coalescing |
| * them into a composite statement. |
| */ |
| ConditionalFragment(IASNode site, String condition, Vector<String> statements) |
| { |
| this.site = site; |
| this.condition = condition; |
| this.statement = createInstruction(site); |
| |
| for (String stmt : statements) |
| { |
| this.statement += stmt; |
| } |
| } |
| |
| /** |
| * @return the label of the fragment's condition, or its statement label |
| * if it's the default alternative. |
| */ |
| /* |
| * Label getLabel() { if ( !this.isUnconditionalAlternative() ) return |
| * this.condition.getLabel(); else return getStatementLabel(); } |
| */ |
| |
| /** |
| * Cache and return the label of this conditional fragment's statement |
| * so it can be retrieved after the statement's been added to the |
| * overall conditional statement and this.statement is invalidated. |
| */ |
| /* |
| * Label getStatementLabel() { if ( null == this.statementLabel ) |
| * this.statementLabel = this.statement.getLabel(); return |
| * this.statementLabel; } |
| */ |
| |
| /** |
| * @return true if this is an unconditional alternative, i.e. a default |
| * case or the last else in an if/else if/else. |
| */ |
| boolean isUnconditionalAlternative() |
| { |
| return null == this.condition; |
| } |
| } |
| |
| /** |
| * The RuntimeMultiname class holds the components of the various types of |
| * runtime multiname, and puts them together in the right order to generate |
| * the names and instruction sequences to jamble up the AVM. Only some |
| * permutations of the compile-time/runtime qualifier::name possibilities |
| * are valid: Name::expression generates MultinameL expression::expression |
| * generates RTQnameL expression::name generates RTQname (name::name is a |
| * plain Multiname) |
| */ |
| public class RuntimeMultiname |
| { |
| /** |
| * The compile-time qualifier of a MultinameL. compileTimeName isn't |
| * valid (this would be a compile-time constant Multiname). |
| */ |
| Name compileTimeQualifier; |
| |
| /** |
| * The runtime qualifier of a RTQname or RTQNameL. Compile-time and |
| * runtime names are valid. |
| */ |
| String runtimeQualifier; |
| |
| /** |
| * The compile-time constant name of a RTQName. compileTimeQualifier |
| * isn't valid (this would be a compile-time constant Multiname). |
| */ |
| Name compileTimeName; |
| |
| /** |
| * The runtime name of a MultinameL or RTQnameL. |
| */ |
| String runtimeName; |
| |
| /** |
| * Construct a RTQnameL-type runtime name. |
| * |
| * @param qualifier - the runtime qualifier. |
| * @param name - the runtime name. |
| */ |
| RuntimeMultiname(String qualifier, String runtime_name) |
| { |
| this.runtimeQualifier = qualifier; |
| this.runtimeName = runtime_name; |
| } |
| |
| /** |
| * Construct a RTQname-type runtime name. |
| * |
| * @param qualifier - the runtime qualifier. |
| * @param name - the compile-time name. |
| */ |
| RuntimeMultiname(String qualifier, Name name) |
| { |
| this.runtimeQualifier = qualifier; |
| this.compileTimeName = name; |
| } |
| |
| /** |
| * Construct a MultinameL-type runtime name. |
| * |
| * @param qualifier - the Multiname. Note this is the only kind of |
| * runtime name that receives a qualifying name. |
| * @param nameL - the runtime expression that yields the base name. |
| */ |
| RuntimeMultiname(Name qualifier, String nameL) |
| { |
| assert (qualifier.getKind() == CONSTANT_MultinameL || qualifier.getKind() == CONSTANT_MultinameLA) : "Bad qualifier kind " + qualifier.getKind() + " on name " + qualifier.toString(); |
| this.compileTimeQualifier = qualifier; |
| this.runtimeName = nameL; |
| } |
| |
| /** |
| * Assemble the ingredients into an expression. |
| * |
| * @param opcode - one of OP_getproperty or OP_setproperty. |
| * @param rhs - the value of a setproperty call. Passed to this routine |
| * because there may be name-specific instructions before and after the |
| * value. |
| */ |
| private String generateGetOrSet(IASNode iNode, int opcode, String rhs) |
| { |
| // TODO: Optimize InstructionList size. |
| String result = createInstruction(iNode); |
| |
| // Note: numerous microoptimization opportunities here |
| // to avoid storing values in temps by creative use of |
| // OP_dup and OP_swap, especially in getproperty gen. |
| |
| if (this.compileTimeQualifier != null && this.runtimeName != null) |
| { |
| result += runtimeName; |
| if (rhs != null) |
| result += rhs; |
| |
| /* |
| * // Generate MultinameL type code. Binding name_temp = |
| * currentScope.allocateTemp(); result.addAll(this.runtimeName); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(name_temp.setlocal()); // |
| * findprop(MultinameL) consumes a name from the value stack. |
| * result.addInstruction(OP_findpropstrict, |
| * this.compileTimeQualifier); |
| * result.addInstruction(name_temp.getlocal()); if ( rhs != null |
| * ) result.addAll(rhs); // get/setprop(MultinameL) consumes a |
| * name from the value stack. result.addInstruction(opcode, |
| * this.compileTimeQualifier); |
| */ |
| |
| } |
| else if (this.runtimeQualifier != null && this.runtimeName != null) |
| { |
| /* |
| * // Generate RTQnameL type code. Binding name_temp = |
| * currentScope.allocateTemp(); Binding qual_temp = |
| * currentScope.allocateTemp(); Name rtqnl = new |
| * Name(CONSTANT_RTQnameL, null, null); |
| * result.addAll(getRuntimeName(iNode)); |
| * result.addInstruction(name_temp.setlocal()); |
| * result.addAll(getRuntimeQualifier(iNode)); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(qual_temp.setlocal()); |
| * result.addInstruction(name_temp.getlocal()); // |
| * findprop(RTQNameL) consumes namespace and name from the value |
| * stack. result.addInstruction(OP_findpropstrict, rtqnl); |
| * result.addInstruction(qual_temp.getlocal()); |
| * result.addInstruction(name_temp.getlocal()); if ( rhs != null |
| * ) result.addAll(rhs); // get/setprop(RTQNameL) consumes |
| * namespace and name from the value stack. |
| * result.addInstruction(opcode, rtqnl); |
| * result.addAll(currentScope.releaseTemp(name_temp)); |
| */ |
| |
| result += getRuntimeName(iNode); |
| if (rhs != null) |
| result += rhs; |
| } |
| else |
| { |
| // Last valid combination generates a RTQname. |
| assert (this.runtimeQualifier != null && this.compileTimeName != null) : "Unknown runtime name configuration: " + this.toString(); |
| |
| result += getRuntimeName(iNode); |
| if (rhs != null) |
| result += rhs; |
| |
| /* |
| * Name rtqn = new Name(CONSTANT_RTQname, null, |
| * this.compileTimeName.getBaseName()); |
| * result.addAll(getRuntimeQualifier(iNode)); |
| * result.addInstruction(OP_dup); // findprop(RTQName) consumes |
| * a namespace from the value stack. |
| * result.addInstruction(OP_findpropstrict, rtqn); |
| * result.addInstruction(OP_swap); if ( rhs != null ) |
| * result.addAll(rhs); // get/setprop(RTQName) consumes a |
| * namespace from the value stack. result.addInstruction(opcode, |
| * rtqn); |
| */ |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Generate the InstructionList that expresses this RuntimeName's |
| * qualifier. |
| * |
| * @param iNode - an IASNode to contribute debug info to the result |
| * InstructionList. |
| * @return the runtime qualifier setup instructions. |
| */ |
| public String getRuntimeQualifier(IASNode iNode) |
| { |
| assert (hasRuntimeQualifier()); |
| String result = createInstruction(iNode); |
| |
| result += this.runtimeQualifier; |
| |
| /* |
| * // Ensure the last instruction is an OP_coerce to Namespace. |
| * Instruction last = result.lastElement(); if ( last.getOpcode() != |
| * OP_coerce || last.getOperandCount() == 0 || ! |
| * namespaceType.equals(last.getOperand(0)) ) { |
| * result.addInstruction(OP_coerce, namespaceType); } |
| */ |
| return result; |
| } |
| |
| /** |
| * @return true if this RuntimeName has a runtime qualifier. |
| */ |
| public boolean hasRuntimeQualifier() |
| { |
| return this.runtimeQualifier != null; |
| } |
| |
| /** |
| * @return true if this RuntimeName has a runtime name. |
| */ |
| public boolean hasRuntimeName() |
| { |
| return this.runtimeName != null; |
| } |
| |
| /** |
| * Generate the InstructionList that expresses this RuntimeName's name. |
| * |
| * @param iNode - an IASNode to contribute debug info to the result |
| * InstructionList. |
| * @return the runtime name setup instructions. |
| */ |
| public String getRuntimeName(IASNode iNode) |
| { |
| assert (hasRuntimeName()); |
| |
| String result = createInstructionList(iNode); |
| |
| /* |
| * result.addAll(replicate(this.runtimeName)); if ( |
| * result.lastElement().getOpcode() != OP_coerce_s) |
| * result.addInstruction(OP_coerce_s); |
| */ |
| |
| result += this.runtimeName; |
| |
| return result; |
| } |
| |
| /** |
| * Generate the runtime name appropriate to this qualifier/name |
| * combination. |
| * |
| * @return the runtime name appropriate to this qualifier/name |
| * combination. |
| */ |
| public Name generateName() |
| { |
| Name result; |
| |
| if (this.compileTimeQualifier != null) |
| { |
| result = this.compileTimeQualifier; |
| } |
| else if (this.runtimeQualifier != null && this.runtimeName != null) |
| { |
| result = new Name(CONSTANT_RTQnameL, null, null); |
| } |
| else |
| { |
| // Last valid combination generates a RTQname. |
| assert (this.runtimeQualifier != null && this.compileTimeName != null) : "Unknown runtime name configuration: " + this.toString(); |
| |
| result = new Name(CONSTANT_RTQname, null, this.compileTimeName.getBaseName()); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Convenience method generates an assignment |
| * |
| * @param value - the value to set. |
| * @return the instruction sequence to set a the given multiname's |
| * value. |
| */ |
| String generateAssignment(IASNode iNode, String value) |
| { |
| return generateGetOrSet(iNode, OP_setproperty, value); |
| } |
| |
| /** |
| * Convenience method generates an r-value. |
| * |
| * @return the instruction sequence to look up the given multiname. |
| */ |
| String generateRvalue(IASNode iNode) |
| { |
| return generateGetOrSet(iNode, OP_getproperty, null); |
| } |
| |
| /** |
| * Diagnostic toString() method, used primarily to debug the assertion |
| * in generate(). |
| */ |
| @Override |
| public String toString() |
| { |
| return "\n\t" + compileTimeQualifier + ",\n\t" + runtimeQualifier + ",\n\t" + compileTimeName + ",\n\t" + runtimeName; |
| } |
| } |
| |
| /** |
| * The current LexicalScope. Set by the caller, changes during reduction |
| * when a scoped construct such as an embedded function or a with statement |
| * is encountered. |
| */ |
| private LexicalScope currentScope; |
| |
| /** |
| * Strings sent by the caller to be added to a function definition. Usually |
| * these are field initialization expressions in a constructor. |
| */ |
| // private String instanceInitializers; |
| |
| /** |
| * A shared name for the Namespace type. |
| */ |
| public static final Name namespaceType = new Name("Namespace"); |
| |
| /** |
| * A shared name for the XML type. |
| */ |
| public static final Name xmlType = new Name("XML"); |
| |
| /** |
| * A shared name for the XMLList type. |
| */ |
| public static final Name xmlListType = new Name("XMLList"); |
| |
| /** |
| * A shared name for the RegExp type. |
| */ |
| public static final Name regexType = new Name("RegExp"); |
| |
| /** |
| * A shared name for the void type. |
| */ |
| public static final Name voidType = new Name("void"); |
| |
| /** |
| * Generate code to push a numeric constant. |
| */ |
| public static void pushNumericConstant(long value, String result_list) |
| { |
| // do nothing |
| // result_list.pushNumericConstant(value); |
| } |
| |
| /** |
| * Active labeled control-flow region's substatements. Populated by the |
| * labeledStmt Prologue, and winnowed by the labeledStmt reduction's |
| * epilogue. |
| */ |
| private Set<IASNode> labeledNodes = new HashSet<IASNode>(); |
| |
| /** |
| * @return the currently active collection of problems. |
| */ |
| public Collection<ICompilerProblem> getProblems() |
| { |
| return currentScope.getProblems(); |
| } |
| |
| /** |
| * Generate a binary operator. |
| * |
| * @param l - the left-hand operand. |
| * @param r - the right-hand operand. |
| * @param opcode - the operator's opcode. |
| * @return the combined String sequence with the operator appended. |
| */ |
| String binaryOp(IASNode iNode, String l, String r, int opcode) |
| { |
| checkBinaryOp(iNode, opcode); |
| |
| switch (opcode) |
| { |
| // Binary logical operators. |
| case OP_istypelate: |
| return reduce_istypeExprLate(iNode, l, r); |
| case OP_astypelate: |
| return reduce_astypeExprLate(iNode, l, r); |
| case OP_instanceof: |
| return reduce_instanceofExpr(iNode, l, r); |
| case OP_in: |
| return reduce_inExpr(iNode, l, r); |
| } |
| |
| if (l == null) |
| l = "null"; |
| if (r == null) |
| r = "null"; |
| final String operator = opToString(iNode, opcode); |
| String result = createInstruction(iNode, l.length() + r.length() + 1); |
| result += binaryOp(l.toString(), r.toString(), operator); |
| return result; |
| |
| /* |
| * String result = createInstruction(iNode, l.size() + r.size() + 1); |
| * result.addAll(l); result.addAll(r); result.addInstruction(opcode); |
| * return result; |
| */ |
| } |
| |
| public void checkBinaryOp(IASNode iNode, int opcode) |
| { |
| currentScope.getMethodBodySemanticChecker().checkBinaryOperator(iNode, opcode); |
| } |
| |
| public String conditionalJump(IASNode iNode, String l, String r, int opcode) |
| { |
| return binaryOp(iNode, l, r, opcode); |
| } |
| |
| String binaryOp(String l, String r, String operator) |
| { |
| if (l == null) |
| l = "null"; |
| if (r == null) |
| r = "null"; |
| String result = "(" + stripTabs(stripNS(l.toString())) + " " + operator + " " + stripTabs(stripNS(r.toString())) + ")"; |
| return result; |
| } |
| |
| /** |
| * Resolve a dotted name, e.g., foo.bar.baz |
| */ |
| Binding dottedName(IASNode iNode, String qualifiers, String base_name) |
| { |
| if (iNode instanceof IdentifierNode) |
| { |
| return currentScope.resolveName((IdentifierNode)iNode); |
| } |
| else if (iNode instanceof ExpressionNodeBase) |
| { |
| ExpressionNodeBase expr = (ExpressionNodeBase)iNode; |
| ICompilerProject project = currentScope.getProject(); |
| Name n = expr.getMName(project); |
| |
| if (n == null) |
| { |
| currentScope.addProblem(new CodegenInternalProblem(iNode, "Unable to resove member name: " + iNode.toString())); |
| n = new Name(CONSTANT_Qname, new Nsset(new Namespace(CONSTANT_PackageNs, qualifiers)), base_name); |
| } |
| |
| return currentScope.getBinding(iNode, n, expr.resolve(project)); |
| } |
| |
| //else |
| currentScope.addProblem(new CodegenInternalProblem(iNode, "Unable to resove to a dotted name: " + iNode.toString())); |
| return new Binding(iNode, new Name(CONSTANT_Qname, new Nsset(new Namespace(CONSTANT_PackageNs, qualifiers)), base_name), null); |
| } |
| |
| /** |
| * Resolve a dotted name, e.g., foo.bar.baz, where the whole dotted name is |
| * a package this is an error, and a diagnostic will be emitted |
| */ |
| Binding errorPackageName(IASNode iNode, String qualifiers, String base_name) |
| { |
| currentScope.addProblem(new PackageCannotBeUsedAsValueProblem(iNode, qualifiers + "." + base_name)); |
| return new Binding(iNode, new Name(qualifiers + "." + base_name), null); |
| } |
| |
| /** |
| * Common routine used by reductions that need an empty list. |
| * |
| * @param iNode - the current AST node. |
| */ |
| public String createInstruction(IASNode iNode) |
| { |
| return createInstruction(iNode, 0); |
| } |
| |
| /** |
| * Common routine used by reductions that need an empty list. |
| * |
| * @param iNode - the current AST node. |
| * @param capacity - requested capacity of the new list. May be adjusted to |
| * accomodate debug Strings. |
| */ |
| public String createInstruction(IASNode iNode, int capacity) |
| { |
| String result = ""; |
| |
| String file_name = iNode.getSourcePath(); |
| |
| if (file_name == null) |
| { |
| // Fall back on the file specification. |
| // This may also be null (or assert if it's feeling moody), |
| // in which case the file name remains null and file/line |
| // processing noops. |
| try |
| { |
| IFileSpecification fs = iNode.getFileSpecification(); |
| if (fs != null) |
| file_name = fs.getPath(); |
| } |
| catch (Throwable no_fs) |
| { |
| // No file specification available, probably |
| // because this node is some kind of syntho. |
| } |
| } |
| |
| // Note: getLine() uses zero-based counting. |
| int line_num = iNode.getLine() + 1; |
| |
| // Adjust the capacity requirement if debug |
| // Strings are to be emitted. |
| if (currentScope.emitFile(file_name)) |
| capacity++; |
| if (currentScope.emitLine(line_num)) |
| capacity++; |
| |
| // If the required capacity is less than three |
| // Strings, the String can hold |
| // them organically. Specifying a capacity to |
| // the String ctor causes it to allocate |
| // a separate ArrayList. |
| if (capacity > 3) |
| result = new String(/* capacity */); |
| else |
| result = new String(); |
| |
| if (currentScope.emitFile(file_name)) |
| { |
| // result.addInstruction(OP_debugfile, file_name); |
| currentScope.setDebugFile(file_name); |
| } |
| |
| if (currentScope.emitLine(line_num)) |
| { |
| // result.addInstruction(OP_debugline, line_num); |
| currentScope.setDebugLine(line_num); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Error trap. |
| */ |
| public Binding error_namespaceAccess(IASNode iNode, IASNode raw_qualifier, Binding qualified_name) |
| { |
| String qualifier = ((IdentifierNode)raw_qualifier).getName(); |
| |
| // TODO: In some circumstances, the namespace qualifier |
| // may be an invalid attribute on a declaration. |
| currentScope.addProblem(new UnknownNamespaceProblem(raw_qualifier, qualifier)); |
| |
| return qualified_name; |
| } |
| |
| /** |
| * Error trap. |
| */ |
| public String error_reduce_Op_AssignId(IASNode iNode, String non_lvalue, String rvalue) |
| { |
| currentScope.addProblem(new InvalidLvalueProblem(iNode)); |
| return createInstruction(iNode); |
| } |
| |
| /** |
| * Generate access to a named entity. |
| * |
| * @param name - the entity's name. |
| * @return an instruction sequence to access the entity. |
| */ |
| String generateAccess(IASNode iNode, Binding name) |
| { |
| StringBuilder result = new StringBuilder(); |
| generateAccess(iNode, name, result); |
| return result.toString(); |
| } |
| |
| /** |
| * Generate access to a named entity. |
| * |
| * @param name - the entity's name. |
| * @param result - the instruction sequence to generate into. |
| */ |
| void generateAccess(IASNode iNode, Binding binding, StringBuilder result) |
| { |
| if (binding.isLocal()) |
| { |
| final String name = bindingToString(iNode, binding, false, false); |
| result.append(name); |
| } |
| else |
| { |
| assert (binding.getName() != null) : "non-local Binding " + binding + " must have a name"; |
| currentScope.getMethodBodySemanticChecker().checkGetProperty(binding); |
| // generateAccess( iNode, binding.getName(), result); |
| final String name = bindingToString(iNode, binding, true, true); |
| result.append(name); |
| } |
| } |
| |
| /** |
| * Generate access to a named entity. |
| * |
| * @param name - the entity's name. |
| * @param result - the instruction sequence to generate into. |
| */ |
| void generateAccess(IASNode iNode, Name name, StringBuilder result) |
| { |
| final String str = nameToString(iNode, name, true, true); |
| result.append(str); |
| |
| /* |
| * if ( name.isTypeName() ) { generateAccess(name.getTypeNameBase(), |
| * result); generateTypeNameParameter(name.getTypeNameParameter(), |
| * result); result.addInstruction(OP_applytype, 1); } else { |
| * result.addInstruction(OP_findpropstrict, name); |
| * result.addInstruction(OP_getproperty, name); } |
| */ |
| } |
| |
| /** |
| * Generate code to assign to a named entity. |
| * |
| * @param target - the entity to be assigned to. |
| * @param rvalue - the value to assign. |
| * @return an String sequence that stores the given rvalue in the target. |
| */ |
| String generateAssignment(IASNode iNode, Binding target, String rvalue) |
| { |
| return generateAssignment(iNode, target, rvalue, false); |
| } |
| |
| /** |
| * Generate code to assign to a named entity. |
| * |
| * @param target - the entity to be assigned to. |
| * @param rvalue - the value to assign. |
| * @param need_value - when true, leave a DUP of the rvalue on the stack. |
| * @return an String sequence that stores the given rvalue in the target, |
| * and leaves a DUP of the rvalue on the stack if need_value is set. |
| */ |
| String generateAssignment(IASNode iNode, Binding target, String rvalue, boolean need_value) |
| { |
| String result = createInstruction(iNode, rvalue.length() + 4); |
| |
| result += rvalue; |
| |
| /* |
| * result.addAll(rvalue); if ( need_value ) |
| * result.addInstruction(OP_dup); if ( target.isLocal() ) { if ( |
| * target.getDefinition() != null ) { ITypeDefinition type = |
| * target.getDefinition |
| * ().resolveType(currentScope.getDefinitionCache()); if ( type != null |
| * && type != ClassDefinition.getAnyTypeClassDefinition() ) { |
| * result.addInstruction(OP_coerce, |
| * ((DefinitionBase)type).getMName(currentScope.getProject())); } } |
| * result.addInstruction(target.setlocal()); } else { |
| * result.addInstruction(OP_findproperty, target.getName()); |
| * result.addInstruction(OP_swap); result.addInstruction(OP_setproperty, |
| * target.getName()); } |
| */ |
| return result; |
| } |
| |
| /** |
| * Generate a catch block. |
| * |
| * @param catch_proto - the catch block's prototype. |
| */ |
| /* |
| * String generateCatchBlock( Label try_start, Label try_end, CatchPrototype |
| * catch_proto) { String scope_reinit = |
| * currentScope.getFlowManager().getScopeStackReinit(); String current_catch |
| * = new String(catch_proto.catchBody.size() + scope_reinit.size() + 15); // |
| * Common prologue code. current_catch.addInstruction(OP_getlocal0); |
| * current_catch.addInstruction(OP_pushscope); if( |
| * currentScope.needsActivation() ) { // Restore the activation object. |
| * current_catch |
| * .addInstruction(currentScope.getActivationStorage().getlocal()); |
| * current_catch.addInstruction(OP_pushscope); } // Re-establish enclosing |
| * exception scopes. current_catch.addAll(scope_reinit); int handler_number |
| * = currentScope.getMethodBodyVisitor().visitException(try_start, try_end, |
| * current_catch.getLabel(), catch_proto.catchType, |
| * catch_proto.catchVarName); Binding exception_storage = |
| * currentScope.getFlowManager().getFinallyContext().getExceptionStorage(); |
| * current_catch.addInstruction(OP_newcatch, handler_number); |
| * current_catch.addInstruction(OP_dup); if ( exception_storage != null ) { |
| * current_catch.addInstruction(exception_storage.setlocal()); |
| * current_catch.addInstruction(OP_dup); } |
| * current_catch.addInstruction(OP_pushscope); |
| * current_catch.addInstruction(OP_swap); |
| * current_catch.addInstruction(OP_setslot, 1); |
| * current_catch.addAll(catch_proto.catchBody); if ( |
| * current_catch.canFallThrough() || current_catch.hasPendingLabels() ) { |
| * current_catch.addInstruction(OP_popscope); } return current_catch; } |
| */ |
| |
| /** |
| * Generate a compound assignment statement. |
| */ |
| String generateCompoundAssignment(IASNode iNode, Binding operand, String expr, int opcode, boolean need_value) |
| { |
| String result = createInstruction(iNode, expr.length() + (operand.isLocal() ? 4 : 8)); |
| /* |
| * if ( operand.isLocal() ) { result.addInstruction(operand.getlocal()); |
| * result.addAll(expr); result.addInstruction(opcode); if ( need_value ) |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(operand.setlocal()); } else { Binding |
| * value_temp = null; result.addInstruction(OP_findpropstrict, |
| * operand.getName()); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_getproperty, operand.getName()); |
| * result.addAll(expr); result.addInstruction(opcode); if ( need_value ) |
| * { value_temp = currentScope.allocateTemp(); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(value_temp.setlocal()); } |
| * result.addInstruction(OP_setproperty, operand.getName()); if ( |
| * need_value ) { result.addInstruction(value_temp.getlocal()); |
| * result.addInstruction(value_temp.kill()); } } |
| */ |
| final String op = opToString(iNode, opcode); |
| final String methodName = bindingToString(iNode, operand, true, true); |
| if (methodName.contains(".")) |
| { |
| final String stem = methodName.substring(0, methodName.lastIndexOf(".")); |
| final String member = getBasenameFromBinding(operand); |
| result += resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, member), op + "=", expr, false, true); |
| } |
| else |
| { |
| result += binaryOp(methodName, expr, op + "="); |
| } |
| |
| if (need_value) |
| result = "(" + result + ")"; |
| else |
| result += ";" + endl(); |
| |
| return result; |
| } |
| |
| String generateCompoundBracketAssignment(IASNode iNode, String stem, String index, String expr, int opcode, boolean need_value) |
| { |
| String result = createInstruction(iNode, stem.length() * 2 + index.length() + expr.length() + 11); |
| |
| /* |
| * // Although ASC evaluates the stem twice, it only evaluates the index |
| * once. // TODO: Inspect the index expression for side effects so the |
| * temp can be // elided in most cases. Binding index_temp = |
| * currentScope.allocateTemp(); result.addAll(index); |
| * result.addInstruction(index_temp.setlocal()); |
| * result.addAll(replicate(stem)); |
| * result.addInstruction(index_temp.getlocal()); |
| * result.addInstruction(OP_getproperty); result.addAll(expr); |
| * result.addInstruction(opcode); Binding value_temp = |
| * currentScope.allocateTemp(); |
| * result.addInstruction(value_temp.setlocal()); result.addAll(stem); |
| * result.addInstruction(index_temp.getlocal()); |
| * result.addInstruction(value_temp.getlocal()); |
| * result.addInstruction(OP_setproperty); if ( need_value ) |
| * result.addInstruction(value_temp.getlocal()); |
| * result.addAll(currentScope.releaseTemp(value_temp)); |
| * result.addAll(currentScope.releaseTemp(index_temp)); |
| */ |
| |
| // String result = binaryOp(stem + "[" + index + "]", expr, opcode + "="); |
| final String op = opToString(iNode, opcode); |
| result += resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, index), op + "=", expr, true, true); |
| if (need_value) |
| result = "(" + result + ")"; |
| else |
| result += ";" + endl(); |
| return result; |
| } |
| |
| String generateCompoundMemberAssignment(IASNode iNode, String stem, Binding member, String expr, int fetch_opcode, int assign_opcode, boolean need_value) |
| { |
| String result = createInstruction(iNode, stem.length() * 2 + expr.length() + 5); |
| |
| /* |
| * // TODO: Depending on the resolution of ASC-4159 and // the |
| * corresponding Falcon backwards compatibility // issue, cache the stem |
| * expression in a local to avoid // multiple evaluations. |
| * result.addAll(replicate(stem)); result.addInstruction(fetch_opcode, |
| * member.getName()); result.addAll(expr); |
| * result.addInstruction(assign_opcode); if ( need_value ) { |
| * result.addInstruction(OP_dup); } result.addAll(stem); |
| * result.addInstruction(OP_swap); result.addInstruction(OP_setproperty, |
| * member.getName()); |
| */ |
| |
| // String result = binaryOp(stem + "." + getBasenameFromBinding( member ), expr, opcode + "="); |
| final String op = opToString(iNode, assign_opcode); |
| result += resolveSetterName(iNode, stem, member, op + "=", expr, false, true); |
| if (need_value) |
| result = "(" + result + ")"; |
| else |
| result += ";" + endl(); |
| return result; |
| } |
| |
| String generateCompoundAssignmentToRuntimeName(IASNode iNode, RuntimeMultiname name, String expr, int opcode, boolean need_value) |
| { |
| String rhs = opToString(iNode, opcode) + "= " + expr; |
| |
| return name.generateAssignment(iNode, rhs); |
| } |
| |
| /** |
| * Generate a compound logical assignment expression to a named lvalue. |
| * |
| * @param iNode - the assignment operator (root of the subtree). |
| * @param lvalue - the lvalue's name. |
| * @param expr - the expression to assign. |
| * @param is_and - true if the expression is &&=, false if it's ||=. |
| * @param need_value - true if the expression's not used in a void context. |
| */ |
| String generateCompoundLogicalAssignment(IASNode iNode, Binding lvalue, String expr, boolean is_and, boolean need_value) |
| { |
| String result = createInstructionList(iNode); |
| |
| /* |
| * Label tail = new Label(); int failure_test = is_and? OP_iffalse : |
| * OP_iftrue; if ( lvalue.isLocal() ) { // Fetch and test the current |
| * value. result.addInstruction(lvalue.getlocal()); // The current value |
| * may not be the result value, // but for now assume it is. if ( |
| * need_value ) result.addInstruction( OP_dup); result.addInstruction( |
| * failure_test, tail ); // Test succeeded: reset the value, but first |
| * // pop the value speculatively dup'd above. if ( need_value ) |
| * result.addInstruction(OP_pop); result.addAll(expr); if ( need_value ) |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(lvalue.setlocal()); } else { // Fetch, |
| * speculatively dup, and test the current value. |
| * result.addInstruction(OP_findpropstrict, lvalue.getName()); |
| * result.addInstruction(OP_getproperty, lvalue.getName()); if ( |
| * need_value ) result.addInstruction(OP_dup); |
| * result.addInstruction(failure_test, tail); if ( need_value ) |
| * result.addInstruction(OP_pop); result.addAll(expr); if ( need_value ) |
| * { result.addInstruction(OP_dup); } |
| * result.addInstruction(OP_findpropstrict, lvalue.getName()); |
| * result.addInstruction(OP_swap); result.addInstruction(OP_setproperty, |
| * lvalue.getName()); } result.labelNext(tail); |
| */ |
| |
| final String lvalStr = bindingToString(iNode, lvalue, true, true); |
| result += lvalStr + " = (" + lvalStr + ") "; |
| if (is_and) |
| result += "&& "; |
| else |
| result += "|| "; |
| result += "(" + expr + ");\n"; |
| |
| return result; |
| } |
| |
| /** |
| * Generate compound logical assignment to a runtime name, e.g., n::x ||= |
| * foo; |
| * |
| * @param iNode - the root of the assignment subtree. |
| * @param name - the runtime name. |
| * @param expr - the second operand of the implied binary expression. |
| * @param is_and - true if the result is set to the second operand iff the |
| * first operand is true. |
| * @param need_value - true if the value of the assignment is required. |
| */ |
| String generateCompoundLogicalRuntimeNameAssignment(IASNode iNode, RuntimeMultiname name, String expr, boolean is_and, boolean need_value) |
| { |
| return (is_and) ? |
| String.format("%s &&= %s", name.generateRvalue(iNode), expr) |
| : |
| String.format("%s ||= %s", name.generateRvalue(iNode), expr); |
| } |
| |
| /** |
| * Generate a compound logical assignment expression to a a[i] type lvalue. |
| * |
| * @param iNode - the assignment operator (root of the subtree). |
| * @param stem - the expression that generates the lvalue's stem, e.g., a in |
| * a[i] |
| * @param index - the index expression. |
| * @param expr - the expression to assign. |
| * @param is_and - true if the expression is &&=, false if it's ||=. |
| * @param need_value - true if the expression's not used in a void context. |
| */ |
| String generateCompoundLogicalBracketAssignment(IASNode iNode, String stem, String index, String expr, boolean is_and, boolean need_value) |
| { |
| String result = createInstructionList(iNode); |
| |
| /* |
| * String result = createInstructionList(iNode, stem.size() * 2 + |
| * index.size() + expr.size() + 11 ); Label tail = new Label(); int |
| * failure_test = is_and? OP_iffalse : OP_iftrue; // Although ASC |
| * evaluates the stem twice, it only evaluates the index once. // TODO: |
| * Inspect the index expression for side effects so the temp can be // |
| * elided in most cases. Binding index_temp = |
| * currentScope.allocateTemp(); result.addAll(index); |
| * result.addInstruction(index_temp.setlocal()); |
| * result.addAll(replicate(stem)); |
| * result.addInstruction(index_temp.getlocal()); result.addAll(stem); |
| * result.addInstruction(index_temp.getlocal()); |
| * result.addInstruction(OP_getproperty); // Assume this is the result. |
| * result.addInstruction(OP_dup); result.addInstruction(failure_test, |
| * tail); // Pop the speculative result and assign the correct one. |
| * result.addInstruction(OP_pop); result.addAll(expr); |
| * result.labelNext(tail); Binding value_temp = null; if ( need_value ) |
| * { value_temp = currentScope.allocateTemp(); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(value_temp.setlocal()); } |
| * result.addInstruction(OP_setproperty); if ( need_value ) { |
| * result.addInstruction(value_temp.getlocal()); |
| * result.addAll(currentScope.releaseTemp(value_temp)); } |
| * result.addAll(currentScope.releaseTemp(index_temp)); |
| */ |
| |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "logical bracket assignment")); |
| return result; |
| } |
| |
| /** |
| * Generate a compound logical assignment expression to a foo.bar type |
| * lvalue |
| * |
| * @param iNode - the assignment operator (root of the subtree). |
| * @param stem - the expression that generates the lvalue's stem, e.g., a in |
| * a[i] |
| * @param index - the index expression. |
| * @param expr - the expression to assign. |
| * @param is_and - true if the expression is &&=, false if it's ||=. |
| * @param need_value - true if the expression's not used in a void context. |
| */ |
| String generateCompoundLogicalMemberAssignment(IASNode iNode, String stem, Binding member, String expr, int fetch_opcode, boolean is_and, boolean need_value) |
| { |
| String result = createInstructionList(iNode); |
| |
| /* |
| * Label tail = new Label(); int failure_test = is_and? OP_iffalse : |
| * OP_iftrue; result.addAll(replicate(stem)); result.addAll(stem); |
| * result.addInstruction(OP_getproperty, member.getName()); // Assume |
| * this is the result. result.addInstruction(OP_dup); |
| * result.addInstruction(failure_test, tail); |
| * result.addInstruction(OP_pop); result.addAll(expr); |
| * result.labelNext(tail); Binding value_temp = null; if ( need_value ) |
| * { value_temp = currentScope.allocateTemp(); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(value_temp.setlocal()); } |
| * result.addInstruction(OP_setproperty, member.getName()); if ( |
| * need_value ) { result.addInstruction(value_temp.getlocal()); |
| * result.addAll(currentScope.releaseTemp(value_temp)); } |
| */ |
| |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "logical member assignment")); |
| return result; |
| |
| } |
| |
| /** |
| * Generate a for/in or for each/in loop. |
| * |
| * @param iNode - the for/in or for/each node. |
| * @param opcode - OP_nextname or OP_nextvalue. |
| * @param it - the name of the iterator variable, e.g., v in for (v in foo) |
| * {f(v);}. |
| * @param base - the source of names/values, e.g., foo in for (v in foo) |
| * {f(v);}. |
| * @param body - the loop body, e.g., {f(v);} in for (v in foo) {f(v);}. |
| */ |
| private String generateForKeyOrValueLoop(IASNode iNode, int opcode, Binding it, String base, String body) |
| { |
| /* |
| * String result = new String(body.size() + base.size() + 15); Label |
| * test = new Label(); Label loop = new Label(); // Set up the object |
| * and index registers. LexicalScope.Hasnext2Wrapper hasnext = |
| * currentScope.hasnext2(); result.addInstruction(OP_pushbyte, 0); |
| * result.addInstruction(hasnext.index_temp.setlocal()); |
| * result.addAll(base); result.addInstruction(OP_coerce_a); |
| * result.addInstruction(hasnext.stem_temp.setlocal()); // Go to the |
| * loop test. result.addInstruction(OP_jump, test); // Top of loop |
| * processing. result.addInstruction(OP_label); |
| * result.labelCurrent(loop); String nextvalue = new String(3); |
| * result.addInstruction(hasnext.stem_temp.getlocal()); |
| * result.addInstruction(hasnext.index_temp.getlocal()); |
| * nextvalue.addInstruction(opcode); |
| * result.addAll(generateAssignment(iNode, it, nextvalue)); |
| * result.addAll(body); // Loop test. // It needs its own list so the |
| * continue target // can be properly resolved; this label is always // |
| * attached to the head of an String. String test_list = new String(1); |
| * test_list.addInstruction(hasnext.String); |
| * test_list.labelCurrent(test); |
| * currentScope.getFlowManager().resolveContinueLabel(test_list); |
| * test_list.addInstruction(OP_iftrue, loop); result.addAll(test_list); |
| * result.addAll(hasnext.release()); |
| */ |
| |
| String result = new String(); |
| return result; |
| } |
| |
| HashMap<IASNode, Boolean> nestedFunctions = new HashMap<IASNode, Boolean>(); |
| |
| /** |
| * generateFunctionBody() wrapper suitable for calling from the BURM. See |
| * JBurg ENHRQ <N> : the grammar that accepts the BURM's parameters to a |
| * JBurg.Reduction routine doesn't grok function calls, so the BURM cannot |
| * call generateFunctionBody(body, name.getName()); |
| */ |
| String generateFunctionBody(IASNode iNode, String function_body, Binding return_type) |
| { |
| return generateFunctionBody(iNode, function_body, return_type != null ? return_type.getName() : null); |
| } |
| |
| /** |
| * Generate boilerplate function prolog/epilog code. |
| * |
| * @param block - the actual CFG. |
| * @param return_type - the function's return type. Not presently used. |
| */ |
| String generateFunctionBody(IASNode iNode, String function_body, Name return_type) |
| { |
| currentScope.getMethodInfo().setReturnType(return_type); |
| String result = ""; |
| final String methodName = getMethodName(); |
| final Boolean isAnonymousFunction = isAnonymousFunction(methodName); |
| final Boolean isCtor = methodName != null && methodName.equals(getCurrentClassName()); |
| |
| if (return_type != null) |
| usedTypes.add(getBasenameFromName(return_type)); |
| |
| final Boolean isNestedFunction = nestedFunctions.containsKey(iNode); |
| |
| /* |
| * !isAnonymousFunction is necessary. AS and JS are different in the way |
| * "this" is being treated in anonymous functions. i.e. public function |
| * whoIsThis() : void { const localFunction : Function = funciton():void |
| * { this.callMe(); }; } In this example this.callMe() refers to the |
| * callMe method of the class. In JS "this" refers to the local |
| * function! By supressing "var self = this;" for anonymous functions |
| * we'll emulate the AS behavior. The JS result then looks like this: |
| * tests.Test.whoIsThis.prototype = function() { var self = this; const |
| * localFunction : Function = funciton():void { // NOT EMITTED: var self |
| * = this; self.callMe(); }; }; |
| */ |
| if (//!JSSharedData.m_useSelfParameter && !isAnonymousFunction && |
| /* TODO: !needsSelfParameter(createFullClassName(false)) && */ !isNestedFunction && function_body.contains("self.")) |
| { |
| // final Binding fullClassName = makeBinding(createFullClassName(false)); |
| // this.registerLocalVariable(currentScope, makeBinding("self"), fullClassName, fullClassName); |
| |
| final Binding fullClassName = new Binding(iNode, makeName(createFullClassName(false)), null); |
| this.registerLocalVariable(currentScope, new Binding(iNode, makeName("self"), null), fullClassName, fullClassName); |
| result += indentBlock("/** @type {" + createFullClassName(true) + "} */" + endl(), 1); |
| result += indentBlock("var self = this;" + endl(), 1); |
| } |
| |
| // Constructor-specific processing: add the instance initializers, |
| // add a constructsuper call if none exists. |
| if (haveAPrioriInstructions() && !isAnonymousFunction) |
| { |
| result += indentBlock(aPrioriInstructions, 1); |
| |
| // If this is a constructor and there's no explicit |
| // super() call, synthesize one. |
| // Note that this may be a semantic error if the |
| // superclass' constructor needs arguments. |
| /* |
| * if ( isCtor && !block.contains("super(") ) { // Call the |
| * superclass' constructor after the instance // init instructions; |
| * this doesn't seem like an abstractly // correct sequence, but |
| * it's what ASC does. result += "" // TODO: // |
| * result.addInstruction(OP_getlocal0); // |
| * result.addInstruction(OP_constructsuper, 0); } |
| */ |
| } |
| |
| if (!function_body.isEmpty()) |
| result += indentBlock(function_body, 1); |
| |
| // popIndent(); |
| // result += indent() + "}"; |
| return result; |
| } |
| |
| private IFunctionNode getFunctionNodeFromNode(IASNode iNode) |
| { |
| IFunctionNode fn = null; |
| if (iNode instanceof FunctionObjectNode) |
| { |
| FunctionObjectNode afn = (FunctionObjectNode)iNode; |
| fn = afn.getFunctionNode(); |
| } |
| else if (iNode instanceof IFunctionNode) |
| { |
| fn = (IFunctionNode)iNode; |
| } |
| |
| if (fn == null) |
| currentScope.addProblem(new JSInternalCompilerProblem(iNode, "cannot extract function node")); |
| |
| return fn; |
| } |
| |
| private IFunctionDefinition getFunctionDefinitionFromNode(IASNode iNode) |
| { |
| final IFunctionNode fn = getFunctionNodeFromNode(iNode); |
| return fn.getDefinition(); |
| } |
| |
| /** |
| * Generate a named nested function. |
| * |
| * @param func_name - the function's name. |
| * @param return_type - the function's return type. |
| * @param function_body - the body of the function. |
| * @pre the function's lexical scope must be at the top of the lexical scope |
| * stack, with the declaring function's lexical scope under it. |
| * @post the nested function's MethodInfo is filled in, the function body is |
| * attached to its MethodBodyInfo, and the declaring function's |
| * initialization sequence gets code to declare the function. |
| */ |
| private String generateNestedFunction(IASNode iNode, Binding func_name, Name return_type, String function_body) |
| { |
| nestedFunctions.put(iNode, true); |
| |
| final Boolean isAnonymousFunction = func_name == null && iNode instanceof FunctionObjectNode; |
| |
| // only the outer function of a nested function should be converted to |
| final Boolean convertLocalVarsToMembers = !isAnonymousFunction && convertLocalVarsToMembers(iNode); |
| |
| String result = ""; |
| if (!isAnonymousFunction) |
| { |
| currentScope.setFunctionName(getBasenameFromBinding(func_name)); |
| result = generateFunctionBody(iNode, function_body, new Binding(iNode, return_type, null)); |
| } |
| |
| currentScope.generateNestedFunction(null); |
| // Pull the nested function's MethodInfo out of its scope before we pop it. |
| MethodInfo nested_method_info = currentScope.getMethodInfo(); |
| |
| currentScope = currentScope.popFrame(); |
| |
| // Intiialize the nested function; add a variable |
| // to the containing function scope and add |
| // newfunction/setproperty logic to the containing |
| // function's hoisted initialization instructions. |
| if (!isAnonymousFunction && !convertLocalVarsToMembers) |
| currentScope.makeVariable(func_name); |
| |
| /* |
| * String init_insns = currentScope.getHoistedInitInstructions(); |
| * init_insns.addInstruction(OP_findproperty, func_name); // |
| * Optimization: declare the function at global scope // (in a utility |
| * namespace) and only new it once. |
| * init_insns.addInstruction(OP_newfunction, nested_method_info); |
| * init_insns.addInstruction(OP_setproperty, func_name); |
| */ |
| |
| { |
| final FunctionNode fn = (FunctionNode)getFunctionNodeFromNode(iNode); |
| |
| // Workaround for Falcon bug. |
| // In theory, just using |
| // MethodInfo mi = currentScope.methodInfo; |
| // should work. But in 307387 the parameter names and types are empty. |
| // currentScope.methodInfo does contain the default values, because |
| // reduce_optionalParameter got called |
| if (!nested_method_info.hasParamNames()) |
| { |
| MethodInfo miRepaired = JSGenerator.createMethodInfo(this, getEmitter(), currentScope, fn); |
| final Vector<PooledValue> defaultValues = nested_method_info.getDefaultValues(); |
| for (PooledValue pv : defaultValues) |
| { |
| miRepaired.addDefaultValue(pv); |
| } |
| nested_method_info = miRepaired; |
| } |
| |
| String[] chunk = null; |
| try |
| { |
| // chunk = emitParameters(currentScope.getProject(), fn.getDefinition()); |
| chunk = emitParameters(nested_method_info); |
| } |
| catch (Exception e) |
| { |
| |
| } |
| |
| final String a_priori_insns = chunk[0]; |
| final String params = chunk[1]; |
| final String baseName = getBasenameFromBinding(func_name); |
| |
| // String result = generateFunction((IFunctionNode)__p, null, n, block, return_type ); |
| result = ""; |
| |
| /* |
| * internal compiler error generated with optimize enabled compiling |
| * as3_enumerate.fla and fails to release the JS file |
| * http://watsonexp.corp.adobe.com/#bug=3047880 We have to emit |
| * something, otherwise you'd get: |
| * @type {function(, , )} as that happened in as3_enumerate |
| * MainTimeline. In the original MainTimeline.as change() is a local |
| * function with untyped parameters: function change(identifier, |
| * object, correct) which gets promoted to a method via |
| * [ConvertLocalVarsToMembers] of frame0(). It seems that there are |
| * two bugs: 1. JSDoc comments for functions with untyped parameters |
| * should use "*" instead: |
| * @type {function(*, *, *)} 2. JSDoc comments for promoted local |
| * functions should be omitted. The change below addresses #2. |
| */ |
| |
| // JSDoc |
| if (!convertLocalVarsToMembers) |
| result += getJSDocForFunction(nested_method_info); |
| |
| /* |
| * @@@ if( !isAnonymousFunction && convertLocalVarsToMembers ) { |
| * result += getCurrentFullClassName() + ".prototype." + baseName + |
| * " = "; } |
| */ |
| |
| // support for [ConvertLocalVarsToMembers] |
| if (convertLocalVarsToMembers) |
| { |
| result += "\n"; |
| if (a_priori_insns != null && !a_priori_insns.isEmpty()) |
| result += a_priori_insns + "\n"; |
| |
| if (!JSSharedData.m_useSelfParameter && |
| /* |
| * TODO: !needsSelfParameter(createFullClassName(false)) |
| * && |
| */function_body.contains(JSSharedData.THIS) && |
| !function_body.contains("var " + JSSharedData.THIS)) |
| { |
| // final Binding fullClassName = makeBinding(createFullClassName(false)); |
| // this.registerLocalVariable(currentScope, makeBinding("self"), fullClassName, fullClassName); |
| |
| final Binding fullClassName = new Binding(iNode, makeName(createFullClassName(false)), null); |
| this.registerLocalVariable(currentScope, new Binding(iNode, makeName("self"), null), fullClassName, fullClassName); |
| result += indentBlock("/** @type {" + createFullClassName(true) + "} */" + endl(), 1); |
| result += indentBlock("var " + JSSharedData.THIS + " = this;" + endl(), 1); |
| } |
| |
| result += function_body; |
| |
| final JSEmitter emitter = getEmitter(); |
| final FunctionNode func = (FunctionNode)iNode; |
| final ITraitsVisitor itraits = emitter.getCurrentClassVisitor().visitInstanceTraits(); |
| itraits.visitMethodTrait(DirectiveProcessor.functionTraitKind(func, TRAIT_Method), getNameFromBinding(func_name), 0, nested_method_info); |
| |
| IMethodVisitor mv = emitter.visitMethod(nested_method_info); |
| mv.visit(); |
| |
| MethodBodyInfo mbi = new MethodBodyInfo(); |
| mbi.setMethodInfo(nested_method_info); |
| |
| IMethodBodyVisitor mbv = mv.visitBody(mbi); |
| mbv.visit(); |
| |
| InstructionList insns = new InstructionList(); |
| insns.addInstruction(JSSharedData.OP_JS, result); |
| |
| mbv.visitInstructionList(insns); |
| |
| mbv.visitEnd(); |
| mv.visitEnd(); |
| |
| result = ""; |
| |
| /* |
| * internal compiler error generated with optimize enabled |
| * compiling as3_enumerate.fla and fails to release the JS file |
| * We have to emit something, otherwise you'd get: |
| * @type {function(, , )} as that happened in as3_enumerate |
| * MainTimeline. In the original MainTimeline.as change() is a |
| * local function with untyped parameters: function |
| * change(identifier, object, correct) which gets promoted to a |
| * method via [ConvertLocalVarsToMembers] of frame0(). It seems |
| * that there are two bugs: 1. JSDoc comments for functions with |
| * untyped parameters should use "*" instead: |
| * @type {function(*, *, *)} 2. JSDoc comments for promoted |
| * local functions should be omitted. The change below addresses |
| * #2. |
| */ |
| // result += getJSDocForFunction(nested_method_info); |
| |
| this.registerConvertedMember(baseName); |
| |
| // result += "\nvar " + baseName + " /* : Function */ = " + JSSharedData.THIS + "." + baseName + ";\n"; |
| } |
| else |
| { |
| if (isAnonymousFunction) |
| result += "function"; |
| else |
| result += "\nfunction " + baseName; |
| |
| result += "(" + params + ")"; |
| |
| final Name retType = nested_method_info.getReturnType(); |
| if (retType != null) |
| result += " /* : " + getBasenameFromName(retType) + " */"; |
| else |
| result += " /* : void */"; |
| |
| result += indent() + "\n"; |
| result += indent() + "{\n"; |
| if (a_priori_insns != null && !a_priori_insns.isEmpty()) |
| result += indentBlock(a_priori_insns, 1) + "\n"; |
| result += indentBlock(function_body, 1); |
| result += indent() + "}"; |
| if (!isAnonymousFunction) |
| result += "\n"; |
| } |
| } |
| |
| // nested functions use "self" to get at instance vars |
| if (result.contains("this.")) |
| { |
| result = result.replace("this.", "self."); |
| } |
| return result; |
| } |
| |
| /** |
| * Generate a try/catch/finally (or try/finally) compound statement. |
| * |
| * @param try_stmt - the body of the try block. |
| * @param catch_blocks - associated catch blocks. May be null if no catch |
| * blocks are present. |
| * @param finally_stmt - the body of the finally block. |
| */ |
| String generateTryCatchFinally(IASNode iNode, String try_stmt, Vector<CatchPrototype> catch_blocks, String finally_stmt) |
| { |
| /* |
| * // TODO: Optimize these String sizes. String normal_flow_fixup = new |
| * String(); String catch_insns = new String(); String final_catch = new |
| * String(); String finally_insns = new String(); String final_throw = |
| * new String(); ExceptionHandlingContext finally_context = |
| * currentScope.getFlowManager().getFinallyContext(); Label |
| * final_catch_target = final_catch.getLabel(); // We need a local to |
| * store the caught exception. Binding exception_storage = |
| * finally_context.getExceptionStorage(); Collection<Label> |
| * pending_normal_control_flow = try_stmt.stripPendingLabels(); if ( |
| * try_stmt.canFallThrough() || pending_normal_control_flow != null ) { |
| * normal_flow_fixup.addInstruction(OP_jump, |
| * finally_context.finallyBlock); } else { // Extend the region past a |
| * terminating // throw statement to give the AVM a // little buffer to |
| * figure out its // exception-handling regions. |
| * normal_flow_fixup.addInstruction(OP_nop); } Label try_start = |
| * try_stmt.getLabel(); Label try_end = |
| * normal_flow_fixup.getLastLabel(); Label finally_region_end = null; if |
| * ( null == catch_blocks ) { finally_region_end = try_end; } else { for |
| * ( CatchPrototype catch_proto: catch_blocks ) { String catch_body = |
| * generateCatchBlock(try_start, try_end, catch_proto); boolean |
| * is_last_catch = catch_proto.equals(catch_blocks.lastElement()); if ( |
| * catch_body.canFallThrough() ) { // Signal the finally block that this |
| * execution succeeded. catch_body.addInstruction(OP_pushbyte, 0); |
| * catch_body.addInstruction(OP_coerce_a); |
| * catch_body.addInstruction(finally_context |
| * .finallyReturnStorage.setlocal()); catch_body.addInstruction(OP_jump, |
| * finally_context.finallyBlock); } else if ( is_last_catch ) { // |
| * Extend the region past a terminating throw // insn to give the AVM a |
| * little buffer. catch_body.addInstruction(OP_nop); } if ( |
| * is_last_catch ) finally_region_end = catch_body.getLastLabel(); |
| * catch_insns.addAll(catch_body); } } // Set up the exception handler |
| * for the finally block. |
| * currentScope.getMethodBodyVisitor().visitException(try_start, |
| * finally_region_end, final_catch_target, null, null); // The final |
| * catch block only needs to save the // caught exception for a rethrow. |
| * final_catch.addInstruction(OP_getlocal0); |
| * final_catch.addInstruction(OP_pushscope); if( |
| * currentScope.needsActivation() ) { // Restore the activation object |
| * final_catch |
| * .addInstruction(currentScope.getActivationStorage().getlocal()); |
| * final_catch.addInstruction(OP_pushscope); } |
| * final_catch.addAll(currentScope |
| * .getFlowManager().getScopeStackReinit()); |
| * final_catch.addInstruction(exception_storage.setlocal()); // Signal |
| * the finally epilog that this execution failed // and should rethrow. |
| * final_catch.addInstruction(OP_pushbyte, |
| * currentScope.getFlowManager().getFinallyAlternativesSize() + 1); |
| * final_catch.addInstruction(OP_coerce_a); |
| * final_catch.addInstruction(finally_context |
| * .finallyReturnStorage.setlocal()); // falls through // |
| * final_catch.addInstruction(OP_jump, finally_head); |
| * finally_insns.addInstruction |
| * (finally_context.finallyReturnStorage.getlocal()); |
| * finally_insns.addInstruction(OP_convert_i); |
| * finally_insns.addInstruction |
| * (finally_context.finallyReturnStorage.kill()); |
| * finally_insns.addAll(currentScope |
| * .getFlowManager().getFinallySwitch()); // Label the start of the |
| * final set of Strings. if (!finally_stmt.isEmpty()) { |
| * finally_stmt.labelFirst(finally_context.finallyBlock); } else // This |
| * is just an expedient for this degenerate finally. |
| * finally_insns.labelFirst(finally_context.finallyBlock); |
| * final_throw.addInstruction(exception_storage.getlocal()); |
| * final_throw.labelCurrent(finally_context.finallyDoRethrow); |
| * final_throw.addInstruction(OP_throw); // Assemble the statement. |
| * String result = new String(); // Initialize the finally return |
| * storage location to appease // legacy verifiers. // TODO: This could |
| * travel on the value stack. // See: |
| * http://bugs.adobe.com/jira/browse/CMP-104 |
| * result.addInstruction(OP_pushbyte, 0); |
| * result.addInstruction(OP_coerce_a); |
| * result.addInstruction(finally_context |
| * .finallyReturnStorage.setlocal()); result.addAll(try_stmt); // Send |
| * all "next statement" type control flow into the finally block. |
| * result.addAllPendingLabels(pending_normal_control_flow); |
| * result.addAll(normal_flow_fixup); result.addAll(catch_insns); |
| * result.addAll(final_catch); result.addAll(finally_stmt); |
| * result.addAll(finally_insns); result.addAll(final_throw); for ( |
| * ExceptionHandlingContext.FinallyReturn retblock: |
| * finally_context.finallyReturns ) |
| * result.addAll(retblock.getStrings()); // TODO: Need more analysis of |
| * how this temp travels through // the system before it can be safely |
| * released. For now // just leak it. |
| * //result.addAll(currentScope.releaseTemp(exception_storage)); // |
| * TODO: Removing a hanging kill exposed a latent bug // in |
| * hasPendingLabels() and end-of-routine processing. // Give the CG a |
| * harmless String that will generate // a returnvoid if this is the |
| * last statement in the routine. result.addInstruction(OP_nop); // |
| * Fallthrough out of the finally block. |
| * result.labelNext(finally_context.finallyDoFallthrough); |
| */ |
| |
| // statement = Pattern tryCatchFinallyStmt |
| String result = ""; |
| |
| // Do nothing if the try statement's empty. |
| if (try_stmt.length() == 0) |
| return result; |
| |
| result = indent() + "try" + endl(); |
| result += indent() + "{" + endl(); |
| result += indentBlock(try_stmt, 1); |
| result += indent() + "}" + endl(); |
| |
| if (catch_blocks != null) |
| { |
| for (CatchPrototype catch_proto : (Vector<CatchPrototype>)catch_blocks) |
| { |
| final Binding nameBinding = new Binding(iNode, catch_proto.catchVarName, null); |
| Binding typeBinding; |
| if (catch_proto.catchType == null) |
| typeBinding = new Binding(iNode, makeName("*"), null); |
| else |
| typeBinding = new Binding(iNode, catch_proto.catchType, null); |
| |
| registerLocalVariable(currentScope, nameBinding, typeBinding, typeBinding); |
| |
| result += indent() + "catch("; |
| if (catch_proto.catchType != null) |
| { |
| final StringBuilder sb = new StringBuilder(); |
| if (!nameToJSDocType(iNode, typeBinding.getName(), sb)) |
| m_needsSecondPass = true; |
| result += "/** @type {" + sb.toString() + "}*/ "; |
| } |
| result += getBasenameFromName(catch_proto.catchVarName); |
| if (catch_proto.catchType != null) |
| result += " /* : " + getBasenameFromBinding(typeBinding) + "*/"; |
| result += ")" + endl(); |
| result += indent() + "{" + endl(); |
| |
| if (catch_proto.catchBody.length() > 0) |
| result += indentBlock(catch_proto.catchBody, 1); |
| |
| // boolean is_last_catch = catch_proto.equals(catch_blocks.lastElement()); |
| //if( !is_last_catch && canFallThrough(catch_body) ) |
| // catch_body.addInstruction(OP_jump, catch_tail); |
| |
| result += indent() + "}" + endl(); |
| } |
| } |
| |
| if (finally_stmt != null && finally_stmt.length() != 0) |
| { |
| result += indent() + "finally" + endl(); |
| result += indent() + "{" + endl(); |
| result += indentBlock(finally_stmt, 1); |
| result += indent() + "}" + endl(); |
| } |
| |
| return result; |
| } |
| |
| public String reduce_typeNameParameter(IASNode iNode, Binding param_name) |
| { |
| StringBuilder result = new StringBuilder(); |
| |
| generateTypeNameParameter(iNode, param_name.getName(), result); |
| |
| return result.toString(); |
| } |
| |
| /** |
| * Generate the String sequence that designates the parameter of a |
| * parameterized type, e.g., String in Vector.<String> or * in |
| * Vector.<*>. |
| * |
| * @param name - the type parameter's name. May be null in the * case. |
| * @param result - the String sequence to generate into. |
| */ |
| void generateTypeNameParameter(IASNode iNode, Name param_name, StringBuilder result) |
| { |
| if (param_name == null || param_name.couldBeAnyType()) |
| result.append("null"); |
| else |
| generateAccess(iNode, param_name, result); |
| } |
| |
| /** |
| * Find the specified break target. |
| * |
| * @param criterion - the search criterion. |
| * @throws UnknownControlFlowTargetException if the criterion is not on the |
| * control-flow stack. |
| */ |
| /* |
| * private String getBreakTarget(Object criterion) throws |
| * UnknownControlFlowTargetException { return |
| * currentScope.getFlowManager().getBreakTarget(criterion); } |
| */ |
| |
| /** |
| * Find the specified continue label. |
| * |
| * @param criterion - the search criterion. |
| * @throws UnknownControlFlowTargetException if the criterion is not on the |
| * control-flow stack. |
| */ |
| /* |
| * private String getContinueLabel(Object criterion) throws |
| * UnknownControlFlowTargetException { return |
| * currentScope.getFlowManager().getContinueLabel(criterion); } |
| */ |
| |
| /** |
| * @return the double content of a numeric literal. |
| * @param iNode - the literal node. |
| */ |
| Double getDoubleContent(IASNode iNode) |
| { |
| return new Double(((INumericLiteralNode)iNode).getNumericValue().toNumber()); |
| } |
| |
| /** |
| * @return the name of an identifier. |
| * @param iNode - the IIdentifier node. |
| */ |
| String getIdentifierContent(IASNode iNode) |
| { |
| return ((IIdentifierNode)iNode).getName(); |
| } |
| |
| /** |
| * @return the int content of a numeric literal. |
| * @param iNode - the literal node. |
| */ |
| Integer getIntegerContent(IASNode iNode) |
| { |
| return new Integer(((INumericLiteralNode)iNode).getNumericValue().toInt32()); |
| } |
| |
| /** |
| * @return always zero. |
| * @param iNode - the literal node. |
| */ |
| Integer getIntegerZeroContent(IASNode iNode) |
| { |
| return 0; |
| } |
| |
| /** |
| * @return always zero. |
| * @param iNode - the literal node. |
| */ |
| Long getIntegerZeroContentAsLong(IASNode iNode) |
| { |
| return 0L; |
| } |
| |
| /** |
| * @return a node cast to MXMLEventSpecifierNode type. |
| * @param iNode - the MXMLEventSpecifierNode node. |
| */ |
| IMXMLEventSpecifierNode getMXMLEventSpecifierContent(IASNode iNode) |
| { |
| return (IMXMLEventSpecifierNode)iNode; |
| } |
| |
| /** |
| * Find all active exception handling blocks or scopes, and set up finally |
| * return sequences and/or popscopes. |
| * |
| * @param original - the original control-flow sequence. |
| * @param criterion - the search criterion that identifies the boundary of |
| * the region to be exited. |
| * @return a control-flow sequence that is either the original sequence or |
| * the start of a trampoline of finally blocks, popscopes, and whatever else |
| * is necessary to exit the control-flow region. |
| * @throws UnknownControlFlowTargetException if the criterion is not on the |
| * control-flow stack. |
| */ |
| /* |
| * private String getNonLocalControlFlow(String original, Object criterion) |
| * throws UnknownControlFlowTargetException { return |
| * currentScope.getFlowManager().getNonLocalControlFlow(original, |
| * criterion); } |
| */ |
| |
| /** |
| * @return a parameter's definition. |
| * @param iNode - the parameter node. |
| */ |
| IDefinition getParameterContent(IASNode iNode) |
| { |
| return (((IParameterNode)iNode).getDefinition()); |
| } |
| |
| /** |
| * @return the string content of a literal. |
| * @param iNode - the literal node. |
| */ |
| String getStringLiteralContent(IASNode iNode) |
| { |
| // Literals. |
| // Pattern stringLiteral |
| // LiteralStringID(void); |
| String s = ((ILiteralNode)iNode).getValue(); |
| if (s == null || s.length() == 0) |
| return "''"; |
| |
| s = s.replaceAll("\n", "__NEWLINE_PLACEHOLDER__"); |
| s = s.replaceAll("\r", "__CR_PLACEHOLDER__"); |
| s = s.replaceAll("\t", "__TAB_PLACEHOLDER__"); |
| s = s.replaceAll("\f", "__FORMFEED_PLACEHOLDER__"); |
| s = s.replaceAll("\b", "__BACKSPACE_PLACEHOLDER__"); |
| s = s.replaceAll("\\\\\"", "__QUOTE_PLACEHOLDER__"); |
| s = s.replaceAll("\\\\", "__ESCAPE_PLACEHOLDER__"); |
| s = "\"" + s.replaceAll("\"", "\\\\\"") + "\""; |
| s = s.replaceAll("__ESCAPE_PLACEHOLDER__", "\\\\\\\\"); |
| s = s.replaceAll("__QUOTE_PLACEHOLDER__", "\\\\\""); |
| s = s.replaceAll("__BACKSPACE_PLACEHOLDER__", "\\\\b"); |
| s = s.replaceAll("__FORMFEED_PLACEHOLDER__", "\\\\f"); |
| s = s.replaceAll("__TAB_PLACEHOLDER__", "\\\\t"); |
| s = s.replaceAll("__CR_PLACEHOLDER__", "\\\\r"); |
| s = s.replaceAll("__NEWLINE_PLACEHOLDER__", "\\\\n"); |
| |
| // Note: we don't try to preserve \ u NNNN or \ x NN escape codes, since the actual Unicode chars can appear |
| // inside the literal just fine (unless they translate to one of the above chars, e.g. CR, in which case |
| // the above substitutions will occur. |
| |
| return s; |
| } |
| |
| /** |
| * @return the uint content of a numeric literal. |
| * @param iNode - the literal node. |
| */ |
| Long getUintContent(IASNode iNode) |
| { |
| return new Long(((INumericLiteralNode)iNode).getNumericValue().toUint32()); |
| } |
| |
| /** |
| * @return the Boolean content of a BOOLEAN literal. |
| * @param iNode - the literal node. |
| */ |
| boolean getBooleanContent(IASNode iNode) |
| { |
| return SemanticUtils.getBooleanContent(iNode); |
| } |
| |
| /** |
| * @return true if a priori Strings are present. |
| */ |
| /* |
| * private boolean haveinstanceInitializers() { return instanceInitializers |
| * != null; } |
| */ |
| |
| /* |
| * ******************************* |
| * ** Cost/Decision Functions ** ******************************* |
| */ |
| |
| /** |
| * Explore a MemberAccessNode and decide if its stem is a reference to a |
| * package. |
| */ |
| int isDottedName(IASNode n) |
| { |
| int result = Integer.MAX_VALUE; |
| |
| if (n instanceof MemberAccessExpressionNode) |
| { |
| MemberAccessExpressionNode ma = (MemberAccessExpressionNode)n; |
| |
| if (ma.stemIsPackage()) |
| // This needs to be greater than the value returned from isPackageName, |
| // so that isPackageName wins |
| result = 2; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Explore a MemberAccessNode and decide if it is a reference to a package. |
| */ |
| int isPackageName(IASNode n) |
| { |
| int result = Integer.MAX_VALUE; |
| |
| if (n instanceof MemberAccessExpressionNode) |
| { |
| MemberAccessExpressionNode ma = (MemberAccessExpressionNode)n; |
| |
| if (ma.isPackageReference()) |
| // This needs to be less than the value returned from isDottedName, |
| // so that isPackageName wins |
| result = 1; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Check if the given AST (the root AST of a looping construct) is in the |
| * set of active labeled control-flow contexts. If it is, then its |
| * control-flow context is being managed by the labeledStatement production |
| * that's reducing the loop's LabeledStatementNode, and the loop should use |
| * that context so that the labeled context's continue label is resolved to |
| * the correct top-of-loop location. |
| * |
| * @param loop_node - the AST at the root of the loop. |
| * @return true if loop_node is known to be the substatment of a labeled |
| * statement. |
| */ |
| private boolean isLabeledSubstatement(IASNode loop_node) |
| { |
| return labeledNodes.contains(loop_node); |
| } |
| |
| /** |
| * Get the definition associated with a node's qualifier and decide if the |
| * qualifier is a compile-time constant. |
| * |
| * @param iNode - the node to check. |
| * @pre - the node has an IdentifierNode 0th child. |
| * @return an attractive cost if the child has a known namespace, i.e., it's |
| * a compile-time constant qualifier. |
| */ |
| int qualifierIsCompileTimeConstant(IASNode iNode) |
| { |
| IdentifierNode qualifier = (IdentifierNode)SemanticUtils.getNthChild(iNode, 0); |
| IDefinition def = qualifier.resolve(currentScope.getProject()); |
| |
| int result = def instanceof NamespaceDefinition ? 1 : Integer.MAX_VALUE; |
| return result; |
| } |
| |
| /** |
| * Get the definition associated with a node's qualifier and decide if the |
| * qualifier is an interface name. |
| * |
| * @param iNode - the node to check. |
| * @pre - the node has an IdentifierNode 0th child. |
| * @return an attractive cost if the child is an interface. |
| */ |
| int qualifierIsInterface(IASNode iNode) |
| { |
| IdentifierNode qualifier = (IdentifierNode)SemanticUtils.getNthChild(iNode, 0); |
| IDefinition def = qualifier.resolve(currentScope.getProject()); |
| |
| int result = def instanceof InterfaceDefinition ? 1 : Integer.MAX_VALUE; |
| return result; |
| |
| } |
| |
| /** |
| * @return a feasible cost if a node has a compile-time constant defintion. |
| */ |
| int isCompileTimeConstant(IASNode iNode) |
| { |
| if (SemanticUtils.transformNameToConstantValue(iNode, currentScope.getProject()) != null) |
| return 1; |
| else |
| return Integer.MAX_VALUE; |
| } |
| |
| /** |
| * @return a feasible cost if the function call node is a call to a constant |
| * function. |
| */ |
| int isCompileTimeConstantFunction(IASNode iNode) |
| { |
| //if ( SemanticUtils.isConstantFunction(currentScope.getProject(), iNode) ) |
| // return 1; |
| // else |
| return Integer.MAX_VALUE; |
| } |
| |
| /** |
| * @return a feasible cost if a parameterized type's base and parameter |
| * types are resolved types, ERROR_TRAP if not. |
| */ |
| int parameterTypeIsConstant(IASNode iNode) |
| { |
| return Math.max( |
| isKnownType(SemanticUtils.getNthChild(iNode, 0)), |
| isKnownType(SemanticUtils.getNthChild(iNode, 1)) |
| ); |
| } |
| |
| /** |
| * @return a feasible cost if the type's parameter is a resolved type, |
| * ERROR_TRAP if not. |
| */ |
| int isKnownType(IASNode iNode) |
| { |
| boolean isConstant = false; |
| |
| if (iNode instanceof IExpressionNode) |
| { |
| IExpressionNode qualifier = (IExpressionNode)iNode; |
| isConstant = qualifier.resolve(currentScope.getProject()) instanceof ITypeDefinition; |
| } |
| |
| return isConstant ? 1 : ERROR_TRAP; |
| } |
| |
| public Object transform_constant_function_to_value(IASNode iNode, Binding method, Vector<Object> constant_args) |
| { |
| assert false : "attempting to reduce a non-constant function as a constant. " + |
| "isCompileTimeConstantFunction Should have guarded against this"; |
| |
| return null; |
| } |
| |
| /** |
| * Check a Binding to decide if its referent is statically known to be a |
| * Namespace. |
| * |
| * @param b - the Binding to check. |
| * @return true if the given Binding refers to a namespace. |
| */ |
| /* |
| * private boolean isNamespace(Binding b) { return b.getDefinition() |
| * instanceof NamespaceDefinition; } |
| */ |
| |
| /** |
| * Check a Binding to see if it's (often incorrectly) defined as the |
| * ANY_TYPE. |
| * |
| * @param b - the Binding to check. |
| * @return true if the Binding's definition is the ANY_TYPE definition. |
| */ |
| /* |
| * private boolean isAnyType(Binding b) { return b.getDefinition() == |
| * ClassDefinition.getAnyTypeClassDefinition(); } |
| */ |
| |
| /** |
| * @return a feasible cost (1) if the node is for 'new Array()' |
| */ |
| int isEmptyArrayConstructor(IASNode iNode) |
| { |
| ICompilerProject project = currentScope.getProject(); |
| IIdentifierNode identifierNode = (IIdentifierNode)SemanticUtils.getNthChild(iNode, 1); |
| if (identifierNode.resolve(project) == project.getBuiltinType(BuiltinType.ARRAY)) |
| return 1; |
| |
| return Integer.MAX_VALUE; |
| } |
| |
| /** |
| * @return a feasible cost (1) if the node is for 'new Object()' |
| */ |
| int isEmptyObjectConstructor(IASNode iNode) |
| { |
| ICompilerProject project = currentScope.getProject(); |
| IIdentifierNode identifierNode = (IIdentifierNode)SemanticUtils.getNthChild(iNode, 1); |
| if (identifierNode.resolve(project) == project.getBuiltinType(BuiltinType.OBJECT)) |
| return 1; |
| |
| return Integer.MAX_VALUE; |
| } |
| |
| /* |
| * ************************ |
| * ** Reduction prologues ************************ |
| */ |
| |
| public void prologue_anonymousFunction(IASNode iNode) |
| { |
| // TODO: The current scope may not |
| // need an activation if the anonymous |
| // function doesn't access any of the |
| // current scope's locals. |
| currentScope = currentScope.pushFrame(); |
| currentScope.declareAnonymousFunction(); |
| prologue_function(iNode); |
| |
| // Register arguments as local Variables. |
| final Binding b_name = new Binding(iNode, new Name("arguments"), null); |
| final Binding b_type = new Binding(iNode, new Name("Array"), null); |
| registerLocalVariable(currentScope, b_name, b_type, b_type); |
| } |
| |
| public void prologue_functionObject(IASNode iNode) |
| { |
| // TODO: The current scope may not |
| // need an activation if the anonymous |
| // function doesn't access any of the |
| // current scope's locals. |
| currentScope = currentScope.pushFrame(); |
| final FunctionNode innerFunctionNode = ((FunctionObjectNode)iNode).getFunctionNode(); |
| currentScope.declareFunctionObject(innerFunctionNode.getName()); |
| prologue_function(iNode); |
| |
| // Register arguments as local Variables. |
| final Binding b_name = new Binding(iNode, new Name("arguments"), null); |
| final Binding b_type = new Binding(iNode, new Name("Array"), null); |
| registerLocalVariable(currentScope, b_name, b_type, b_type); |
| } |
| |
| public void prologue_blockStmt(IASNode iNode) |
| { |
| } |
| |
| public void prologue_blockStmt_to_finally_clause(IASNode iNode) |
| { |
| // workaround for Falcon bug. |
| // java.lang.ClassCastException: org.apache.flex.compiler.internal.as.codegen.ControlFlowContext cannot be cast to org.apache.flex.compiler.internal.as.codegen.ExceptionHandlingContext |
| try |
| { |
| currentScope.getFlowManager().startFinallyContext(iNode); |
| } |
| catch (ClassCastException e) |
| { |
| |
| } |
| } |
| |
| public void prologue_catchBlock(IASNode iNode) |
| { |
| // workaround for Falcon bug. |
| // java.lang.ClassCastException: org.apache.flex.compiler.internal.as.codegen.ControlFlowContext cannot be cast to org.apache.flex.compiler.internal.as.codegen.ExceptionHandlingContext |
| try |
| { |
| currentScope.getFlowManager().startCatchContext((ICatchNode)iNode); |
| } |
| catch (ClassCastException e) |
| { |
| |
| } |
| } |
| |
| public void prologue_countedForStmt(IASNode iNode) |
| { |
| if (!isLabeledSubstatement(iNode)) |
| currentScope.getFlowManager().startLoopControlFlowContext(((IForLoopNode)iNode).getStatementContentsNode()); |
| } |
| |
| public void prologue_doStmt(IASNode iNode) |
| { |
| if (!isLabeledSubstatement(iNode)) |
| currentScope.getFlowManager().startLoopControlFlowContext(((IWhileLoopNode)iNode).getStatementContentsNode()); |
| } |
| |
| public void prologue_forEachStmt(IASNode iNode) |
| { |
| if (!isLabeledSubstatement(iNode)) |
| currentScope.getFlowManager().startLoopControlFlowContext(((IForLoopNode)iNode).getStatementContentsNode()); |
| } |
| |
| public void prologue_forInStmt(IASNode iNode) |
| { |
| if (!isLabeledSubstatement(iNode)) |
| currentScope.getFlowManager().startLoopControlFlowContext(((IForLoopNode)iNode).getStatementContentsNode()); |
| } |
| |
| void prologue_function(IASNode n) |
| { |
| FunctionDefinition func = (FunctionDefinition)getFunctionDefinitionFromNode(n); |
| |
| IFunctionNode funcNode = null; |
| |
| if (n instanceof IFunctionNode) |
| funcNode = (IFunctionNode)n; |
| else if (n instanceof FunctionObjectNode) |
| funcNode = ((FunctionObjectNode)n).getFunctionNode(); |
| |
| if (funcNode != null) |
| func = (FunctionDefinition)funcNode.getDefinition(); |
| assert (func != null) : n + " has no definition."; |
| |
| // workaroudn for Falcon bug CMP-??? |
| // setLocalASScope() throws "Local scope already set.". |
| // |
| try |
| { |
| currentScope.setLocalASScope(func.getContainedScope()); |
| } |
| |
| catch (AssertionError e) |
| { |
| |
| } |
| currentScope.resetDebugInfo(); |
| |
| currentScope.getMethodBodySemanticChecker().checkFunctionDefinition(funcNode, func); |
| |
| for (int i = 0; i < n.getChildCount(); i++) |
| scanFunctionBodyForActivations(n.getChild(i)); |
| } |
| |
| public void prologue_labeledStmt(IASNode iNode) |
| { |
| // Since the Prologue code runs before the subtrees have been reduced, |
| // we can't get the label string via the non_resolving_identifier reduction. |
| // NonResolvingIdentifierNode label = (NonResolvingIdentifierNode)JSGeneratingReducer.getNthChild(iNode, 0); |
| try |
| { |
| currentScope.getFlowManager().startLabeledStatementControlFlowContext((LabeledStatementNode)iNode); |
| } |
| catch (DuplicateLabelException dup_label) |
| { |
| currentScope.addProblem(new DuplicateLabelProblem(iNode)); |
| } |
| |
| // If the label applies to a particular statement, |
| // not to a block, then add that statement to the set |
| // of labeled nodes so that the statement's reduction |
| // can use this labeled control flow context. |
| if (iNode.getChildCount() == 2) |
| labeledNodes.add(SemanticUtils.getNthChild(iNode, 1)); |
| } |
| |
| public void prologue_mxmlEventSpecifier(IASNode iNode) |
| { |
| // The method that implements an event specifier has a single parameter. |
| // Its name is "event" (which is discovered from the ParameterDefinition |
| // that the event node creates). |
| // Its type, such as flash.events.MouseEvent, has already been determined |
| // when we created the MethodInfo, so we access it there |
| // rather than creatinga another equivalent Name. |
| currentScope.makeParameter( |
| getMXMLEventSpecifierContent(iNode).getEventParameterDefinition(), |
| currentScope.getMethodInfo().getParamTypes().get(0)); |
| |
| } |
| |
| public void prologue_switchStmt(IASNode iNode) |
| { |
| // Always establish a flow context, even if the statement |
| // was labeled; control flow can't branch backwards via |
| // continue. |
| currentScope.getFlowManager().startSwitchContext((SwitchNode)iNode); |
| } |
| |
| public void prologue_tryCatchFinallyStmt(IASNode iNode) |
| { |
| currentScope.getFlowManager().startExceptionContext((ITryNode)iNode); |
| currentScope.getFlowManager().getFinallyContext().setHasFinallyBlock(true); |
| } |
| |
| public void prologue_tryCatchStmt(IASNode iNode) |
| { |
| currentScope.getFlowManager().startExceptionContext((ITryNode)iNode); |
| } |
| |
| public void prologue_tryFinallyStmt(IASNode iNode) |
| { |
| currentScope.getFlowManager().startExceptionContext((ITryNode)iNode); |
| currentScope.getFlowManager().getFinallyContext().setHasFinallyBlock(true); |
| } |
| |
| public void prologue_typedFunction_to_statement(IASNode iNode) |
| { |
| // Note: A named nested function does |
| // always need an activation record. |
| currentScope = currentScope.pushFrame(); |
| currentScope.declareNestedFunction(); |
| prologue_function(iNode); |
| } |
| |
| public void prologue_typelessFunction_to_statement(IASNode iNode) |
| { |
| // Note: A named nested function does |
| // always need an activation record. |
| currentScope = currentScope.pushFrame(); |
| currentScope.declareNestedFunction(); |
| prologue_function(iNode); |
| } |
| |
| public void prologue_typeof(IASNode iNode) |
| { |
| // ABC-specific processing not relevant for source-to-source translation. |
| } |
| |
| public void prologue_whileStmt(IASNode iNode) |
| { |
| currentScope.getFlowManager().startLoopControlFlowContext(((IWhileLoopNode)iNode).getStatementContentsNode()); |
| } |
| |
| public void prologue_withStmt(IASNode iNode) |
| { |
| currentScope.getFlowManager().startWithContext(((IWithNode)iNode).getStatementContentsNode()); |
| } |
| |
| /* |
| * ************************* |
| * ** Reduction actions ** ************************* |
| */ |
| public String reduce_functionObject(IASNode iNode, IASNode function_name, String plist, Binding return_type, String function_body) |
| { |
| return reduce_anonymousFunction(iNode, function_body); |
| } |
| |
| public String reduce_anonymousFunction(IASNode iNode, String function_body) |
| { |
| /* |
| * currentScope.generateNestedFunction(function_body); String result = |
| * createInstruction(iNode, 1); result.addInstruction(OP_newfunction, |
| * currentScope.methodInfo); |
| */ |
| |
| currentScope.generateNestedFunction(null); |
| String result = createInstruction(iNode, 1); |
| |
| MethodInfo mi = currentScope.getMethodInfo(); |
| |
| // Workaround for Falcon bug. |
| // In theory, just using |
| // MethodInfo mi = currentScope.methodInfo; |
| // should work. But in 307387 the parameter names and types are empty. |
| // currentScope.methodInfo does contain the default values, because |
| // reduce_optionalParameter got called |
| { |
| final FunctionNode fn = (FunctionNode)getFunctionNodeFromNode(iNode); |
| if (!mi.hasParamNames()) |
| { |
| MethodInfo miRepaired = JSGenerator.createMethodInfo(this, (JSEmitter)currentScope.getEmitter(), currentScope, fn); |
| final Vector<PooledValue> defaultValues = mi.getDefaultValues(); |
| for (PooledValue pv : defaultValues) |
| { |
| miRepaired.addDefaultValue(pv); |
| } |
| mi = miRepaired; |
| } |
| |
| String[] chunk = null; |
| try |
| { |
| // chunk = emitParameters(currentScope.getProject(), fn.getDefinition()); |
| chunk = emitParameters(mi); |
| } |
| catch (Exception e) |
| { |
| |
| } |
| |
| final String a_priori_insns = chunk[0]; |
| final String params = chunk[1]; |
| |
| // String result = generateFunction((IFunctionNode)iNode, null, n, block, return_type ); |
| |
| // JSDoc |
| result += getJSDocForFunction(mi); |
| result += "function(" + params + ")"; |
| |
| final Name retType = mi.getReturnType(); |
| if (retType != null) |
| result += " /* : " + getBasenameFromName(retType) + " */"; |
| else |
| result += " /* : void */"; |
| |
| result += indent() + "\n"; |
| result += indent() + "{\n"; |
| if (a_priori_insns != null && !a_priori_insns.isEmpty()) |
| result += indentBlock(a_priori_insns, 1) + "\n"; |
| result += indentBlock(function_body, 1); |
| result += indent() + "}"; |
| } |
| |
| currentScope = currentScope.popFrame(); |
| return result; |
| } |
| |
| public String reduce_arrayIndexExpr(IASNode iNode, String stem, boolean is_super, String index) |
| { |
| // TODO: handle is_super |
| if (is_super) |
| currentScope.getMethodBodySemanticChecker().checkSuperAccess(iNode); |
| |
| // No semantic restrictions on fetching an array value. |
| |
| String result = createInstruction(iNode, stem.length() + index.length() + 1); |
| |
| String member = stripTabs(index); |
| |
| // NOTE: Sometimes the part in brackets ("member") is a member of the LHS type, and sometimes it's not. |
| // E.g. in instance["foo"] foo is a member name; but in instance[foo], foo is an expression evaluating |
| // to a string, and that string is the member name. This distinction is handled by resolveGetterName() |
| // and resolveSetterName(). They may ignore useBrackets in certain cases due to this. |
| result += resolveGetterName(iNode, stem, makeMemberBinding(iNode, stem, member), true); |
| |
| return result; |
| } |
| |
| public String reduce_arrayLiteral(IASNode iNode, Vector<String> elements) |
| { |
| // No semantic restrictions on array literals. |
| |
| // TODO: Investigate optimizing. |
| String result = createInstruction(iNode); |
| |
| /* |
| * for ( String element: elements ) result.addAll(element); |
| * result.addInstruction(OP_newarray, elements.size()); |
| */ |
| |
| result += "["; |
| boolean comma = false; |
| for (String element : elements) |
| { |
| if (comma) |
| result += ", "; |
| else |
| comma = true; |
| result += stripTabs(element); |
| } |
| |
| result += "]"; |
| return result; |
| } |
| |
| public String reduce_assignToBracketExpr_to_expression(IASNode iNode, String stem, String index, String r, boolean is_super) |
| { |
| currentScope.getMethodBodySemanticChecker().checkAssignToBracketExpr(iNode); |
| |
| /* |
| * Binding local = currentScope.allocateTemp(); String result = |
| * createInstruction(iNode, stem.size() + index.size() + r.size() + 5); |
| * result.addAll(stem); result.addAll(index); result.addAll(r); |
| * result.addInstruction(OP_dup); result.addInstruction(local.setlocal() |
| * ); result.addInstruction(OP_setproperty); |
| * result.addInstruction(local.getlocal() ); |
| * result.addAll(currentScope.releaseTemp(local)); |
| */ |
| |
| // void_expression = Pattern assignToBracketExpr, expression |
| // Assignment to a[i] type lvalue. |
| |
| // trim quotes |
| String member = stripTabs(index); |
| |
| String result = createInstruction(iNode, stem.length() + index.length() + r.length() + 5); |
| final Binding memberBinding = makeMemberBinding(iNode, stem, member); |
| final String setter = resolveSetterName(iNode, stem, memberBinding, "=", stripTabs(r), true, true); |
| final String getter = resolveGetterName(iNode, stem, memberBinding, true); |
| if (getter.equals(stem + "[" + member + "]")) |
| result += "(" + setter + ")"; |
| else |
| result += "(" + setter + ", " + getter + ")"; |
| |
| return result; |
| } |
| |
| public String reduce_assignToBracketExpr_to_void_expression(IASNode iNode, String stem, String index, String r, boolean is_super) |
| { |
| currentScope.getMethodBodySemanticChecker().checkAssignToBracketExpr(iNode); |
| |
| /* |
| * String result = createInstruction(iNode, stem.size() + index.size() + |
| * r.size() + 1); result.addAll(stem); result.addAll(index); |
| * result.addAll(r); result.addInstruction(OP_setproperty); |
| */ |
| |
| String result = createInstruction(iNode, stem.length() + index.length() + r.length() + 1); |
| |
| // void_expression = Pattern assignToBracketExpr, void_expression |
| // Assignment to a[i] type lvalue. |
| |
| // String result = indent() + stem + "[" + stripTabs(index) + "] = " + stripTabs(r) + ";" + endl(); |
| |
| // trim quotes |
| final String member = stripTabs(index); |
| result += indent() + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, member), "=", stripTabs(r), true, true) + ";" + endl(); |
| return result; |
| } |
| |
| public String reduce_assignToMemberExpr_to_expression(IASNode iNode, String stem, Binding member, String r) |
| { |
| currentScope.getMethodBodySemanticChecker().checkAssignment(iNode, member); |
| |
| /* |
| * Binding local = currentScope.allocateTemp(); String result = |
| * createInstruction(iNode, stem.size() + r.size() + 5); |
| * result.addAll(stem); result.addAll(r); result.addInstruction(OP_dup); |
| * result.addInstruction(local.setlocal() ); |
| * result.addInstruction(OP_setproperty, member.getName()); |
| * result.addInstruction(local.getlocal() ); |
| * result.addAll(currentScope.releaseTemp(local)); |
| */ |
| |
| String result = createInstruction(iNode, stem.length() + r.length() + 5); |
| // Assignment to a more general lvalue |
| // Pattern assignToMemberExpr, expression |
| // Op_AssignId(MemberAccessExpressionID(expression stem, name member), expression r); |
| // String result = indent() + stem + "." + getBasenameFromBinding(member) + " = " + stripTabs(r) + ";" + endl(); |
| |
| result += "(" + resolveSetterName(iNode, stem, member, "=", stripTabs(r), false, true) + ", " + resolveGetterName(iNode, stem, member, false) + ")"; |
| return result; |
| } |
| |
| public String reduce_assignToMemberExpr_to_void_expression(IASNode iNode, String stem, Binding member, String r) |
| { |
| currentScope.getMethodBodySemanticChecker().checkAssignment(iNode, member); |
| |
| /* |
| * String result = createInstruction(iNode, stem.size() + r.size() + 1); |
| * result.addAll(stem); result.addAll(r); |
| * result.addInstruction(OP_setproperty, member.getName()); |
| */ |
| |
| if (r == null) |
| r = "null"; |
| |
| String result = createInstruction(iNode, stem.length() + r.length() + 1); |
| // Assignment to a more general lvalue |
| // Pattern assignToMemberExpr, void_expression |
| // Op_AssignId(MemberAccessExpressionID(expression stem, name member), expression r); |
| |
| result += indent(); |
| result += resolveSetterName(iNode, stem, member, "=", stripTabs(r), false, true) + ";" + endl(); |
| return result; |
| } |
| |
| public String reduce_assignToDescendantsExpr(IASNode iNode, String stem, Binding member, String r, boolean need_value) |
| { |
| // Note: This code doesn't assign to descendants! |
| // While one might think it should, it does in fact |
| // mirror the bytecode ASC emitted. |
| currentScope.getMethodBodySemanticChecker().checkAssignment(iNode, member); |
| |
| /* |
| * Binding local = null; String result = createInstruction(iNode, |
| * stem.size() + r.size() + 5); result.addAll(stem); result.addAll(r); |
| * if ( need_value ) { local = currentScope.allocateTemp(); |
| * result.addInstruction(OP_dup); result.addInstruction(local.setlocal() |
| * ); } result.addInstruction(OP_setproperty, member.getName()); if ( |
| * need_value ) { result.addInstruction(local.getlocal() ); |
| * result.addAll(currentScope.releaseTemp(local)); } |
| */ |
| |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "E4X (ECMA-357)")); |
| String result = createInstruction(iNode, stem.length() + r.length() + 5); |
| return result; |
| } |
| |
| public String reduce_assignToNameExpr_to_void_expression(IASNode iNode, Binding lvalue, String r) |
| { |
| if (!isSyntheticBinding(lvalue)) |
| currentScope.getMethodBodySemanticChecker().checkAssignment(iNode, lvalue); |
| |
| // TODO: return generateAssignment(iNode, lvalue, r); |
| |
| // Pattern assignToNameExpr, void_expression |
| // Op_AssignId(name lval, expression r); |
| |
| String name = bindingToString(iNode, lvalue, true, true); |
| if (name.contains(".")) |
| name = name.substring(0, name.lastIndexOf(".")); |
| else |
| name = ""; |
| return indent() + resolveSetterName(iNode, name, lvalue, "=", stripTabs(r), false, true) + ";" + endl(); |
| } |
| |
| public String reduce_assignToNameExpr_to_expression(IASNode iNode, Binding lvalue, String r) |
| { |
| if (!isSyntheticBinding(lvalue)) |
| currentScope.getMethodBodySemanticChecker().checkAssignment(iNode, lvalue); |
| // TODO: return generateAssignment(iNode, lvalue, r, true); |
| |
| // Pattern assignToNameExpr, expression |
| // Op_AssignId(name lval, expression r); |
| |
| final String name = bindingToString(iNode, lvalue, true, true); |
| String stem = ""; |
| if (name.contains(".")) |
| stem = name.substring(0, name.lastIndexOf(".")); |
| return "(" + resolveSetterName(iNode, stem, lvalue, "=", stripTabs(r), false, true) + ", " + name + ")"; |
| } |
| |
| public String reduce_assignToQualifiedMemberExpr(IASNode iNode, String stem, Binding qualifier, Binding member, String rhs, boolean need_value) |
| { |
| String result = createInstruction(iNode); |
| result += "(" + resolveSetterName(iNode, stem, member, "=", stripTabs(rhs), false, true) + ", " + resolveGetterName(iNode, stem, member, false) + ")"; |
| return result; |
| } |
| |
| public String reduce_assignToQualifiedRuntimeMemberExpr(IASNode iNode, String stem, Binding qualifier, String runtime_member_selector, String rhs, boolean need_value) |
| { |
| String result = createInstruction(iNode); |
| |
| final Binding member = qualifier; |
| result += "(" + resolveSetterName(iNode, stem, member, "=", stripTabs(rhs), false, true) + ", " + resolveGetterName(iNode, stem, member, false) + ")"; |
| return result; |
| } |
| |
| public String reduce_assignToQualifiedAttributeExpr(IASNode iNode, String stem, Binding qualifier, Binding member, String rhs, boolean need_value) |
| { |
| String result = createInstruction(iNode); |
| |
| result += "(" + resolveSetterName(iNode, stem, member, "=", stripTabs(rhs), false, true) + ", " + resolveGetterName(iNode, stem, member, false) + ")"; |
| return result; |
| } |
| |
| public String reduce_assignToQualifiedRuntimeAttributeExpr(IASNode iNode, String stem, Binding qualifier, String runtime_member_selector, String rhs, boolean need_value) |
| { |
| String result = createInstruction(iNode); |
| |
| final Binding member = qualifier; |
| result += "(" + resolveSetterName(iNode, stem, member, "=", stripTabs(rhs), false, true) + ", " + resolveGetterName(iNode, stem, member, false) + ")"; |
| return result; |
| } |
| |
| public String reduce_assignToRuntimeNameExpr(IASNode iNode, RuntimeMultiname lval, String r, final boolean need_value) |
| { |
| return lval.generateAssignment(iNode, r); |
| } |
| |
| public String reduce_assignToUnqualifiedRuntimeAttributeExpr(IASNode iNode, String stem, String rt_attr, String rhs, boolean need_value) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "assign to unqualified runtime attribute")); |
| |
| String result = createInstruction(iNode); |
| return result; |
| } |
| |
| public Binding reduce_attributeName(IASNode iNode, Binding attr_name) |
| { |
| // do nothing - just return the contained name |
| // which will already be set up correctly for an attribute name |
| return attr_name; |
| } |
| |
| public String reduce_blockStmt_to_finally_clause(IASNode iNode, Vector<String> stmts) |
| { |
| String result = createInstruction(iNode); |
| |
| for (String stmt : stmts) |
| result += stmt; |
| |
| // workaround for Falcon bug. |
| // java.lang.ClassCastException: org.apache.flex.compiler.internal.as.codegen.ControlFlowContext cannot be cast to org.apache.flex.compiler.internal.as.codegen.ExceptionHandlingContext |
| try |
| { |
| currentScope.getFlowManager().endFinallyContext(); |
| } |
| catch (ClassCastException e) |
| { |
| |
| } |
| |
| return result; |
| |
| } |
| |
| public String reduce_blockStmt_to_statement(IASNode iNode, Vector<String> stmts) |
| { |
| // TODO: See if optimizing this is useful. |
| // Iterating twice over the statements |
| // might cost more than it's worth. |
| String result = createInstruction(iNode); |
| |
| for (String stmt : stmts) |
| { |
| // Some statement-level reductions return null. |
| if (stmt != null) |
| { |
| result += stmt; |
| } |
| } |
| |
| return result; |
| } |
| |
| public Boolean reduce_booleanLiteral(IASNode iNode) |
| { |
| if ("true".equals(removeQuotes(getStringLiteralContent(iNode)))) |
| return Boolean.TRUE; |
| else |
| return Boolean.FALSE; |
| } |
| |
| public String reduce_breakStmt(IASNode iNode) |
| { |
| /* |
| * try { return getNonLocalControlFlow( |
| * getBreakTarget(ControlFlowContextManager.FIND_LAST_CONTEXT), |
| * ControlFlowContextManager.FIND_LAST_CONTEXT ); } catch ( |
| * UnknownControlFlowTargetException no_target ) { |
| * currentScope.getProblems().add(new UnknownBreakTargetProblem(iNode)); |
| * return createInstruction(iNode); } |
| */ |
| // statement = Pattern breakStmt |
| String result = createInstruction(iNode); |
| result += indent() + "break;" + endl(); |
| return result; |
| |
| } |
| |
| public String reduce_by_concatenation(IASNode iNode, String first, String second) |
| { |
| return first + "." + second; |
| } |
| |
| public CatchPrototype reduce_catchBlockTyped(IASNode iNode, Binding var_name, Binding exception, String action) |
| { |
| CatchPrototype result = new CatchPrototype(); |
| result.catchVarName = var_name.getName(); |
| result.catchType = exception.getName(); |
| result.catchBody = action; |
| |
| // workaround for Falcon bug. |
| // java.lang.ClassCastException: org.apache.flex.compiler.internal.as.codegen.ControlFlowContext cannot be cast to org.apache.flex.compiler.internal.as.codegen.ExceptionHandlingContext |
| try |
| { |
| currentScope.getFlowManager().startCatchContext((ICatchNode)iNode); |
| } |
| catch (ClassCastException e) |
| { |
| |
| } |
| return result; |
| } |
| |
| public CatchPrototype reduce_catchBlockUntyped(IASNode iNode, Binding var_name, String action) |
| { |
| CatchPrototype result = new CatchPrototype(); |
| result.catchVarName = var_name.getName(); |
| result.catchType = null; |
| result.catchBody = action; |
| |
| // workaround for Falcon bug. |
| // java.lang.ClassCastException: org.apache.flex.compiler.internal.as.codegen.ControlFlowContext cannot be cast to org.apache.flex.compiler.internal.as.codegen.ExceptionHandlingContext |
| try |
| { |
| currentScope.getFlowManager().endCatchContext(); |
| } |
| catch (ClassCastException e) |
| { |
| |
| } |
| return result; |
| } |
| |
| public String reduce_commaExpr(IASNode iNode, String payload_expr, Vector<String> exprs) |
| { |
| // TODO: Investigate optimizing this. |
| String result = createInstruction(iNode); |
| |
| // Pattern commaExpr |
| // Op_CommaID(expression payload_expr, void_expression exprs+); |
| |
| // The payload expression is the last expression in the |
| // comma list. It was inverted by IASNodeAdapter into |
| // the 0th position to work around limitations of the |
| // BURG's n-ary operand processor. |
| |
| Boolean comma = false; |
| result += "("; |
| for (String other_expr : exprs) |
| { |
| if (comma) |
| result += ", "; |
| else |
| comma = true; |
| // CMP-???: remove trailing LF |
| if (other_expr.charAt(other_expr.length() - 1) == '\n') |
| other_expr = other_expr.substring(0, other_expr.length() - 1); |
| |
| // CMP-???: remove trailing ";" |
| if (other_expr.charAt(other_expr.length() - 1) == ';') |
| other_expr = other_expr.substring(0, other_expr.length() - 1); |
| |
| result += stripTabs(other_expr); |
| } |
| |
| if (comma) |
| result += ", "; |
| result += payload_expr; |
| result += ")"; |
| return result; |
| } |
| |
| public ConditionalFragment reduce_conditionalFragment(IASNode iNode, String condition, Vector<String> consequents) |
| { |
| // return new ConditionalFragment(iNode, condition, consequents); |
| |
| if (condition == null) |
| return reduce_switchDefault(iNode, consequents); |
| |
| return reduce_switchCase(iNode, condition, consequents); |
| |
| } |
| |
| public ConditionalFragment reduce_constantConditionalFragment(IASNode iNode, Object condition, Vector<String> consequents) |
| { |
| return reduce_conditionalFragment(iNode, condition.toString(), consequents); |
| } |
| |
| public String reduce_constantStringConcatenation(IASNode iNode, String l, String r) |
| { |
| // return l + r; |
| |
| // Concatenate two string literals |
| assert l.endsWith("\""); |
| assert r.startsWith("\""); |
| |
| return l.substring(0, l.length() - 1) + r.substring(1); |
| } |
| |
| /** |
| * reduce a concatenation of a string and a constant value to a string |
| * constant. The constant value will be converted to a string according to |
| * the ECMA spec, and then will be concatenated with the string. |
| * |
| * @param iNode the node |
| * @param l the left string |
| * @param r the right value |
| * @return the concatenated string |
| */ |
| public String reduce_constantStringConcatenation(IASNode iNode, String l, Object r) |
| { |
| return reduce_constantStringConcatenation(iNode, l, ECMASupport.toString(r)); |
| } |
| |
| /** |
| * reduce a concatenation of a constant value and a string to a string |
| * constant. The constant value will be converted to a string according to |
| * the ECMA spec, and then will be concatenated with the string. |
| * |
| * @param iNode the node |
| * @param l the left value |
| * @param r the right string |
| * @return the concatenated string |
| */ |
| public String reduce_constantStringConcatenation(IASNode iNode, Object l, String r) |
| { |
| return reduce_constantStringConcatenation(iNode, ECMASupport.toString(l), r); |
| } |
| |
| /** |
| * reduce addition of two constant values. If either of the constants is a |
| * String, then string concatenation will be performed. OTherwise the |
| * constants will be converted to Numbers, according to the ECMA spec, and |
| * added |
| * |
| * @param iNode the node |
| * @param l the left value |
| * @param r the right value |
| * @return the result of adding the two values |
| */ |
| public Object reduce_constantAddition(IASNode iNode, Object l, Object r) |
| { |
| checkBinaryOp(iNode, OP_add); |
| if (l instanceof String) |
| return reduce_constantStringConcatenation(iNode, (String)l, r); |
| else if (r instanceof String) |
| return reduce_constantStringConcatenation(iNode, l, (String)r); |
| else |
| return ECMASupport.toNumeric(l).doubleValue() + ECMASupport.toNumeric(r).doubleValue(); |
| } |
| |
| public String reduce_continueStmt(IASNode iNode) |
| { |
| /* |
| * try { return getNonLocalControlFlow( |
| * getContinueLabel(ControlFlowContextManager.FIND_LAST_CONTEXT), |
| * ControlFlowContextManager.FIND_LAST_CONTEXT ); } catch ( |
| * UnknownControlFlowTargetException no_target ) { |
| * currentScope.getProblems().add(new |
| * UnknownContinueTargetProblem(iNode)); return |
| * createInstruction(iNode); } |
| */ |
| |
| // statement = Pattern continueStmt |
| String result = createInstruction(iNode); |
| result += indent() + "continue;" + endl(); |
| return result; |
| } |
| |
| public String reduce_countedForStmt(IASNode iNode, String init, String test_insns, String incr, String body) |
| { |
| String result = createInstruction(iNode, init.length() + test_insns.length() + incr.length() + body.length() + 5); |
| |
| /* |
| * // Initialize counters, jump to test result.addAll(init); |
| * result.addInstruction(OP_jump, test_insns.getLabel()); // Loop body |
| * String loop_body = new String(); Label loop_head = new Label(); |
| * loop_body.addInstruction(OP_label); |
| * loop_body.labelCurrent(loop_head); loop_body.addAll(body); // Set the |
| * continue target on the correct String. if ( !incr.isEmpty() ) |
| * currentScope.getFlowManager().resolveContinueLabel(incr); else |
| * currentScope.getFlowManager().resolveContinueLabel(loop_body); |
| * result.addAll(loop_body); // Now add the loop counter increments. // |
| * The continue context needs to be set // first for String hygiene. |
| * result.addAll(incr); // Add the test; a successful test branches // |
| * back to the loop head. The test generation // rules guarantee this |
| * last String is a // branch String that needs a target. |
| * test_insns.lastElement().setTarget(loop_head); |
| * result.addAll(test_insns); if ( ! isLabeledSubstatement(iNode) ) |
| * currentScope.getFlowManager().finishControlFlowContext(result); |
| */ |
| |
| { |
| // Pattern countedForStmt |
| // ForLoopID(ContainerID( void_expression init, conditionalJump test_insns, void_expression incr ), statement body ); |
| |
| // Initializer clause (1st clause) |
| // (init may be "" if clause was empty in AS) |
| init = removeSuffixIfPresent(init, ";\n"); // strip final ; so we can easily munge any other ;s below |
| init = init.replace("\n", ""); |
| init = stripTabs(init); |
| |
| // Deal with multiple var declarations in first clause |
| int indexOfVar = init.indexOf("var "); |
| if (indexOfVar != -1) |
| { |
| String initRest = init.substring(indexOfVar + 3); |
| if (initRest.contains("var ")) |
| { |
| // Multiple vars are present: 'init' currently contains a semicolon-separated series of var declarations. |
| // That is not valid JS nor AS: we want a comma-separated series with only ONE var keyword at the start. |
| // TODO: We still leave in extraneous @type JSDoc comments... does this cause a problem for Closure? |
| initRest = initRest.replace(";", ","); // change semicolon separators into commas |
| initRest = initRest.replaceAll("\\bvar ", " "); // remove all later var keywords |
| init = init.substring(0, indexOfVar + 3) + initRest; |
| } |
| } |
| |
| init = init + ";"; // restore final ; removed in first step (or was never present, if init was originally "") |
| |
| // Test clause (2nd clause) |
| String test = ""; |
| if (test_insns != null) |
| { |
| // Workaround for Falcon bug. |
| // FJS-9: for (count = 0; ; count++) |
| if (test_insns.equals("== null")) |
| test = ";"; |
| else |
| test = test_insns + ";"; |
| } |
| |
| // Increment clause (3rd clause) |
| // (incr may be "" if clause was empty in AS) |
| incr = removeSuffixIfPresent(incr, ";\n"); |
| incr = stripTabs(incr); |
| // last clause, so leave it with no trailing ; |
| |
| assert init.endsWith(";"); |
| assert test.endsWith(";"); |
| |
| result += indent() + "for(" + init + " " + test + " " + incr + ")" + endl(); |
| result += indent() + "{" + endl(); |
| // pushIndent(); |
| if (body.length() > 0) |
| result += indentBlock(body, 1); |
| // popIndent(); |
| result += indent() + "}" + endl(); |
| } |
| |
| return result; |
| } |
| |
| public String reduce_defaultXMLNamespace(IASNode iNode, String ns_expr) |
| { |
| String result = createInstruction(iNode); |
| /* |
| * // TODO: This could be optimized for the string constant case, // but |
| * ASC doesn't do so, which means it's future and probably // also not |
| * well tested in the AVM. result.addAll(ns_expr); |
| * result.addInstruction(OP_dxnslate); |
| */ |
| |
| currentScope.setSetsDxns(); |
| |
| result += ns_expr; |
| |
| return result; |
| } |
| |
| public String reduce_deleteBracketExpr(IASNode iNode, String stem, String index) |
| { |
| // ASC doesn't check bracket expressions, and it's difficult |
| // to see what could usefully be done in any case. |
| // currentScope.getMethodBodySemanticChecker().checkDeleteExpr(iNode); |
| String result = createInstruction(iNode, stem.length() + index.length() + 1); |
| |
| /* |
| * result.addAll(stem); result.addAll(index); |
| * result.addInstruction(OP_deleteproperty); |
| */ |
| |
| // Delete expressions. |
| // Pattern deleteBracketExpr |
| // String stem |
| // String index |
| |
| if (isCurrentFramework()) |
| result += "delete(" + stem + "[" + index + "])"; |
| else |
| result += "adobe.deleteProperty(" + JSSharedData.THIS + ", " + stem + ", " + index + ")"; |
| |
| return result; |
| } |
| |
| public String reduce_deleteAtBracketExpr(IASNode iNode, String stem, String index) |
| { |
| // TODO: added for http://bugs.adobe.com/jira/browse/CMP-1511 |
| throw new UnsupportedOperationException(); |
| } |
| |
| public String reduce_deleteDescendantsExpr(IASNode iNode, String stem, Binding field) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "E4X (ECMA-357)")); |
| |
| // TODO: Investigate semantics of this. |
| // This special case should get a warning or some indication it's a tautology. |
| |
| String result = createInstruction(iNode, stem.length() + 1); |
| /* |
| * result.addAll(stem); result.addInstruction(OP_pop); |
| * result.addInstruction(OP_pushtrue); |
| */ |
| return result; |
| } |
| |
| public String reduce_deleteMemberExpr(IASNode iNode, String stem, Binding field) |
| { |
| currentScope.getMethodBodySemanticChecker().checkDeleteExpr(iNode, field); |
| |
| String result = createInstruction(iNode, stem.length() + 1); |
| /* |
| * result.addAll(stem); result.addInstruction(OP_deleteproperty, |
| * field.getName()); |
| */ |
| |
| // Delete expressions. |
| // Pattern deleteMemberExpr |
| // String stem |
| // Binding field |
| |
| if (isCurrentFramework()) |
| result += "delete(" + stem + "." + getBasenameFromBinding(field) + ")"; |
| else |
| result += "adobe.deleteProperty(" + JSSharedData.THIS + ", " + stem + ", \"" + getBasenameFromBinding(field) + "\")"; |
| return result; |
| } |
| |
| public String reduce_deleteRuntimeNameExpr(IASNode iNode, String stem, RuntimeMultiname rt_name) |
| { |
| // TODO: Any relevant semantic checks? |
| String result = createInstruction(iNode); |
| |
| result += "delete "; |
| result += stem; |
| |
| // TODO: inject adobe.deleteProperty() ? |
| if (rt_name.hasRuntimeQualifier()) |
| result += rt_name.getRuntimeQualifier(iNode); |
| if (rt_name.hasRuntimeName()) |
| result += rt_name.getRuntimeName(iNode); |
| // result.addInstruction(OP_deleteproperty, rt_name.generateName()); |
| return result; |
| } |
| |
| public String reduce_deleteNameExpr(IASNode iNode, Binding n) |
| { |
| currentScope.getMethodBodySemanticChecker().checkDeleteExpr(iNode, n); |
| |
| String result = createInstruction(iNode, 2); |
| /* |
| * result.addInstruction(OP_findproperty, n.getName()); |
| * result.addInstruction(OP_deleteproperty, n.getName()); |
| */ |
| |
| // Delete expressions. |
| // Pattern deleteNameExpr |
| // Binding n |
| if (isCurrentFramework()) |
| result += "delete(" + JSSharedData.THIS + "." + getBasenameFromBinding(n) + ")"; |
| else |
| result += "adobe.deleteProperty(" + JSSharedData.THIS + ", \"" + getBasenameFromBinding(n) + "\")"; |
| |
| return result; |
| } |
| |
| public String reduce_deleteExprExprExpr(IASNode iNode, String expr) |
| { |
| return "delete(" + expr + ")"; |
| } |
| |
| public String reduce_doStmt(IASNode iNode, String body, String cond) |
| { |
| String result = createInstruction(iNode, cond.length() + body.length() + 1); |
| |
| /* |
| * currentScope.getFlowManager().resolveContinueLabel(cond); if ( |
| * body.firstElement().getOpcode() != OP_label ) |
| * result.addInstruction(OP_label); result.addAll(body); |
| * result.addAll(cond); result.addInstruction(OP_iftrue, |
| * result.getLabel()); if ( ! isLabeledSubstatement(iNode) ) |
| * currentScope.getFlowManager().finishControlFlowContext(result); |
| */ |
| |
| // Pattern doStmt |
| // DoWhileLoopID(statement body, expression cond); |
| |
| result += indent() + "do" + endl(); |
| result += indent() + "{" + endl(); |
| result += indentBlock(body, 1); |
| result += indent() + "} while(" + cond + ");" + endl(); |
| return result; |
| } |
| |
| public String reduce_e4xFilter(IASNode iNode, String stem, String filter) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "E4X (ECMA-357)")); |
| String result = createInstruction(iNode); |
| |
| /* |
| * LexicalScope.Hasnext2Wrapper hasnext = currentScope.hasnext2(); // |
| * Cache each value of e.employee. Binding value_temp = |
| * currentScope.allocateTemp(); // Cache the result XMLList. Binding |
| * result_temp = currentScope.allocateTemp(); // Emit the stem |
| * expression and cache // the stem object in a local register. |
| * result.addAll(stem); result.addInstruction(OP_checkfilter); |
| * result.addInstruction(OP_coerce_a); |
| * result.addInstruction(hasnext.stem_temp.setlocal() ); // Initialize |
| * the index register. result.addInstruction(OP_pushbyte, 0); |
| * result.addInstruction(hasnext.index_temp.setlocal()); // Create the |
| * result XMLList. result.addInstruction(OP_findpropstrict, |
| * xmlListType); result.addInstruction(OP_getproperty, xmlListType); |
| * result.addInstruction(OP_pushstring, ""); |
| * result.addInstruction(OP_construct, 1); |
| * result.addInstruction(result_temp.setlocal() ); // Jump to the loop |
| * test. Label test = new Label(); result.addInstruction(OP_jump, test); |
| * // Top of the loop: put an AET Label on // the ABC OP_Label. Label |
| * loop = new Label(); result.labelNext(loop); |
| * result.addInstruction(OP_label); // Get the next value and cache it. |
| * result.addInstruction(hasnext.stem_temp.getlocal()); |
| * result.addInstruction(hasnext.index_temp.getlocal()); |
| * result.addInstruction(OP_nextvalue); result.addInstruction(OP_dup); |
| * result.addInstruction(value_temp.setlocal() ); // See ECMA-357 11.2.4 |
| * result.addInstruction(OP_pushwith); result.addAll(filter); Label |
| * no_match = new Label(); result.addInstruction(OP_iffalse, no_match); |
| * result.addInstruction(result_temp.getlocal() ); |
| * result.addInstruction(hasnext.index_temp.getlocal() ); |
| * result.addInstruction(value_temp.getlocal() ); |
| * result.addInstruction(OP_setproperty); result.labelNext(no_match); // |
| * See ECMA-357 11.2.4 result.addInstruction(OP_popscope); |
| * result.labelNext(test); result.addInstruction(hasnext.String); |
| * result.addInstruction(OP_iftrue, loop); |
| * result.addInstruction(result_temp.getlocal()); |
| * result.addAll(hasnext.release()); |
| * result.addAll(currentScope.releaseTemp(value_temp)); |
| * result.addAll(currentScope.releaseTemp(result_temp)); |
| */ |
| return result; |
| } |
| |
| public String reduce_embed(IASNode iNode) |
| {/* |
| * String name = ""; try { name = |
| * ((EmbedNode)iNode).getName(currentScope.getProject(), getProblems()); } |
| * catch (InterruptedException e) { // Throw a RuntimeException here rather |
| * than the InterruptedException // so that we don't need to modify the |
| * throws clause of all calling functions. // |
| * ABCGenerator.handleBurmError() will catch the RuntimeException, check // |
| * that the cause is a InterruptedException and respond accordingly. throw |
| * new RuntimeException(e); } Name embedName = new Name(name); |
| */ |
| String result = createInstruction(iNode); |
| /* |
| * result.addInstruction(OP_findproperty, embedName); |
| * result.addInstruction(OP_getproperty, embedName); |
| */ |
| |
| return result; |
| } |
| |
| /* |
| * FOR EACH gives you access to the element in the array. FOR IN gives you |
| * access to the index of the array. |
| */ |
| |
| public String reduce_forKeyValueStmt(IASNode iNode, Binding it, String base, String body, int opcode) |
| { |
| boolean isForInLoop = iNode.getNodeID() == ASTNodeID.ForLoopID; |
| String result = generateForKeyOrValueLoop(iNode, opcode, it, base, body); |
| result += reduce_forLoop(iNode, it, base, body, isForInLoop); |
| return result; |
| } |
| |
| public String reduce_forKeyValueArrayStmt(IASNode iNode, String stem, String index, String base, String body, int opcode, boolean is_super) |
| { |
| throw new IllegalStateException("Not implemented: for each(a[i] in ...)"); |
| } |
| |
| public String reduce_forKeyValueMemberStmt(IASNode iNode, String stem, Binding member, String base, String body, int opcode, boolean is_super) |
| { |
| throw new IllegalStateException("Not implemented: for each(x.y in ...)"); |
| } |
| |
| private IDefinition getDefinitionForNode(IASNode iNode, Boolean toRight) |
| { |
| // This is based on an idea Peter came up with... |
| try |
| { |
| IASNode node = iNode; |
| while (node != null) |
| { |
| if (node instanceof IBinaryOperatorNode && !(node instanceof MemberAccessExpressionNode)) |
| { |
| if (toRight) |
| node = ((IBinaryOperatorNode)node).getRightOperandNode(); |
| else |
| node = ((IBinaryOperatorNode)node).getLeftOperandNode(); |
| } |
| else if (node instanceof IFunctionCallNode) |
| node = ((IFunctionCallNode)node).getNameNode(); |
| else if (node instanceof IDynamicAccessNode) |
| node = ((IDynamicAccessNode)node).getLeftOperandNode(); |
| else if (node instanceof IUnaryOperatorNode) |
| node = ((IUnaryOperatorNode)node).getOperandNode(); |
| // return new Binding(iNode,makeName(member),((IUnaryOperatorNode)node).getDefinition()); |
| else if (node instanceof IForLoopNode) |
| node = ((IForLoopNode)node).getChild(0).getChild(0); |
| else if (node instanceof IVariableNode) |
| { |
| if (toRight) |
| node = ((IVariableNode)node).getAssignedValueNode(); |
| else |
| node = ((IVariableNode)node).getVariableTypeNode(); |
| } |
| else if (node instanceof IExpressionNode) |
| { |
| IDefinition def = ((IExpressionNode)node).resolve(currentScope.getProject()); |
| if (def instanceof VariableDefinition) |
| { |
| final VariableDefinition variable = (VariableDefinition)def; |
| def = variable.resolveType(currentScope.getProject()); |
| } |
| else if (def instanceof FunctionDefinition) |
| { |
| final FunctionDefinition functionDef = (FunctionDefinition)def; |
| final IReference typeRef = functionDef.getReturnTypeReference(); |
| if (typeRef != null) |
| def = typeRef.resolve(currentScope.getProject(), (ASScope)getScopeFromNode(iNode), DependencyType.INHERITANCE, false); |
| } |
| else if (def instanceof IGetterDefinition) |
| { |
| // return new Binding(iNode,makeName(member),leftDef); |
| final ITypeDefinition returnType = ((IGetterDefinition)def).resolveReturnType(currentScope.getProject()); |
| def = m_sharedData.getDefinition(returnType.getQualifiedName()); |
| } |
| |
| if (def != null && def instanceof ClassDefinition) |
| { |
| return def; |
| /* |
| * IDefinition memberDef = getDefinitionForMember( |
| * iNode, def, removeQuotes(member), |
| * ClassificationValue.MEMBERS_AND_TYPES ); if( |
| * memberDef != null ) return new |
| * Binding(iNode,makeName(member),memberDef); |
| */ |
| } |
| node = null; |
| } |
| else |
| { |
| node = null; |
| } |
| } |
| } |
| catch (Exception e) |
| { |
| // getDefinitionForNode(iNode,toRight); |
| |
| // getDefinition() sometimes crashes, e.g. when looking at a cast to an interface in some cases, |
| // FunctionDefinition.getParameters() returns null and ExpressionNodeBase.determineIfFunction() chokes on it |
| printWarning(iNode, "getDefinitionForNode() failed for" + iNode); |
| } |
| return null; |
| } |
| |
| private Boolean isDataClassDefinition(IDefinition def) |
| { |
| if (def != null && def instanceof IClassDefinition) |
| { |
| IClassDefinition classDef = (IClassDefinition)def; |
| Iterator<IClassDefinition> superClassIterator = classDef.classIterator(currentScope.getProject(), true); |
| |
| while (superClassIterator.hasNext()) |
| { |
| final IClassDefinition superClass = superClassIterator.next(); |
| if (superClass.getMetaTagByName("DataClass") != null) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private Boolean isProxyDefinition(IDefinition def) |
| { |
| if (def != null && def instanceof IClassDefinition) |
| { |
| IClassDefinition classDef = (IClassDefinition)def; |
| Iterator<IClassDefinition> superClassIterator = classDef.classIterator(currentScope.getProject(), true); |
| |
| while (superClassIterator.hasNext()) |
| { |
| final IClassDefinition superClass = superClassIterator.next(); |
| final String superClassName = superClass.getQualifiedName(); |
| |
| if (superClassName.equals("flash.utils.Proxy")) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * FOR EACH gives you access to the element in the array. FOR IN gives you |
| * access to the index of the array. |
| */ |
| private String reduce_forLoop(IASNode iNode, Binding it, String base, String body, Boolean isForInLoop) |
| { |
| final Boolean convertLocalVarsToMembers = convertLocalVarsToMembers(iNode); |
| |
| if (convertLocalVarsToMembers) |
| { |
| // support for [ConvertLocalVarsToMembers] |
| // reduce_forEachStmt() registers variables as members if convertLocalVarsToMembers(iNode) |
| // is true via switching to the class scope. |
| LexicalScope scope = currentScope.getEnclosingFrame(); |
| Boolean resetTraitsVisitor = false; |
| if (scope.traitsVisitor == null) |
| { |
| final JSEmitter emitter = (JSEmitter)currentScope.getEmitter(); |
| scope.traitsVisitor = emitter.getCurrentClassVisitor().visitInstanceTraits(); |
| resetTraitsVisitor = true; |
| } |
| |
| scope.makeVariable(it); |
| |
| if (resetTraitsVisitor) |
| scope.traitsVisitor = null; |
| |
| this.registerConvertedMember(getBasenameFromBinding(it)); |
| } |
| |
| final IDefinition def = getDefinitionForNode(iNode, true); |
| final String container = base; // bindingToString(__p, base,true,true); |
| // final ASDefinitionCache cache = currentScope.getDefinitionCache(); |
| final String baseName = getBasenameFromBinding(it); |
| final Boolean isFramework = getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME); |
| |
| String result = ""; |
| if (isFramework || isDataClassDefinition(def)) |
| { |
| result += indent() + "for(" + getVarSnippet(iNode, baseName) + " in "; |
| result += container; |
| result += ")" + endl(); |
| result += "{" + endl(); |
| if (!isForInLoop) |
| { |
| final IDefinition itDef = it.getDefinition(); |
| if (itDef != null && |
| itDef.resolveType(currentScope.getProject()) == currentScope.getProject().getBuiltinType(BuiltinType.STRING)) |
| { |
| result += indentBlock(baseName + " = " + container + "[" + baseName + "].toString();", 1); |
| } |
| else |
| result += indentBlock(baseName + " = " + container + "[" + baseName + "];", 1); |
| } |
| result += indentBlock(body, 1); |
| result += "}" + endl(); |
| } |
| /* |
| * TODO: This code does not work because the body may contain 'break', |
| * 'continue', or 'return' statements. For now we cannot else if(def == |
| * null || def == cache.getBuiltinType(BuiltinType.ANY_TYPE) || def == |
| * cache.getBuiltinType(BuiltinType.OBJECT) ) { final String |
| * forLoopFunction = isForInLoop ? "forInLoop" : "forEachLoop"; result |
| * += indent() + "adobe." + forLoopFunction + "( " + container + ",\n"; |
| * result += indentBlock( "function(" + baseName + ")\n", 2 ); result += |
| * indentBlock( "{" + endl(), 2 ); result += indentBlock( body, 3 ); |
| * result += indentBlock( "});" + endl(), 2 ); } |
| */ |
| else if (isProxyDefinition(def)) |
| { |
| final String typeComment = isForInLoop ? "/** @type {string} */\n" : "/** @type {object} */\n"; |
| final String nextFunction = isForInLoop ? "nextName" : "nextValue"; |
| final String nextName = baseName + "_next"; |
| result += "\n"; |
| result += "// Inlining for..in expression for Proxy object.\n"; |
| result += "/** @type {number} */\n"; |
| result += "var " + nextName + " = " + container + ".nextNameIndex(0);\n"; |
| result += "while(" + nextName + " > 0)\n"; |
| result += "{\n"; |
| result += indentBlock(typeComment, 1); |
| result += indentBlock("var " + baseName + " = " + container + "." + nextFunction + "(" + nextName + ");\n", 1); |
| result += indentBlock(body, 1); |
| result += indentBlock(nextName + " = " + container + ".nextNameIndex(" + nextName + ");\n", 1); |
| result += "}\n"; |
| } |
| else |
| { |
| result += indent() + "for(" + getVarSnippet(iNode, baseName) + " in "; |
| // AJH disable for now |
| // if (getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| result += container; |
| // else |
| // result += "adobe.getOwnProperties(" + container + ")"; |
| |
| result += ")" + endl(); |
| result += "{" + endl(); |
| if (!isForInLoop) |
| { |
| final IDefinition itDef = it.getDefinition(); |
| if (itDef != null && |
| itDef.resolveType(currentScope.getProject()) == currentScope.getProject().getBuiltinType(BuiltinType.STRING)) |
| result += indentBlock(baseName + " = " + container + "[" + baseName + "].toString();", 1); |
| else |
| result += indentBlock(baseName + " = " + container + "[" + baseName + "];", 1); |
| } |
| result += indentBlock(body, 1); |
| result += "}" + endl(); |
| } |
| |
| return result; |
| } |
| |
| public String reduce_forInArrayStmt(IASNode iNode, String base, String stem, String index, String body) |
| { |
| throw new IllegalStateException("Not implemented: for (a[i] in ...)"); |
| } |
| |
| public String reduce_forInMemberStmt(IASNode iNode, String base, String stem, String index, String body) |
| { |
| throw new IllegalStateException("Not implemented: for (x.y in ...)"); |
| } |
| |
| public String reduce_forVarDeclInStmt(IASNode __p, String single_decl, String base, String body, int opNextvalue) |
| { |
| throw new IllegalStateException("Not implemented: for (var x = 100 in ...)"); |
| } |
| |
| public String reduce_functionAsBracketExpr(IASNode iNode, String stem, String index, Vector<String> args) |
| { |
| // TODO: Investigate optimizing this. |
| String result = createInstruction(iNode); |
| |
| // Primary expressions. |
| // expression = Pattern functionAsBracketExpr : 4 |
| result += indent(); |
| String extraParams = ""; |
| final String name = index; //removeQuotes(index); |
| |
| final Binding memberBinding = makeMemberBinding(iNode, stem, index); |
| |
| final IDefinition def = memberBinding.getDefinition(); |
| |
| // constant propagation for initialized members |
| if (!m_hasSideEffects && hasSideEffects(def)) |
| m_hasSideEffects = true; |
| |
| // DataClass classes must not declare methods. |
| if (isDataClass(currentScope.getProject(), def)) |
| currentScope.addProblem(new JSDataClassMethodError(iNode)); |
| |
| // AJH disable for now |
| // dynamic classes need to call adobe.callProperty |
| /* |
| if (!isCurrentFramework() && (def == null || def.isDynamic())) |
| { |
| registerAccessedPropertyName(iNode, name); |
| result += indent() + "adobe.callProperty( this, " + stem + ", " + name + ", ["; |
| result += collectCallParameters(iNode, stem, memberBinding, args, JSSharedData.m_useSelfParameter, extraParams); |
| result += "])"; |
| } |
| else |
| { |
| */ |
| // result += getRootName() + "."; |
| if (m_convertToStatic) |
| { |
| String owner = getTypeOfInstance(iNode, stripNS(stem)); |
| if (owner.length() == 0 || isDataType(owner) || isBrowserDataType(owner) || owner.equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| result += stripNS(stem); |
| else |
| { |
| owner = findClassWithMethod(owner, name); |
| result += createFullName(owner, true); |
| } |
| result += "." + name; |
| } |
| else |
| { |
| result += stripNS(stem); |
| if (!stem.equals(JSSharedData.THIS + "." + JSSharedData._SUPER)) |
| result += "[" + name + "]"; |
| } |
| |
| result += "("; |
| result += collectCallParameters(iNode, stem, memberBinding, args, JSSharedData.m_useSelfParameter, extraParams); |
| result += ")"; |
| /* AJH } */ |
| return result; |
| } |
| |
| public String reduce_functionAsMemberExpr(IASNode iNode, String stem, Binding method_name, Vector<String> args) |
| { |
| |
| currentScope.getMethodBodySemanticChecker().checkFunctionCall(iNode, method_name, args); |
| |
| // TODO: Investigate optimizing this. |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); for ( String arg: args) result.addAll(arg); |
| * result.addInstruction(OP_callproperty, new Object[] |
| * {method_name.getName(), args.size() } ); |
| */ |
| |
| // Primary expressions. |
| // Pattern functionAsMemberExpr, expression |
| // FunctionCallID(MemberAccessExpressionID(expression stem, name method_name), expressionList args); |
| |
| final String basename = getBasenameFromBinding(method_name); |
| |
| IDefinition def = method_name.getDefinition(); |
| |
| // items from Vectors trigger adobe.getProperty() |
| if (def == null && iNode instanceof IFunctionCallNode) |
| { |
| final Binding b = this.makeMemberBinding(iNode, stem, basename); |
| def = b.getDefinition(); |
| } |
| |
| // constant propagation for initialized members |
| if (!m_hasSideEffects && hasSideEffects(def)) |
| m_hasSideEffects = true; |
| |
| // DataClass classes must not declare methods. |
| if (isDataClass(currentScope.getProject(), def)) |
| currentScope.addProblem(new JSDataClassMethodError(iNode)); |
| |
| // AJH disable |
| /* |
| // dynamic classes need to call adobe.callProperty |
| if (!isCurrentFramework() && (def == null || def.isDynamic())) |
| { |
| registerAccessedPropertyName(iNode, basename); |
| result += indent() + "adobe.callProperty( this, " + stem + ", \"" + basename + "\", ["; |
| result += collectCallParameters(iNode, "", method_name, args, JSSharedData.m_useSelfParameter, ""); |
| result += "])"; |
| } |
| else |
| { |
| */ |
| result += indent(); |
| |
| // result += getRootName() + "."; |
| if (m_convertToStatic) |
| { |
| String owner = getTypeOfInstance(iNode, stripNS(stem)); |
| if (owner.length() == 0 || isDataType(owner) || isBrowserDataType(owner) || owner.equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| result += stripNS(stem); |
| else |
| { |
| owner = findClassWithMethod(owner, basename); |
| result += createFullName(owner, true); |
| } |
| result += "." + basename; |
| } |
| else |
| { |
| result += stripNS(stem); |
| if (!stem.equals(JSSharedData.THIS + "." + JSSharedData._SUPER)) |
| result += "." + basename; |
| } |
| result += "("; |
| result += collectCallParameters(iNode, stem, method_name, args, JSSharedData.m_useSelfParameter, ""); |
| result += ")"; |
| /* AJH } */ |
| return result; |
| } |
| |
| public String reduce_functionAsRandomExpr(IASNode iNode, String random_expr, Vector<String> args) |
| { |
| // TODO: Investigate optimizing this. |
| String result = createInstruction(iNode); |
| |
| // constant propagation for initialized members |
| m_hasSideEffects = true; |
| |
| // random_expr can be just a comment (see reduce_parameterizedName_expression) |
| // i.e. in fl.motion.MatrixTransformer3D you'll find this line of code: |
| // var vec3D:Vector.<Number> = Vector.<Number>(getRawDataVector(mat)); |
| final Boolean isComment = random_expr.startsWith("/*") && random_expr.endsWith("*/"); |
| |
| if (!isComment) |
| result += "(" + random_expr + ")"; |
| |
| result += "("; |
| result += collectCallParameters(iNode, "", makeBinding(iNode, random_expr), args, JSSharedData.m_useSelfParameter, ""); |
| result += ")"; |
| |
| if (isComment) |
| result += " " + random_expr; |
| return result; |
| } |
| |
| public String reduce_functionCallExpr_to_expression(IASNode iNode, Binding method_name, Vector<String> args) |
| { |
| return reduce_functionCall_common(iNode, method_name, args, true); |
| } |
| |
| public String reduce_functionCallExpr_to_void_expression(IASNode iNode, Binding method_name, Vector<String> args) |
| { |
| return reduce_functionCall_common(iNode, method_name, args, false); |
| } |
| |
| private String reduce_functionCall_common(IASNode iNode, Binding method_name, Vector<String> args, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkFunctionCall(iNode, method_name, args); |
| String result = createInstruction(iNode); |
| |
| // Primary expressions. |
| // Pattern functionCallExpr, expression |
| // FunctionCallID(name method_name, expressionList args); |
| |
| // Sometimes, what looks syntactically like a function call is actally a type-cast expression. And |
| // sometimes, a cast expression is actually a 'new' operator in disguise. Either way, we shouldn't |
| // generate function call syntax in JS, since in JS that syntax *always* means a functon call. |
| result = handleCastExpr(iNode, method_name, args); |
| if (result != null) |
| return result; |
| |
| String extraParams = ""; |
| String mname = bindingToString(iNode, method_name, true, true); |
| |
| // dynamic classes need to call adobe.callProperty |
| final IDefinition def = method_name.getDefinition(); |
| |
| // constant propagation for initialized members |
| if (!m_hasSideEffects && hasSideEffects(def)) |
| m_hasSideEffects = true; |
| |
| // AJH disable for now |
| /* |
| if (mname.contains(".") && !isCurrentFramework() && (def == null || def.isDynamic())) |
| { |
| final String stem = mname.substring(0, mname.lastIndexOf(".")); |
| final String basename = mname.substring(mname.lastIndexOf(".") + 1); |
| registerAccessedPropertyName(iNode, basename); |
| result = indent() + "adobe.callProperty( this, " + stem + ", \"" + basename + "\", ["; |
| result += collectCallParameters(iNode, "", method_name, args, JSSharedData.m_useSelfParameter, ""); |
| result += "])"; |
| } |
| else |
| { |
| */ |
| if (JSSharedData.m_useClosureLib) |
| { |
| String fname = ""; |
| if (currentScope != null && |
| currentScope.getMethodInfo() != null && |
| currentScope.getMethodInfo().getMethodName() != null) |
| { |
| fname = currentScope.getMethodInfo().getMethodName(); |
| } |
| |
| if (mname.equals(JSSharedData.THIS + "." + JSSharedData.CLASS_NAME + ".superClass_")) |
| { |
| if (!fname.isEmpty()) |
| { |
| mname += "." + fname + ".call"; |
| extraParams = JSSharedData.THIS; |
| } |
| } |
| else if (mname.equals("goog.base")) |
| { |
| if (!fname.isEmpty()) |
| extraParams = "this, \"" + fname + "\""; |
| else |
| extraParams = "this, undefined"; |
| } |
| else if (mname.equals("adobe.base")) |
| { |
| printWarning(iNode, "Hand-written adobe.base() calls are not supported"); |
| assert false; |
| } |
| } |
| |
| result = indent() + mname + "("; |
| result += collectCallParameters(iNode, "", method_name, args, JSSharedData.m_useSelfParameter, extraParams); |
| result += ")"; |
| /* AJH } */ |
| |
| if (!need_result) |
| result += ";" + endl(); |
| |
| return result; |
| } |
| |
| public String reduce_functionCallOfSuperclassMethod_to_expression(IASNode iNode, String stem, Binding method_name, Vector<String> args) |
| { |
| // TODO: Investigate optimizing this. |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addInstruction(OP_getlocal0); for ( String arg: args) |
| * result.addAll(arg); result.addInstruction(OP_callsuper, new Object[] |
| * {method_name.getName(), args.size() } ); |
| */ |
| |
| // Note: if method_name == null then this is a super() call inside a constructor. |
| // If method_name != null then this is a super.foo() call (which can be anywhere). |
| |
| // Could this be a ctor super() call where the superclass is Object? We don't want to emit anything |
| // for that no-op case since goog.base() chokes when the super is Object. |
| if (method_name == null && args.size() == 0) |
| { |
| if (getEmitter().getCurrentSuperClassFullName().equals("Object")) |
| return ""; |
| } |
| |
| // constant propagation for initialized members |
| if (!m_hasSideEffects && method_name != null) |
| { |
| final IDefinition def = method_name.getDefinition(); |
| if (!m_hasSideEffects && hasSideEffects(def)) |
| m_hasSideEffects = true; |
| } |
| |
| // goog.base() doesn't support the construct A() { super.B() } - need to use custom adobe.base() instead |
| boolean needAdobeBase = false; |
| String invokingMethodName = null; |
| IClassDefinition invokingMethodClass = null; |
| if (method_name != null) |
| { |
| String invokingMethodRawName = getCurrentMethodName(); //method we're looking at, containing the super.foo() call |
| IFunctionDefinition enclosingDef = getCurrentMethod(iNode); |
| invokingMethodName = mungeMethodName(invokingMethodRawName, enclosingDef, false); |
| |
| String superMethodName = getBasenameFromBinding(method_name); // method being invoked via super, e.g. "foo" in super.foo() |
| if (!invokingMethodName.equals(superMethodName)) |
| { |
| needAdobeBase = true; |
| invokingMethodClass = (IClassDefinition)enclosingDef.getParent(); |
| } |
| } |
| |
| String extraParams = ""; |
| Boolean addSelfParameter = JSSharedData.m_useSelfParameter; |
| String _name; |
| if (JSSharedData.m_useClosureLib) |
| { |
| if (!needAdobeBase) |
| { |
| _name = "goog.base"; |
| extraParams = "this"; |
| if (method_name != null) |
| extraParams += ", \"" + getBasenameFromBinding(method_name) + "\""; |
| } |
| else |
| { |
| _name = "adobe.base"; |
| extraParams = "this"; |
| extraParams += ", " + getSuperMethodReference(invokingMethodClass, getBasenameFromBinding(method_name)); |
| } |
| } |
| else |
| { |
| if (m_convertToStatic) |
| { |
| String fullClassName = createFullClassName(false); |
| if (m_sharedData.hasClass(makeName(fullClassName))) |
| { |
| fullClassName = getCurrentSuperClassFullName(); |
| _name = fullClassName; |
| } |
| else |
| _name = createFullClassName(true) + "." + JSSharedData._SUPER; |
| |
| if (this.m_functionName.length() > 0) |
| _name += "." + this.m_functionName; |
| } |
| else |
| { |
| final Boolean isFramework = getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME); |
| final String thisName = isFramework ? "this" : JSSharedData.THIS; |
| _name = "goog.base(this, \"" + invokingMethodName + "\", "; |
| } |
| } |
| |
| result += indent() + _name; |
| result += collectCallParameters(iNode, "", method_name, args, addSelfParameter, extraParams); |
| |
| result += ")"; |
| return result; |
| } |
| |
| public String reduce_functionCallOfSuperclassMethod_to_void_expression(IASNode iNode, String stem, Binding method_name, Vector<String> args) |
| { |
| String result = createInstruction(iNode); |
| /* |
| * result.addInstruction(OP_getlocal0); for ( String arg: args) |
| * result.addAll(arg); result.addInstruction(OP_callsupervoid, new |
| * Object[] {method_name.getName(), args.size() } ); |
| */ |
| |
| // void_expression = Pattern functionCallOfSuperclassMethod : 1 |
| // Binding method_name, Vector<InstructionList> args |
| result += reduce_functionCallOfSuperclassMethod_to_expression(iNode, stem, method_name, args) + ";" + endl(); |
| |
| return result; |
| } |
| |
| public String reduce_groupedVoidExpression(IASNode iNode, Vector<String> contents) |
| { |
| String result = createInstruction(iNode); |
| |
| for (String expr : contents) |
| result += expr; |
| |
| return result; |
| } |
| |
| public String reduce_ifElseIf(IASNode iNode, String test, String then, Vector<ConditionalFragment> if_elseif) |
| { |
| // TODO: Experiment with optimizing this. |
| // Iterating twice over the arms of the if |
| // might cost more than it helps. |
| String result = createInstruction(iNode); |
| |
| /* |
| * boolean has_else = !if_elseif.isEmpty(); // Label to the |
| * "next statement." Label tail = new Label(); // TODO: Use |
| * conditionalJump style tests. result.addAll(test); if ( has_else ) |
| * result.addInstruction(OP_iffalse, |
| * if_elseif.firstElement().getLabel()); else |
| * result.addInstruction(OP_iffalse, tail); result.addAll(then); if ( |
| * has_else ) { result.addInstruction(OP_jump, tail); for ( int i = 0; i |
| * < if_elseif.size() - 1; i++ ) { ConditionalFragment alternative = |
| * if_elseif.elementAt(i); if ( |
| * !alternative.isUnconditionalAlternative() ) { // Add the test and |
| * conditional jump to the next alternative. |
| * result.addAll(alternative.condition); ConditionalFragment |
| * next_alternative = if_elseif.elementAt(i+1); |
| * result.addInstruction(OP_iffalse, next_alternative.getLabel()); } |
| * result.addAll(alternative.statement); result.addInstruction(OP_jump, |
| * tail); } ConditionalFragment last_clause = if_elseif.lastElement(); |
| * if ( !last_clause.isUnconditionalAlternative() ) { |
| * result.addAll(last_clause.condition); |
| * result.addInstruction(OP_iffalse, tail); |
| * result.addAll(last_clause.statement); } else { |
| * result.addAll(last_clause.statement); } } result.labelNext(tail); |
| */ |
| // statement = Pattern ifElseIf |
| |
| result += indent() + "if (" + (test) + ")" + endl(); |
| result += indent() + "{" + endl(); |
| result += indentBlock(then, 1); |
| result += indent() + "}" + endl(); |
| |
| // Vector<ConditionalFragment> if_elseif = (Vector<ConditionalFragment>) raw_if_elseif; |
| boolean has_else = !if_elseif.isEmpty(); |
| if (has_else) |
| { |
| for (int i = 0; i < if_elseif.size(); i++) |
| { |
| result += indent() + "else"; |
| ConditionalFragment alternative = if_elseif.elementAt(i); |
| if (!alternative.isUnconditionalAlternative()) |
| { |
| result += " if (" + alternative.condition + ")"; |
| } |
| result += endl(); |
| result += indent() + "{" + endl(); |
| result += indentBlock(alternative.statement, 1); |
| result += indent() + "}" + endl(); |
| } |
| } |
| return result; |
| } |
| |
| public String reduce_importDirective(IASNode iNode) |
| { |
| currentScope.getMethodBodySemanticChecker().checkImportDirective(((IImportNode)iNode)); |
| return createInstruction(iNode); |
| } |
| |
| public String reduce_labeledBreakStmt(IASNode iNode) |
| { |
| final String target = ((IdentifierNode)SemanticUtils.getNthChild(iNode, 0)).getName(); |
| return "break " + target + ";\n"; |
| } |
| |
| public String reduce_labeledContinueStmt(IASNode iNode) |
| { |
| final String target = ((IdentifierNode)SemanticUtils.getNthChild(iNode, 0)).getName(); |
| return "continue " + target + ";\n"; |
| } |
| |
| public String reduce_labeledStmt(IASNode iNode, String label, Vector<String> substatements) |
| { |
| String result = createInstruction(iNode); |
| |
| // statement = Pattern labeledStmt |
| |
| result += label + ": "; |
| result += "{\n"; |
| String subs = ""; |
| for (String substatement : substatements) |
| subs += substatement; |
| result += indentBlock(subs, 1); |
| result += "};\n"; |
| |
| // currentScope.getFlowManager().finishControlFlowContext(result); |
| // if ( iNode.getChildCount() == 2 ) |
| // labeledNodes.remove(JSGeneratingReducer.getNthChild(iNode, 1)); |
| return result; |
| } |
| |
| public String reduce_gotoStmt(IASNode iNode) |
| { |
| String result = ""; |
| assert false : "implement me!!"; |
| return result; |
| } |
| |
| public String reduce_labeledStmt(IASNode iNode, String label, String substatement) |
| { |
| String result = ""; |
| assert false : "fix me!!!"; |
| return result; |
| } |
| |
| public ConditionalFragment reduce_lastElse(IASNode iNode, String else_clause) |
| { |
| return new ConditionalFragment(iNode, null, else_clause); |
| } |
| |
| public String reduce_logicalAndExpr(IASNode iNode, String l, String r) |
| { |
| String result = createInstruction(iNode, l.length() + r.length() + 3); |
| |
| // Pattern logicalAndExpr |
| // Op_LogicalAndID(expression l, expression r); |
| result += binaryOp(l, r, "&&"); |
| |
| return result; |
| } |
| |
| public String reduce_logicalNotExpr(IASNode iNode, String expr) |
| { |
| String result = createInstruction(iNode, expr.length() + 1); |
| |
| // Pattern logicalNotExpr |
| // Op_LogicalNotID(expression expr); |
| result += "(!" + stripTabs(stripNS(expr)) + ")"; |
| return result; |
| } |
| |
| public String reduce_logicalOrExpr(IASNode iNode, String l, String r) |
| { |
| String result = createInstruction(iNode, l.length() + r.length() + 3); |
| |
| // Pattern logicalOrExpr |
| // Op_LogicalOrID(expression l, expression r); |
| result += binaryOp(l, r, "||"); |
| |
| return result; |
| } |
| |
| public String reduce_memberAccessExpr(IASNode iNode, String stem, Binding member, int opcode) |
| { |
| currentScope.getMethodBodySemanticChecker().checkMemberAccess(iNode, member, opcode); |
| |
| String result = createInstruction(iNode, stem.length() + 1); |
| |
| // items from Vectors trigger adobe.getProperty() |
| IDefinition def = member.getDefinition(); |
| if (def == null && iNode instanceof IExpressionNode) |
| { |
| def = ((IExpressionNode)iNode).resolve(currentScope.getProject()); |
| if (def != null) |
| { |
| def = def.getParent(); |
| if (def != null && def instanceof ClassDefinition) |
| { |
| final String basename = getBasenameFromBinding(member); |
| IDefinition memberDef = getDefinitionForMember(iNode, def, removeQuotes(basename), ClassificationValue.MEMBERS_AND_TYPES); |
| if (memberDef != null) |
| member = new Binding(iNode, member.getName(), memberDef); |
| } |
| } |
| } |
| |
| // Primary expressions. |
| // Pattern memberAccessExpr |
| // MemberAccessExpressionID(expression stem, name member); |
| result += resolveGetterName(iNode, stem, member, false); |
| |
| return result; |
| } |
| |
| public String reduce_qualifiedMemberAccessExpr(IASNode iNode, String stem, Binding qualifier, Binding member, int opcode) |
| { |
| currentScope.getMethodBodySemanticChecker().checkMemberAccess(iNode, member, opcode); |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "qualified member access")); |
| |
| String result = createInstruction(iNode); |
| return result; |
| } |
| |
| public String reduce_qualifiedAttributeRuntimeMemberExpr(IASNode iNode, String stem, Binding qualifier, String runtime_member_selector, int opcode) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "qualified attribute runtime member access")); |
| |
| String result = createInstruction(iNode); |
| return result; |
| } |
| |
| public String reduce_qualifiedMemberRuntimeNameExpr(IASNode iNode, String stem, Binding qualifier, String runtime_member_selector) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "qualified member runtime member access")); |
| |
| String result = createInstructionList(iNode); |
| return result; |
| } |
| |
| public String reduce_qualifiedAttributeExpr(IASNode iNode, String stem, Binding qualifier, Binding member, int opcode) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "qualified attribute expression")); |
| |
| String result = createInstruction(iNode); |
| return result; |
| } |
| |
| public String reduce_unqualifiedAttributeExpr(IASNode iNode, String stem, String rt_attr, int opcode) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "unqualified attribute expression")); |
| |
| String result = createInstruction(iNode); |
| return result; |
| } |
| |
| public String reduce_runtimeAttributeExp(IASNode iNode, String rt_attr) |
| { |
| // TODO: Unsupported operation. See also reduce_unqualifiedAttributeExpr(). |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "unqualified attribute expression")); |
| String result = createInstruction(iNode); |
| return result; |
| } |
| |
| public String reduce_mxmlEventSpecifier(IASNode iNode, Vector<String> stmts) |
| { |
| // TODO: Investigate optimizing. |
| String body = createInstruction(iNode); |
| |
| /* |
| * // An MXMLEventSpecifierNode can have N child nodes which are |
| * statements. // We need to codegen all of them as an effective method |
| * body. |
| */ |
| for ( String stmt: stmts ) |
| body += stmt; |
| return generateFunctionBody(iNode, body, LexicalScope.anyType); |
| |
| // function = Pattern mxmlEventSpecifier |
| // ArgumentID(Vector<String> stmts) |
| |
| // An MXMLEventSpecifierNode can have N child nodes which are statements. |
| // We need to codegen all of them as an effective method body. |
| // for ( String stmt: stmts ) |
| // body += stmt; |
| |
| // The effective method they're inside of has a single parameter. |
| // Its name is "event" (which is discovered from the ParameterDefinition |
| // that the event node creates). |
| // Its type, such as flash.events.MouseEvent, has already been determined |
| // when we created the MethodInfo, so we access it there |
| // rather than creatinga another equivalent Name. |
| |
| // TODO: |
| /* |
| * currentScope.makeParameter(#MXMLEventSpecifierID#. |
| * getEventParameterDefinition(), |
| * currentScope.methodInfo.getParamTypes().get(0)); return |
| * generateFunctionBody(body, null); |
| */ |
| |
| // return null; |
| } |
| |
| public Binding reduce_namespaceAccess(IASNode iNode, IASNode qualifier, Binding qualified_name) |
| { |
| currentScope.getMethodBodySemanticChecker().checkQualifier(qualifier); |
| // All the qualifying syntax nodes are just concrete. |
| return qualified_name; |
| } |
| |
| public RuntimeMultiname reduce_namespaceAccess_to_runtime_name(IASNode iNode, Binding qualified_name) |
| { |
| String qualifier = createInstruction(iNode); |
| qualifier += generateAccess(iNode, reduce_simpleName(iNode.getChild(0))); |
| // qualifier.addInstruction(OP_coerce, namespaceType); |
| return new RuntimeMultiname(qualifier.toString(), qualified_name.getName()); |
| } |
| |
| public String reduce_namespaceAsName_to_expression(IASNode iNode) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "namespace as name")); |
| |
| String result = createInstruction(iNode); |
| return result; |
| } |
| |
| public Name reduce_namespaceAsName_to_multinameL(IASNode iNode) |
| { |
| return reduce_namespaceAsName_to_multinameL(iNode, false); |
| } |
| |
| public Name reduce_namespaceAsName_to_multinameL(IASNode iNode, final boolean is_attribute) |
| { |
| IdentifierNode qualifier = (IdentifierNode)iNode; |
| NamespaceDefinition ns = (NamespaceDefinition)qualifier.resolve(currentScope.getProject()); |
| |
| int name_kind = is_attribute ? CONSTANT_MultinameLA : CONSTANT_MultinameL; |
| return new Name(name_kind, new Nsset(ns.resolveAETNamespace(currentScope.getProject())), null); |
| } |
| |
| public Binding reduce_namespaceAsName_to_name(IASNode iNode) |
| { |
| return currentScope.resolveName((org.apache.flex.compiler.internal.tree.as.IdentifierNode)iNode); |
| } |
| |
| public String reduce_namespaceDeclaration(IASNode iNode, Binding ns_name) |
| { |
| // Get the AET Namespace from the namespace's definition. |
| NamespaceDefinition def = ((NamespaceNode)iNode).getDefinition(); |
| return reduce_namespaceDeclarationConstantInitializer(iNode, ns_name, def.resolveAETNamespace(currentScope.getProject())); |
| } |
| |
| public String reduce_namespaceDeclarationConstantInitializer(IASNode iNode, Binding ns_name, String uri) |
| { |
| // Make a new CONSTANT_Namespace with the given URI - all user defined namespaces initialized with a |
| // String literal become CONSTANT_Namespaces |
| final Namespace ns = new Namespace(CONSTANT_Namespace, uri); |
| return reduce_namespaceDeclarationConstantInitializer(iNode, ns_name, ns); |
| } |
| |
| /** |
| * Does the work for the various reduce_namespaceDecl methods. Creates a new |
| * property, with an initial value of the Namespace passed in. |
| */ |
| public String reduce_namespaceDeclarationConstantInitializer(IASNode iNode, Binding ns_name, Namespace initializer) |
| { |
| NamespaceDefinition def = ((NamespaceNode)iNode).getDefinition(); |
| |
| switch (SemanticUtils.getMultiDefinitionType(def, currentScope.getProject())) |
| { |
| case AMBIGUOUS: |
| currentScope.addProblem(new DuplicateNamespaceDefinitionProblem(iNode)); |
| break; |
| case NONE: |
| break; |
| default: |
| assert false; // I don't think namespaces can have other type of multiple definitions |
| } |
| |
| if (initializer == null) |
| { |
| // Can't declare a namespace if we don't know what its initial value is |
| getProblems().add(new InvalidOverrideProblem(iNode)); |
| return null; |
| } |
| |
| currentScope.makeNamespace(ns_name, initializer); |
| /* |
| * if ( ns_name.isLocal() ) { // Locals have no initializer, so the |
| * variable // needs explicit initialization code. |
| * currentScope.getHoistedInitInstructions |
| * ().addInstruction(OP_pushnamespace, initializer); |
| * currentScope.getHoistedInitInstructions |
| * ().addInstruction(ns_name.setlocal()); } |
| */ |
| |
| // workaround for Falcon bug, see checkintests/as3/AbcDecoder/accessSpecifiers.as |
| String uri = initializer.getName(); |
| uri = removeQuotes(uri); |
| m_sharedData.registerNamespace(uri, def); |
| |
| return null; |
| } |
| |
| public String reduce_namespaceDeclarationInitializer(IASNode iNode, Binding ns_name, Binding second_ns) |
| { |
| String uri; |
| |
| if (second_ns.getDefinition() instanceof NamespaceDefinition) |
| { |
| NamespaceDefinition ns_def = (NamespaceDefinition)second_ns.getDefinition(); |
| uri = ns_def.resolveAETNamespace(currentScope.getProject()).getName(); |
| } |
| else |
| { |
| getProblems().add(new InvalidOverrideProblem(iNode)); |
| return null; |
| } |
| |
| return reduce_namespaceDeclarationConstantInitializer(iNode, ns_name, uri); |
| } |
| |
| public RuntimeMultiname reduce_namespaceMultinameL(IASNode iNode, IASNode qualifier_node, String expr) |
| { |
| Name qualifier = reduce_namespaceAsName_to_multinameL(qualifier_node); |
| return new RuntimeMultiname(qualifier, expr); |
| } |
| |
| public RuntimeMultiname reduce_namespaceRTQName(IASNode iNode, String qualifier, Binding qualified_name) |
| { |
| return new RuntimeMultiname(qualifier, qualified_name.getName()); |
| } |
| |
| public RuntimeMultiname reduce_namespaceRTQNameL(IASNode iNode, String qualifier, String expr) |
| { |
| return new RuntimeMultiname(qualifier, expr); |
| } |
| |
| public String reduce_neqExpr(IASNode iNode, String l, String r) |
| { |
| /* |
| * String result = binaryOp(iNode, l, r, OP_equals); |
| * result.addInstruction(OP_not); |
| */ |
| |
| // Binary logical operators. |
| // Pattern neqExpr |
| // Op_NotEqualID(expression l, expression r) ; |
| String result = binaryOp(l, r, "!="); |
| return result; |
| } |
| |
| public Binding reduce_nameToTypeName(Binding name, boolean check_name) |
| { |
| if (check_name) |
| { |
| currentScope.getMethodBodySemanticChecker().checkTypeName(name); |
| } |
| return name; |
| } |
| |
| public String reduce_newMemberProperty(IASNode iNode, String stem, Binding member, Vector<String> args) |
| { |
| String result = createInstruction(iNode); |
| |
| result += "new "; |
| result += stem; |
| result += "."; |
| result += member.getName().getBaseName(); |
| result += "("; |
| |
| for (int i = 0; i < args.size(); i++) |
| { |
| if (i > 0) |
| result += ","; |
| result += args.get(i); |
| } |
| |
| result += ")"; |
| |
| return result; |
| } |
| |
| public String reduce_newAsRandomExpr(IASNode iNode, String random_expr, Vector<String> args) |
| { |
| // TODO: Investigate optimizing this. |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(random_expr); for ( String arg: args) |
| * result.addAll(arg); result.addInstruction(OP_construct, args.size() |
| * ); |
| */ |
| |
| // expression = Pattern newAsRandomExpr : 30 |
| // String random_expr, Vector<String> args |
| |
| // TODO: add test case to Test.as |
| result += generateNewCall(iNode, makeBinding(iNode, random_expr), args); |
| |
| return result; |
| } |
| |
| /** |
| * Reduces <code>new Array()</code>. |
| */ |
| public String reduce_newEmptyArray(IASNode iNode) |
| { |
| return "[]"; |
| } |
| |
| /** |
| * Reduces <code>new Object()</code>. |
| */ |
| public String reduce_newEmptyObject(IASNode iNode) |
| { |
| return "{}"; |
| } |
| |
| public String reduce_newExpr(IASNode iNode, Binding class_binding, Vector<String> args) |
| { |
| // currentScope.getMethodBodySemanticChecker().checkNewExpr(iNode, class_binding, args); |
| |
| // TODO: Investigate optimizing this. |
| |
| String result = createInstruction(iNode); |
| |
| // Primary expressions. |
| // Pattern newExpr |
| // FunctionCallID(new_keyword xnew, name class_name, expressionList args); |
| |
| // TODO: Investigate optimizing this. |
| final Name class_name = class_binding.getName(); |
| |
| if (class_name != null && class_name.isTypeName()) |
| { |
| // generateAccess(class_name, result); |
| |
| // result.addInstruction(OP_construct, args.size()); |
| |
| // support for vectors. |
| if (isVectorTypeName(class_name)) |
| { |
| Boolean comma = false; |
| result = "new Array("; |
| for (String arg : args) |
| { |
| if (comma) |
| result += ", "; |
| else |
| comma = true; |
| result += arg; |
| } |
| result += ")"; |
| result += " /* of " + class_name.getTypeNameParameter().getBaseName() + " */"; |
| } |
| else |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "new " + getBasenameFromName(class_name))); |
| } |
| } |
| else |
| { |
| result = generateNewCall(iNode, class_binding, args); |
| } |
| return result; |
| } |
| |
| public String reduce_newVectorLiteral(IASNode iNode, String literal) |
| { |
| return literal; |
| } |
| |
| public String reduce_nilExpr_to_conditionalJump(IASNode iNode) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_jump); |
| */ |
| |
| // Pattern nilExpr, conditionalJump |
| // NilID(void); |
| result += "== null"; |
| return result; |
| } |
| |
| public String reduce_nilExpr_to_expression(IASNode iNode) |
| { |
| String result = createInstruction(iNode, 1); |
| /* |
| * result.addInstruction(OP_pushundefined); |
| */ |
| return result; |
| } |
| |
| public Object reduce_nullLiteral_to_constant_value(IASNode iNode) |
| { |
| return null; |
| } |
| |
| public String reduce_nullLiteral_to_object_literal(IASNode iNode) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_pushnull); |
| */ |
| |
| // Pattern nullLiteral |
| // object_constant = nullLiteral |
| result += "null"; |
| |
| return result; |
| } |
| |
| public String reduce_objectLiteral(IASNode iNode, Vector<String> elements) |
| { |
| // TODO: Investigate optimizing. |
| String result = createInstruction(iNode); |
| |
| /* |
| * for ( String element: elements ) { result.addAll(element); } |
| * result.addInstruction(OP_newobject, elements.size()); |
| */ |
| |
| // Pattern objectLiteral |
| // ObjectLiteralExpressionID(ContainerID(object_literal_element elements*)); |
| |
| result += "{"; |
| boolean comma = false; |
| for (String element : (Vector<String>)elements) |
| { |
| if (comma) |
| result += ", "; |
| else |
| comma = true; |
| result += element; |
| } |
| result += "}"; |
| return result; |
| } |
| |
| public String reduce_objectLiteralElement(IASNode iNode, String id, String value) |
| { |
| String result = createInstruction(iNode, value.length() + 1); |
| |
| /* |
| * result.addAll(id); // TODO: Push type analysis up through the CG, // |
| * so that string constants don't go through // convert_s. See |
| * http://bugs.adobe.com/jira/browse/CMP-118 |
| * result.addInstruction(OP_convert_s); result.addAll(value); |
| */ |
| |
| // Pattern objectLiteralElement |
| // ObjectLiteralValuePairID(expression id, expression value); |
| |
| final IObjectLiteralValuePairNode node = (IObjectLiteralValuePairNode)iNode; |
| final IExpressionNode valueNode = node.getValueNode(); |
| final IDefinition valueDef = valueNode.resolve(currentScope.getProject()); |
| |
| result += id; |
| result += ": " + injectCreateProxy(iNode, valueDef, value); |
| |
| // TODO: Push type analysis up through the CG, |
| // so that string constants don't go through |
| // convert_s. See http://bugs.adobe.com/jira/browse/CMP-118 |
| return result; |
| } |
| |
| public String reduce_optionalParameter(IASNode iNode, Name param_name, Binding param_type, Object default_value) |
| { |
| /* |
| * currentScope.makeParameter(getParameterContent(iNode), |
| * param_type.getName()); currentScope.addDefaultValue(default_value); |
| */ |
| |
| // Pattern optionalParameter |
| // ArgumentID(name param_name, name param_type, Object default_value); |
| |
| if (param_type.getName() != null) |
| usedTypes.add(getBasenameFromBinding(param_type)); |
| // this.makeLocalVariable(currentScope, param_name, param_type, param_type); |
| this.makeParameter(iNode, ((org.apache.flex.compiler.internal.tree.as.ParameterNode)iNode), currentScope, param_name, param_type, param_type); |
| |
| // currentScope.addDefaultValue(default_value); |
| if (default_value == null) |
| default_value = ABCConstants.NULL_VALUE; |
| currentScope.getMethodInfo().addDefaultValue(new PooledValue(default_value)); |
| |
| return null; |
| } |
| |
| public Binding reduce_parameterizedTypeName(IASNode iNode, Binding base, Binding param) |
| { |
| Name param_name = param.getName(); |
| if (param_name.couldBeAnyType()) |
| param_name = null; |
| return new Binding(iNode, new Name(base.getName(), param_name), null); |
| } |
| |
| public String reduce_parameterizedTypeExpression(IASNode iNode, String base, String param) |
| { |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(base); generateTypeNameParameter(param.getName(), |
| * result); result.addInstruction(OP_applytype, 1); |
| */ |
| |
| // expression = Pattern parameterizedName : 1 |
| // Binding base, Binding param |
| |
| // We get here due to AS code that looks like this: (note: no "new" operator) |
| // Vector.<String>( ["foo", "bar"] ) |
| // The resulting emitted JS will look like this: |
| // (adobe.arrayToVector)( ["foo", "bar"] ) |
| // (which is a function call, despite the extra parens) |
| result += "adobe.arrayToVector"; |
| |
| return result; |
| } |
| |
| public String reduce_plist(IASNode iNode, Vector<String> pdecl) |
| { |
| // Parameter processing is done by side effect |
| // in the parameter reduction. |
| return null; |
| } |
| |
| public String reduce_postDecBracketExpr(IASNode iNode, String stem, String index, boolean need_result) |
| { |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); Binding stem_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(stem_tmp.setlocal() ); result.addAll(index); |
| * result.addInstruction(OP_dup); // Get the initial value, and dup a |
| * copy // onto the stack as the value of this expression. Binding |
| * index_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(index_tmp.setlocal() ); |
| * result.addInstruction(OP_getproperty); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_decrement); Binding result_tmp = |
| * currentScope.allocateTemp(); |
| * result.addInstruction(result_tmp.setlocal() ); |
| * result.addInstruction(stem_tmp.getlocal() ); |
| * result.addInstruction(index_tmp.getlocal() ); |
| * result.addInstruction(result_tmp.getlocal() ); |
| * result.addInstruction(OP_setproperty); |
| * result.addAll(currentScope.releaseTemp(stem_tmp)); |
| * result.addAll(currentScope.releaseTemp(index_tmp)); |
| * result.addAll(currentScope.releaseTemp(result_tmp)); |
| */ |
| |
| // prefix unary operator. |
| // Pattern postDecBracketExpr |
| // Op_PostDecrID(ArrayIndexExpressionID(expression stem, expression index)); |
| // String result = indent() + "(" + stem + "[" + index + "]--" + ")"; |
| result += "(" + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, index), "-=", "1", true, true) + ")"; |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_postDecMemberExpr(IASNode iNode, String stem, Binding field, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, false); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_getproperty, field.getName()); // Save a |
| * copy of the original value to use as the value of this expression. |
| * result.addInstruction(OP_dup); Binding orig_tmp = |
| * currentScope.allocateTemp(); |
| * result.addInstruction(orig_tmp.setlocal() ); |
| * result.addInstruction(OP_decrement); |
| * result.addInstruction(OP_setproperty, field.getName()); |
| * result.addInstruction(orig_tmp.getlocal() ); |
| * result.addAll(currentScope.releaseTemp(orig_tmp)); |
| */ |
| |
| // prefix unary operator. |
| // Pattern postDecMemberExpr |
| // Op_PostDecrID(MemberAccessExpressionID(expression stem, name field)); |
| // String result = indent() + "(" + stem + "." + getBasenameFromBinding(field) + "--" + ")"; |
| result += "(" + resolveSetterName(iNode, stem, field, "-=", "1", false, true) + ")"; |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_postDecNameExpr(IASNode iNode, Binding unary, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, false); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * if ( unary.isLocal() ) { result.addInstruction(unary.getlocal()); |
| * result.addInstruction(unary.declocal()); } else { Name n = |
| * unary.getName(); result.addInstruction(OP_findpropstrict,n); |
| * result.addInstruction(OP_getproperty,n); |
| * result.addInstruction(OP_dup); result.addInstruction(OP_decrement); |
| * result.addInstruction(OP_findpropstrict,n); |
| * result.addInstruction(OP_swap); |
| * result.addInstruction(OP_setproperty,n); } |
| */ |
| |
| // postfix unary operator. |
| // Pattern postDecNameExpr |
| // Op_PostDecrID(name unary); |
| // final String result = indent() + "(" + bindingToString(iNode,unary, true, true) + "--" + ")"; |
| |
| final String methodName = bindingToString(iNode, unary, true, true); |
| if (methodName.contains(".")) |
| { |
| final String stem = methodName.substring(0, methodName.lastIndexOf(".")); |
| final String member = getBasenameFromBinding(unary); |
| result += "(" + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, member), "-=", "1", false, true) + ")"; |
| } |
| else |
| { |
| result += indent() + "(" + methodName + "--" + ")"; |
| } |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_postIncBracketExpr(IASNode iNode, String stem, String index, boolean need_result) |
| { |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); Binding stem_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(stem_tmp.setlocal() ); result.addAll(index); |
| * result.addInstruction(OP_dup); // Get the initial value, and dup a |
| * copy // onto the stack as the value of this expression. Binding |
| * index_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(index_tmp.setlocal() ); |
| * result.addInstruction(OP_getproperty); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_increment); Binding result_tmp = |
| * currentScope.allocateTemp(); |
| * result.addInstruction(result_tmp.setlocal()); |
| * result.addInstruction(stem_tmp.getlocal() ); |
| * result.addInstruction(index_tmp.getlocal() ); |
| * result.addInstruction(result_tmp.getlocal() ); |
| * result.addInstruction(OP_setproperty); |
| * result.addAll(currentScope.releaseTemp(stem_tmp)); |
| * result.addAll(currentScope.releaseTemp(index_tmp)); |
| * result.addAll(currentScope.releaseTemp(result_tmp)); |
| */ |
| |
| // prefix unary operator. |
| // Pattern postIncBracketExpr |
| // Op_PostIncrID(ArrayIndexExpressionID(expression stem, expression index)); |
| // String result = indent() + "(" + stem + "[" + index + "]++" + ")"; |
| result += "(" + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, index), "+=", "1", true, true) + ")"; |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_postIncMemberExpr(IASNode iNode, String stem, Binding field, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, true); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_getproperty, field.getName()); // Save a |
| * copy of the original value to use as the value of this expression. |
| * result.addInstruction(OP_dup); Binding orig_tmp = |
| * currentScope.allocateTemp(); |
| * result.addInstruction(orig_tmp.setlocal() ); |
| * result.addInstruction(OP_increment); |
| * result.addInstruction(OP_setproperty, field.getName()); |
| * result.addInstruction(orig_tmp.getlocal() ); |
| * result.addAll(currentScope.releaseTemp(orig_tmp)); |
| */ |
| |
| // prefix unary operator. |
| // Pattern postIncMemberExpr |
| // Op_PostIncrID(MemberAccessExpressionID(expression stem, name field)); |
| |
| result += "(" + resolveSetterName(iNode, stem, field, "+=", "1", false, true) + ")"; |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_postIncNameExpr(IASNode iNode, Binding unary, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, true); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * if ( unary.isLocal() ) { result.addInstruction(unary.getlocal()); |
| * result.addInstruction(unary.inclocal()); } else { Name n = |
| * unary.getName(); result.addInstruction(OP_findpropstrict,n); |
| * result.addInstruction(OP_getproperty,n); |
| * result.addInstruction(OP_dup); result.addInstruction(OP_increment); |
| * result.addInstruction(OP_findpropstrict,n); |
| * result.addInstruction(OP_swap); |
| * result.addInstruction(OP_setproperty,n); } |
| */ |
| |
| // postfix unary operator. |
| // Pattern postIncNameExpr |
| // Op_PostIncrID(name unary); |
| |
| final String methodName = bindingToString(iNode, unary, true, true); |
| if (methodName.contains(".")) |
| { |
| final String stem = methodName.substring(0, methodName.lastIndexOf(".")); |
| final String member = getBasenameFromBinding(unary); |
| result += "(" + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, member), "+=", "1", false, true) + ")"; |
| } |
| else |
| { |
| result += indent() + "(" + methodName + "++" + ")"; |
| } |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_preDecBracketExpr(IASNode iNode, String stem, String index, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, false); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); Binding stem_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(stem_tmp.setlocal() ); result.addAll(index); |
| * result.addInstruction(OP_dup); // Get the initial value. Binding |
| * index_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(index_tmp.setlocal()); |
| * result.addInstruction(OP_getproperty); // Increment, and dup a copy |
| * onto // the stack as the value of this expression. |
| * result.addInstruction(OP_decrement); result.addInstruction(OP_dup); |
| * Binding result_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(result_tmp.setlocal()); |
| * result.addInstruction(stem_tmp.getlocal() ); |
| * result.addInstruction(index_tmp.getlocal() ); |
| * result.addInstruction(result_tmp.getlocal() ); |
| * result.addInstruction(OP_setproperty); |
| * result.addAll(currentScope.releaseTemp(stem_tmp)); |
| * result.addAll(currentScope.releaseTemp(index_tmp)); |
| * result.addAll(currentScope.releaseTemp(result_tmp)); |
| */ |
| |
| // prefix unary operator. |
| // Pattern preDecBracketExpr |
| // Op_PreDecrID(ArrayIndexExpressionID(expression stem, expression index)); |
| // String result = indent() + "(" + "--" + stem + "[" + index + "]" + ")"; |
| result += "(" + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, index), "-=", "1", true, false) + ")"; |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_preDecMemberExpr(IASNode iNode, String stem, Binding field, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, false); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_getproperty, field.getName()); |
| * result.addInstruction(OP_decrement); // Save a copy of the result to |
| * use as the value of this expression. result.addInstruction(OP_dup); |
| * Binding result_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(result_tmp.setlocal() ); |
| * result.addInstruction(OP_setproperty, field.getName()); |
| * result.addInstruction(result_tmp.getlocal() ); |
| * result.addAll(currentScope.releaseTemp(result_tmp)); |
| */ |
| |
| // prefix unary operator. |
| // Pattern preDecMemberExpr |
| // Op_PreDecrID(MemberAccessExpressionID(expression stem, name field)); |
| // String result = indent() + "(" + "--" + stem + "." + getBasenameFromBinding(field) + ")"; |
| result += "(" + resolveSetterName(iNode, stem, field, "-=", "1", false, false) + ")"; |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_preDecNameExpr(IASNode iNode, Binding unary, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, false); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * if ( unary.isLocal() ) { result.addInstruction(unary.declocal()); |
| * result.addInstruction(unary.getlocal()); } else { Name n = |
| * unary.getName(); result.addInstruction(OP_findpropstrict,n); |
| * result.addInstruction(OP_getproperty,n); |
| * result.addInstruction(OP_decrement); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_findpropstrict,n); |
| * result.addInstruction(OP_swap); |
| * result.addInstruction(OP_setproperty,n); } |
| */ |
| |
| // prefix unary operator. |
| // Pattern preDecNameExpr |
| // Op_PreDecrID(name unary); |
| // String result = indent() + "(" + "--" + bindingToString(iNode,unary, true, true) + ")"; |
| |
| final String methodName = bindingToString(iNode, unary, true, true); |
| if (methodName.contains(".")) |
| { |
| final String stem = methodName.substring(0, methodName.lastIndexOf(".")); |
| final String member = getBasenameFromBinding(unary); |
| result += "(" + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, member), "-=", "1", false, false) + ")"; |
| } |
| else |
| { |
| result += indent() + "(" + "--" + methodName + ")"; |
| } |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_preIncBracketExpr(IASNode iNode, String stem, String index, boolean need_result) |
| { |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); Binding stem_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(stem_tmp.setlocal() ); result.addAll(index); |
| * result.addInstruction(OP_dup); // Get the initial value. Binding |
| * index_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(index_tmp.setlocal() ); |
| * result.addInstruction(OP_getproperty); // Increment, and dup a copy |
| * onto // the stack as the value of this expression. |
| * result.addInstruction(OP_increment); result.addInstruction(OP_dup); |
| * Binding result_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(result_tmp.setlocal() ); |
| * result.addInstruction(stem_tmp.getlocal() ); |
| * result.addInstruction(index_tmp.getlocal()); |
| * result.addInstruction(result_tmp.getlocal()); |
| * result.addInstruction(OP_setproperty); |
| * result.addAll(currentScope.releaseTemp(stem_tmp)); |
| * result.addAll(currentScope.releaseTemp(index_tmp)); |
| * result.addAll(currentScope.releaseTemp(result_tmp)); |
| */ |
| |
| // prefix unary operator. |
| // Pattern preIncBracketExpr |
| // Op_PreIncrID(ArrayIndexExpressionID(expression stem, expression index)); |
| // String result = indent() + "(" + "++" + stem + "[" + index + "]" + ")"; |
| result += "(" + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, index), "+=", "1", true, false) + ")"; |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_preIncMemberExpr(IASNode iNode, String stem, Binding field, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, true); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addAll(stem); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_getproperty, field.getName()); |
| * result.addInstruction(OP_increment); // Save a copy of the result to |
| * use as the value of this expression. result.addInstruction(OP_dup); |
| * Binding result_tmp = currentScope.allocateTemp(); |
| * result.addInstruction(result_tmp.setlocal()); |
| * result.addInstruction(OP_setproperty, field.getName()); |
| * result.addInstruction(result_tmp.getlocal()); |
| * result.addAll(currentScope.releaseTemp(result_tmp)); |
| */ |
| |
| // prefix unary operator. |
| // Pattern preIncMemberExpr |
| // Op_PreIncrID(MemberAccessExpressionID(expression stem, name field)); |
| // String result = indent() + "(" + "++" + stem + "." + getBasenameFromBinding(field) + ")"; |
| result += "(" + resolveSetterName(iNode, stem, field, "+=", "1", false, false) + ")"; |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_preIncNameExpr(IASNode iNode, Binding unary, boolean need_result) |
| { |
| currentScope.getMethodBodySemanticChecker().checkIncDec(iNode, true); |
| |
| String result = createInstruction(iNode); |
| |
| /* |
| * if ( unary.isLocal() ) { result.addInstruction(unary.inclocal()); |
| * result.addInstruction(unary.getlocal()); } else { Name n = |
| * unary.getName(); result.addInstruction(OP_findpropstrict,n); |
| * result.addInstruction(OP_getproperty,n); |
| * result.addInstruction(OP_increment); result.addInstruction(OP_dup); |
| * result.addInstruction(OP_findpropstrict,n); |
| * result.addInstruction(OP_swap); |
| * result.addInstruction(OP_setproperty,n); } |
| */ |
| |
| // prefix unary operator. |
| // Pattern preIncNameExpr |
| // Op_PreIncrID(name unary); |
| // String result = indent() + "(" + "++" + bindingToString(iNode,unary, true, true) + ")"; |
| |
| final String methodName = bindingToString(iNode, unary, true, true); |
| if (methodName.contains(".")) |
| { |
| final String stem = methodName.substring(0, methodName.lastIndexOf(".")); |
| final String member = getBasenameFromBinding(unary); |
| result += "(" + resolveSetterName(iNode, stem, makeMemberBinding(iNode, stem, member), "+=", "1", false, false) + ")"; |
| } |
| else |
| { |
| result += indent() + "(" + "++" + methodName + ")"; |
| } |
| |
| if (!need_result) |
| result = transform_expression_to_void_expression(iNode, result); |
| |
| return result; |
| } |
| |
| public String reduce_regexLiteral(IASNode iNode) |
| { |
| String result = createInstruction(iNode, 4); |
| |
| /* |
| * result.addInstruction(OP_findpropstrict, regexType); |
| * result.addInstruction(OP_getproperty, regexType); |
| * result.addInstruction(OP_pushstring, getStringLiteralContent(iNode)); |
| * result.addInstruction(OP_construct, 1); |
| */ |
| |
| final IRegExpLiteralNode regExNode = (IRegExpLiteralNode)iNode; |
| final String flags = regExNode.getFlagString(); |
| |
| // object_constant = Pattern regexLiteral |
| if (flags != null && !flags.isEmpty()) |
| result += "new RegExp(" + getStringLiteralContent(iNode) + ", \"" + flags + "\")"; |
| else |
| result += "new RegExp(" + getStringLiteralContent(iNode) + ")"; |
| return result; |
| } |
| |
| public String reduce_requiredParameter(IASNode iNode, Name param_name, Binding param_type) |
| { |
| /* |
| * currentScope.makeParameter(getParameterContent(iNode), |
| * param_type.getName()); |
| */ |
| // Pattern requiredParameter |
| // ArgumentID(Binding param_name, Binding param_type); |
| |
| // currentScope.makeParameter(#ArgumentID#, param_type.getName()); |
| this.makeParameter(iNode, ((org.apache.flex.compiler.internal.tree.as.ParameterNode)iNode), currentScope, param_name, param_type, param_type); |
| return null; |
| } |
| |
| public String reduce_restParameter(IASNode iNode, Name param_name, Binding param_type) |
| { |
| /* |
| * currentScope.makeParameter(getParameterContent(iNode), |
| * param_type.getName()); |
| */ |
| // parameter = Pattern restParameter |
| // ArgumentID(Binding param_name, Binding param_type); |
| |
| // currentScope.makeParameter((((org.apache.flex.compiler.as.tree.IParameterNode)iNode).getDefinition()), param_type.getName()); |
| this.makeParameter(iNode, ((ParameterNode)iNode), currentScope, param_name, param_type, param_type); |
| return null; |
| } |
| |
| public String reduce_returnValue(IASNode iNode, String value) |
| { |
| // workaround for falcon bug |
| // Date to class triggers |
| // Implicit coercion of a value of type Date to an unrelated type Class. |
| // currentScope.getMethodBodySemanticChecker().checkReturnValue(iNode); |
| |
| if (value == null) |
| value = "null"; |
| String result = createInstruction(iNode, value.length() + 1); |
| |
| /* |
| * result.addAll(value); result.addInstruction(OP_returnvalue); try { |
| * return getNonLocalControlFlow(result, |
| * ControlFlowContextManager.FIND_ALL_CONTEXTS); } catch ( |
| * UnknownControlFlowTargetException cant_return ) { |
| * currentScope.getProblems().add(new UnexpectedReturnProblem(iNode)); |
| * return createInstruction(iNode); } |
| */ |
| |
| // Pattern returnValue |
| // ReturnStatementID(void_expression no_value); |
| |
| String fname = ""; |
| if (currentScope != null && |
| currentScope.getMethodInfo() != null && |
| currentScope.getMethodInfo().getMethodName() != null) |
| { |
| fname = currentScope.getMethodInfo().getMethodName(); |
| } |
| |
| result += indent() + "return "; |
| |
| if ((value.startsWith("goog.base") && !value.startsWith("goog.base(this")) || (value.startsWith("adobe.base") && !value.startsWith("adobe.base(this"))) |
| { |
| assert false : "FalconJS should never generate 'goog.base(' or 'adobe.base(' not followed by 'this'"; |
| |
| /* |
| * if( !fname.isEmpty() ) { final String getter = |
| * resolveGetterName(iNode, JSSharedData.THIS, |
| * makeMemberBinding(iNode, JSSharedData.THIS, fname), false); if( |
| * getter.startsWith(JSSharedData.THIS + "." + |
| * JSSharedData.GETTER_PREFIX)) fname = JSSharedData.GETTER_PREFIX + |
| * fname; } result += "goog.base(this"; if( !fname.isEmpty() ) |
| * result += ", \"" + fname + "\""; if(!params.isEmpty()) { result |
| * += ", " + params; } result += ");" + endl(); |
| */ |
| } |
| else |
| { |
| // Symbol support |
| if (fname.equals(JSSharedData.SYMBOL_GET_LINKAGE)) |
| { |
| String _className = getCurrentClassName(); |
| final IDefinition def = getDefinitionForName(iNode, makeName(_className)); |
| if (def != null && def instanceof ClassDefinition) |
| { |
| if (isSymbolClass(currentScope.getProject(), def)) |
| { |
| // TODO: check whether value is derived from MovieClip. |
| _className = createFullNameFromDefinition(currentScope.getProject(), def); |
| this.m_sharedData.registerSymbol(value, _className); |
| } |
| } |
| } |
| |
| result += stripNS(value) + ";" + endl(); |
| } |
| return result; |
| } |
| |
| public String reduce_returnVoidSideEffect(IASNode iNode, String expr) |
| { |
| return String.format("%sreturn void(%s);%s", indent(), expr, endl()); |
| } |
| |
| public String reduce_returnVoid(IASNode iNode) |
| { |
| String result = createInstruction(iNode, 1); |
| currentScope.getMethodBodySemanticChecker().checkReturnVoid(iNode); |
| |
| /* |
| * result.addInstruction(OP_returnvoid); try { return |
| * getNonLocalControlFlow(result, |
| * ControlFlowContextManager.FIND_ALL_CONTEXTS); } catch ( |
| * UnknownControlFlowTargetException cant_return ) { |
| * currentScope.getProblems().add(new UnexpectedReturnProblem(iNode)); |
| * return createInstruction(iNode); } |
| */ |
| |
| // Pattern returnVoid |
| // ReturnStatementID(void); |
| |
| result += indent() + "return;" + endl(); |
| return result; |
| } |
| |
| public String reduce_returnVoidValue(IASNode iNode, String no_value) |
| { |
| String result = createInstruction(iNode, no_value.length() + 1); |
| currentScope.getMethodBodySemanticChecker().checkReturnVoid(iNode); |
| |
| /* |
| * // The void expression might have side effects. |
| * result.addAll(no_value); result.addInstruction(OP_returnvoid); try { |
| * return getNonLocalControlFlow(result, |
| * ControlFlowContextManager.FIND_ALL_CONTEXTS); } catch ( |
| * UnknownControlFlowTargetException cant_return ) { |
| * currentScope.getProblems().add(new UnexpectedReturnProblem(iNode)); |
| * return createInstruction(iNode); } |
| */ |
| |
| // Pattern returnVoidValue |
| // ReturnStatementID(void_expression no_value); |
| |
| if (!no_value.isEmpty()) |
| currentScope.addProblem(new JSInternalCompilerProblem(iNode, "value is not empty")); |
| |
| // just weird, this might be a bug. |
| result += indent() + "return;"; |
| return result; |
| } |
| |
| public String reduce_runtimeNameExpression(IASNode iNode, String expr) |
| { |
| return expr; |
| } |
| |
| public Binding reduce_simpleName(IASNode iNode) |
| { |
| /* |
| * return currentScope.resolveName((IdentifierNode)iNode); |
| */ |
| |
| try |
| { |
| Binding b; |
| |
| // decompiling builtin.abc |
| if (((IdentifierNode)iNode).getName().equals("...")) |
| b = makeBinding(iNode, "arguments"); |
| else |
| { |
| b = currentScope.resolveName((IdentifierNode)iNode); |
| } |
| return b; |
| } |
| |
| catch (NullPointerException e) |
| { |
| throw e; |
| } |
| } |
| |
| public Name reduce_declName(IASNode iNode) |
| { |
| return reduce_simpleName(iNode).getName(); |
| } |
| |
| public String reduce_strictneqExpr(IASNode iNode, String l, String r) |
| { |
| /* |
| * String result = binaryOp(iNode, l, r, OP_strictequals); |
| * result.addInstruction(OP_not); |
| */ |
| |
| // Binary logical operators. |
| // Pattern strictneqExpr |
| // Op_StrictNotEqualID(expression l, expression r); |
| String result = binaryOp(l, r, "!=="); |
| return result; |
| } |
| |
| public Binding reduce_superAccess(IASNode iNode, Binding qualified_name) |
| { |
| /* |
| * // TODO: in-flight SEW changes superseded by bugfix, uncomment when |
| * this routine is available. // |
| * currentScope.getMethodBodySemanticChecker().checkSuperAccess(iNode, |
| * qualified_name); // The super qualifier is just concrete syntax. |
| * return qualified_name; |
| */ |
| |
| // Pattern superAccess |
| // ArgumentID(Binding qualified_name); |
| |
| final String baseName = getBasenameFromBinding(qualified_name); |
| |
| // The super qualifier is just concrete syntax. |
| // final String superName = JSSharedData._SUPER + "." + baseName; |
| // final Name name = new Name( qualified_name.getName().getQualifiers(), superName ); |
| |
| String superName; |
| if (JSSharedData.m_useClosureLib) |
| { |
| // name of method containing the super.foo assignment |
| String invokingMethodRawName = getCurrentMethodName(); |
| IFunctionDefinition enclosingDef = getCurrentMethod(iNode); |
| String invokingMethodName = mungeMethodName(invokingMethodRawName, enclosingDef, false); |
| |
| // name of method being invoked via super, e.g. "set_foo" for "super.foo = bar" |
| final String superMethodName; |
| |
| // Note: getDefinitionForName() isn't aware of the "super." prefix, so it can be unreliable; prefer def already in binding if exists |
| IFunctionDefinition def = (IFunctionDefinition)qualified_name.getDefinition(); |
| if (def == null) |
| def = (IFunctionDefinition)getDefinitionForName(iNode, qualified_name.getName()); |
| |
| // Note: we use get_ prefix even if def is a setter since we won't know until later whether we're in an |
| // lvalue; resolveSetterName() will fix this up later if needed. |
| superMethodName = mungeMethodName(baseName, def, true); |
| |
| boolean needAdobeBase = !invokingMethodName.equals(superMethodName); |
| |
| // NOTE: in the ISetterDefinition case, this string is later munged by resolveSetterName() to add one last |
| // arg, the RHS value; see its call to convertSuperAccessToSetter(). |
| if (!needAdobeBase) |
| superName = "goog.base(this, \"" + superMethodName + "\")"; |
| else |
| { |
| IClassDefinition invokingMethodClass = (IClassDefinition)enclosingDef.getParent(); |
| |
| superName = "adobe.base(this, " + getSuperMethodReference(invokingMethodClass, baseName) + ")"; |
| } |
| } |
| else |
| { |
| superName = JSSharedData.THIS + "." + JSSharedData._SUPER; |
| } |
| |
| final Name name = new Name(qualified_name.getName().getQualifiers(), superName); |
| return new Binding(iNode, name, null); |
| } |
| |
| public String reduce_superStandalone(IASNode iNode) |
| { |
| return JSSharedData.THIS + "." + JSSharedData._SUPER; |
| } |
| |
| public String reduce_superCallExpr(IASNode iNode, Vector<String> args) |
| { |
| /* |
| * currentScope.getMethodBodySemanticChecker().checkExplicitSuperCall(iNode |
| * , args); // TODO: Investigate optimizing this. String result = |
| * createInstruction(iNode); // Special case first part: don't look for |
| * the "super" name, // push "this" on the stack. |
| * result.addInstruction(OP_getlocal0); for ( String arg: args) |
| * result.addAll(arg); // Special case second part: call constructsuper. |
| * result.addInstruction(OP_constructsuper, args.size() ); |
| */ |
| |
| // using call() instead of goog.base() |
| return ""; // reduce_functionCallOfSuperclassMethod_to_expression(iNode, null, null, args) + ";" + endl(); |
| } |
| |
| public String reduce_switchStmt(IASNode iNode, String switch_expr, Vector<ConditionalFragment> cases) |
| { |
| // TODO: Optimize String size. |
| String result = createInstruction(iNode); |
| |
| /* |
| * Label default_case_label = null; Label switch_tail = null; // Get the |
| * switch value and save it in a temp. Binding switch_temp = |
| * currentScope.allocateTemp(); result.addAll(switch_expr); |
| * result.addInstruction(switch_temp.setlocal()); // First, jump to the |
| * switch table. if ( cases.size() > 0 ) { |
| * result.addInstruction(OP_jump, cases.elementAt(0).getLabel()); } for |
| * ( ConditionalFragment current_case: cases ) { // Ensure the case |
| * begins with a OP_label String // so we can branch back to it. if ( |
| * current_case.statement.isEmpty() || |
| * !(current_case.statement.firstElement().getOpcode() == OP_label ) ) { |
| * String labeled_list = createInstruction(iNode); |
| * labeled_list.addInstruction(OP_label); |
| * labeled_list.addAll(current_case.statement); current_case.statement = |
| * labeled_list; } if ( current_case.isUnconditionalAlternative() ) { // |
| * Note: Verified that the parser rejects duplicate default |
| * alternatives. assert ( null == default_case_label); |
| * default_case_label = current_case.statement.getLabel(); } // else |
| * TODO: Check for duplicate cases. current_case.getStatementLabel(); // |
| * Touch the label so it will be properly recorded. |
| * result.addAll(current_case.statement); } // Jump to the tail. |
| * result.addInstruction(switch_temp.kill()); switch_tail = new Label(); |
| * result.addInstruction(OP_jump, switch_tail); // Emit the |
| * "switch table" as if/else statements. for ( ConditionalFragment |
| * current_case: cases ) { if ( |
| * !current_case.isUnconditionalAlternative() ) { |
| * result.addAll(current_case.condition); |
| * result.addInstruction(switch_temp.getlocal()); |
| * result.addInstruction(OP_ifstricteq, current_case.getStatementLabel() |
| * ); } } // Emit the jump to the default case. if ( default_case_label |
| * != null ) { result.addInstruction(switch_temp.kill()); |
| * result.addInstruction(OP_jump, default_case_label); } // Note the |
| * temp has already been killed // since we don't want it reused in a |
| * control // flow region that might confuse the Verifier. |
| * result.addAll(currentScope.releaseTemp(switch_temp)); // No continue |
| * here, and thus there is no need // to check for previous labeled flow |
| * contexts. |
| * currentScope.getFlowManager().finishControlFlowContext(result); if ( |
| * switch_tail != null ) result.labelNext(switch_tail); |
| */ |
| |
| // statement = Pattern switchStmt |
| |
| result += indent() + "switch(" + switch_expr + ")" + endl(); |
| result += "{" + endl(); |
| |
| String casesBlock = ""; |
| |
| // Emit the switch cases. |
| // Vector<ConditionalFragment> cases = (Vector<ConditionalFragment>) raw_cases; |
| for (ConditionalFragment current_case : cases) |
| { |
| if (current_case.isUnconditionalAlternative()) |
| casesBlock += "default"; |
| else |
| { |
| casesBlock += "case "; |
| casesBlock += current_case.condition; |
| } |
| casesBlock += ": " + endl(); |
| if (current_case.statement.length() > 0) |
| { |
| casesBlock += "{" + endl(); |
| casesBlock += indentBlock(current_case.statement, 1); // Touch the label so it will be properly recorded. |
| casesBlock += "}" + endl(); |
| } |
| } |
| |
| result += indentBlock(casesBlock, 1); |
| result += "}" + endl(); |
| return result; |
| } |
| |
| public String reduce_ternaryExpr(IASNode iNode, String test, String when_true, String when_false) |
| { |
| if (when_true == null) |
| when_true = "null"; |
| if (when_false == null) |
| when_false = "null"; |
| String result = createInstruction(iNode, test.length() + when_true.length() + when_false.length() + 2); |
| |
| /* |
| * Label tail = new Label(); result.addAll(test); |
| * result.addInstruction(OP_iffalse, when_false.getLabel()); |
| * result.addAll(when_true); result.addInstruction(OP_jump, tail); |
| * result.addAll(when_false); result.labelNext(tail); |
| */ |
| |
| // Pattern ternaryExpr |
| // TernaryExpressionID(expression test, expression when_true, expression when_false); |
| |
| result += "(" + test + " ? " + stripTabs(stripNS(when_true)) + " : " + stripTabs(stripNS(when_false)) + ")"; |
| |
| return result; |
| } |
| |
| public String reduce_throwStmt(IASNode iNode, String tossable) |
| { |
| String result = createInstruction(iNode, tossable.length() + 1); |
| |
| /* |
| * result.addAll(tossable); result.addInstruction(OP_throw); |
| */ |
| |
| // statement = Pattern throwStmt |
| result += indent() + "throw " + tossable + ";" + endl(); |
| |
| return result; |
| } |
| |
| public String reduce_tryCatchFinallyStmt(IASNode iNode, String try_stmt, String finally_stmt, Vector<CatchPrototype> catch_blocks) |
| { |
| String result = generateTryCatchFinally(iNode, try_stmt, catch_blocks, finally_stmt); |
| // workaround for Falcon bug. |
| // java.lang.ClassCastException: org.apache.flex.compiler.internal.as.codegen.ControlFlowContext cannot be cast to org.apache.flex.compiler.internal.as.codegen.ExceptionHandlingContext |
| try |
| { |
| currentScope.getFlowManager().finishExceptionContext(); |
| } |
| catch (ClassCastException e) |
| { |
| |
| } |
| |
| return result; |
| } |
| |
| public String reduce_tryCatchStmt(IASNode iNode, String try_stmt, Vector<CatchPrototype> catch_blocks) |
| { |
| /* |
| * // TODO: Optimize. String result = createInstruction(iNode); if ( |
| * try_stmt.isEmpty() ) { // TODO: The catch clause gen may have // side |
| * effects, so we can't skip this. try_stmt = createInstruction(iNode); |
| * try_stmt.addInstruction(OP_nop); } Label catch_tail = new Label(); |
| * result.addAll(try_stmt); result.addInstruction(OP_jump, catch_tail); |
| * // Get labels for the start and end of the try block. Label try_start |
| * = result.getLabel(); Label try_end = result.getLastLabel(); for ( |
| * CatchPrototype catch_proto: catch_blocks ) { boolean is_last_catch = |
| * catch_proto.equals(catch_blocks.lastElement()); String catch_body = |
| * generateCatchBlock(try_start, try_end, catch_proto); if( |
| * !is_last_catch && catch_body.canFallThrough() ) |
| * catch_body.addInstruction(OP_jump, catch_tail); |
| * result.addAll(catch_body); } |
| * currentScope.getFlowManager().finishExceptionContext(); |
| * result.labelNext(catch_tail); |
| */ |
| |
| String result = reduce_tryCatchFinallyStmt(iNode, try_stmt, null, catch_blocks); |
| return result; |
| } |
| |
| public String reduce_tryFinallyStmt(IASNode iNode, String try_stmt, String finally_stmt) |
| { |
| /* |
| * String result = generateTryCatchFinally(try_stmt, null, |
| * finally_stmt); |
| * currentScope.getFlowManager().finishExceptionContext(); |
| */ |
| |
| // statement = Pattern tryFinallyStmt |
| String result = reduce_tryCatchFinallyStmt(iNode, try_stmt, finally_stmt, null); |
| return result; |
| } |
| |
| public String reduce_typedFunction_to_statement(IASNode iNode, String plist, Binding return_type, String block) |
| { |
| /* |
| * Binding nestedFunctionName = |
| * currentScope.resolveName((IdentifierNode) |
| * JSGeneratingReducer.getNthChild(iNode, 0)); |
| * this.generateNestedFunction(nestedFunctionName, |
| * return_type.getName(), block); return createInstruction(iNode); |
| */ |
| |
| // statement = Pattern typedFunction : 0 |
| // ArgumentID( Binding n, String plist, Binding return_type , String block ); |
| |
| Binding nestedFunctionName = currentScope.resolveName((IdentifierNode)SemanticUtils.getNthChild(iNode, 0)); |
| return this.generateNestedFunction(iNode, nestedFunctionName, return_type.getName(), block); |
| } |
| |
| public String reduce_typedVariableDecl(IASNode iNode, Name var_name, Binding var_type, Vector<String> chained_decls) |
| { |
| BaseVariableNode var_node = (BaseVariableNode)iNode; |
| Binding var = currentScope.resolveName((IdentifierNode)var_node.getNameExpressionNode()); |
| currentScope.makeVariable(var, var_type.getName(), ((BaseDefinitionNode)iNode).getMetaInfos()); |
| |
| /* |
| * String result = createInstruction(iNode); if ( var_name.isLocal() ) { |
| * // Add initializer code to the init Strings. String init_insns = |
| * currentScope.getHoistedInitInstructions(); ASDefinitionCache cache = |
| * currentScope.getDefinitionCache(); IDefinition type_def = |
| * var_type.getDefinition(); if ( type_def == |
| * cache.getBuiltinType(BuiltinType.INT) || type_def == |
| * cache.getBuiltinType(BuiltinType.UINT) ) { |
| * init_insns.addInstruction(OP_pushbyte, 0); |
| * init_insns.addInstruction(var_name.setlocal()); } else if ( type_def |
| * == cache.getBuiltinType(BuiltinType.BOOLEAN) ) { |
| * init_insns.addInstruction(OP_pushfalse); |
| * init_insns.addInstruction(var_name.setlocal()); } else if ( type_def |
| * == cache.getBuiltinType(BuiltinType.NUMBER) ) { |
| * init_insns.addInstruction(OP_pushdouble, Double.NaN); |
| * init_insns.addInstruction(var_name.setlocal()); } else if ( type_def |
| * == cache.getBuiltinType(BuiltinType.ANY_TYPE) ) { |
| * init_insns.addInstruction(OP_pushundefined); |
| * init_insns.addInstruction(var_name.setlocal()); } else if ( |
| * type_def.resolveType |
| * (cache).isInstanceOf(cache.getBuiltinType(BuiltinType |
| * .OBJECT).resolveType(cache), cache) ) { |
| * init_insns.addInstruction(OP_pushnull); |
| * init_insns.addInstruction(var_name.setlocal()); } else { |
| * init_insns.addInstruction(OP_pushundefined); |
| * init_insns.addInstruction(var_name.setlocal()); } } for ( String |
| * decl: chained_decls ) result.addAll(decl); return result; |
| */ |
| |
| // In AS, uninitialized variables have a type-specific default value (not always undefined) |
| String var_initializer = null; |
| String defaultInitializer = JSEmitter.getDefaultInitializerForVariable(var_type.getName()); |
| if (!defaultInitializer.equals("undefined")) // don't emit anything if JS would default to the right value anyway |
| { |
| // Don't emit anything for member vars - JSEmitter has already put the right initializer on the prototype |
| IDefinition varDef = var_node.getDefinition(); |
| if (varDef == null || isLocalVariable((IVariableDefinition)varDef)) |
| var_initializer = defaultInitializer; |
| } |
| |
| return reduce_variableDecl(iNode, var_name, var_type, var_initializer, chained_decls); |
| } |
| |
| public String reduce_typedVariableDeclWithInitializer(IASNode iNode, Name var_name, Binding var_type, String var_initializer, Vector<String> chained_decls) |
| { |
| BaseVariableNode var_node = (BaseVariableNode)iNode; |
| Binding var = currentScope.resolveName((IdentifierNode)var_node.getNameExpressionNode()); |
| currentScope.makeVariable(var, var_type.getName(), ((BaseDefinitionNode)iNode).getMetaInfos()); |
| |
| /* |
| * String result = generateAssignment(iNode, var_name, var_initializer); |
| * for ( String decl: chained_decls ) result.addAll(decl); return |
| * result; |
| */ |
| |
| return reduce_variableDecl(iNode, var_name, var_type, var_initializer, chained_decls); |
| } |
| |
| public String reduce_typedVariableDeclWithConstantInitializer(IASNode iNode, Name var_name, Binding var_type, Object var_initializer, Vector<String> chained_decls) |
| { |
| if (var_initializer == null) |
| var_initializer = "null"; |
| return reduce_typedVariableDeclWithInitializer(iNode, var_name, var_type, var_initializer.toString(), chained_decls); |
| } |
| |
| public String reduce_typedBindableVariableDecl(IASNode iNode, Name var_name, Binding var_type, Vector<String> chained_decls) |
| { |
| BaseVariableNode vn = (BaseVariableNode)iNode; |
| currentScope.getMethodBodySemanticChecker().checkBindableVariableDeclaration(iNode, vn.getDefinition()); |
| String result = createInstruction(iNode); |
| Binding var = currentScope.resolveName((IdentifierNode)vn.getNameExpressionNode()); |
| currentScope.makeBindableVariable(var, var_type.getName(), vn.getMetaInfos()); |
| for (String decl : chained_decls) |
| result += decl; |
| return result; |
| } |
| |
| public String reduce_typedBindableVariableDeclWithInitializer(IASNode iNode, Name var_name, Binding var_type, String var_initializer, Vector<String> chained_decls) |
| { |
| // constant propagation for initialized members |
| m_varInitValue = var_initializer; |
| |
| BaseVariableNode vn = (BaseVariableNode)iNode; |
| |
| currentScope.getMethodBodySemanticChecker().checkBindableVariableDeclaration(iNode, vn.getDefinition()); |
| Binding var = currentScope.resolveName((IdentifierNode)vn.getNameExpressionNode()); |
| currentScope.makeBindableVariable(var, var_type.getName(), vn.getMetaInfos()); |
| String result = generateAssignment(iNode, new Binding(iNode, BindableHelper.getBackingPropertyName(var_name), vn.getDefinition()), var_initializer); |
| for (String decl : chained_decls) |
| result += decl; |
| return result; |
| } |
| |
| public Binding reduce_typedVariableExpression(IASNode iNode, Name var_name, Binding var_type) |
| { |
| /* |
| * currentScope.makeVariable(var_name, var_type.getName(), null); return |
| * var_name; |
| */ |
| |
| // name = Pattern typedVariableExpression |
| VariableExpressionNode var_expr_node = (VariableExpressionNode)iNode; |
| BaseVariableNode var_node = (BaseVariableNode)var_expr_node.getTargetVariable(); |
| String varType = bindingToString(iNode, var_type, false, true); |
| usedTypes.add(removeRootName(varType)); |
| this.makeVariable(var_node, currentScope, var_name, var_type, null); |
| return currentScope.resolveName((IdentifierNode)var_node.getNameExpressionNode()); |
| } |
| |
| public String reduce_typelessFunction(IASNode iNode, String plist, String block) |
| { |
| /* |
| * Binding nestedFunctionName = |
| * currentScope.resolveName((IdentifierNode) |
| * JSGeneratingReducer.getNthChild(iNode, 0)); |
| * this.generateNestedFunction(nestedFunctionName, null, block); return |
| * createInstruction(iNode); |
| */ |
| |
| // statement = Pattern typelessFunction : 0 |
| // ArgumentID( Binding n, String plist, String block ); |
| |
| Binding nestedFunctionName = currentScope.resolveName((IdentifierNode)SemanticUtils.getNthChild(iNode, 0)); |
| return this.generateNestedFunction(iNode, nestedFunctionName, null, block); |
| } |
| |
| private String getTypeOfString(ITypeDefinition typezo) |
| { |
| ICompilerProject project = currentScope.getProject(); |
| |
| String result = null; |
| if (typezo != null) |
| { |
| if (typezo == project.getBuiltinType(BuiltinType.STRING)) |
| { |
| result = "\"string\""; |
| } |
| else if (typezo == project.getBuiltinType(BuiltinType.NUMBER) || |
| typezo == project.getBuiltinType(BuiltinType.INT) || |
| typezo == project.getBuiltinType(BuiltinType.UINT)) |
| { |
| result = "\"number\""; |
| } |
| else if (typezo == project.getBuiltinType(BuiltinType.BOOLEAN)) |
| { |
| result = "\"boolean\""; |
| } |
| } |
| return result; |
| } |
| |
| public String reduce_typeof_name(IASNode iNode, Binding binding) |
| { |
| String result = null; |
| |
| final IDefinition def = binding.getDefinition(); |
| if (def != null) |
| { |
| final ITypeDefinition typezo = def.resolveType(currentScope.getProject()); |
| result = getTypeOfString(typezo); |
| } |
| |
| if (result == null) |
| { |
| /* |
| * result = generateAccess(binding); |
| * result.addInstruction(OP_typeof); |
| */ |
| |
| result = "typeof(" + bindingToString(iNode, binding, true, true) + ")"; |
| } |
| |
| return result; |
| } |
| |
| public String reduce_typeof_expr(IASNode iNode, String expr) |
| { |
| return String.format("typeof(%s)", expr); |
| } |
| |
| public String reduce_variableExpression(IASNode iNode, Vector<String> decls) |
| { |
| String result = createInstruction(iNode); |
| |
| /* |
| * for ( String var_decl: decls ) result.addAll(var_decl); |
| */ |
| |
| // void_expression = Pattern variableExpression |
| // ArgumentID(Vector<String> decls) |
| for (String var_decl : decls) |
| result += var_decl; |
| return result; |
| } |
| |
| public String reduce_useNamespaceDirective(IASNode iNode, Binding ns_name) |
| { |
| currentScope.getMethodBodySemanticChecker().checkUseNamespaceDirective(iNode, ns_name); |
| String result = createInstruction(iNode); |
| |
| final JSEmitter emitter = getEmitter(); |
| final IDefinition def = ns_name.getDefinition(); |
| if (def != null) |
| emitter.visitUseNamespace(def.getQualifiedName()); |
| else |
| emitter.visitUseNamespace(getBasenameFromBinding(ns_name)); |
| |
| return result; |
| } |
| |
| public String reduce_typeNameParameterAsType(IASNode iNode, Binding type_param) |
| { |
| return type_param.getName().getBaseName(); |
| } |
| |
| public String reduce_vectorLiteral(IASNode iNode, String type_param, Vector<String> elements) |
| { |
| currentScope.getMethodBodySemanticChecker().checkVectorLiteral(iNode, null); |
| String result = createInstruction(iNode); |
| |
| result += "new Array()"; |
| |
| return result; |
| } |
| |
| public String reduce_vectorLiteralAsExpression(IASNode iNode, String type, Vector<String> elements) |
| { |
| return "new Array()"; |
| } |
| |
| public String reduce_voidExpr_to_expression(IASNode iNode) |
| { |
| String il = createInstructionList(iNode); |
| // il.addInstruction(OP_pushundefined); |
| return il; |
| } |
| |
| public Binding reduce_voidExpr_to_return_type_name(IASNode node) |
| { |
| return new Binding(node, voidType, null); |
| } |
| |
| public Binding reduce_voidExpr_to_type_name(IASNode node) |
| { |
| getProblems().add(new VoidTypeProblem(node)); |
| return new Binding(node, voidType, null); |
| } |
| |
| public Object reduce_void0Literal_to_constant_value(IASNode iNode) |
| { |
| /* |
| * return UNDEFINED_VALUE; |
| */ |
| |
| // object_literal = Pattern void0Literal |
| // ArgumentID() |
| return "undefined"; |
| } |
| |
| public String reduce_void0Literal_to_object_literal(IASNode iNode) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_pushundefined); |
| */ |
| |
| // object_literal = Pattern void0Literal |
| // ArgumentID() |
| result += "undefined"; |
| return result; |
| } |
| |
| public String reduce_instructionListExpression(IASNode iNode) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "instruction list expression")); |
| // return ((InstructionListNode)iNode).getInstructions(); |
| return ""; |
| } |
| |
| public String reduce_void0Operator(IASNode iNode) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_pushundefined); |
| */ |
| |
| // object_literal = Pattern voidOperator |
| // ArgumentID() |
| result += "undefined"; |
| |
| return result; |
| } |
| |
| public Object reduce_voidOperator_to_constant_value(IASNode iNode, Object expr) |
| { |
| /* |
| * return UNDEFINED_VALUE; |
| */ |
| |
| // constant_value = Pattern voidOperator |
| // ArgumentID() |
| String result = createInstruction(iNode, 1); |
| |
| result += "undefined"; |
| return result; |
| } |
| |
| public String reduce_voidOperator_to_expression(IASNode iNode, String expr) |
| { |
| // String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addAll(expr); result.addInstruction(OP_pop); |
| * result.addInstruction(OP_pushundefined); return result; |
| */ |
| |
| // object_literal = Pattern voidOperator |
| // String expr |
| |
| // be careful, typeof(null) returns "object". |
| // We have to return undefined here. |
| return "undefined"; |
| } |
| |
| public String reduce_whileStmt(IASNode iNode, String cond, String body) |
| { |
| String result = createInstruction(iNode, cond.length() + body.length() + 5); |
| |
| /* |
| * currentScope.getFlowManager().resolveContinueLabel(cond); if( |
| * !isAnonymousFunction && !convertLocalVarsToMembers ) |
| * currentScope.makeVariable(new Binding(func_name,null)); // Jump to |
| * the test. result.addInstruction(OP_jump, cond.getLabel()); // Create |
| * an emitter-time label, and attach // it to an OP_label String for the |
| * // backwards branch. result.addInstruction(OP_label); Label loop = |
| * result.getLastLabel(); result.addAll(body); result.addAll(cond); |
| * result.addInstruction(OP_iftrue, loop); if ( ! |
| * isLabeledSubstatement(iNode) ) |
| * currentScope.getFlowManager().finishControlFlowContext(result); |
| */ |
| |
| // Pattern whileStmt |
| // WhileLoopID(expression cond, statement body); |
| result += indent() + "while(" + cond + ")" + endl(); |
| result += indent() + "{" + endl(); |
| // pushIndent(); |
| result += indentBlock(body, 1); |
| // popIndent(); |
| result += indent() + "}" + endl(); |
| return result; |
| } |
| |
| public String reduce_withStmt(IASNode iNode, String new_scope, String body) |
| { |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "'with' statements")); |
| return ""; |
| |
| /* |
| * // statement = Pattern withStmt String result = indent() + "with(" + |
| * new_scope + ")" + endl(); result += indent() + "{" + endl(); result |
| * += indentBlock( body, 1 ); result += indent() + "}" + endl(); |
| */ |
| |
| /* |
| * String result = createInstruction(iNode); |
| * currentScope.getMethodBodySemanticChecker |
| * ().popWithScope(currentScope); result.addAll(new_scope); if ( |
| * currentScope.getFlowManager().hasWithStorage()) { |
| * result.addInstruction(OP_dup); |
| * result.addInstruction(currentScope.getFlowManager |
| * ().getWithStorage().setlocal()); } |
| * result.addInstruction(OP_pushwith); result.addAll(body); |
| * result.addInstruction(OP_popscope); |
| * currentScope.getFlowManager().finishWithContext(result); return |
| * result; |
| */ |
| } |
| |
| /** |
| * The enumeration of states that the code generator finds interesting in an |
| * XML literal. There are states here than the reducer, below, doesn't |
| * consider especially interesting; see computeXMLContentStateMatrix() for |
| * their usage. |
| */ |
| // private enum XMLContentState { TagStart, TagLiteral, TagName, Attr, Value, TagEnd, ContentLiteral, ContentExpression }; |
| |
| public String reduce_XMLContent(IASNode iNode, Vector<String> exprs) |
| { |
| String result = createInstruction(iNode); |
| |
| /* |
| * result.addInstruction(OP_findpropstrict, xmlType); |
| * result.addInstruction(OP_getproperty, xmlType); // Add all the |
| * arguments up into a single String. XMLContentState[] content_state = |
| * computeXMLContentStateMatrix(exprs); |
| * result.addAll(exprs.elementAt(0)); for ( int i = 1; i < exprs.size(); |
| * i++ ) { result.addAll(exprs.elementAt(i)); if ( content_state[i] == |
| * XMLContentState.TagName ) { // Parser elides whitespace after tag |
| * name expressions. result.addInstruction(OP_pushstring, " "); |
| * result.addInstruction(OP_add); } else if ( content_state[i] == |
| * XMLContentState.Attr ) { // Parser elides whitespace before attribute |
| * name expressions. if ( content_state[i-1] != XMLContentState.TagName |
| * ) { result.addInstruction(OP_pushstring, " "); |
| * result.addInstruction(OP_swap); result.addInstruction(OP_add); } } |
| * else if ( content_state[i] == XMLContentState.Value ) { if ( |
| * content_state[i-1] == XMLContentState.Attr ) { // Parser elided this |
| * token. result.addInstruction(OP_pushstring, "="); |
| * result.addInstruction(OP_swap); result.addInstruction(OP_add); } |
| * result.addInstruction(OP_esc_xattr); // The attribute will need quote |
| * marks for the AVM's XML parser. result.addInstruction(OP_pushstring, |
| * "\""); result.addInstruction(OP_swap); result.addInstruction(OP_add); |
| * result.addInstruction(OP_pushstring, "\""); |
| * result.addInstruction(OP_add); } else if ( content_state[i] == |
| * XMLContentState.ContentExpression ) |
| * result.addInstruction(OP_esc_xelem); result.addInstruction(OP_add); } |
| * result.addInstruction(OP_construct, 1); return result; |
| */ |
| |
| // expression = Pattern XMLContent: |
| // String literal |
| |
| // TODO: check implementation |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "E4X (ECMA-357)")); |
| return result; |
| } |
| |
| public String reduce_XMLList(IASNode iNode, Vector<String> exprs) |
| { |
| String result = createInstruction(iNode); |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "E4X (ECMA-357)")); |
| return result; |
| } |
| |
| public String reduce_XMLListConst(IASNode iNode, Vector<String> elements) |
| { |
| String result = createInstruction(iNode); |
| currentScope.addProblem(new JSUnsupportedFeatureProblem(iNode, "E4X (ECMA-357)")); |
| return result; |
| } |
| |
| /** |
| * Clone an String. |
| * |
| * @return the clone, appropriately cast. |
| * @note clone() always returns Object. |
| */ |
| String replicate(String prototype) |
| { |
| final StringBuilder sb = new StringBuilder(); |
| sb.append(prototype); |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Set this reducer's list of a priori Strings; typically these are field |
| * initializers passed in to a constructor routine. |
| */ |
| public void setInstanceInitializers(String insns) |
| { |
| // this.instanceInitializers = insns; |
| } |
| |
| public void setInstanceInitializers(InstructionList insns) |
| { |
| // this.instanceInitializers = instructionListToString(insns, false); |
| } |
| |
| /** |
| * Set this reducer's initial LexicalScope. |
| */ |
| public void setCurrentscope(LexicalScope scope) |
| { |
| this.currentScope = scope; |
| } |
| |
| /* |
| * ****************************** |
| * ** Transformation routines ** ****************************** |
| */ |
| |
| public String transform_boolean_constant(IASNode iNode, Boolean boolean_constant) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| // Literals. |
| // Pattern booleanLiteral |
| // LiteralBooleanID(void); |
| |
| if (Boolean.TRUE.equals(boolean_constant)) |
| result += "true"; |
| else |
| result += "false"; |
| |
| return result; |
| } |
| |
| public String transform_double_constant(IASNode iNode, Double double_constant) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_pushdouble, double_constant); |
| */ |
| |
| // expression = double_constant |
| result += double_constant.toString(); |
| if (result.length() > 2 && result.charAt(result.length() - 1) == '0' && result.charAt(result.length() - 2) == '.') |
| result = result.replace(".0", ""); |
| return result; |
| } |
| |
| public String transform_numeric_constant(IASNode iNode, Number numeric_constant) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_pushdouble, double_constant); |
| */ |
| if (numeric_constant instanceof Integer) |
| return transform_integer_constant(iNode, (Integer)numeric_constant); |
| if (numeric_constant instanceof Double) |
| return transform_double_constant(iNode, (Double)numeric_constant); |
| return result; |
| } |
| |
| public String transform_constant_value(IASNode iNode, Object constant_value) |
| { |
| if (constant_value instanceof Boolean) |
| { |
| return transform_boolean_constant(iNode, (Boolean)constant_value); |
| } |
| if (constant_value instanceof Number) |
| { |
| return transform_numeric_constant(iNode, (Number)constant_value); |
| } |
| if (constant_value instanceof String) |
| { |
| return transform_string_constant(iNode, (String)constant_value); |
| } |
| |
| String result = null; |
| if (constant_value instanceof Namespace) |
| { |
| // Doesn't look like FlaconJS has the equivalent of push namespace... |
| //result.addInstruction(OP_pushnamespace, (Namespace)constant_value); |
| } |
| else if (constant_value == ABCConstants.NULL_VALUE) |
| { |
| result = "null"; |
| } |
| else if (constant_value == ABCConstants.UNDEFINED_VALUE) |
| { |
| result = "undefined"; |
| } |
| else |
| { |
| assert false : "unknown constant type"; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * transform a string_constant to a constant_value - essentially a no-op, |
| * but need a reduction so we can assign it a cost |
| */ |
| public Object transform_string_constant_to_constant(IASNode iNode, String string_constant) |
| { |
| return string_constant; |
| } |
| |
| /** |
| * transform a boolean_constant to a constant_value - essentially a no-op, |
| * but need a reduction so we can assign it a cost |
| */ |
| public Object transform_boolean_constant_to_constant(IASNode iNode, Boolean boolean_constant) |
| { |
| return boolean_constant; |
| } |
| |
| /** |
| * transform a numeric_constant to a constant_value - essentially a no-op, |
| * but need a reduction so we can assign it a cost |
| */ |
| public Object transform_numeric_constant_to_constant(IASNode iNode, Number numeric_constant) |
| { |
| return numeric_constant; |
| } |
| |
| public String transform_expression_to_conditionalJump(IASNode iNode, String expression) |
| { |
| String result = createInstruction(iNode, expression.length() + 1); |
| |
| /* |
| * result.addAll(expression); result.addInstruction(OP_iftrue); |
| */ |
| |
| // conditionalJump = expression |
| // String result = indent() +"if" + expression; |
| result += expression; |
| return result; |
| } |
| |
| public Object transform_expression_to_constant_value(IASNode iNode, String expression) |
| { |
| // return null - something higher up will report any appropriate diagnostics. |
| return null; |
| } |
| |
| public String transform_expression_to_void_expression(IASNode iNode, String expression) |
| { |
| String result = createInstruction(iNode, expression.length() + 1); |
| |
| /* |
| * result.addAll(expression); result.addInstruction(OP_pop); |
| */ |
| |
| // void_expression = expression |
| result += expression.toString() + ";" + endl(); |
| return result; |
| } |
| |
| public String transform_integer_constant(IASNode iNode, Integer integer_constant) |
| { |
| // long value = integer_constant.longValue(); |
| |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * // Choose an String. // Could also be done at label name, but there's |
| * // not a lot of value in doing so. result.pushNumericConstant(value); |
| */ |
| |
| // expression = integer_constant |
| result += integer_constant.longValue(); |
| |
| // this confuses addDynamicCastToValue() |
| // if( integer_constant >= 16 ) |
| // result += " /* 0x" + Long.toHexString(integer_constant).toUpperCase() + " */"; |
| |
| return result; |
| } |
| |
| public Object transform_name_to_constant_value(IASNode iNode) |
| { |
| return SemanticUtils.transformNameToConstantValue(iNode, currentScope.getProject()); |
| } |
| |
| public String transform_name_to_expression(IASNode iNode, Binding name) |
| { |
| /* |
| * return generateAccess(name); |
| */ |
| |
| // expression = name |
| return bindingToString(iNode, name, true, true); |
| } |
| |
| public String transform_non_resolving_identifier(IASNode iNode, String non_resolving_identifier) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_pushstring, non_resolving_identifier); |
| */ |
| |
| // Pattern nonResolvingIdentifier, expression |
| // NonResolvingIdentifierID(void); |
| |
| result += non_resolving_identifier; |
| |
| return result; |
| } |
| |
| public String transform_runtime_name_expression(IASNode iNode, RuntimeMultiname runtime_name_expression) |
| { |
| return runtime_name_expression.generateRvalue(iNode); |
| } |
| |
| public String transform_string_constant(IASNode iNode, String string_constant) |
| { |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_pushstring, string_constant); |
| */ |
| |
| // expression = string_constant |
| // (AJH) sometimes folded constants don't have quotes. |
| // Probably should be fixed somewhere else but hacking |
| // a solution here for now |
| if (string_constant.length() == 0) |
| { |
| string_constant = ""; |
| } |
| else |
| { |
| char firstChar = string_constant.charAt(0); |
| if (firstChar != '\"' && firstChar != '\'') |
| { |
| // missing quotes, add them |
| string_constant = "\"" + string_constant + "\""; |
| } |
| } |
| result += string_constant; |
| |
| return result; |
| } |
| |
| public String transform_uint_constant(IASNode iNode, Long uint_constant) |
| { |
| long value = uint_constant.longValue(); |
| |
| String result = createInstruction(iNode, 1); |
| |
| /* |
| * result.addInstruction(OP_pushuint, value); |
| */ |
| |
| // expression = uint_constant |
| // ArgumentID(Long uint_constant) |
| result += value; |
| if (uint_constant >= 16) |
| result += " /* 0x" + Long.toHexString(uint_constant).toUpperCase() + " */"; |
| return result; |
| } |
| |
| /** |
| * Generate a unary operator. |
| * |
| * @param operand - the operand expression. |
| * @param opcode - the operator's opcode. |
| * @return an String that applies the operator to the operand. |
| */ |
| String unaryOp(IASNode iNode, String operand, int opcode) |
| { |
| currentScope.getMethodBodySemanticChecker().checkUnaryOperator(iNode, opcode); |
| String result = createInstruction(iNode, operand.length() + 1); |
| |
| /* |
| * result.addAll(operand); result.addInstruction(opcode); |
| */ |
| |
| switch (opcode) |
| { |
| case OP_negate: |
| result += reduce_unaryMinusExpr(iNode, operand); |
| break; |
| case OP_convert_d: |
| result += reduce_unaryPlusExpr(iNode, operand); |
| break; |
| case OP_typeof: |
| result += reduce_typeofExpr(iNode, operand); |
| break; |
| case OP_bitnot: |
| result += reduce_bitNotExpr(iNode, operand); |
| break; |
| case OP_unplus: |
| result += reduce_unaryPlusExpr(iNode, operand); |
| break; |
| default: |
| currentScope.addProblem(new JSInternalCompilerProblem(iNode, "Unknown unary op '" + opcode + "'")); |
| } |
| |
| return result; |
| } |
| |
| void checkUnaryOp(IASNode iNode, int opcode) |
| { |
| currentScope.getMethodBodySemanticChecker().checkUnaryOperator(iNode, opcode); |
| } |
| |
| /** |
| * Walk a syntax tree and note any construct that will cause the ABC |
| * function to need an activation record. |
| */ |
| void scanFunctionBodyForActivations(IASNode n) |
| { |
| if (n != null) |
| { |
| switch (n.getNodeID()) |
| { |
| case WithID: |
| case CatchID: |
| case AnonymousFunctionID: |
| case FinallyID: |
| case FunctionID: |
| { |
| currentScope.setNeedsActivation(); |
| break; |
| } |
| default: |
| { |
| for (int i = 0; i < n.getChildCount(); i++) |
| scanFunctionBodyForActivations(n.getChild(i)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Is this expression a string literal? |
| * |
| * @return true if the expression is a string literal, i.e., it has one |
| * pushstring String. |
| */ |
| /* |
| * private static boolean isStringLiteral(String insns) { return |
| * insns.lastElement().getOpcode() == OP_pushstring; } |
| */ |
| |
| /** |
| * Extract the string literal from an String. |
| * |
| * @param insns - the String of interest. |
| * @return the string literal from insns' final (and presumably only |
| * executable) String. |
| */ |
| /* |
| * private static String getStringLiteral(String insns) { assert |
| * isStringLiteral(insns); return |
| * insns.lastElement().getOperand(0).toString(); } |
| */ |
| |
| /** |
| * Work around the linear presentation of XML initializer elements from the |
| * parser by doing ad-hoc pattern matching. |
| * |
| * @param exprs - a Vector of expressions. |
| * @return A state transition matrix to generate code for the elements. |
| * @see ECMA-357 11.1.4 "XML Initialiser" Expressions may be used to compute |
| * parts of an XML initialiser. Expressions are delimited by curly braces |
| * and may appear inside tags or element content. Inside a tag, expressions |
| * may be used to compute a tag name, attribute name, or attribute value. |
| * Inside an element, expressions may be used to compute element content. |
| */ |
| /* |
| * private XMLContentState[] computeXMLContentStateMatrix( Vector<String> |
| * exprs) { XMLContentState[] result = new XMLContentState[exprs.size()]; // |
| * TagStart, TagLiteral, TagName, Attr, Value, TagEnd, ContentLiteral, |
| * ContentExpression // First element must be a string or the parser |
| * wouldn't have put us here. // But it may be a simple < or </ literal or a |
| * <foo literal; the former // imply that the next expression is going to be |
| * a TagName, the latter just // is a TagLiteral that puts us into the |
| * middle of things. assert isStringLiteral(exprs.elementAt(0)) : |
| * exprs.elementAt(0); String first_element = |
| * getStringLiteral(exprs.elementAt(0)); if ( first_element.equals("<") || |
| * first_element.equals("</") ) result[0] = XMLContentState.TagStart; else |
| * result[0] = XMLContentState.TagLiteral; for ( int i = 1; i < |
| * exprs.size(); i++ ) { String insns = exprs.elementAt(i); if ( |
| * isStringLiteral(insns) ) { switch ( result[i-1] ) { case TagStart: case |
| * TagLiteral: case TagName: case Attr: case Value: { if ( |
| * getStringLiteral(insns).matches(".*>") ) result[i] = |
| * XMLContentState.TagEnd; else result[i] = XMLContentState.TagLiteral; |
| * break; } case TagEnd: case ContentLiteral: case ContentExpression: if ( |
| * getStringLiteral(insns).matches("<.*") ) result[i] = |
| * XMLContentState.TagStart; else result[i] = |
| * XMLContentState.ContentLiteral; } } else { // Now the fun starts. // The |
| * most important thing to do here is to get the // Value, and |
| * ContentExpression states right; // these are the entities that require |
| * special Strings. switch ( result[i-1] ) { case TagStart: result[i] = |
| * XMLContentState.TagName; break; // An expression following a tag name or |
| * // an attribute value should be an attribute name. case TagName: case |
| * Value: result[i] = XMLContentState.Attr; break; // An expression |
| * following an attribute name // should be an attribute value. case Attr: |
| * result[i] = XMLContentState.Value; break; case TagEnd: case |
| * ContentLiteral: case ContentExpression: result[i] = |
| * XMLContentState.ContentExpression; break; case TagLiteral: { String |
| * literal = getStringLiteral(exprs.elementAt(i-1)); if ( |
| * literal.endsWith("=") ) result[i] = XMLContentState.Value; else result[i] |
| * = XMLContentState.Attr; break; } default: assert false: "Unhandled case " |
| * + result[i-1]; } } } return result; } |
| */ |
| |
| // ======================================================================================= |
| |
| public String reduce_untypedParameterInitializer(IASNode iNode, Binding param_name, String param_initializer) |
| { |
| // Pattern untypedParameterInitializer |
| // ArgumentID(name param_name, expression param_initializer); |
| |
| this.makeParameter(iNode, ((org.apache.flex.compiler.internal.tree.as.ParameterNode)iNode), currentScope, param_name, null, null); |
| return null; |
| } |
| |
| public String reduce_typedParameterInitializer(IASNode iNode, Binding param_name, Binding param_type, String param_initializer) |
| { |
| // Pattern typedParameterInitializer |
| // ArgumentID(name param_name, name param_type, expression param_initializer); |
| |
| if (param_type.getName() != null) |
| usedTypes.add(getBasenameFromBinding(param_type)); |
| this.makeParameter(iNode, ((org.apache.flex.compiler.internal.tree.as.ParameterNode)iNode), currentScope, param_name, param_type, param_type); |
| |
| return null; |
| } |
| |
| private static String removeSuffixIfPresent(String str, String suffix) |
| { |
| if (str.endsWith(suffix)) |
| return str.substring(0, str.length() - suffix.length()); |
| return str; |
| } |
| |
| /* |
| * General form of switch statement: expand into if/elseif. |
| */ |
| |
| // BlockID(switchCases cases+); |
| |
| public ConditionalFragment reduce_switchCase(IASNode iNode, String case_expr, Vector<String> case_actions) |
| { |
| // conditionalElements = Pattern switchCase |
| // String case_expr, Vector<String> case_actions |
| return new ConditionalFragment(iNode, case_expr, case_actions); |
| } |
| |
| public ConditionalFragment reduce_switchDefault(IASNode iNode, String case_actions) |
| { |
| // conditionalElements = Pattern switchDefault |
| return new ConditionalFragment(iNode, null, case_actions); |
| } |
| |
| public ConditionalFragment reduce_switchDefault(IASNode iNode, java.util.Vector<String> case_actions) |
| { |
| // conditionalElements = Pattern switchDefault |
| String s = ""; |
| for (int i = 0; i < case_actions.size(); i++) |
| s += case_actions.get(i); |
| |
| return new ConditionalFragment(iNode, null, s); |
| } |
| |
| public void prologue_labeledStmt(IASNode p, int iRule) throws java.lang.Exception |
| { |
| // statement = Pattern labeledStmt |
| } |
| |
| public void prologue_withStmt(IASNode p, int iRule) throws java.lang.Exception |
| { |
| // statement = Pattern withStmt |
| } |
| |
| /* |
| * ******************** |
| * ** Declarations ** ******************** |
| */ |
| |
| private String reduce_variableDecl(IASNode iNode, Name var_name, Binding var_type, String var_initializer, Vector<String> chained_decls) |
| { |
| // m_numMemberAsUint = num; needs to |
| var_initializer = addDynamicCastToValue(iNode, var_initializer); |
| |
| // constant propagation for initialized members |
| m_varInitValue = var_initializer; |
| |
| String result = ""; |
| final Boolean isStaticInit = currentScope.traitsVisitor == null; |
| |
| // CMP-???: {PackageInternalNs:"tests"}::n |
| Name vname = var_name; |
| Namespace ns = vname.getQualifiers().length() > 0 ? null : vname.getSingleQualifier(); |
| if (ns == null || ns.getKind() == CONSTANT_PackageInternalNs) |
| vname = new Name(new Nsset(new Namespace(CONSTANT_PackageNs)), getBasenameFromName(var_name)); |
| |
| if (!isStaticInit) |
| { |
| // support for [ConvertLocalVarsToMembers] |
| // reduce_variableDecl() registers variables as members if convertLocalVarsToMembers(__p) |
| // is true via switching to the class scope. |
| LexicalScope scope = currentScope; |
| Boolean resetTraitsVisitor = false; |
| if (convertLocalVarsToMembers(iNode)) |
| { |
| scope = scope.getEnclosingFrame(); |
| if (scope.traitsVisitor == null) |
| { |
| final JSEmitter emitter = (JSEmitter)currentScope.getEmitter(); |
| scope.traitsVisitor = emitter.getCurrentClassVisitor().visitInstanceTraits(); |
| resetTraitsVisitor = true; |
| } |
| |
| this.registerConvertedMember(getBasenameFromName(var_name)); |
| } |
| BaseVariableNode var_node = (BaseVariableNode)iNode; |
| Binding var = scope.resolveName((IdentifierNode)var_node.getNameExpressionNode()); |
| if (var_type != null && var_type.getName() != null) |
| scope.makeVariable(var, var_type.getName(), ((BaseDefinitionNode)iNode).getMetaInfos()); |
| else |
| scope.makeVariable(var); |
| |
| // support for [ConvertLocalVarsToMembers] |
| if (resetTraitsVisitor) |
| scope.traitsVisitor = null; |
| } |
| |
| final VariableNode var = (VariableNode)iNode; |
| final boolean isStaticName = var.hasModifier(ASModifier.STATIC); |
| final String varName = nameToString(iNode, var_name, false, isStaticName); |
| final boolean isMember = var.getDefinition().getParent() instanceof ClassDefinition; |
| |
| // add explicit dependencies. |
| // regression, see shell.as: var playerType:String = Capabilities.playerType; |
| // regression requires add explicit dependencies, see WSDK's Deferred.ctor, |
| // which references static Activity.current. Without modification Falcon does not |
| // recognize that Deferred is dependent on Activity. |
| if (var_initializer != null && !var_initializer.isEmpty()) |
| { |
| for (IDefinition def : m_referencedDefinitions) |
| { |
| final String fullName = createFullNameFromDefinition(currentScope.getProject(), def); |
| if (var_initializer.contains(fullName) && def.getParent() instanceof ClassDefinition) |
| { |
| final ClassDefinition addTo = (ClassDefinition)def.getParent(); |
| addDependency(def, addTo, DependencyType.INHERITANCE); |
| // m_sharedData.addDependency(def, addTo); |
| break; |
| } |
| } |
| } |
| |
| if (isMember) |
| { |
| result += (isStaticInit ? "" : (indent() + indent())) + varName; |
| } |
| else |
| { |
| if (var_type != null && var_type.getName() != null) |
| { |
| final StringBuilder sb = new StringBuilder(); |
| final Boolean foundDefinition = nameToJSDocType(var_type, sb); |
| /* |
| * IDefinition typeDef = var_type.getDefinition(); if( typeDef |
| * == null ) typeDef = getDefinitionForName(iNode, |
| * var_type.getName() ); final StringBuilder sb = new |
| * StringBuilder(); Boolean foundDefinition = false; if( typeDef |
| * == null ) foundDefinition = nameToJSDocType( var_type, sb ); |
| * else foundDefinition = nameToJSDocType( typeDef, sb ); |
| */ |
| if (!foundDefinition) |
| m_needsSecondPass = true; |
| |
| result += indent() + "/** @type {" + sb.toString() + "} */\n"; |
| } |
| result += indent() + getVarSnippet(iNode, varName); |
| } |
| |
| // if (var_type != null && var_type.getName() != null) |
| // result += " /* : " + getBasenameFromBinding(var_type) + " */"; |
| |
| if (var_initializer != null && !var_initializer.isEmpty()) |
| result += " = " + var_initializer; |
| |
| result += ";" + endl(); |
| |
| for (String decl : chained_decls) |
| { |
| result += decl; |
| } |
| return result; |
| } |
| |
| private boolean isLocalVariable(IVariableDefinition def) |
| { |
| return def.getVariableClassification() == IVariableDefinition.VariableClassification.LOCAL; |
| } |
| |
| public String emptyList(IASNode iNode) |
| { |
| { |
| // Pattern nilExpr |
| // NilID(void); |
| return new String(""); |
| } |
| } |
| |
| public String reduce_unaryMinusExpr(IASNode iNode, String e) |
| { |
| { |
| // postfix unary operator. |
| // Pattern unaryMinusExpr |
| // Op_SubtractID(expression e); |
| String result = "(-" + stripNS(e) + ")"; |
| return result; |
| } |
| } |
| |
| public String reduce_unaryPlusExpr(IASNode iNode, String e) |
| { |
| { |
| // Pattern unaryPlusExpr |
| String result = "(+" + stripNS(e) + ")"; |
| return result; |
| } |
| } |
| |
| public String reduce_typeofExpr(IASNode iNode, String expr) |
| { |
| String result = null; |
| |
| // Pattern typeofExpr |
| // Op_TypeOfID(expression expr); |
| |
| // e15_6_2, e15_4_2_2_2 |
| // see makeMemberBinding() |
| if (iNode instanceof IUnaryOperatorNode) |
| { |
| final IASNode exprNode = ((IUnaryOperatorNode)iNode).getOperandNode(); |
| if (exprNode instanceof FunctionCallNode) |
| { |
| final FunctionCallNode fcn = (FunctionCallNode)exprNode; |
| final IDefinition def = fcn.resolveType(currentScope.getProject()); |
| if (def != null && def instanceof ITypeDefinition) |
| { |
| final ITypeDefinition type = (ITypeDefinition)def; |
| result = getTypeOfString(type); |
| } |
| } |
| } |
| |
| if (result == null) |
| result = "typeof(" + stripNS(expr) + ")"; |
| return result; |
| } |
| |
| public String reduce_bitNotExpr(IASNode iNode, String unary) |
| { |
| { |
| // Pattern bitNotExpr |
| // Op_BitwiseNotID(expression unary); |
| String result = "~(" + stripNS(unary) + ")"; |
| return result; |
| } |
| } |
| |
| /* |
| * Miscellaneous binary expressions. |
| */ |
| |
| public String reduce_istypeExprLate(IASNode iNode, String expr, String typename) |
| { |
| { |
| // Pattern istypeExprLate |
| // Op_IsID(expression expr, expression typename); |
| |
| // do not use stripNS, because 'org.w3c.dom.Document.createElementNS(adobe.root.browser.SVGUtils.ns, "g")' then becomes 'ns, "g")'. |
| // String result = stripNS(expr) + " instanceof " + stripNS(typename); |
| String result = reduce_instanceofExpr(iNode, expr, typename); |
| return result; |
| } |
| } |
| |
| public String reduce_astypeExprLate(IASNode iNode, String expr, String typename) |
| { |
| { |
| // Pattern astypeExprLate |
| // Op_AsID(expression expr, expression typename); |
| |
| String result; |
| |
| // Don't emit dynamicCastAs() call when the typename on the RHS is one of our fake classes standing in for |
| // 'native' browser or JS APIs, or when we're inside the core adobe.* code. |
| boolean suppress = true; // AJH disabled getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME) || |
| // isExternalClass(currentScope.getProject(), getDefinitionForName(iNode, makeName(typename))); |
| |
| /* |
| * // As with handleCastExpr(), we don't handle conversion to |
| * int/uint primitives yet - FJS-54 if( !suppress && |
| * (typename.equals("int") || typename.equals("uint")) ) { |
| * printWarning(iNode, |
| * "Conversion to "+typename+" will be ignored!"); suppress = true; |
| * } |
| */ |
| |
| if (suppress) |
| result = expr + " /* as " + stripNS(typename) + " */"; |
| else |
| { |
| if (typename.equals("int")) |
| typename = JSSharedData.FRAMEWORK_CLASS + ".IntClass"; |
| else if (typename.equals("uint")) |
| typename = JSSharedData.FRAMEWORK_CLASS + ".UIntClass"; |
| result = "adobe.dynamicCastAs(" + stripNS(expr) + ", " + stripNS(typename) + ")"; |
| } |
| return result; |
| } |
| } |
| |
| public String reduce_inExpr(IASNode iNode, String needle, String haystack) |
| { |
| // Pattern inExpr |
| // Op_InID(expression needle, expression haystack); |
| String result; |
| |
| final boolean isFramework = getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME); |
| if (isFramework) |
| result = "(" + stripNS(needle) + " in " + stripNS(haystack) + ")"; |
| else |
| { |
| // TODO: static analysis of haystack. |
| // Get IDefinition of haystack and check against Proxy's IDefinition. |
| // If haystack is not a Proxy we can simply emit: |
| // result = "(" + stripNS(needle) + " in " + stripNS(haystack) + ")"; |
| result = "adobe.inProperty(self, " + stripNS(needle) + ", " + stripNS(haystack) + ")"; |
| } |
| return result; |
| } |
| |
| private Boolean isDerivedFromInterface(IDefinition def, IInterfaceDefinition interf, Boolean includingDef) |
| { |
| if (def == interf) |
| return includingDef; |
| |
| if (def != null) |
| { |
| IClassDefinition classDef = null; |
| if (def instanceof IClassDefinition) |
| classDef = (IClassDefinition)def; |
| else if (def instanceof ITypeDefinition) |
| { |
| final ITypeDefinition type = (ITypeDefinition)def; |
| def = type.resolveType(currentScope.getProject()); |
| } |
| |
| if (classDef != null) |
| { |
| Iterator<IClassDefinition> superClassIterator = classDef.classIterator(currentScope.getProject(), true); |
| while (superClassIterator.hasNext()) |
| { |
| final IClassDefinition superClass = superClassIterator.next(); |
| if (superClass == interf) |
| return true; |
| |
| Iterator<IInterfaceDefinition> interfaceIterator = classDef.interfaceIterator(currentScope.getProject()); |
| while (interfaceIterator.hasNext()) |
| { |
| final IInterfaceDefinition implementedInterface = interfaceIterator.next(); |
| if (implementedInterface == interf) |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| public String reduce_instanceofExpr(IASNode iNode, String expr, String typename) |
| { |
| final Boolean optimize = true; |
| String result; |
| |
| // Pattern instanceofExpr |
| // Op_InstanceOfID(expression expr, expression typename); |
| final boolean isFramework = getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME); |
| |
| if (optimize) |
| { |
| boolean useNative = isFramework; |
| final String warnString = "is/instanceof expression is a constant: " + expr + " instanceof " + typename; |
| |
| // As with handleCastExpr(), we don't handle conversion to int/uint primitives yet - FJS-54 |
| boolean suppress = false; |
| if (!isFramework && (typename.equals("int") || typename.equals("uint"))) |
| { |
| printWarning(iNode, warnString); |
| suppress = true; |
| } |
| else if (!useNative) |
| { |
| // optimization for "instanceof" |
| IDefinition typeDef = m_sharedData.getDefinition(typename); |
| if (typeDef == null) |
| typeDef = getDefinitionForNode(iNode, true); |
| |
| if (typeDef != null) |
| { |
| final Boolean simpleOptimization = true; |
| if (simpleOptimization) |
| { |
| if (!(typeDef instanceof IInterfaceDefinition)) |
| useNative = true; |
| } |
| else |
| { |
| IDefinition exprDef = m_sharedData.getDefinition(expr); |
| if (exprDef == null) |
| exprDef = getDefinitionForNode(iNode, false); |
| |
| ICompilerProject project = currentScope.getProject(); |
| if (!(typeDef instanceof IInterfaceDefinition)) |
| { |
| if (typeDef instanceof IClassDefinition && |
| exprDef instanceof IClassDefinition && |
| typeDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.OBJECT) && |
| typeDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.ANY_TYPE) && |
| typeDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.CLASS) && |
| exprDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.OBJECT) && |
| exprDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.ANY_TYPE) && |
| exprDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.CLASS)) |
| { |
| final IClassDefinition classDef = (IClassDefinition)typeDef; |
| Iterator<IClassDefinition> superClassIterator = classDef.classIterator(currentScope.getProject(), true); |
| while (superClassIterator.hasNext()) |
| { |
| final IClassDefinition superClass = superClassIterator.next(); |
| if (superClass == exprDef) |
| return "true" + " /* " + warnString + " */"; |
| } |
| |
| return "false" + " /* " + warnString + " */"; |
| } |
| useNative = true; |
| } |
| else |
| { |
| final IInterfaceDefinition interf = (IInterfaceDefinition)typeDef; |
| final Boolean isDerived = isDerivedFromInterface(exprDef, interf, true); |
| |
| // If the rhs is not an Object or "*" then we can evaluate instanceof statically |
| // and we can use the result of isDerivedFromInterface() as a constant. |
| if (exprDef != null && interf != null && |
| exprDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.OBJECT) && |
| exprDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.ANY_TYPE) && |
| exprDef != project.getBuiltinType(IASLanguageConstants.BuiltinType.CLASS)) |
| { |
| if (isDerived) |
| return "true" + " /* " + warnString + " */"; |
| else |
| return "false" + " /* " + warnString + " */"; |
| } |
| |
| useNative = isDerived; |
| } |
| } |
| } |
| } |
| |
| if (useNative) |
| result = "(" + stripNS(expr) + " instanceof " + stripNS(typename) + ")"; |
| else if (suppress) |
| result = "false" + " /* " + warnString + " */"; |
| else |
| result = "adobe.instanceOf(" + stripNS(expr) + ", " + stripNS(typename) + ")"; |
| } |
| else |
| { |
| // As with handleCastExpr(), we don't handle conversion to int/uint primitives yet - FJS-54 |
| boolean suppress = false; |
| if (!isFramework && (typename.equals("int") || typename.equals("uint"))) |
| { |
| printWarning(iNode, "is/instanceof " + typename + " will always be false!"); |
| suppress = true; |
| } |
| |
| if (isFramework) |
| result = "(" + stripNS(expr) + " instanceof " + stripNS(typename) + ")"; |
| else if (suppress) |
| result = "false"; |
| else |
| result = JSSharedData.JS_FRAMEWORK_NAME + "." + "instanceOf(" + stripNS(expr) + ", " + stripNS(typename) + ")"; |
| } |
| |
| return result; |
| } |
| |
| public Binding makeMemberBinding(IASNode iNode, String stem, String member) |
| { |
| /* |
| * if( stem.equals(JSSharedData.THIS) && m_functionNode != null ) { |
| * final String currentFullClassName = getCurrentFullClassName(); final |
| * IDefinition classDef = |
| * m_sharedData.getDefinition(currentFullClassName); if( classDef != |
| * null ) { IDefinition memberDef = getDefinitionForMember( iNode, |
| * classDef, removeQuotes(member), ClassificationValue.MEMBERS_AND_TYPES |
| * ); if( memberDef != null ) return new |
| * Binding(iNode,makeName(member),memberDef); } } |
| */ |
| |
| // This is based on an idea Peter came up with... |
| try |
| { |
| IASNode node = iNode; |
| while (node != null) |
| { |
| if (node instanceof IBinaryOperatorNode && !(node instanceof MemberAccessExpressionNode)) |
| { |
| node = ((IBinaryOperatorNode)node).getLeftOperandNode(); |
| } |
| else if (node instanceof IFunctionCallNode) |
| node = ((IFunctionCallNode)node).getNameNode(); |
| else if (node instanceof IDynamicAccessNode) |
| node = ((IDynamicAccessNode)node).getLeftOperandNode(); |
| else if (node instanceof IUnaryOperatorNode) |
| node = ((IUnaryOperatorNode)node).getOperandNode(); |
| // return new Binding(iNode,makeName(member),((IUnaryOperatorNode)node).getDefinition()); |
| else if (node instanceof IExpressionNode) |
| { |
| IDefinition leftDef = ((IExpressionNode)node).resolve(currentScope.getProject()); |
| if (leftDef instanceof VariableDefinition) |
| { |
| final VariableDefinition variable = (VariableDefinition)leftDef; |
| leftDef = variable.resolveType(currentScope.getProject()); |
| |
| // items from Vectors trigger adobe.getProperty() |
| if (leftDef instanceof AppliedVectorDefinition) |
| { |
| final AppliedVectorDefinition vn = (AppliedVectorDefinition)leftDef; |
| leftDef = vn.resolveElementType(currentScope.getProject()); |
| if (leftDef != null && leftDef instanceof ClassDefinition) |
| return new Binding(iNode, makeName(member), leftDef); |
| } |
| } |
| else if (leftDef instanceof FunctionDefinition) |
| { |
| final FunctionDefinition functionDef = (FunctionDefinition)leftDef; |
| final IReference typeRef = functionDef.getReturnTypeReference(); |
| |
| // Tamarin's ecma3/Expressions/e11_2_3_1.as triggers ClassCastException |
| // http://watsonexp.corp.adobe.com/#bug=3026796 |
| if (typeRef != null) |
| leftDef = typeRef.resolve(currentScope.getProject(), (ASScope)getScopeFromNode(iNode), DependencyType.INHERITANCE, false); |
| } |
| else if (leftDef instanceof IGetterDefinition) |
| { |
| return new Binding(iNode, makeName(member), leftDef); |
| // final ITypeDefinition returnType = ((IGetterDefinition)leftDef).resolveReturnType(currentScope.getDefinitionCache()); |
| // leftDef = m_sharedData.getDefinition(returnType.getQualifiedName()); |
| } |
| |
| if (leftDef != null && leftDef instanceof ClassDefinition) |
| { |
| IDefinition memberDef = getDefinitionForMember(iNode, leftDef, removeQuotes(member), ClassificationValue.MEMBERS_AND_TYPES); |
| if (memberDef != null) |
| return new Binding(iNode, makeName(member), memberDef); |
| } |
| node = null; |
| } |
| else |
| { |
| node = null; |
| } |
| } |
| } |
| catch (Exception e) |
| { |
| // getDefinition() sometimes crashes, e.g. when looking at a cast to an interface in some cases, |
| // FunctionDefinition.getParameters() returns null and ExpressionNodeBase.determineIfFunction() chokes on it |
| printWarning(iNode, "makeMemberBinding() failed for member-access expression " + ((IMemberAccessExpressionNode)iNode).getDisplayString()); |
| } |
| |
| Binding b = makeBinding(iNode, member); |
| return b; |
| } |
| |
| private String handleCastExpr(IASNode iNode, Binding method_name, Vector<String> args) |
| { |
| // if( !JSSharedData.m_useClosureLib ) |
| { |
| String className = bindingToString(iNode, method_name, false, true); |
| className = removeRootName(className); |
| |
| boolean noOpCast = false; // If true, the function-call syntax is entirely stripped out |
| boolean injectMissingNew = false; // Else, if true, we inject a 'new' operator |
| // If both false, we keep the function-call syntax unchanged |
| |
| IDefinition def = getDefinitionForName(iNode, makeName(className)); |
| if (def != null) |
| { |
| // Cast to a built-in ActionScript language type? |
| if (isDataType(def)) |
| { |
| // Casts to a built-in data type fall into several categories: conversions, and effective no-ops |
| // Case A: cast to built-in object type (e.g. Function, Date) |
| // Case B: conversion to built-in object (implicit 'new'), which browser will also grok |
| // given the same cast-like syntax (e.g. Boolean, RegExp, Array) |
| // Case C: conversion to built-in object (implicit 'new'), which requires an explicit |
| // 'new' in browser (e.g. Error, XML, XMLList) |
| // Case D: conversion to primitive type (e.g. int), which requires generating special-case |
| // code (e.g. Math.floor() in the case of int) |
| final String typeName = def.getBaseName(); |
| if (isComplexDataType(typeName)) |
| { |
| if (isErrorDataType(typeName) || typeName.equals("XML") || typeName.equals("XMLList")) |
| // Case C: casts (e.g. from String) implicitly new up an instance in AS, but not in JS - must inject a 'new' keyword |
| // TODO: need to do the same for subclasses of Error too! - FJS-71 |
| injectMissingNew = true; |
| else if (typeName.equals("Array") || typeName.equals("RegExp")) |
| // Case B: casts (e.g. from String) implicitly new up an instance in both AS and JS |
| // safe to emit cast-like code (e.g. function call syntax) |
| noOpCast = false; |
| else |
| // Case A: just regular cast-to-object operations, like the !isDataType() case |
| noOpCast = true; |
| } |
| else |
| { |
| if (typeName.equals("Number") || typeName.equals("Boolean") || typeName.equals("String")) |
| { |
| // safe to emit cast-like code (e.g. function call syntax), so leave both flags false |
| } |
| else if (typeName.equals("IntClass")) |
| { |
| // According to Peter Math.floor() gives incorrect results for negative numbers. |
| // He suggests using ~~ instead. |
| // (Fixes FJS-54 in handleCastExpr()) |
| |
| // int(undefined) = 0 |
| // int(null) = 0 |
| // int(1.6) = 1 |
| // int(-1) = -1 |
| // int(-1.2) = -1 |
| // int(-1.6) = -1 |
| |
| return "~~(" + args.get(0) + ") /* Cast to " + className + " */"; |
| } |
| else if (typeName.equals("UIntClass")) |
| { |
| // (Fixes FJS-54 in handleCastExpr()) |
| |
| // uint(undefined) = 0 |
| // uint(null) = 0 |
| // uint(1.6) = 1 |
| // uint(1.2) = 1 |
| // uint(-1.2) = 1 |
| // uint(-1) = 4294967295 |
| // uint(-1.2) = 4294967295 |
| // uint(-1.6) = 4294967295 |
| |
| // ((~~(-1.6)) < 0) ? (4294967296+(~~(-1.6))) : (~~(-1.6)) |
| |
| return "(((~~(" + args.get(0) + ")) < 0 ) ? (4294967296+(~~(" + args.get(0) + "))) : (~~(" + args.get(0) + ")) /* Cast to " + className + " */)"; |
| } |
| else |
| { |
| // TODO: (remaining cases -- Null, *, void -- should never be encountered) |
| printWarning(iNode, "Cast to " + typeName + " will be ignored!"); |
| noOpCast = true; |
| } |
| } |
| |
| } |
| else |
| { |
| // Casts to a user-defined type are *always* a simple cast (like Case A above) |
| boolean isType = (def instanceof IClassDefinition) || (def instanceof IInterfaceDefinition); |
| if (isType) |
| { |
| if (args.size() == 1) |
| noOpCast = true; |
| else |
| injectMissingNew = true; // TODO: is this case ever valid AS code? |
| } |
| } |
| } |
| |
| // prevent java.lang.ArrayIndexOutOfBoundsException, |
| // see falconjs_cs6: Tamarin's ecma3/Date/e15_9_2_2_1.as triggers ArrayIndexOutOfBoundsException |
| // http://watsonexp.corp.adobe.com/#bug=3022808 |
| if (noOpCast && args.size() == 1) |
| { |
| return args.get(0) + " /* Cast to " + className + " */"; |
| } |
| else if (injectMissingNew) |
| { |
| return "/* Injecting missing new */ " + generateNewCall(iNode, method_name, args); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns a "direct" reference to the version of superMethodName defined by |
| * the closest ancestor of invokingMethodClass (but not the version defined |
| * by invokingMethodClass itself, if it has one; this mirrors the semantics |
| * of super.foo() calls). A "direct" reference means a static-like reference |
| * to what is actually an instance method. E.g. if the defining ancestor |
| * class is Foo and the method name if bar, then the direct reference is |
| * "Foo.prototype.bar" |
| * |
| * @param invokingMethodClass Class whose code contains the super() call. |
| * @param superMethodNameUnmunged Raw AS name of super method being invoked; |
| * NO get_/set_ prefix. |
| */ |
| public String getSuperMethodReference(IClassDefinition invokingMethodClass, String superMethodNameUnmunged) |
| { |
| assert !superMethodNameUnmunged.startsWith("get_"); |
| assert !superMethodNameUnmunged.startsWith("set_"); |
| |
| IClassDefinition superClass = invokingMethodClass.resolveBaseClass(currentScope.getProject()); |
| |
| // note: context must be invokingMethodClass, not superClass, so we have the right set of namespaces open for search |
| ASDefinitionFilter filter = new ASDefinitionFilter( |
| ClassificationValue.MEMBERS_AND_TYPES, SearchScopeValue.INHERITED_MEMBERS, |
| AccessValue.ALL, invokingMethodClass); |
| IDefinition superMethod = MemberedDefinitionUtils.getMemberByName( |
| superClass, currentScope.getProject(), superMethodNameUnmunged, filter); |
| |
| if (superMethod instanceof IFunctionDefinition) |
| { |
| IFunctionDefinition method = (IFunctionDefinition)superMethod; |
| IClassDefinition definingClass = (IClassDefinition)method.getParent(); |
| String definingClassReference = definitionToString(currentScope.getProject(), definingClass); |
| |
| String superMethodNameMunged = mungeMethodName(superMethodNameUnmunged, method, true); |
| return definingClassReference + ".prototype." + superMethodNameMunged; |
| |
| } |
| else |
| { |
| m_sharedData.stdout("##### Unable to generate super method reference #####"); |
| return "pf_unknown"; |
| } |
| |
| // Note: an alternative impl would be to do a search for the function def using invokingMethodClass as the |
| // membereddef scope, and then if the resulting IFunctionDefinition's parent is invokingMethodClass, calling IFunctionDefinition.resolveOverriddenFunction(). |
| } |
| |
| /* |
| * Expression lists. |
| */ |
| |
| public Vector<String> reduce_exprList(IASNode iNode, Vector<String> args) |
| { |
| // Expression lists. |
| // Pattern exprList |
| // ContainerID(expression args+); |
| return args; |
| } |
| |
| /** |
| * Convert ActionScript function name to JavaScript function name (with |
| * get_/set_ prefix as needed). |
| * |
| * @param forceGetForSetters If true, a get_ prefix is used when |
| * 'definition' is a setter. Use this if you can expect the result to later |
| * be passed to resolveSetterName() (which replaces get_ with set_) if the |
| * expression is ultimately being used as an lvalue. |
| */ |
| private String mungeMethodName(String asName, IFunctionDefinition definition, boolean forceGetForSetters) |
| { |
| if (definition instanceof IGetterDefinition) |
| return JSSharedData.GETTER_PREFIX + asName; |
| else if (definition instanceof ISetterDefinition) |
| { |
| String prefix = forceGetForSetters ? JSSharedData.GETTER_PREFIX : JSSharedData.SETTER_PREFIX; |
| return prefix + asName; |
| } |
| else |
| return asName; |
| } |
| |
| // TODO: The parser should differentiate various constants |
| // (and parse their values, or prepare them for parsing), |
| // see http://bugs.adobe.com/jira/browse/CMP-54 |
| public Long reduce_numericLiteral(IASNode iNode) |
| { |
| { |
| // Literals. |
| // Pattern numericLiteral |
| // LiteralNumberID(void); |
| return Long.decode(((org.apache.flex.compiler.tree.as.ILiteralNode)iNode).getValue()); |
| } |
| } |
| |
| public int isIntegerConstant(IASNode iNode) |
| { |
| // Horribly inefficient, but a robust expedient. |
| try |
| { |
| Long.decode(((org.apache.flex.compiler.internal.tree.as.LiteralNode)iNode).getValue()); |
| } |
| catch (NumberFormatException ex) |
| { |
| return Integer.MAX_VALUE; |
| } |
| return 0; |
| } |
| |
| public Long reduce_uintLiteral(IASNode iNode) |
| { |
| { |
| // uint_constant = Pattern uintLiteral |
| // ArgumentID() |
| final Long uint_constant = new Long(((org.apache.flex.compiler.tree.as.INumericLiteralNode)iNode).getNumericValue().toUint32()); |
| return uint_constant; |
| } |
| } |
| |
| public String toString() |
| { |
| if (m_functionName.length() > 0) |
| return "JSGeneratingReducer: " + this.createFullName(this.m_functionName, false); |
| |
| return "JSGeneratingReducer: " + this.createFullClassName(false); |
| } |
| |
| private static String[] emitParameters(List<String> paramNames, |
| Vector<Name> paramTypes, |
| Vector<PooledValue> defaultValues, |
| Boolean needsRest) throws Exception |
| { |
| String code = ""; |
| |
| String a_priori_insns = ""; |
| Boolean emitComma = false; |
| |
| // CMP-355: paramNames is always empty |
| /* |
| * if( paramNames.isEmpty() ) { for (Trait t : traits) { if( t.getKind() |
| * != TRAIT_Var ) throw new |
| * IllegalArgumentException("Unknown parameter trait kind " + |
| * t.getKind()); Name name = t.getNameAttr("name"); Namespace ns = |
| * name.getSingleQualifier(); if( ns.getKind() == |
| * CONSTANT_PackageInternalNs ) { paramNames.add( |
| * getBasenameFromName(name) ); } } } |
| */ |
| |
| int nthParam = 0; |
| final int defaultsStartAt = paramTypes.size() - defaultValues.size(); |
| final int restStartsAt = needsRest ? paramNames.size() - 1 : paramNames.size(); |
| final String varPrefix = JSSharedData.DEFAULT_PARAM_PREFIX; |
| final String indent = " "; |
| for (String argName : paramNames) |
| { |
| if (nthParam == restStartsAt) |
| { |
| // param is rest argument |
| |
| // JavaScript only knows arguments |
| if (!argName.equals("arguments")) |
| { |
| a_priori_insns += "var " + argName + " = arguments;\n"; |
| } |
| |
| // now remove the parameters from arguments that we already got. |
| final int argsLen = paramNames.size(); |
| if (argsLen > 1) |
| { |
| // Argument is an Object and not an Array. These lines implement shift(). |
| a_priori_insns += indent + "var _args = [];\n"; |
| a_priori_insns += indent + "for( var i = 0; i < (arguments.length - " + (argsLen - 1) + "); i++)\n"; |
| a_priori_insns += indent + "{\n"; |
| a_priori_insns += indent + " _args.push(arguments[i+" + (argsLen - 1) + "]);\n"; |
| a_priori_insns += indent + "}\n"; |
| a_priori_insns += indent + "arguments = _args;\n"; |
| } |
| } |
| else |
| { |
| if (emitComma) |
| code += ", "; |
| else |
| emitComma = true; |
| |
| final Name nextParam = paramTypes.elementAt(nthParam); |
| final String argType = nextParam == null ? "*" : getBasenameFromName(nextParam); |
| if (nthParam < defaultsStartAt) |
| { |
| // param without default value |
| code += argName; // + " /* : " + argType + " */"; |
| } |
| else |
| { |
| // param with default value |
| String defaultVal = "undefined"; |
| PooledValue val = defaultValues.elementAt(nthParam - defaultsStartAt); |
| if (val != null) |
| { |
| switch (val.getKind()) |
| { |
| case ABCConstants.CONSTANT_Int: |
| defaultVal = val.getIntegerValue().toString(); |
| break; |
| case ABCConstants.CONSTANT_UInt: |
| defaultVal = val.getLongValue().toString(); |
| break; |
| case ABCConstants.CONSTANT_Double: |
| defaultVal = val.getDoubleValue().toString(); |
| break; |
| case ABCConstants.CONSTANT_Utf8: |
| { |
| defaultVal = val.getStringValue(); |
| if (!defaultVal.startsWith("\"") && !defaultVal.startsWith("\'")) |
| defaultVal = "\"" + defaultVal; |
| if (!defaultVal.endsWith("\"") && !defaultVal.endsWith("\'")) |
| defaultVal += "\""; |
| } |
| break; |
| case ABCConstants.CONSTANT_True: |
| defaultVal = "true"; |
| break; |
| case ABCConstants.CONSTANT_False: |
| defaultVal = "false"; |
| break; |
| case ABCConstants.CONSTANT_Undefined: |
| defaultVal = "undefined"; |
| break; |
| case ABCConstants.CONSTANT_Null: |
| defaultVal = "null"; |
| break; |
| default: |
| { |
| final Namespace ns = val.getNamespaceValue(); |
| if (ns != null) |
| defaultVal = ns.getName() + " /* (namespace) */"; |
| } |
| break; |
| } |
| } |
| |
| code += varPrefix + argName; // + " /* : " + argType + " = " + defaultVal + " */"; |
| |
| // feed a_priori_insns |
| a_priori_insns += indent + "/** @type {" + argType + "} */\n"; |
| a_priori_insns += indent + "var " + argName + " = " + defaultVal + ";\n"; |
| a_priori_insns += indent + "if (typeof(" + varPrefix + argName + ") != 'undefined') "; |
| a_priori_insns += "{ " + argName + " = " + varPrefix + argName + "; }\n"; |
| } |
| } |
| |
| nthParam++; |
| } |
| |
| String[] ret = new String[] {a_priori_insns, code}; |
| |
| return ret; |
| } |
| |
| public static String[] emitParameters(MethodInfo mi) throws Exception |
| { |
| Vector<PooledValue> defaultValues = mi.getDefaultValues(); |
| Vector<Name> paramTypes = mi.getParamTypes(); |
| List<String> paramNames = mi.getParamNames(); |
| final Boolean needsRest = (mi.getFlags() & ABCConstants.NEED_REST) > 0; |
| |
| return emitParameters(paramNames, paramTypes, defaultValues, needsRest); |
| } |
| |
| private static PooledValue createPooledValue(ICompilerProject project, String value, TypeDefinitionBase type) |
| { |
| if (value.equals("null")) |
| return new PooledValue(ABCConstants.CONSTANT_Null, null); |
| |
| if (value.equals("undefined")) |
| return new PooledValue(ABCConstants.CONSTANT_Undefined, ABCConstants.UNDEFINED_VALUE); |
| |
| if (type == project.getBuiltinType(IASLanguageConstants.BuiltinType.STRING)) |
| { |
| if (value.startsWith("\"") && value.endsWith("\"")) |
| return new PooledValue(ABCConstants.CONSTANT_Utf8, value); |
| } |
| |
| if (type == project.getBuiltinType(IASLanguageConstants.BuiltinType.INT)) |
| { |
| if (value.startsWith("0x")) |
| return new PooledValue(ABCConstants.CONSTANT_Int, Integer.parseInt(value.substring(2), 16)); |
| else |
| return new PooledValue(ABCConstants.CONSTANT_Int, Integer.parseInt(value)); |
| } |
| |
| if (type == project.getBuiltinType(IASLanguageConstants.BuiltinType.UINT)) |
| { |
| if (value.startsWith("0x")) |
| return new PooledValue(ABCConstants.CONSTANT_UInt, Long.parseLong(value.substring(2), 16)); |
| else |
| return new PooledValue(ABCConstants.CONSTANT_UInt, Long.parseLong(value)); |
| } |
| |
| if (type == project.getBuiltinType(IASLanguageConstants.BuiltinType.NUMBER)) |
| return new PooledValue(ABCConstants.CONSTANT_Double, Double.parseDouble(value)); |
| |
| if (type == project.getBuiltinType(IASLanguageConstants.BuiltinType.BOOLEAN)) |
| { |
| final Boolean bool = Boolean.parseBoolean(value); |
| if (bool) |
| return new PooledValue(ABCConstants.CONSTANT_True, true); |
| else |
| return new PooledValue(ABCConstants.CONSTANT_False, false); |
| } |
| |
| if (type == project.getBuiltinType(IASLanguageConstants.BuiltinType.Undefined)) |
| return new PooledValue(ABCConstants.CONSTANT_Undefined, ABCConstants.UNDEFINED_VALUE); |
| |
| if (type == project.getBuiltinType(IASLanguageConstants.BuiltinType.NULL)) |
| return new PooledValue(ABCConstants.CONSTANT_Null, null); |
| |
| if (type == project.getBuiltinType(IASLanguageConstants.BuiltinType.NAMESPACE)) |
| return new PooledValue(ABCConstants.CONSTANT_Namespace, null); |
| |
| // Note that I am returning CONSTANT_Undefined with value if I can't figure out what value is. |
| JSSharedData.instance.stderr("Warning: cannot create PooledValue for '" + value + "' of type " + type.getBaseName()); |
| return new PooledValue(ABCConstants.CONSTANT_Undefined, value); |
| } |
| |
| public static String[] emitParameters(ICompilerProject project, FunctionDefinition fdef, MethodInfo mi) throws Exception |
| { |
| Vector<PooledValue> defaultValues = mi.getDefaultValues(); |
| Vector<Name> paramTypes = mi.getParamTypes(); |
| List<String> paramNames = mi.getParamNames(); |
| Boolean needsRest = (mi.getFlags() & ABCConstants.NEED_REST) > 0; |
| |
| // workaround for Falcon bugs. |
| // mi.defaultValues and funcDef.getParameters() can get out of sync and are not |
| // corrected in Abc/JSGenerator::createMethodInfo. |
| |
| // is mi.hasOptional() reliable? |
| if (!paramNames.isEmpty() && |
| (defaultValues == null || defaultValues.isEmpty()) && |
| fdef != null && |
| fdef.getParameters().length > 0) |
| { |
| defaultValues = new Vector<PooledValue>(); |
| |
| needsRest = false; |
| final ParameterDefinition[] args = fdef.getParameters(); |
| paramTypes = new Vector<Name>(); |
| paramNames = new ArrayList<String>(); |
| |
| if (args.length > 0) |
| { |
| for (ParameterDefinition arg : args) |
| { |
| TypeDefinitionBase arg_type = arg.resolveType(project); |
| Name type_name = arg_type != null ? arg_type.getMName(project) : null; |
| |
| if (arg.hasDefaultValue()) |
| { |
| final String defaultVal = arg.resolveDefaultValue(project).toString(); |
| final PooledValue pv = createPooledValue(project, defaultVal, arg.resolveType(project)); |
| defaultValues.add(pv); |
| } |
| |
| if (arg.isRest()) |
| { |
| needsRest = true; |
| paramNames.add(arg.getBaseName()); |
| } |
| else |
| { |
| paramTypes.add(type_name); |
| paramNames.add(arg.getBaseName()); |
| } |
| } |
| } |
| } |
| |
| return emitParameters(paramNames, paramTypes, defaultValues, needsRest); |
| } |
| |
| /** |
| * Prints a warning message to the console, including file name and line |
| * number |
| */ |
| private static void printWarning(IASNode iNode, String message) |
| { |
| final StringBuilder sb = new StringBuilder(); |
| sb.append("Warning: " + message + "\n"); |
| if (iNode != null) |
| sb.append(" " + iNode.getSourcePath() + ": " + (iNode.getLine() + 1) + "\n"); |
| else |
| sb.append(" (unknown location in code)\n"); |
| sb.append("\n"); |
| JSSharedData.instance.stderr(sb.toString()); |
| } |
| |
| /** Exception thrown due to an unexpected, internal FalconJS error */ |
| /* |
| * public static class FalconJSError extends RuntimeException { // TODO: |
| * require passing an IASNode so we can indicate which line of AS code |
| * triggered the internal error public FalconJSError(String message) { |
| * super(message); } } |
| */ |
| /** |
| * Convenience method generates an r-value. |
| * |
| * @return the instruction sequence to look up the given multiname. |
| */ |
| /* |
| * private String generateRvalue() { return generate(OP_getproperty, null); |
| * } |
| */ |
| |
| /** |
| * Convenience method generates an assignment |
| * |
| * @param value - the value to set. |
| * @return the instruction sequence to set a the given multiname's value. |
| */ |
| /* |
| * private String generateAssignment(String value) { return |
| * generate(OP_setproperty, value); } |
| */ |
| |
| /** |
| * Get the definition associated with a node and decide if the qualifier is |
| * a compile-time constant. |
| * |
| * @param iNode - the node to check. |
| * @pre - the node is an IdentifierNode and has a child. |
| * @return true if the child has a known namespace, i.e., it's a |
| * compile-time constant qualifier. |
| */ |
| int isMultiname(IASNode iNode) |
| { |
| IdentifierNode qualifier = (IdentifierNode)SemanticUtils.getNthChild(iNode, 0); |
| IDefinition def = qualifier.resolve(currentScope.getProject()); |
| |
| int result = def instanceof NamespaceDefinition ? 1 : Integer.MAX_VALUE; |
| return result; |
| } |
| |
| private String getMethodName() |
| { |
| String methodName = null; |
| final MethodInfo mi = currentScope.getMethodInfo(); |
| if (mi != null) |
| methodName = mi.getMethodName(); |
| else if (m_functionNode != null) |
| methodName = m_functionNode.getName(); |
| return methodName; |
| } |
| |
| private Boolean isAnonymousFunction(String methodName) |
| { |
| return methodName != null && methodName.endsWith(":anonymous"); |
| } |
| |
| /** |
| * Converts a definition to a String |
| * |
| * @param def |
| * @return name of def, or null |
| */ |
| public static String definitionToString(ICompilerProject project, IDefinition def) |
| { |
| if (def != null) |
| { |
| // register this definition as a used extern if necessary. |
| if (def.getMetaTagByName(JSSharedData.EXTERN_METATAG) != null) |
| { |
| JSSharedData.instance.registerExtern(def); |
| JSSharedData.instance.registerUsedExtern(def.getQualifiedName()); |
| } |
| |
| final String baseName = def.getBaseName(); |
| |
| // doesn't work, check for InternalNamespaceDefinition instead. |
| if (def instanceof GetterDefinition || def instanceof SetterDefinition) |
| { |
| // Note: setter defs are given a get_ prefix here; resolveSetterName() will turn this into set_ later if |
| // the def is really being used in an assignment context. But often, when we look up a property def we'll |
| // get the setter def even if it's being used in a get context, and thus the get_ prefix will remain. |
| return makeGetterCall(project, def, JSSharedData.THIS, baseName); |
| } |
| else if (isPackageFunction(def)) |
| { |
| return createFullNameFromDefinition(project, def); |
| } |
| /* |
| * Used to be not necessary, because we interface for external |
| * classes: |
| * goog.provide("adobe.root.org.w3c.dom.events.EventTarget"); |
| * adobe.root.org.w3c.dom.events.EventTarget = typeof(EventTarget) |
| * == "undefined" ? function() {} : EventTarget; But we don't emit |
| * externs anymore. So this code is necessary: |
| */ |
| else if (JSSharedData.instance.isExtern(def.getQualifiedName())) |
| { |
| return JSSharedData.instance.getExternName(def.getQualifiedName()); |
| } |
| else if (def instanceof ClassDefinition || def instanceof InterfaceDefinition) |
| { |
| return createFullNameFromDefinition(project, def); |
| } |
| else if (def.getContainingScope() instanceof CatchScope) |
| { |
| return baseName; |
| } |
| if (isLocalName(def)) |
| { |
| return baseName; |
| } |
| else if (def instanceof ParameterDefinition) |
| { |
| return baseName; |
| } |
| else if (isGlobalName(def)) |
| { |
| return baseName; |
| } |
| else |
| { |
| final Boolean isStatic = m_convertToStatic || def.isStatic(); |
| if (isStatic) |
| return createFullNameFromDefinition(project, def); |
| |
| return "this" + "." + baseName; |
| } |
| } |
| return null; |
| } |
| |
| public String nameToString(IASNode iNode, IDefinition def, Name name) |
| { |
| if (def == null) |
| currentScope.addProblem(new JSInternalCompilerProblem(iNode, "no definition for " + name.toString())); |
| |
| // see FJS-44, ambiguous_shortname testcase |
| if (def instanceof AmbiguousDefinition) |
| { |
| m_needsSecondPass = true; |
| |
| String unambiguousName = name.getBaseName(); |
| /* |
| * if( unambiguousName != null ) { final Nsset nsset = |
| * name.getQualifiers(); if( nsset != null && nsset.length() > 0 ) |
| * unambiguousName = nsset.iterator().next().getName() + "." + |
| * unambiguousName; } |
| */ |
| return unambiguousName + " /* WARNING: ambiguous name */"; |
| } |
| |
| final String baseName = def.getBaseName(); |
| |
| // Simple name mappings: Table, base names |
| if (m_nameMap.containsKey(baseName)) |
| { |
| return m_nameMap.get(baseName); |
| } |
| |
| // filter out the strings |
| if (isLongNumberString(baseName) || isStringWithQuotes(baseName) || baseName.contains(" ")) |
| return baseName; |
| |
| // see action_superAccess |
| if (baseName.startsWith(JSSharedData._SUPER + ".")) |
| { |
| return JSSharedData.THIS + "." + baseName; |
| } |
| |
| // resolving the name... |
| final String str = definitionToString(currentScope.getProject(), def); |
| if (str != null && str.length() > 0) |
| { |
| // support for [ConvertLocalVarsToMembers] |
| if (convertLocalVarsToMembers(iNode) && |
| !str.startsWith(JSSharedData.THIS + ".") && |
| (def instanceof VariableDefinition || def instanceof FunctionDefinition) && |
| !(def instanceof ParameterDefinition)) |
| { |
| Boolean addThis = true; |
| |
| if (def instanceof FunctionDefinition) |
| { |
| final IFunctionDefinition fdef = (FunctionDefinition)def; |
| if (fdef.isStatic()) |
| addThis = false; |
| else if (fdef.getParent() == null || !(fdef.getParent() instanceof ClassDefinition)) |
| addThis = false; |
| } |
| |
| if (addThis) |
| { |
| this.registerConvertedMember(str); |
| return JSSharedData.THIS + "." + str; |
| } |
| } |
| return str; |
| } |
| |
| final String packageName = getPackageNameFromName(iNode, name, false, false); |
| if (packageName != null && !packageName.isEmpty()) |
| { |
| final String fullName = packageName + "." + baseName; |
| if (m_sharedData.getVarType(fullName) != null) |
| return fullName; |
| return fullName; |
| } |
| |
| // support for vectors |
| if (name.getBaseName() == null && name.getTypeNameBase().getBaseName().equals("Vector")) |
| return baseName; |
| |
| return "/* unknown */ " + baseName; |
| } |
| |
| public String nameToString(IASNode iNode, Name name, Boolean withThis, Boolean resolve) |
| { |
| IDefinition def = getDefinitionForName(iNode, name); |
| final String str = nameToString(iNode, def, name); |
| |
| return str; |
| /* |
| * final String baseName = getBasenameFromName(name); // Simple name |
| * mappings: Table, base names if( m_nameMap.containsKey(baseName) ) { |
| * return m_nameMap.get(baseName); } // filter out the strings if( |
| * isLongNumberString(baseName) || isStringWithQuotes(baseName) || |
| * baseName.contains(" ")) return baseName; // see action_superAccess |
| * if( baseName.startsWith( JSSharedData._SUPER + "." ) ) { return |
| * JSSharedData.THIS + "." + baseName; } // resolving the name... |
| * IDefinition def = getDefinitionForName( iNode, name ); final String |
| * str = definitionToString(currentScope.getProject(), def); if( str != |
| * null && str.length() > 0 ) { // support for |
| * [ConvertLocalVarsToMembers] if( convertLocalVarsToMembers(iNode) && |
| * !str.startsWith(JSSharedData.THIS + ".") && def instanceof |
| * VariableDefinition && !(def instanceof ParameterDefinition) ) { |
| * return JSSharedData.THIS + "." + str; } return str; } final String |
| * packageName = getPackageNameFromName(name, false, false); if( |
| * packageName != null && !packageName.isEmpty() ) { final String |
| * fullName = packageName + "." + baseName; if( |
| * m_sharedData.getVarType(fullName) != null ) return fullName; return |
| * fullName; } // support for vectors if( name.getBaseName() == null && |
| * isVectorTypeName(name)) return baseName; return "/ * unknown * / " + |
| * baseName;" |
| */ |
| } |
| |
| public static boolean isVectorTypeName(Name name) |
| { |
| return name.getTypeNameBase().getBaseName().equals("Vector"); |
| } |
| |
| public static Boolean isPackageFunction(IDefinition def) |
| { |
| if (def instanceof FunctionDefinition) |
| { |
| /* |
| * If a definition comes from a SWC then the parent always seems to |
| * be null for package functions. In those cases we look for the |
| * containing scope and test against ASFileScope. In the case of |
| * getDefinitionByName from flash.swc we'll see a SWCFileScope. |
| */ |
| if (def.getContainingScope() instanceof ASFileScope) |
| return true; |
| |
| // VALIDATE: currentScope.methodInfo != null && |
| if (def.getParent() != null) |
| { |
| IDefinition parentDef = def.getParent(); |
| if (parentDef instanceof PackageDefinition) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private String opToString(IASNode iNode, int op) |
| { |
| switch (op) |
| { |
| // Binary logical operators. |
| case OP_equals: |
| return "=="; |
| case OP_strictequals: |
| return "==="; |
| case OP_greaterthan: |
| return ">"; |
| case OP_greaterequals: |
| return ">="; |
| case OP_lessthan: |
| return "<"; |
| case OP_lessequals: |
| return "<="; |
| |
| // Binary branching constructs |
| case OP_ifeq: |
| return "=="; |
| case OP_ifne: |
| return "!="; |
| case OP_ifstricteq: |
| return "==="; |
| case OP_ifstrictne: |
| return "!=="; |
| case OP_ifgt: |
| return ">"; |
| case OP_ifge: |
| return ">="; |
| case OP_iflt: |
| return "<"; |
| case OP_ifle: |
| return "<="; |
| |
| // Binary arithmetic operators |
| case OP_add: |
| return "+"; |
| case OP_subtract: |
| return "-"; |
| case OP_divide: |
| return "/"; |
| case OP_multiply: |
| return "*"; |
| case OP_modulo: |
| return "%"; |
| case OP_bitor: |
| return "|"; |
| case OP_bitand: |
| return "&"; |
| case OP_bitxor: |
| return "^"; |
| case OP_lshift: |
| return "<<"; |
| case OP_rshift: |
| return ">>"; |
| case OP_urshift: |
| return ">>>"; |
| |
| // |
| case OP_in: |
| return "in"; |
| } |
| currentScope.addProblem(new JSInternalCompilerProblem(iNode, "Unknown op '" + op + "'")); |
| return ""; |
| } |
| |
| private final static String THIS_SNIPPET = JSSharedData.THIS + "."; // support for [ConvertLocalVarsToMembers] |
| private final static String JS_THIS_SNIPPET = JSSharedData.JS_THIS + "."; // support for [ConvertLocalVarsToMembers] |
| |
| private final static String VAR_SNIPPET = "var "; // support for [ConvertLocalVarsToMembers] |
| private final static Map<String, String> m_typeMap = createTypeMap(); |
| |
| private String aPrioriInstructions; |
| private String m_functionName = ""; |
| private int m_indent = 0; |
| private Map<String, String> m_nameMap = new HashMap<String, String>(); |
| |
| public Set<String> usedTypes = new TreeSet<String>(); |
| public Set<String> usedClasses = new TreeSet<String>(); |
| public static Boolean m_convertToStatic = JSSharedData.m_useSelfParameter && false; |
| private ICompilationUnit.Operation m_buildPhase = Operation.INVALIDATE_CU; |
| private JSSharedData m_sharedData; |
| |
| private Boolean m_needsSecondPass = false; |
| private FunctionNode m_functionNode = null; // support for [ConvertLocalVarsToMembers] |
| private Set<String> m_convertedMembers = new HashSet<String>(); // support for [ConvertLocalVarsToMembers] |
| private DefinitionBase m_currentVariable = null; // constant propagation for initialized members |
| private Boolean m_hasSideEffects = false; // constant propagation for initialized members |
| private String m_varInitValue = null; // constant propagation for initialized members |
| |
| private ClassDefinition m_currentClassDefinition = null; |
| private Set<IDefinition> m_referencedDefinitions = new HashSet<IDefinition>(); |
| |
| public JSSharedData getSharedData() |
| { |
| return m_sharedData; |
| } |
| |
| public void setClassDefinition(ICompilerProject project, ClassDefinition classDef) |
| { |
| m_currentClassDefinition = classDef; |
| |
| // poor man's type inference. |
| if (m_currentClassDefinition != null && project != null) |
| { |
| JSSharedData.instance.registerMethods(project, m_currentClassDefinition); |
| } |
| |
| } |
| |
| private static Map<String, String> createTypeMap() |
| { |
| Map<String, String> map = new HashMap<String, String>(); |
| map.put("String", "string"); |
| map.put("uint", "number"); |
| map.put("int", "number"); |
| map.put("Number", "number"); |
| map.put("Boolean", "boolean"); |
| map.put("Class", "function(...)"); |
| map.put("Function", "function(...)"); |
| return map; |
| } |
| |
| private static Boolean vectorNameToJSDocType(Name name, StringBuilder sb) |
| { |
| final String baseName = name.getBaseName(); |
| if (baseName == null && name.getTypeNameBase().getBaseName().equals("Vector")) |
| { |
| final String param = getBasenameFromName(name); |
| sb.append(param); |
| return true; |
| } |
| |
| if (baseName.equals("...")) |
| { |
| sb.append("Array"); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private static Boolean nameToJSDocType(IDefinition type, StringBuilder sb) |
| { |
| if (type == null) |
| { |
| sb.append("*"); |
| return false; |
| } |
| |
| String _type = type.getBaseName(); |
| final String packageName = type.getPackageName(); |
| if (packageName != null && !packageName.isEmpty()) |
| { |
| if (type.getQualifiedName().startsWith("__AS3__.vec.Vector$")) |
| { |
| if (m_typeMap.containsKey(_type)) |
| sb.append("Array.<" + m_typeMap.get(_type) + ">"); |
| else |
| sb.append("Array.<" + _type + ">"); |
| } |
| else |
| { |
| _type = JSSharedData.ROOT_NAME + packageName + "." + _type; |
| if (_type.contains(":")) |
| _type = _type.substring(_type.indexOf(":") + 1); |
| } |
| } |
| else |
| { |
| final String mapped = m_typeMap.get(_type); |
| if (mapped != null) |
| _type = mapped; |
| } |
| |
| sb.append(_type); |
| return true; |
| } |
| |
| private Boolean nameToJSDocType(Binding binding, StringBuilder sb) |
| { |
| IDefinition def = binding.getDefinition(); |
| if (def != null) |
| return nameToJSDocType(def, sb); |
| |
| final Name name = getNameFromBinding(binding); |
| return nameToJSDocType(currentScope.getProject(), name, sb); |
| } |
| |
| public static Boolean nameToJSDocType(ICompilerProject project, Name name, StringBuilder sb) |
| { |
| if (project == null || name == null) |
| { |
| sb.append("*"); |
| return false; |
| } |
| |
| if (vectorNameToJSDocType(name, sb)) |
| return true; |
| |
| final IDefinition typeDef = getDefinitionForName(project, name); |
| |
| // see FJS-31 & FJS-63 |
| if (typeDef == null || typeDef instanceof AmbiguousDefinition) |
| { |
| String packageName = ""; |
| final String baseName = getBasenameFromName(name); |
| final Nsset nsset = name.getQualifiers(); |
| if (nsset != null && nsset.length() > 0) |
| { |
| packageName = nsset.iterator().next().getName(); |
| if (packageName.contains(":")) |
| packageName = packageName.substring(packageName.indexOf(":") + 1); |
| if (packageName.contains(":")) |
| packageName = packageName.replaceAll(":", "."); |
| } |
| if (!packageName.isEmpty()) |
| sb.append(packageName + "." + baseName); |
| else |
| sb.append(baseName); |
| return false; |
| } |
| |
| return nameToJSDocType(typeDef, sb); |
| } |
| |
| private Boolean nameToJSDocType(IASNode iNode, Name name, StringBuilder sb) |
| { |
| if (iNode == null || name == null) |
| { |
| sb.append("Object"); |
| return false; |
| } |
| |
| if (vectorNameToJSDocType(name, sb)) |
| return true; |
| |
| final IDefinition typeDef = getDefinitionForName(iNode, name); |
| return nameToJSDocType(typeDef, sb); |
| } |
| |
| // 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; |
| } |
| |
| public void setNeedsSecondPass() |
| { |
| m_needsSecondPass = true; |
| } |
| |
| public static String now() |
| { |
| final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss"; |
| Calendar cal = Calendar.getInstance(); |
| SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_NOW); |
| return sdf.format(cal.getTime()); |
| } |
| |
| public static String getTimeStampString() |
| { |
| if (JSSharedData.OUTPUT_TIMESTAMPS) |
| return "CROSS-COMPILED BY " + JSSharedData.COMPILER_NAME + " (" + JSSharedData.COMPILER_VERSION + ") ON " + JSGeneratingReducer.now() + "\n"; |
| else |
| return "CROSS-COMPILED BY " + JSSharedData.COMPILER_NAME + "\n"; |
| } |
| |
| private static Boolean checkConstant(int c1, int c2, String name) |
| { |
| if (c1 != c2) |
| throw JSSharedData.backend.createException("Constants differ: CmcEmitter." + name + "CmcJSEmitter." + name + "(" + c1 + " != " + c2 + ")"); |
| return true; |
| } |
| |
| public static Boolean validate() |
| { |
| checkConstant(CmcEmitter.__expression_NT, CmcJSEmitter.__expression_NT, "__expression_NT"); |
| checkConstant(CmcEmitter.__boolean_literal_NT, CmcJSEmitter.__boolean_literal_NT, "__boolean_literal_NT"); |
| checkConstant(CmcEmitter.__comparison_expression_NT, CmcJSEmitter.__comparison_expression_NT, "__comparison_expression_NT"); |
| checkConstant(CmcEmitter.__void_expression_NT, CmcJSEmitter.__void_expression_NT, "__void_expression_NT"); |
| checkConstant(CmcEmitter.__function_NT, CmcJSEmitter.__function_NT, "__function_NT"); |
| checkConstant(CmcEmitter.__parameter_NT, CmcJSEmitter.__parameter_NT, "__parameter_NT"); |
| checkConstant(CmcEmitter.__return_type_name_NT, CmcJSEmitter.__return_type_name_NT, "__return_type_name_NT"); |
| checkConstant(CmcEmitter.__catch_block_NT, CmcJSEmitter.__catch_block_NT, "__catch_block_NT"); |
| // checkConstant(CmcEmitter.__multinameL_NT, CmcJSEmitter.__multinameL_NT, "__multinameL_NT" ); |
| checkConstant(CmcEmitter.__runtime_name_expression_NT, CmcJSEmitter.__runtime_name_expression_NT, "__runtime_name_expression_NT"); |
| checkConstant(CmcEmitter.__parameterList_NT, CmcJSEmitter.__parameterList_NT, "__parameterList_NT"); |
| checkConstant(CmcEmitter.__name_NT, CmcJSEmitter.__name_NT, "__name_NT"); |
| checkConstant(CmcEmitter.__object_literal_NT, CmcJSEmitter.__object_literal_NT, "__object_literal_NT"); |
| checkConstant(CmcEmitter.__double_constant_NT, CmcJSEmitter.__double_constant_NT, "__double_constant_NT"); |
| checkConstant(CmcEmitter.__vector_literal_NT, CmcJSEmitter.__vector_literal_NT, "__vector_literal_NT"); |
| checkConstant(CmcEmitter.__finally_clause_NT, CmcJSEmitter.__finally_clause_NT, "__finally_clause_NT"); |
| checkConstant(CmcEmitter.__array_literal_NT, CmcJSEmitter.__array_literal_NT, "__array_literal_NT"); |
| checkConstant(CmcEmitter.__e4x_literal_NT, CmcJSEmitter.__e4x_literal_NT, "__e4x_literal_NT"); |
| checkConstant(CmcEmitter.__var_decl_NT, CmcJSEmitter.__var_decl_NT, "__var_decl_NT"); |
| checkConstant(CmcEmitter.__qualifiedNamePart_NT, CmcJSEmitter.__qualifiedNamePart_NT, "__qualifiedNamePart_NT"); |
| checkConstant(CmcEmitter.__statement_NT, CmcJSEmitter.__statement_NT, "__statement_NT"); |
| checkConstant(CmcEmitter.__boolean_constant_NT, CmcJSEmitter.__boolean_constant_NT, "__boolean_constant_NT"); |
| checkConstant(CmcEmitter.__object_literal_element_NT, CmcJSEmitter.__object_literal_element_NT, "__object_literal_element_NT"); |
| checkConstant(CmcEmitter.__uint_constant_NT, CmcJSEmitter.__uint_constant_NT, "__uint_constant_NT"); |
| checkConstant(CmcEmitter.__constant_value_NT, CmcJSEmitter.__constant_value_NT, "__constant_value_NT"); |
| checkConstant(CmcEmitter.__new_type_name_NT, CmcJSEmitter.__new_type_name_NT, "__new_type_name_NT"); |
| checkConstant(CmcEmitter.__non_resolving_identifier_NT, CmcJSEmitter.__non_resolving_identifier_NT, "__non_resolving_identifier_NT"); |
| checkConstant(CmcEmitter.__integer_constant_NT, CmcJSEmitter.__integer_constant_NT, "__integer_constant_NT"); |
| checkConstant(CmcEmitter.__string_constant_NT, CmcJSEmitter.__string_constant_NT, "__string_constant_NT"); |
| checkConstant(CmcEmitter.__type_name_NT, CmcJSEmitter.__type_name_NT, "__type_name_NT"); |
| checkConstant(CmcEmitter.__conditionalJump_NT, CmcJSEmitter.__conditionalJump_NT, "__conditionalJump_NT"); |
| checkConstant(CmcEmitter.__dottedNamePart_NT, CmcJSEmitter.__dottedNamePart_NT, "__dottedNamePart_NT"); |
| checkConstant(CmcEmitter.__literal_NT, CmcJSEmitter.__literal_NT, "__literal_NT"); // = 28; |
| checkConstant(CmcEmitter.__conditionalElements_NT, CmcJSEmitter.__conditionalElements_NT, "__conditionalElements_NT"); |
| |
| // really only checking whether the number of constants has increased: |
| checkConstant(CmcEmitter.nStates, 39, "nStates"); |
| |
| return true; |
| } |
| |
| public JSGeneratingReducer(JSSharedData sharedData) |
| { |
| m_sharedData = sharedData; |
| |
| m_nameMap.put(JSSharedData.JS_FRAMEWORK_NAME, JSSharedData.JS_FRAMEWORK_NAME); |
| |
| m_nameMap.put("NULL", "null"); |
| m_nameMap.put("_NEW", "new"); |
| m_nameMap.put("DELETE", "delete"); |
| m_nameMap.put("EMPTY_STRING", "''"); |
| // m_nameMap.put( JSSharedData.THIS, "this" ); |
| // m_nameMap.put( "TRACE", JSSharedData.ROOT_NAME + "flash.utils.trace" ); |
| // m_nameMap.put( "trace", JSSharedData.ROOT_NAME + "flash.utils.trace" ); |
| |
| if (JSSharedData.m_useClosureLib) |
| { |
| m_nameMap.put("super", JSSharedData.THIS + "." + JSSharedData.CLASS_NAME + ".superClass_"); |
| m_nameMap.put(JSSharedData.THIS + "." + JSSharedData.CLASS_NAME + ".superClass_", JSSharedData.THIS + "." + JSSharedData.CLASS_NAME + ".superClass_"); |
| m_nameMap.put("goog.base", "goog.base"); |
| m_nameMap.put("adobe.base", "adobe.base"); |
| // m_nameMap.put( JSSharedData.JS_FRAMEWORK_NAME, "/** @type {object} */ " + JSSharedData.JS_FRAMEWORK_NAME); |
| } |
| else |
| { |
| m_nameMap.put("super", JSSharedData.THIS + "." + JSSharedData._SUPER); |
| m_nameMap.put(JSSharedData.THIS + "." + JSSharedData._SUPER, JSSharedData.THIS + "." + JSSharedData._SUPER); |
| } |
| m_nameMap.put("prototype", "prototype"); |
| m_nameMap.put("...", "Array"); |
| m_nameMap.put("this", JSSharedData.JS_THIS); |
| m_nameMap.put(JSSharedData.THIS, JSSharedData.THIS); |
| m_nameMap.put("_CLASS", "_CLASS"); |
| |
| // operators |
| m_nameMap.put("new", "new"); |
| m_nameMap.put("delete", "delete"); |
| |
| // nameToString(null) must return JSSharedData.THIS |
| m_nameMap.put(null, JSSharedData.THIS); |
| |
| // special values |
| m_nameMap.put("null", "null"); |
| m_nameMap.put("undefined", "undefined"); |
| m_nameMap.put("NaN", "NaN"); |
| m_nameMap.put("Infinity", "Infinity"); |
| |
| // isNumberDataType |
| m_nameMap.put("Number", "Number"); |
| m_nameMap.put("int", "Number"); |
| m_nameMap.put("uint", "Number"); |
| |
| // isPrimitveDataType |
| m_nameMap.put("Boolean", "Boolean"); |
| m_nameMap.put("String", "String"); |
| m_nameMap.put("*", "*"); |
| m_nameMap.put("void", "void"); |
| |
| // isComplexDataType |
| m_nameMap.put("Object", "Object"); |
| m_nameMap.put("Array", "Array"); |
| m_nameMap.put("Date", "Date"); |
| m_nameMap.put("Error", "Error"); |
| m_nameMap.put("Function", "Function"); |
| m_nameMap.put("Class", "Function"); // JS's version of Class is just the the ctor Function |
| m_nameMap.put("RegExp", "RegExp"); |
| m_nameMap.put("XML", "XML"); |
| // m_nameMap.put("XMLList", "XMLList"); |
| |
| // isReservedDataType |
| m_nameMap.put("MathGlue", "Math"); |
| m_nameMap.put("$", "$"); |
| m_nameMap.put("jQuery", "jQuery"); |
| m_nameMap.put("JQuery", "jQuery"); |
| m_nameMap.put("JSError", "Error"); |
| m_nameMap.put("JQEvent", "jQuery.Event"); |
| } |
| |
| /** |
| * Active labeled control-flow region's substatements. Populated by the |
| * labeledStmt Prologue, and winnowed by the labeledStmt reduction's |
| * epilogue. |
| */ |
| // private Set<IASNode> labeledNodes = new HashSet<IASNode>(); |
| |
| /** |
| * Check if the given AST (the root AST of a looping construct) is in the |
| * set of active labeled control-flow contexts. If it is, then its |
| * control-flow context is being managed by the labeledStatement production |
| * that's reducing the loop's LabeledStatementNode, and the loop should use |
| * that context so that the labeled context's continue label is resolved to |
| * the correct top-of-loop location. |
| * |
| * @param loop_node - the AST at the root of the loop. |
| * @return true if loop_node is known to be the substatment of a labeled |
| * statement. |
| */ |
| |
| /* |
| * private boolean isLabeledSubstatement(IASNode loop_node) { return |
| * labeledNodes.contains(loop_node); } |
| */ |
| public Boolean inCodeGen() |
| { |
| return this.getBuildPhase() == Operation.GET_ABC_BYTES; |
| } |
| |
| public void reset() |
| { |
| usedTypes.clear(); |
| m_functionName = ""; |
| m_indent = 0; |
| currentScope = null; |
| } |
| |
| public void setBuildPhase(Operation op) |
| { |
| m_buildPhase = op; |
| } |
| |
| public Operation getBuildPhase() |
| { |
| return m_buildPhase; |
| } |
| |
| public static String instructionListToString(InstructionList instructionList, Boolean initVarCodeOnly) |
| { |
| String str = ""; |
| if (instructionList != null) |
| { |
| List<Instruction> il = instructionList.getInstructions(); |
| for (int i = 0; i < il.size() && !il.get(i).isBranch(); i++) |
| { |
| final Instruction insn = il.get(i); |
| if (insn.getOpcode() == JSSharedData.OP_JS) |
| { |
| String instruction = (String)insn.getOperand(0); |
| |
| // we are only interested in the instruction that initialize variables with a value. |
| if (!initVarCodeOnly || instruction.contains("=")) |
| str += instruction; |
| } |
| } |
| } |
| return str; |
| } |
| |
| /* |
| * Instructions to be inserted into a function prologue; used when |
| * generating initialization procedures such as constructors. |
| */ |
| |
| public void setAprioriinstructions(InstructionList setting) |
| { |
| // TODO |
| if (setting != null) |
| this.aPrioriInstructions = instructionListToString(setting, false); |
| else |
| this.aPrioriInstructions = null; |
| } |
| |
| public LexicalScope getCurrentscope() |
| { |
| return (this.currentScope); |
| } |
| |
| /** |
| * setFunctionNode() is called by JSGenerator::generateInstructions() when |
| * generating functions and members. setFunctionNode() sets |
| * convertLocalVarsToMembers(iNode) to true if the function carries a |
| * [ConvertLocalVarsToMembers] metadata tag. support for |
| * [ConvertLocalVarsToMembers] |
| */ |
| public void setFunctionNode(IASNode node) |
| { |
| m_functionNode = null; |
| if (node instanceof FunctionNode) |
| m_functionNode = (FunctionNode)node; |
| } |
| |
| /** |
| * getVarSnippet() either returns "var " or an empty string dependent on |
| * convertLocalVarsToMembers(iNode). support for [ConvertLocalVarsToMembers] |
| */ |
| private String getVarSnippet(IASNode iNode, String varName) |
| { |
| if (convertLocalVarsToMembers(iNode)) |
| { |
| if (varName.startsWith(THIS_SNIPPET)) |
| return varName; |
| if (varName.startsWith(JS_THIS_SNIPPET)) |
| return varName; |
| return THIS_SNIPPET + varName; |
| } |
| return VAR_SNIPPET + varName; |
| } |
| |
| private void registerMember(IASNode iNode, IDefinition name, IDefinition type) |
| { |
| final String fullName = name.getQualifiedName(); |
| final String _type = type.getQualifiedName(); |
| if (!m_sharedData.hasVarName(fullName)) |
| m_sharedData.registerVarType(fullName, _type); |
| } |
| |
| private IDefinition getDefinitionForMember(IASNode iNode, IDefinition def, String member, ClassificationValue what) |
| { |
| if (what == null) |
| what = ClassificationValue.MEMBERS_AND_TYPES; |
| |
| // weird bug: m_rect = m_data.rect; |
| // JSGeneratingReducer.getDefinitionForMember() returns null, because m_data is of type VariableDefinition in Mandelbrot. |
| // That must be a bug in Falcon... |
| if (def != null && def instanceof VariableDefinition) |
| { |
| VariableDefinition vdef = (VariableDefinition)def; |
| IDefinition resolvedType = vdef.resolveType(currentScope.getProject()); |
| if (resolvedType != null) |
| def = getClassDefinition(currentScope.getProject(), vdef, createFullNameFromDefinition(currentScope.getProject(), resolvedType)); |
| else |
| def = getClassDefinition(currentScope.getProject(), vdef, vdef.getTypeAsDisplayString()); |
| if (def == null) |
| return null; |
| } |
| |
| if (def == null) |
| currentScope.addProblem(new JSInternalCompilerProblem(iNode, "cannot find definition for member")); |
| |
| final String _class = def.getBaseName(); |
| if (_class.equals("Object")) |
| return def; |
| |
| // TODO: feed m_sharedData with var types for Object, Array, Date, Error, Function, RegExp, XML, and XMLList. |
| if (isDataType(_class)) |
| return null; |
| |
| if (def != null && def instanceof MemberedDefinition) |
| { |
| member = removeParentheses(member); |
| |
| MemberedDefinition mdef = (MemberedDefinition)def; |
| ASDefinitionFilter filter = new ASDefinitionFilter( |
| ClassificationValue.MEMBERS_AND_TYPES, SearchScopeValue.ALL_SCOPES, |
| AccessValue.ALL, def); |
| |
| // IDefinition[] memberDefs = mdef.getAllMembersByName(currentScope.getProject(), member, filter); |
| // def = memberDefs[0]; |
| def = MemberedDefinitionUtils.getMemberByName(mdef, currentScope.getProject(), member, filter); |
| |
| // see Test::resolveSetter unit test: |
| // this.instanceXetterThis.instanceXetterThis = this; |
| // should be resolved to self.get_instanceXetterThis().set_instanceXetterThis(self); |
| if (def == null) |
| { |
| if (member.startsWith(JSSharedData.GETTER_PREFIX)) |
| { |
| member = member.substring(JSSharedData.GETTER_PREFIX.length()); |
| def = MemberedDefinitionUtils.getMemberByName(mdef, currentScope.getProject(), member, filter); |
| } |
| else if (member.startsWith(JSSharedData.SETTER_PREFIX)) |
| { |
| member = member.substring(JSSharedData.SETTER_PREFIX.length()); |
| def = MemberedDefinitionUtils.getMemberByName(mdef, currentScope.getProject(), member, filter); |
| } |
| } |
| |
| if (def != null) |
| { |
| if (what == ClassificationValue.SETTERS && def instanceof IGetterDefinition) |
| { |
| final IGetterDefinition getterDef = (IGetterDefinition)def; |
| filter = new ASDefinitionFilter(ClassificationValue.SETTERS, SearchScopeValue.ALL_SCOPES, AccessValue.ALL, mdef); |
| def = MemberedDefinitionUtils.getMemberByName( |
| mdef, currentScope.getProject(), member, filter); |
| |
| // work around CMP-851 |
| if (def == null) |
| def = getterDef.resolveSetter(currentScope.getProject()); |
| |
| return def; |
| } |
| else if (what == ClassificationValue.GETTERS && def instanceof ISetterDefinition) |
| { |
| final ISetterDefinition setterDef = (ISetterDefinition)def; |
| filter = new ASDefinitionFilter(ClassificationValue.GETTERS, SearchScopeValue.ALL_SCOPES, AccessValue.ALL, mdef); |
| def = MemberedDefinitionUtils.getMemberByName( |
| mdef, currentScope.getProject(), member, filter); |
| |
| // work around CMP-851 |
| if (def == null) |
| def = setterDef.resolveGetter(currentScope.getProject()); |
| |
| return def; |
| } |
| } |
| |
| if (def != null) |
| return def; |
| } |
| |
| // 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. |
| m_needsSecondPass = true; |
| |
| return null; |
| } |
| |
| /* |
| * private String getTypeForMember( IASNode iNode, String _class, String |
| * member ) { if( _class.equals("Object") ) return "Object"; // TODO: feed |
| * m_sharedData with var types for Object, Array, Date, Error, Function, |
| * RegExp, XML, and XMLList. if( isDataType(_class) ) return ""; IDefinition |
| * def = getDefinitionForName( iNode, makeName(_class) ); if( def != null ) |
| * { if( def instanceof MemberedDefinition ) { MemberedDefinition mdef = |
| * (MemberedDefinition) def; ASDefinitionFilter filter = new |
| * ASDefinitionFilter(ClassificationValue.MEMBERS_AND_TYPES, |
| * SearchScopeValue.ALL_SCOPES, AccessValue.ALL, def); def = |
| * mdef.getMemberByName(currentScope.getProject(), member, filter); if( def |
| * != null ) { if( def instanceof GetterDefinition || def instanceof |
| * SetterDefinition ) return "Function"; return def.getType(); } } } |
| * if(m_sharedData.hasVarName( _class + "." + member )) return |
| * m_sharedData.getVarType( _class + "." + member ); if( |
| * m_sharedData.hasClass(makeName(_class)) ) return getTypeForMember(iNode, |
| * getFullNameFromName(m_sharedData.getSuperClass(makeName(_class)), false), |
| * member); // 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. |
| * m_needsSecondPass = true; return ""; } private String extractPackageName( |
| * String _class ) { if( _class.lastIndexOf( '.' ) > 0 ) return |
| * _class.substring( 0, _class.lastIndexOf( '.' ) ); return ""; } private |
| * String extractClassName( String _class ) { if( _class.lastIndexOf( '.' ) |
| * > 0 ) return _class.substring( _class.lastIndexOf( '.' ) + 1 ); return |
| * _class; } |
| */ |
| |
| private static boolean isStringWithQuotes(String s) |
| { |
| return s.startsWith("\"") && s.endsWith("\""); |
| } |
| |
| private static boolean isLongNumberString(String s) |
| { |
| try |
| { |
| Long.parseLong(s); |
| } |
| |
| catch (NumberFormatException nfe) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Common routine used by reductions that need an empty list. |
| * |
| * @param iNode - the current AST node. |
| */ |
| public String createInstructionList(IASNode iNode) |
| { |
| return new String(); |
| } |
| |
| private static String removeQuotes(String s) |
| { |
| return s.replaceAll("\\\"", ""); |
| } |
| |
| private static String removeParentheses(String s) |
| { |
| while (s.lastIndexOf(")") == s.length() - 1) |
| { |
| if (s.indexOf("(") == 0) |
| s = s.substring(1, s.lastIndexOf(')')); |
| else if (s.endsWith("()")) |
| return s.substring(0, s.length() - 2); |
| else |
| return s; |
| } |
| return s; |
| } |
| |
| private String stripNS(String s) |
| { |
| if (s == null) |
| return "null"; |
| |
| if (s.indexOf("::") >= 0) |
| { |
| if (s.indexOf("{}::") >= 0) |
| s = s.replaceFirst("\\{\\}::", ""); |
| else |
| { |
| s = s.replaceFirst("\\{", ""); |
| s = s.replaceFirst("\\}::", "."); |
| } |
| } |
| return s; |
| } |
| |
| private String stripTabs(String s) |
| { |
| if (s == null) |
| return "null"; |
| |
| while (s.length() > 0 && s.charAt(0) == '\t') |
| s = s.substring(1); |
| |
| return s; |
| } |
| |
| private void registerLocalVariable(LexicalScope scope, Binding b_name, Binding b_type, Binding b_returnType) |
| { |
| // Name name = getNameFromBinding(b_name); |
| Name type = getNameFromBinding(b_type); |
| if (scope == null) |
| return; |
| |
| // LexicalScope.makeVariable |
| scope.makeVariable(b_name, type, null); // makeLocalVariable |
| } |
| |
| private void makeParameter(IASNode iNode, ParameterNode node, LexicalScope scope, Binding b_name, Binding b_type, Binding b_returnType) |
| { |
| // currentScope.makeParameter(#ArgumentID#); |
| // currentScope.makeParameter(node.getDefinition(),b_type.getName()); |
| currentScope.makeParameter(getParameterContent(node), b_type.getName()); |
| registerLocalVariable(scope, b_name, b_type, b_returnType); |
| } |
| |
| private void makeParameter(IASNode iNode, ParameterNode node, LexicalScope scope, Name name, Binding b_type, Binding b_returnType) |
| { |
| makeParameter(iNode, node, scope, currentScope.getBinding(node.getDefinition()), b_type, b_returnType); |
| } |
| |
| private void makeVariable(BaseVariableNode var_node, LexicalScope scope, Name b_name, Binding b_type, Binding b_returnType) |
| { |
| // Name name = getNameFromBinding( b_name ); |
| Name type = null; |
| // Name returnType = null; |
| |
| if (b_type != null) |
| type = getNameFromBinding(b_type); |
| |
| // if( b_returnType != null ) |
| // returnType = getNameFromBinding( b_returnType ); |
| |
| if (scope == null) |
| return; |
| |
| // LexicalScope.makeVariable |
| Binding var = currentScope.resolveName((IdentifierNode)var_node.getNameExpressionNode()); |
| scope.makeVariable(var, type, null); |
| |
| this.registerMember(var_node, var_node.getDefinition(), b_type.getDefinition()); |
| |
| /* |
| * Name _name = new Name( new Nsset( new |
| * Namespace(CONSTANT_PackageNs,createFullClassName(false))), |
| * getBasenameFromName(name) ); if( returnType != null ) { String _type |
| * = returnType.toString(); if( _type.startsWith("{}::") ) { _type = |
| * _type.replaceFirst( "\\{\\}::", "" ); if( |
| * getCurrentPackageName().length() > 0 && !isDataType(_type) ) _type = |
| * getCurrentPackageName() + "." + _type; } this.registerMember(iNode, |
| * _name.toString(), _type); } else this.registerMember(iNode, |
| * _name.toString(), "*"); |
| */ |
| } |
| |
| public static Name makeName(String name) |
| { |
| String nameSpace = ""; |
| String baseName = name; |
| |
| if (!isStringWithQuotes(baseName) && !baseName.contains(" ") && !baseName.endsWith("()") && !baseName.endsWith("]")) |
| { |
| baseName = removeParentheses(baseName); |
| if (name.indexOf(".") > 0) |
| { |
| nameSpace = baseName.substring(0, baseName.lastIndexOf(".")); |
| baseName = baseName.substring(baseName.lastIndexOf(".") + 1); |
| } |
| } |
| Name _name = new Name(new Nsset(new Namespace(CONSTANT_PackageNs, nameSpace)), baseName); |
| return _name; |
| } |
| |
| private Binding makeBinding(IASNode node, String name) |
| { |
| IDefinition def = null; |
| |
| if (name.startsWith("\"") && name.endsWith("\"")) |
| { |
| return new Binding(node, makeName(name), null); |
| } |
| else if (!name.startsWith(JSSharedData.THIS + ".")) |
| { |
| if (name.contains(".")) |
| { |
| for (String part : name.split("\\.")) |
| { |
| if (def == null) |
| def = getDefinitionForName(node, makeName(part)); |
| else |
| def = getDefinitionForMember(node, def, part, ClassificationValue.MEMBERS_AND_TYPES); |
| } |
| } |
| else |
| { |
| def = this.getDefinitionForName(node, makeName(removeQuotes(name))); |
| } |
| } |
| |
| return new Binding(node, makeName(name), def); |
| } |
| |
| private IDefinition getDefinitionForInstance(IASNode iNode, String instanceName, ClassificationValue what) |
| { |
| if (what == null) |
| what = ClassificationValue.MEMBERS_AND_TYPES; |
| |
| if (isStringWithQuotes(instanceName)) |
| return getDefinitionForName(iNode, makeName("String")); |
| |
| if (instanceName.equals(JSSharedData.THIS)) |
| { |
| if (m_currentClassDefinition != null) |
| return m_currentClassDefinition; |
| return getDefinitionForName(iNode, makeName(createFullClassName(false))); |
| } |
| |
| if (instanceName.equals(JSSharedData.JS_THIS)) |
| { |
| if (isAnonymousFunction(getMethodName())) |
| return m_functionNode.getDefinition(); |
| return m_currentClassDefinition; |
| } |
| |
| if (isDataType(instanceName)) |
| return getDefinitionForName(iNode, makeName(instanceName)); |
| |
| if (instanceName.equals("[]")) |
| return getDefinitionForName(iNode, makeName("Array")); |
| |
| if (instanceName.equals("{}")) |
| return getDefinitionForName(iNode, makeName("Object")); |
| |
| Name typeOfInstance = null; |
| if (instanceName.equals(JSSharedData.THIS + "." + JSSharedData._SUPER)) |
| { |
| typeOfInstance = makeName(createFullClassName(false)); |
| if (m_sharedData.hasClass(typeOfInstance)) |
| typeOfInstance = m_sharedData.getSuperClass(typeOfInstance); |
| return getDefinitionForName(iNode, (typeOfInstance)); |
| } |
| |
| instanceName = removeRootName(instanceName); |
| |
| // remove "/* unknown */" marker |
| if (instanceName.contains("unknown")) |
| instanceName = instanceName.replaceFirst("\\/\\* unknown \\*\\/ ", ""); |
| |
| instanceName = removeParentheses(instanceName); |
| |
| // trim function name |
| if (instanceName.contains("(")) |
| { |
| if (!instanceName.endsWith(")")) |
| return null; |
| |
| final String tmp = instanceName.substring(0, instanceName.indexOf('(')); |
| // IDefinition def = getDefinitionForName( iNode, makeName(tmp) ); |
| IDefinition def = getDefinitionForInstance(iNode, tmp, what); |
| if (def != null && def instanceof FunctionDefinition) |
| { |
| FunctionDefinition fdef = (FunctionDefinition)def; |
| def = getDefinitionForName(iNode, makeName(fdef.getReturnTypeAsDisplayString())); |
| return def; |
| } |
| } |
| |
| // trim index access literals |
| if (instanceName.contains("[")) |
| instanceName = instanceName.substring(0, instanceName.indexOf('[')); |
| |
| // trim trailing comments |
| if (instanceName.contains("/*") && instanceName.contains("*/")) |
| instanceName = instanceName.substring(0, instanceName.indexOf("/*")); |
| |
| if (!instanceName.contains(".")) |
| { |
| IDefinition def = getDefinitionForName(iNode, makeName(instanceName)); |
| /* |
| * NON-SENSE, but kind of cool: if( def == null && |
| * (instanceName.startsWith(JSSharedData.GETTER_PREFIX) || |
| * instanceName.startsWith(JSSharedData.SETTER_PREFIX)) ) { def = |
| * getDefinitionForName( __p, makeName(instanceName.substring(4)) ); |
| * if( def != null && def instanceof GetterDefinition ) return |
| * "Function"; } |
| */ |
| |
| if (def != null) |
| { |
| if (def instanceof ClassDefinition) |
| { |
| return def; |
| |
| /* |
| * final ClassDefinition classDef = (ClassDefinition)def; |
| * IDefinition parent = def.getParent(); return |
| * JSSharedData.ROOT_NAME + |
| * m_sharedData.getPackageNameForClassName |
| * (classDef.getName(), getBaseClassAsStrign(classDef)) + |
| * "." + def.getName(); |
| */ |
| } |
| else if (def instanceof VariableDefinition) |
| { |
| final VariableDefinition variable = (VariableDefinition)def; |
| IDefinition typeDef = variable.resolveType(currentScope.getProject()); |
| if (typeDef != null) |
| return typeDef; |
| return def; |
| } |
| else if (def.getTypeAsDisplayString() != null && !def.getTypeAsDisplayString().isEmpty()) |
| { |
| IDefinition typeDef = getDefinitionForName(iNode, makeName(def.getTypeAsDisplayString())); |
| if (typeDef != null) |
| return typeDef; |
| return def; |
| /* |
| * if( def != null ) { IDefinition parent = def.getParent(); |
| * if( parent == null ) return def.getName(); return |
| * parent.getName() + "." + def.getName(); } |
| */ |
| } |
| else |
| { |
| return def; |
| } |
| } |
| } |
| else |
| { |
| // String type = m_sharedData.getVarType(instanceName); |
| // if( type != null ) |
| // return getDefinitionForName( iNode, makeName(type)); |
| |
| IDefinition obj = null; |
| String name = instanceName; |
| if (name.startsWith(JSSharedData.THIS + ".")) |
| { |
| name = instanceName.replaceFirst(JSSharedData.THIS + ".", ""); |
| obj = getDefinitionForName(iNode, makeName(createFullClassName(false))); |
| } |
| String prop = ""; |
| IDefinition next = null; |
| while (name.length() > 0) |
| { |
| final int nextDot = name.indexOf("."); |
| if (nextDot < 0) |
| { |
| prop = name; |
| name = ""; |
| } |
| else |
| { |
| prop = name.substring(0, nextDot); |
| name = name.substring(nextDot + 1); |
| } |
| |
| // if( isDataType(next) ) |
| // return next; |
| |
| if (obj != null) |
| { |
| if (obj instanceof GetterDefinition) |
| { |
| next = getDefinitionForName(iNode, makeName(((GetterDefinition)obj).getReturnTypeAsDisplayString())); |
| if (next == null) |
| { |
| // throw new Error( "next is null" ); |
| next = getDefinitionForName(iNode, makeName("Object")); |
| } |
| obj = next; |
| } |
| |
| if (name.isEmpty()) |
| next = getDefinitionForMember(iNode, obj, prop, what); |
| else |
| next = getDefinitionForMember(iNode, obj, prop, ClassificationValue.MEMBERS_AND_TYPES); |
| |
| if (next == null) |
| { |
| // i.e. com.wizhelp.fzlib.ZStream.next_in |
| next = null; |
| } |
| |
| if (next == null || isDataType(next.getBaseName())) |
| return next; |
| } |
| else |
| { |
| // next = getDefinitionForInstance(iNode, prop, what ); |
| next = getDefinitionForInstance(iNode, prop, what); |
| // if(next == null) |
| // return null; |
| } |
| |
| obj = next; |
| } |
| return obj; |
| } |
| |
| return null; |
| } |
| |
| private String getTypeOfInstance(IASNode iNode, String instanceName) |
| { |
| IDefinition def = getDefinitionForInstance(iNode, instanceName, ClassificationValue.MEMBERS_AND_TYPES); |
| if (def != null) |
| { |
| if (def instanceof GetterDefinition || def instanceof SetterDefinition) |
| return "Function"; |
| if (def.getTypeAsDisplayString() != null && !def.getTypeAsDisplayString().isEmpty()) |
| return def.getTypeAsDisplayString(); |
| return def.getBaseName(); |
| } |
| |
| return ""; |
| } |
| |
| /** |
| * createFullNameFromDefinition |
| */ |
| |
| public static String createFullNameFromDefinition(ICompilerProject project, IDefinition def) |
| { |
| String name = ""; |
| |
| if (def instanceof ClassDefinition) |
| { |
| String packageName = def.getPackageName(); |
| if (!packageName.isEmpty()) |
| name = packageName + "."; |
| name += def.getBaseName(); |
| } |
| else |
| { |
| final String externName = JSSharedData.instance.getExternName(def.getQualifiedName()); |
| if (externName != null) |
| { |
| return externName; |
| } |
| |
| String fullName = ""; |
| List<String> parts = new ArrayList<String>(); |
| IDefinition parent = def.getParent(); |
| if (parent == null) |
| name = def.getQualifiedName(); |
| else |
| { |
| while (parent != null) |
| { |
| if (parent.getBaseName() != null && !parent.getBaseName().isEmpty()) |
| parts.add(0, parent.getBaseName()); |
| |
| if (!fullName.isEmpty()) |
| fullName += "."; |
| fullName += parent.getBaseName(); |
| |
| parent = parent.getParent(); |
| } |
| |
| for (String part : parts) |
| { |
| if (name.isEmpty() && !part.equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| name = JSSharedData.ROOT_NAME; |
| |
| name += part + "."; |
| } |
| name += def.getBaseName(); |
| } |
| } |
| return name; |
| } |
| |
| private IDefinition getClassDefinition(ICompilerProject project, IDefinition definitionContext, String className) |
| { |
| // TODO: convert to ICompilerProblem? |
| if (!className.contains(".") && !isDataType(className) && m_sharedData.getDefinition(className) == null) |
| { |
| final IASScope scope = definitionContext != null ? definitionContext.getContainingScope() : project.getScope(); |
| final IDefinition def = getDefinitionForNameByScope(project, scope, makeName(className)); |
| if (!isExternalClass(project, def)) |
| { |
| if (def != null && !def.getQualifiedName().equals(className)) |
| m_sharedData.stdout("Warning: Suspicious class name: did you mean '" + def.getQualifiedName() + "' instead of '" + className + "'?"); |
| else |
| m_sharedData.stdout("Warning: Suspicious class name: " + className); |
| } |
| } |
| |
| ASDefinitionFilter filter = new ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, SearchScopeValue.ALL_SCOPES, AccessValue.ALL, definitionContext); |
| // final Multiname multiname = Multiname.crackDottedQName(currentScope.getProject(), className); |
| IDefinition classDef = ASScopeUtils.findDefinitionByName(project.getScope(), project, className, filter); |
| // if( classDef == null ) |
| // classDef = definitionContext.getContainingScope().findDefinitionByName(className, filter, currentScope.getDefinitionCache() ); |
| |
| return classDef; |
| } |
| |
| /* |
| * private IDefinition getBaseClass(ClassDefinition classDef) { |
| * ClassDefinition superclassDefinition = null; String name = |
| * classDef.getBaseClass(); if( name != null ) { |
| * Collection<ICompilerProblem> problems = currentScope.getProblems(); |
| * superclassDefinition = classDef.resolveBaseClass( |
| * currentScope.getProject(), new RecursionGuard(), problems); if ( |
| * superclassDefinition == null ) { // Repair by making the class extend |
| * Object; the CG's caller // is responsible for picking up the problem and |
| * treating the // result of this code generation with appropriate caution. |
| * superclassDefinition = |
| * (ClassDefinition)currentScope.getProject().getBuiltinType |
| * (IASLanguageConstants.BuiltinType.OBJECT); } } return |
| * superclassDefinition; } private String |
| * getBaseClassAsString(ClassDefinition classDef) { final IDefinition |
| * superclassName = getBaseClass(classDef); if( superclassName == null ) |
| * return null; String name = superclassName.getQualifiedName(); return |
| * name; } |
| */ |
| |
| private Boolean hasInterface(Iterator<IInterfaceDefinition> interfaceIterator, List<String> interfaceNames) |
| { |
| while (interfaceIterator.hasNext()) |
| { |
| final IInterfaceDefinition interfaceDef = interfaceIterator.next(); |
| final String extendedInterface = interfaceDef.getQualifiedName(); |
| for (String interfaceName : interfaceNames) |
| { |
| if (extendedInterface.equals(interfaceName) || extendedInterface.equals("public." + interfaceName)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public Boolean hasInterface(ICompilerProject project, IDefinition def, List<String> interfaceNames) |
| { |
| if (def != null) |
| { |
| if (def instanceof IClassDefinition) |
| { |
| IClassDefinition classDef = (IClassDefinition)def; |
| final Iterator<IClassDefinition> superClassIterator = classDef.classIterator(currentScope.getProject(), true); |
| while (superClassIterator.hasNext()) |
| { |
| classDef = superClassIterator.next(); |
| |
| // Workaround for Falcon bug |
| if (isDataType(classDef)) |
| return false; |
| |
| final Iterator<IInterfaceDefinition> interfaceIterator = classDef.interfaceIterator(currentScope.getProject()); |
| if (hasInterface(interfaceIterator, interfaceNames)) |
| return true; |
| } |
| } |
| else if (def instanceof IInterfaceDefinition) |
| { |
| // Workaround for Falcon bug |
| if (isDataType(def)) |
| return false; |
| |
| final IInterfaceDefinition interfaceDef = (IInterfaceDefinition)def; |
| final Iterator<IInterfaceDefinition> interfaceIterator = interfaceDef.interfaceIterator(currentScope.getProject(), true); |
| if (hasInterface(interfaceIterator, interfaceNames)) |
| return true; |
| } |
| |
| } |
| return false; |
| } |
| |
| /** |
| * isExternalClass |
| */ |
| |
| public Boolean isExternalClass(ICompilerProject project, IDefinition def) |
| { |
| if (def == null) |
| return false; |
| |
| // we will eventually switch over to using [Extern] |
| // instead of deriving from IExtern. |
| final IMetaTag extern = def.getMetaTagByName(JSSharedData.EXTERN_METATAG); |
| if (extern != null) |
| return true; |
| |
| final List<String> interfaceNames = new ArrayList<String>(); |
| interfaceNames.add("browser" + "." + JSSharedData.EXTERN_INTERFACE_NAME); |
| interfaceNames.add("browser" + "." + JSSharedData.FRAMEWORK_INTERFACE_NAME); |
| return hasInterface(project, def, interfaceNames); |
| } |
| |
| /** |
| * isDataClass |
| */ |
| |
| public Boolean isDataClass(ICompilerProject project, IDefinition def) |
| { |
| if (def == null) |
| return false; |
| |
| final IMetaTag extern = def.getMetaTagByName(JSSharedData.DATACLASS_METATAG); |
| if (extern != null) |
| return true; |
| |
| def = getSuperClass(project, def); |
| if (def != null && def != currentScope.getProject().getBuiltinType(BuiltinType.OBJECT)) |
| return isDataClass(project, def); |
| |
| return false; |
| } |
| |
| /** |
| * isSymbolClass |
| */ |
| |
| public Boolean isSymbolClass(ICompilerProject project, IDefinition def) |
| { |
| final List<String> interfaceNames = new ArrayList<String>(); |
| interfaceNames.add(JSSharedData.SYMBOL_INTERFACE_NAME); |
| return hasInterface(project, def, interfaceNames); |
| } |
| |
| /** |
| * makeSetterCall |
| */ |
| |
| private String makeSetterCall(IASNode iNode, IDefinition def, |
| String instanceName, Binding member, String memberName, |
| String op, String value, Boolean isPostOperator) |
| { |
| final String selfParam = JSSharedData.m_useSelfParameter ? instanceName + ", " : ""; |
| String name = instanceName; |
| if (def != null) |
| { |
| final Boolean isStatic = m_convertToStatic || def.isStatic(); |
| if (isStatic) |
| { |
| name = createFullNameFromDefinition(currentScope.getProject(), def); |
| name = name.substring(0, name.lastIndexOf(".")); |
| } |
| } |
| |
| String getterName = ""; |
| String result = ""; |
| // if (def == null || !(def instanceof SetterDefinition)) |
| if (false) // AJH disable this clause for now |
| { |
| String self = JSSharedData.THIS; |
| final String currentFullClassName = getCurrentFullClassName(); |
| if (currentFullClassName == null || currentFullClassName.isEmpty()) |
| self = "null"; |
| |
| // could be a member. |
| |
| /* |
| * !convertLocalVarsToMembers(iNode) fixes bug related to injecting |
| * adobe.setProperty and "+=" operator. In order to reproduce the |
| * bug you need: 1. a dynamic class 2. a method marked with |
| * ConvertLocalVarsToMembers 3. two local variables, i.e. A and B. |
| * 4. a plus equal assignment in an expression using A and B, i.e.: |
| * if ((A += B) == 15) The bug was: if( (self.A += |
| * adobe.getProperty(self, self, "A") + self.B)) == 15)) The correct |
| * result is: if(((adobe.setProperty(self, self, "A", |
| * adobe.getProperty(self, self, "A") + self.B)) == 15)) |
| */ |
| if (def != null && |
| def instanceof VariableDefinition && |
| (instanceName.equals(JSSharedData.THIS) || instanceName.equals(JSSharedData.JS_THIS)) && |
| !convertLocalVarsToMembers(iNode)) |
| { |
| result = instanceName + "." + removeQuotes(memberName) + " " + op + " ("; |
| } |
| else |
| { |
| registerAccessedPropertyName(iNode, memberName); |
| result = JSSharedData.JS_FRAMEWORK_NAME + ".setProperty(" + self + ", " + instanceName + ", " + memberName + ", "; |
| } |
| } |
| else |
| result = name + "." + JSSharedData.SETTER_PREFIX + removeQuotes(memberName) + "("; |
| |
| if (op.length() >= 2 && op.endsWith("=")) |
| { |
| getterName = resolveGetterName(iNode, instanceName, member, false); |
| |
| // This very simple test case messed up our set/getProperty injections: |
| // const array : Object = {item:123}; |
| // const num : uint = array.item++; |
| if (isPostOperator && value.equals("1") && getterName.startsWith(JSSharedData.JS_FRAMEWORK_NAME + ".getProperty")) |
| { |
| if (op.equals("+=")) |
| result = getterName.replaceFirst(JSSharedData.JS_FRAMEWORK_NAME + ".getProperty", JSSharedData.JS_FRAMEWORK_NAME + ".postIncProperty"); |
| else if (op.equals("-=")) |
| result = getterName.replaceFirst(JSSharedData.JS_FRAMEWORK_NAME + ".getProperty", JSSharedData.JS_FRAMEWORK_NAME + ".postDecProperty"); |
| else |
| result += getterName + " " + op.substring(0, op.lastIndexOf("=")) + " " + selfParam + value; |
| } |
| else |
| { |
| result += getterName + " " + op.substring(0, op.lastIndexOf("=")) + " " + selfParam + value + ")"; |
| } |
| } |
| else |
| { |
| result += selfParam + value + ")"; |
| } |
| |
| if (!isPostOperator && !getterName.isEmpty()) |
| { |
| // adobe.setProperty now returns the value. |
| if (result.startsWith(JSSharedData.JS_FRAMEWORK_NAME + ".setProperty")) |
| result += ""; |
| else |
| result = "(" + result + ", " + getterName + ")"; |
| } |
| |
| return result; |
| } |
| |
| private String createStringFromBinding(IASNode iNode, Binding b) |
| { |
| final String baseName = getBasenameFromBinding(b); |
| final IDefinition def = b.getDefinition(); |
| if (def != null && def.getBaseName().equals(baseName)) |
| { |
| IDefinition parent = def.getParent(); |
| if (parent == null || !(parent instanceof ClassDefinition)) |
| { |
| if (def.isStatic()) |
| return createFullNameFromDefinition(currentScope.getProject(), def); |
| |
| if (def instanceof VariableDefinition) |
| return def.getBaseName(); |
| |
| return def.getQualifiedName(); |
| } |
| } |
| |
| // if( def != null && !(def instanceof VariableDefinition) ) |
| final Name name = getNameFromBinding(b); |
| { |
| if (name.getQualifiers() != null && name.getQualifiers().length() == 1) |
| { |
| final Namespace sq = name.getSingleQualifier(); |
| final String packageName = sq.getName(); |
| if (packageName.isEmpty()) |
| return baseName; |
| |
| // AJH added to prevent private vars from having namespaces |
| if (sq.getKind() == ABCConstants.CONSTANT_PrivateNs) |
| return baseName; |
| |
| final INamespaceDefinition ns = m_sharedData.getNamespace(packageName); |
| if (ns != null) |
| return baseName; // + " /* NS=" + ns.getName() + " */"; |
| |
| if (packageName.startsWith("http:")) |
| return baseName; // + " /* NS=" + packageName + " */"; |
| |
| return packageName + "." + name.getBaseName(); |
| } |
| } |
| return baseName; // getBasenameFromBinding(b); |
| } |
| |
| /** |
| * getSetterDefinition |
| * |
| * @param iNode |
| * @param stem |
| * @param member |
| * @return |
| */ |
| private IDefinition getSetterDefinition(IASNode iNode, String stem, Binding member) |
| { |
| // TODO: duplicates / very similar to code in resolveGetterName() |
| IDefinition def = member.getDefinition(); |
| |
| if (def == null || !(def instanceof ISetterDefinition)) |
| { |
| final String baseName = removeQuotes(getBasenameFromBinding(member)); |
| if (def instanceof IGetterDefinition) |
| { |
| // def = getDefinitionForInstance(iNode, createFullNameFromDefinition(currentScope.project,def), ClassificationValue.SETTERS ); |
| def = getDefinitionForMember(iNode, def.getParent(), baseName, ClassificationValue.SETTERS); |
| } |
| |
| if (def == null) |
| { |
| String packageName = ""; |
| if (stem != null && !stem.isEmpty()) |
| packageName += removeRootName(stem) + "."; |
| |
| final String methodName = packageName + baseName; |
| final String typeOfInstance = getTypeOfInstance(iNode, methodName); |
| if (typeOfInstance != null && typeOfInstance.equals("Function")) |
| { |
| def = getDefinitionForInstance(iNode, methodName, ClassificationValue.SETTERS); |
| } |
| } |
| } |
| |
| if (def instanceof ISetterDefinition && !isDataType(def.getParent()) && !isExternalClass(currentScope.getProject(), def.getParent()) && !isVectorLength((ISetterDefinition)def)) |
| { |
| return def; |
| } |
| |
| return null; |
| } |
| |
| private boolean isAlphaNumString(String s) |
| { |
| for (int i = 0; i < s.length(); ++i) |
| { |
| final int c = s.codePointAt(i); |
| final boolean isAlphaNumChar = (c >= 'a' && c <= 'z') || |
| (c >= '0' && c <= '9') || |
| (c >= 'A' && c <= 'Z') || |
| (c == '$') || |
| (c == '_'); |
| if (!isAlphaNumChar) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* |
| * Fixes bug that ChrisB pointed out: const numAsUint : uint = num; needs to |
| * be emitted as: var numAsUint = adobe.dynamicCastAs(num, UIntClass); Same |
| * with uint/int members and initializers. The fixes went into |
| * reduce_variableDecl() and resolveSetterName(). They now call |
| * addDynamicCastToValue(). |
| */ |
| private String addDynamicCastToValue(IASNode iNode, String value) |
| { |
| /* AJH disable for now |
| // m_numMemberAsUint = num; needs to be converted. |
| // Tamarin tests trigger NPE in JSGeneratingReducer.addDynamicCastToValue() |
| if (getCurrentClassName() != null && |
| !getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME) && |
| value != null && |
| !value.startsWith("adobe.dynamicCastAs(")) |
| { |
| final IDefinition lhs = getDefinitionForNode(iNode, false); |
| if (lhs != null) |
| { |
| final IDefinition rhs = getDefinitionForNode(iNode, true); |
| if (lhs == currentScope.getProject().getBuiltinType(BuiltinType.INT) && |
| rhs != currentScope.getProject().getBuiltinType(BuiltinType.INT)) |
| { |
| // http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/int.html |
| // AS3, int: The range of values represented by the int class is -2,147,483,648 (-2^31) to 2,147,483,647 (2^31-1). |
| // http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Integer.html |
| // Java, Integer: MIN_VALUE=-2^31, MAX_VALUE=2^31-1 |
| Boolean needsDynamicCast = false; |
| try |
| { |
| @SuppressWarnings("unused") |
| Integer num; |
| if (value.startsWith("0x")) |
| num = Integer.parseInt(value.substring(2), 16); |
| else |
| num = Integer.parseInt(value); |
| } |
| catch (Exception e) |
| { |
| if (!value.equals("NaN")) |
| needsDynamicCast = true; |
| } |
| if (needsDynamicCast) |
| value = "adobe.dynamicCastAs(" + value + ", IntClass)"; // "~~(" + value + " / * Coerce to int * /)"; |
| } |
| else if (lhs == currentScope.getProject().getBuiltinType(BuiltinType.UINT) && |
| rhs != currentScope.getProject().getBuiltinType(BuiltinType.UINT)) |
| { |
| // http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/uint.html |
| // AS3, uint: The range of values represented by the uint class is 0 to 4,294,967,295 (2^32-1) |
| // http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Long.html |
| // Java, Long: MIN_VALUE=-2^63, MAX_VALUE=2^63-1 |
| Boolean needsDynamicCast = false; |
| try |
| { |
| Long num; |
| if (value.startsWith("0x")) |
| num = Long.parseLong(value.substring(2), 16); |
| else |
| num = Long.parseLong(value); |
| if (num < 0 || num > 4294967295L) |
| needsDynamicCast = true; |
| } |
| catch (Exception e) |
| { |
| if (!value.equals("NaN")) |
| needsDynamicCast = true; |
| } |
| if (needsDynamicCast) |
| value = "adobe.dynamicCastAs(" + value + ", UIntClass)"; // "(((~~(" + value + ")) < 0 ) ? (4294967296+(~~(" + value + "))) : (~~(" + value +")) / * Coerce to uint * /)"; |
| } |
| } |
| }*/ |
| return value; |
| } |
| |
| /** |
| * resolveSetterName |
| * |
| * @param iNode |
| * @param instanceName |
| * @param member |
| * @param op |
| * @param value |
| * @param useBrackets |
| * @param isPostOperator |
| * @return |
| */ |
| private String resolveSetterName(IASNode iNode, String instanceName, Binding member, String op, String value, |
| Boolean useBrackets, Boolean isPostOperator) |
| { |
| // constant propagation for initialized members |
| m_hasSideEffects = true; |
| |
| IDefinition def = member.getDefinition(); |
| final String baseNameWithQuotes = getBasenameFromBinding(member); |
| final String baseName = removeQuotes(baseNameWithQuotes); |
| |
| // In instance.foo, foo is a member of instance's type |
| // In instance["foo"], foo is a member of instance's type |
| // In instance[foo], foo is an expression evaluating to a string, and the *string* is a member of instance's type |
| // NOTE: see similar fix for getters in resolveGetterName() |
| // TODO: warn if !baseNameIsMember and instanceName isn't an Array or Vector -- runtime name lookup can't |
| // handle get_/set_ prefixing properly. (Also need such a warning for "in" keyword and "for each" loops). |
| boolean baseNameIsMember = !useBrackets || !baseName.equals(baseNameWithQuotes); |
| |
| // see reduce_superAccess |
| if ( /* instanceName.equals("goog") && */(baseName.startsWith("goog.base(") || baseName.startsWith("adobe.base("))) |
| { |
| assert !useBrackets; |
| return convertSuperAccessToSetter(baseNameWithQuotes, value); |
| } |
| |
| /* |
| * BUG: Found via fl.motion.Animator.setTimeClassic() WRONG: |
| * self._target.get_transform().matrix = targetMatrix; CORRECT: |
| * self._target.get_transform().set_matrix( targetMatrix ); |
| */ |
| |
| // m_numMemberAsUint = num; needs to |
| value = addDynamicCastToValue(iNode, value); |
| |
| if (baseNameIsMember) |
| { |
| final IDefinition setterDef = getSetterDefinition(iNode, instanceName, member); |
| if (setterDef != null) |
| { |
| final String setterCall = makeSetterCall(iNode, setterDef, instanceName, member, baseName, op, value, isPostOperator); |
| return addStaticInitCall(setterDef, setterCall, false); |
| } |
| |
| // e15_6_3_1_4, Trying to verify that Boolean.prototype is read-only. |
| if (baseName.equals("prototype") && isDataType(instanceName)) |
| { |
| return "throw new Error(\"ReferenceError: Error #1074\") /* FOR: " + instanceName + "." + baseName + " " + op + " " + value + " */"; |
| } |
| } |
| |
| String ret = ""; |
| |
| // optimization for post/pre and inc/dec |
| String preOpAndValue = ""; |
| String postOpAndValue = " " + op + " " + value; |
| if (value.equals("1")) |
| { |
| if (op.equals("+=")) |
| { |
| if (isPostOperator) |
| postOpAndValue = "++"; |
| else |
| { |
| preOpAndValue = "++"; |
| postOpAndValue = ""; |
| } |
| } |
| else if (op.equals("-=")) |
| { |
| if (isPostOperator) |
| postOpAndValue = "--"; |
| else |
| { |
| preOpAndValue = "--"; |
| postOpAndValue = ""; |
| } |
| } |
| } |
| |
| String memberName = createStringFromBinding(iNode, member); |
| |
| if (instanceName.isEmpty()) |
| ret = preOpAndValue + memberName + postOpAndValue; |
| else |
| { |
| if (needsRuntimeLookup(iNode, instanceName, member)) |
| { |
| String quote = ""; |
| if (baseNameIsMember && isAlphaNumString(baseNameWithQuotes) && !memberName.startsWith("\"")) |
| quote = "\""; |
| else |
| { |
| // this will ensure that converted members are being resolved correctly. |
| final String mappedName = m_nameMap.get(member); |
| if (mappedName != null && !mappedName.isEmpty()) |
| memberName = mappedName; |
| } |
| ret = makeSetterCall(iNode, def, instanceName, member, quote + memberName + quote, op, value, isPostOperator); |
| |
| /* |
| * String self = JSSharedData.THIS; final String |
| * currentFullClassName = getCurrentFullClassName(); if( |
| * currentFullClassName == null || |
| * currentFullClassName.isEmpty() ) self = "null"; |
| * registerAccessedPropertyName(iNode,memberName); ret = |
| * JSSharedData.JS_FRAMEWORK_NAME + ".setProperty(" + self + |
| * ", " + preOpAndValue + instanceName + ", " + quote + |
| * memberName + quote + ", " + value + ")"; |
| */ |
| } |
| else if (useBrackets) |
| ret = preOpAndValue + instanceName + "[" + memberName + "]" + postOpAndValue; |
| else |
| ret = preOpAndValue + instanceName + "." + memberName + postOpAndValue; |
| } |
| |
| return addStaticInitCall(def, ret, false); |
| } |
| |
| /** |
| * Convert a super getter invocation to a super setter invocation, now that |
| * we know we're in an assignment context. |
| */ |
| private String convertSuperAccessToSetter(String baseNameWithQuotes, String value) |
| { |
| // Method name is passed to goog.base() as a string, so don't removeQuotes() in this case (the code |
| // came from reduce_superAccess(), which is guaranteed to not emit any quotes but the needed ones...) |
| String superSetter = baseNameWithQuotes; |
| |
| // Convert get_ prefix created by reduce_superAccess() to set_ prefix |
| // TODO: this string manipulation is inherently fragile... it might even break in some edge cases |
| if (baseNameWithQuotes.startsWith("adobe.base(")) |
| { |
| int firstGet = baseNameWithQuotes.indexOf("get_"); |
| if (firstGet != -1) |
| { |
| int secondGet = baseNameWithQuotes.indexOf("get_", firstGet + 1); |
| int getToReplace = (secondGet != -1) ? secondGet : firstGet; |
| |
| superSetter = superSetter.substring(0, getToReplace) + "set_" + superSetter.substring(getToReplace + 4); |
| } |
| } |
| else |
| // startsWith("goog.base(") |
| { |
| if (superSetter.contains("\"get_")) |
| superSetter = superSetter.replaceFirst("\"get_", "\"set_"); |
| } |
| |
| // The base() call now needs an extra argument: the RHS value to pass to the setter |
| superSetter = superSetter.replaceFirst("\\)", ", " + value + "\\)"); |
| |
| return superSetter; |
| } |
| |
| /** |
| * makeGetterCall |
| */ |
| private static String makeGetterCall(ICompilerProject project, IDefinition def, String instanceName, String memberName) |
| { |
| final String selfParam = JSSharedData.m_useSelfParameter ? instanceName + ", " : ""; |
| final Boolean isStatic = m_convertToStatic || def.isStatic(); |
| String name = instanceName; |
| if (isStatic) |
| { |
| name = createFullNameFromDefinition(project, def); |
| name = name.substring(0, name.lastIndexOf(".")); |
| } |
| return name + "." + JSSharedData.GETTER_PREFIX + removeQuotes(memberName) + "(" + selfParam + ")"; |
| } |
| |
| /** |
| * needsRuntimeLookup |
| */ |
| |
| private Boolean needsRuntimeLookup(IASNode iNode, String instanceName, Binding member) |
| { |
| if (isCurrentFramework()) |
| return false; |
| |
| // final boolean instanceIsFramework = instanceName.startsWith(JSSharedData.JS_FRAMEWORK_NAME + "."); |
| // if( instanceIsFramework ) |
| // return false; |
| |
| if (instanceName.equals(JSSharedData.JS_THIS) && isAnonymousFunction(getMethodName())) |
| return false; |
| |
| // Object["getOwnPropertyDescriptor"]; |
| if (isDataType(instanceName)) |
| return false; |
| |
| /* |
| * Name x = m_currentClassDefinition.getMName(currentScope.project); |
| * Nsset nsset = x.getQualifiers(); nsset.add(); x = new Name(nsset, |
| * instanceName); IDefinition instanceDef = getDefinitionForName(iNode, |
| * x ); |
| */ |
| IDefinition instanceDef = getDefinitionForName(iNode, makeName(instanceName)); |
| |
| if (instanceDef == null && |
| instanceName.equals(JSSharedData.THIS) && |
| m_currentClassDefinition != null && |
| !isAnonymousFunction(getMethodName())) |
| { |
| instanceDef = m_currentClassDefinition; |
| } |
| |
| if (instanceDef == null) |
| instanceDef = getDefinitionForInstance(iNode, instanceName, ClassificationValue.MEMBERS_AND_TYPES); |
| |
| if (instanceDef != null) |
| { |
| if (instanceDef instanceof VariableDefinition) |
| { |
| final VariableDefinition varDef = (VariableDefinition)instanceDef; |
| final IReference typeRef = varDef.getTypeReference(); |
| if (typeRef == null) |
| return true; |
| |
| final ASScope scope = (ASScope)getScopeFromNode(iNode); |
| instanceDef = typeRef.resolve(currentScope.getProject(), scope, DependencyType.INHERITANCE, false); |
| if (instanceDef == null) |
| instanceDef = getDefinitionForInstance(iNode, typeRef.getName(), ClassificationValue.CLASSES_AND_INTERFACES); |
| |
| // Vectors are an exception |
| if (instanceDef != null && instanceDef instanceof AppliedVectorDefinition) |
| return false; |
| } |
| else if (instanceDef instanceof IFunctionDefinition) |
| { |
| final IFunctionDefinition functionDef = (IFunctionDefinition)instanceDef; |
| instanceDef = functionDef.resolveReturnType(currentScope.getProject()); |
| if (instanceDef == null) |
| return true; |
| } |
| |
| // Once you stick something into an Array it becomes typeless: |
| // const arr : Array = [obj]; |
| // obj[0].a = 1; |
| // unless you have a Vector! |
| if (instanceName.endsWith("]")) |
| return true; |
| |
| // Objects and Arrays are tricky. |
| // For example, in this case we really do want the lookup: |
| // const obj : Object = new MyClass(); |
| // obj.a = 1; |
| //if (instanceDef == currentScope.getProject().getBuiltinType(BuiltinType.OBJECT)) |
| // return true; |
| |
| // Same with "*" |
| // For example, in this case we really do want the lookup: |
| // const obj : * = new MyClass(); |
| // obj.a = 1; |
| //if (instanceDef == currentScope.getProject().getBuiltinType(BuiltinType.ANY_TYPE)) |
| // return true; |
| |
| if (instanceDef instanceof IClassDefinition || instanceDef instanceof IInterfaceDefinition) |
| { |
| final String baseName = getBasenameFromBinding(member); |
| |
| if (isDataType(instanceDef)) |
| return false; |
| |
| if (isExternalClass(currentScope.getProject(), instanceDef)) |
| return false; |
| |
| if (isDataClass(currentScope.getProject(), instanceDef)) |
| return false; |
| |
| // workaround for Falcon bug. |
| // We never find prototype members, but they are always provided through Object. |
| if (baseName != null && baseName.equals("prototype")) |
| return false; |
| |
| if (instanceDef.isDynamic()) |
| { |
| instanceDef = getDefinitionForMember(iNode, instanceDef, baseName, ClassificationValue.MEMBERS_AND_TYPES); |
| if (instanceDef != null) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private String addStaticInitCall(IDefinition def, String code, Boolean provideReturnValue) |
| { |
| if (def != null) |
| { |
| // 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 = def; |
| if (fdef.getParent() != null && fdef.getParent() instanceof ClassDefinition) |
| { |
| // Static initializer is generated but not called for classes without explicit constructor |
| // http://watsonexp.corp.adobe.com/#bug=2984348 |
| final ClassDefinition cdef = (ClassDefinition)fdef.getParent(); |
| if (fdef.isStatic() || cdef.getConstructor() == null) |
| { |
| final String fullName = JSGeneratingReducer.createFullNameFromDefinition(currentScope.getProject(), cdef); |
| if (!fullName.equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| { |
| if (m_sharedData.hasClassInit(fullName)) |
| { |
| final Boolean sameClass = fdef.isStatic() && cdef == m_currentClassDefinition /* |
| * && |
| * cdef |
| * . |
| * getConstructor |
| * ( |
| * ) |
| * != |
| * null |
| */; |
| if (!sameClass) |
| { |
| // constant propagation for initialized members |
| m_hasSideEffects = true; |
| |
| // TODO: If the current function is not a static function and a member of the same class |
| // and we have a ctor, then we know that __staticInit() has been called, because the ctor calls init. |
| // final String pre = "(" + fullName + "." + JSSharedData.STATIC_INIT + "(), "; |
| // final String post = ")"; |
| // return pre + code + post; |
| return code; // AJH remove static inits |
| } |
| } |
| else if (!m_hasSideEffects && fdef.isStatic()) |
| { |
| m_hasSideEffects = true; |
| m_needsSecondPass = true; |
| |
| /* |
| * final ASDefinitionFilter memberFilter = new |
| * ASDefinitionFilter |
| * (ClassificationValue.MEMBERS_AND_TYPES, |
| * SearchScopeValue.INHERITED_MEMBERS, |
| * AccessValue.ALL, cdef); final IDefinition[] |
| * members = |
| * cdef.getAllMembers(currentScope.getProject(), |
| * memberFilter); for( IDefinition member: members ) |
| * { if( member.isStatic() ) { m_hasSideEffects = |
| * true; } } |
| */ |
| } |
| } |
| } |
| } |
| } |
| return code; |
| } |
| |
| /** |
| * resolveGetterName |
| */ |
| |
| private String resolveGetterName(IASNode iNode, String instanceName, Binding member, Boolean useBrackets) |
| { |
| // constant propagation for initialized members |
| m_hasSideEffects = true; |
| |
| IDefinition def = null; |
| final String baseNameWithQuotes = getBasenameFromBinding(member); |
| final String baseName = removeQuotes(baseNameWithQuotes); |
| |
| // In instance.foo, foo is a member of instance's type |
| // In instance["foo"], foo is a member of instance's type |
| // In instance[foo], foo is an expression evaluating to a string, and the *string* is a member of instance's type |
| // NOTE: see similar fix for setters in resolveSetterName() |
| // TODO: warn if !baseNameIsMember and instanceName isn't an Array or Vector -- runtime name lookup can't |
| // handle get_/set_ prefixing properly. (Also need such a warning for "in" keyword and "for each" loops). |
| final boolean baseNameIsMember = !useBrackets || !baseName.equals(baseNameWithQuotes); |
| |
| if (baseNameIsMember) |
| { |
| // TODO: duplicates / very similar to code in getSetterDefinition() |
| /** |
| * RE: baseName.contains(".") In |
| * fl.motion.AnimatorBase.processCurrentFrame() there is this code, |
| * that caused problems: anim.targetParent[anim.targetName] == null |
| * where anim: AnimatorBase targetParent: getter returning |
| * DisplayObjectContainer targetName: getter returning string |
| * FalconJS 314157.0 generated this incorrect code: |
| * (anim.get_targetParent().get_anim.get_targetName()() == null) The |
| * correct code is: (anim.get_targetParent()[anim.get_targetName()] |
| * == null) |
| */ |
| // TODO: push some of this code into a getGetterDefinition() to mirror resolveSetterName() better? |
| if (!baseName.contains(".")) |
| { |
| def = member.getDefinition(); |
| } |
| |
| if (def != null && def instanceof ISetterDefinition) |
| { |
| // def = getDefinitionForInstance(iNode, createFullNameFromDefinition(def), ClassificationValue.GETTERS ); |
| def = getDefinitionForMember(iNode, def.getParent(), baseName, ClassificationValue.GETTERS); |
| } |
| |
| if (def != null && |
| def instanceof IGetterDefinition && |
| !isDataType(def.getParent()) && |
| !isExternalClass(currentScope.getProject(), def.getParent()) && |
| !isVectorLength((IGetterDefinition)def)) |
| { |
| return addStaticInitCall(def, makeGetterCall(currentScope.getProject(), def, instanceName, baseName), true); |
| } |
| |
| // add explicit dependencies. |
| if (def != null && def.getParent() != null && def != m_currentClassDefinition && |
| !isDataType(def.getParent()) && !isExternalClass(currentScope.getProject(), def.getParent())) |
| { |
| m_referencedDefinitions.add(def); |
| } |
| } |
| |
| String ret = ""; |
| String memberName = createStringFromBinding(iNode, member); |
| |
| if (instanceName.isEmpty()) |
| ret = memberName; |
| else |
| { |
| /* AJH disable for now |
| if (needsRuntimeLookup(iNode, instanceName, member)) |
| { |
| String quote = ""; |
| if (baseNameIsMember && isAlphaNumString(baseNameWithQuotes) && !memberName.startsWith("\"")) |
| quote = "\""; |
| else |
| { |
| // this will ensure that converted members are being resolved correctly. |
| final String mappedName = m_nameMap.get(member); |
| if (mappedName != null && !mappedName.isEmpty()) |
| memberName = mappedName; |
| } |
| |
| String self = JSSharedData.THIS; |
| final String currentFullClassName = getCurrentFullClassName(); |
| if (currentFullClassName == null || currentFullClassName.isEmpty()) |
| self = "null"; |
| |
| registerAccessedPropertyName(iNode, memberName); |
| ret = JSSharedData.JS_FRAMEWORK_NAME + ".getProperty(" + self + ", " + instanceName + ", " + quote + memberName + quote + ")"; |
| } |
| else */if (useBrackets) |
| ret = instanceName + "[" + memberName + "]"; |
| else |
| ret = instanceName + "." + memberName; |
| } |
| |
| /* |
| * doesn't work yet and reports too many false negatives. if( |
| * member.getDefinition() == null && !instanceName.equals("arguments") |
| * && !instanceName.equals(JSSharedData.JS_FRAMEWORK_NAME) && |
| * instanceName != null && !instanceName.isEmpty() && |
| * !getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME)) { |
| * instanceName = removeParentheses( instanceName ); String |
| * typeOfInstance = ""; def = getDefinitionForInstance(iNode, |
| * instanceName, ClassificationValue.MEMBERS_AND_TYPES); if( def != null |
| * ) { if( def instanceof GetterDefinition || def instanceof |
| * SetterDefinition ) typeOfInstance = "Function"; else { if( |
| * def.getType() != null && !def.getType().isEmpty() ) def = |
| * getDefinitionForInstance(iNode, def.getType() , |
| * ClassificationValue.MEMBERS_AND_TYPES); if( def != null ) |
| * typeOfInstance = |
| * createFullNameFromDefinition(currentScope.project,def); } } if( |
| * typeOfInstance == null ) typeOfInstance = ""; if( |
| * !isDataType(typeOfInstance) && !isBrowserDataType(typeOfInstance) && |
| * !isReservedDataType(typeOfInstance) ) { ret += |
| * " / * WARNING: cannot resolve type for lval. This expression might call a getter function. " |
| * + "I=" + instanceName + ", M=" + memberName + ", T=" + typeOfInstance |
| * + " * / "; } } |
| */ |
| |
| return addStaticInitCall(def, ret, true); |
| } |
| |
| /** |
| * Returns true if the given accrssor is the getter or setter for |
| * Vector.length. We use this to avoid FJS-27: Vector is translated to JS |
| * Array, where length is a plain old field and thus shouldn't have a |
| * get_/set_ prefix. |
| */ |
| private boolean isVectorLength(IAccessorDefinition def) |
| { |
| // Is accessor for a property called "length"? |
| if (def.getBaseName().equals("length")) |
| { |
| // Is "length" property a member of Vector? |
| IDefinition classContainingProp = def.getParent(); |
| boolean isClassVector = classContainingProp instanceof IAppliedVectorDefinition; // Vector of Objects |
| if (!isClassVector) |
| { |
| // Vector of int/uint has a different definition under the hood (though this may be a bug) |
| String className = classContainingProp.getBaseName(); |
| isClassVector = className.startsWith("Vector$"); |
| } |
| return isClassVector; |
| } |
| return false; |
| } |
| |
| /* |
| * private Boolean isTopLevelClass( String name ) { return name.equals( |
| * "Math" ) || name.equals( "Date" ) ; } private Boolean isTopLevelFunction( |
| * String name ) { return name.equals( "parseInt" ) || name.equals( |
| * "parseFloat" ) || name.equals( "setInterval" ) || name.equals( |
| * "clearInterval" ) || name.equals( "setTimeout" ) || name.equals( |
| * "clearTimeout" ) || name.equals( "isNaN" ) || name.equals( "isFinite" ) |
| * || name.equals( "escape" ) || name.equals( "unescape" ) || name.equals( |
| * "decodeURIComponent" ) || name.equals( "encodeURIComponent" ); } private |
| * Boolean isLocal( Name name ) { LexicalScope scope = currentScope; while( |
| * scope != null && scope.traitsVisitor instanceof JSTraits ) { JSTraits |
| * traits = (JSTraits)scope.traitsVisitor; if( traits.hasLocalVariable( |
| * getBasenameFromName(name) ) ) return true; scope = scope.enclosingFrame; |
| * } return false; } |
| */ |
| |
| private Name getNameFromBinding(Binding binding) |
| { |
| if (binding == null) |
| throw JSSharedData.backend.createException("invalid binding"); |
| |
| /* |
| * this in anonymous functions and ctor functions. If code in anonymous |
| * functions or ctor functions use "this" explicitly then we should emit |
| * "this" and not "self". Furthermore anonymous functions should not |
| * declare "var self = this;" at the begin of the function. See |
| * https://zerowing.corp.adobe.com/display/htmldevtools/FalconJS+Bugs |
| */ |
| if (binding.getName() == null) |
| { |
| final IDefinition def = binding.getDefinition(); |
| if (def != null) |
| { |
| /* |
| * ICompilerProject project = currentScope.getProject(); |
| * ASDefinitionCache cache = currentScope.getDefinitionCache(); |
| * Name defName = def.getMName(project, |
| * currentScope.getProblems()); |
| */ |
| return makeName(def.getQualifiedName()); |
| } |
| |
| final Boolean isAnonymousFunction = isAnonymousFunction(getMethodName()); |
| if (isAnonymousFunction) |
| return makeName(JSSharedData.JS_THIS); |
| else |
| return makeName(JSSharedData.THIS); |
| } |
| |
| // Bindings can have Definitions. |
| // REVISIT: Doesn't work yet. Interfers with nameToString(). |
| /* |
| * final IDefinition def = binding.getDefinition(); if( def != null ) { |
| * return makeName( |
| * createFullNameFromDefinition(currentScope.project,def) ); } |
| */ |
| |
| //if( getBasenameFromBinding(binding).equals("...") ) |
| // throw new Error( "Invalid binding: ellipsis has ot been converted to Array." ); |
| |
| // TODO: cleanup |
| // if( binding.getName().getQualifiers().length() > 1 ) |
| // throw new Error( "Invalid binding: name has more than one qualifier." ); |
| |
| return binding.getName(); |
| } |
| |
| public static String getBasenameFromName(Name name) |
| { |
| if (name == null) |
| return JSSharedData.JS_THIS; |
| |
| // support for vectors. |
| final String baseName = name.getBaseName(); |
| if (baseName == null && isVectorTypeName(name)) |
| { |
| String param = name.getTypeNameParameter().getBaseName(); |
| if (m_typeMap.containsKey(param)) |
| return "Array.<" + m_typeMap.get(param) + ">"; |
| return "Array.<" + param + ">"; |
| } |
| |
| if (baseName.equals("...")) |
| return "Array"; |
| return baseName; |
| } |
| |
| public static String getBasenameFromBinding(Binding binding) |
| { |
| if (binding == null) |
| return JSSharedData.THIS; |
| return getBasenameFromName(binding.getName()); |
| } |
| |
| private String bindingToString(IASNode iNode, Binding binding, Boolean withThis, Boolean resolve) |
| { |
| final Name name = getNameFromBinding(binding); |
| |
| IDefinition def = binding.getDefinition(); |
| if (def == null) |
| def = getDefinitionForName(iNode, name); |
| if (def == null) |
| { |
| // see superGetter_setterDef testcase |
| // The basename can be something like goog.base(...) |
| final String baseName = getBasenameFromName(name); |
| if (baseName.contains("(") && baseName.contains(")")) |
| return baseName; |
| |
| final Boolean isAnonymousFunction = isAnonymousFunction(getMethodName()); |
| if (isAnonymousFunction) |
| return JSSharedData.JS_THIS; |
| else |
| return JSSharedData.THIS; |
| } |
| |
| return nameToString(iNode, def, name); |
| |
| /* |
| * final Name name = getNameFromBinding( binding ); if( name == null ) |
| * return JSSharedData.THIS; return nameToString(iNode, name, withThis, |
| * resolve); |
| */ |
| } |
| |
| private static INamespaceDefinition getNamespaceDefinition(ASScope asScope, Name name) |
| { |
| if (name.getQualifiers() == null || name.getQualifiers().length() != 1) |
| return null; |
| |
| final Namespace sq = name.getSingleQualifier(); |
| if (sq.getKind() != ABCConstants.CONSTANT_Namespace) |
| return null; |
| |
| final INamespaceDefinition ns = JSSharedData.instance.getNamespace(sq.getName()); |
| |
| // Workaround for Falcon bug: Namespace dependencies are not correctly handled. |
| /* |
| * if( ns == null && sq.getName().startsWith( "http://" ) ) { |
| * m_needsSecondPass = true; } |
| */ |
| return ns; |
| } |
| |
| public static IDefinition getDefinitionForVectorName(ICompilerProject project, Name name) |
| { |
| IDefinition def = null; |
| if (name.getTypeNameBase() != null) |
| { |
| // Vector |
| final Nsset nsset = name.getTypeNameBase().getQualifiers(); |
| if (nsset != null) |
| { |
| final IDefinition definitionContext = null; |
| final ASDefinitionFilter filter = new ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, SearchScopeValue.ALL_SCOPES, AccessValue.ALL, definitionContext); |
| |
| for (Namespace ns : nsset) |
| { |
| String defParam = name.getTypeNameParameter().getBaseName(); |
| String defName = ns.getName() + ".Vector$"; |
| if (defParam == null) |
| defName += "object"; |
| else if (defParam.equals("Number")) |
| defName += "double"; |
| else if (defParam.equals("int")) |
| defName += "int"; |
| else if (defParam.equals("uint")) |
| defName += "uint"; |
| else |
| defName += "object"; |
| def = ASScopeUtils.findDefinitionByName(project.getScope(), project, defName, filter); |
| if (def != null) |
| break; |
| } |
| } |
| } |
| return def; |
| } |
| |
| /** |
| * Warning: this may return an AmbiguousName if name has a package |
| * qualifier. TODO: should probably fix this, since it's bug-prone (see |
| * FJS-44 & FJS-62) |
| */ |
| public static IDefinition getDefinitionForNameByScope(ICompilerProject project, IASScope scope, Name name) |
| { |
| // Tamarin's ecma3/Exceptions/global_001_rt.as triggers NPE |
| // http://watsonexp.corp.adobe.com/#bug=3026511 |
| if (name == null) |
| return null; |
| |
| IDefinition def = JSSharedData.instance.getDefinition(name); |
| if (def != null) |
| return def; |
| |
| if (name.getTypeNameBase() != null) |
| return getDefinitionForVectorName(project, name); |
| |
| final String filePrivateNS = "FilePrivateNS:"; |
| |
| final String baseName = getBasenameFromName(name); |
| final IDefinition definitionContext = null; |
| final ASDefinitionFilter filterClassAndInterfaces = new ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, SearchScopeValue.ALL_SCOPES, AccessValue.ALL, definitionContext); |
| final Nsset nsset = name.getQualifiers(); |
| for (Namespace ns : nsset) |
| { |
| String defName = ns.getName(); |
| |
| // find definition |
| if (ns.getKind() == CONSTANT_PrivateNs && defName.startsWith(filePrivateNS)) |
| { |
| // find the def that is the owner of filePrivateNS |
| defName = defName.substring(filePrivateNS.length()); |
| def = ASScopeUtils.findDefinitionByName(scope, project, defName, filterClassAndInterfaces); |
| |
| final ASDefinitionFilter filterPrivate = new ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, SearchScopeValue.CONTAINING_SCOPES, AccessValue.ALL, def); |
| def = ASScopeUtils.findDefinitionByName(scope, project, name.getBaseName(), filterPrivate); |
| |
| if (def == null) |
| def = AmbiguousDefinition.get(); |
| } |
| else if (ns.getKind() == CONSTANT_PrivateNs) |
| { |
| final ASDefinitionFilter filterPrivate = new ASDefinitionFilter(ClassificationValue.VARIABLES_AND_FUNCTIONS, SearchScopeValue.CONTAINING_SCOPES, AccessValue.ALL, definitionContext); |
| defName += "." + name.getBaseName(); |
| def = ASScopeUtils.findDefinitionByName(scope, project, defName, filterPrivate); |
| if (def == null && scope instanceof ASScopeBase) |
| { |
| final ASScope asScope = (ASScope)scope; |
| List<IDefinition> definitions = new ArrayList<IDefinition>(1); |
| Set<INamespaceDefinition> namespaceSet = null; |
| namespaceSet = new HashSet<INamespaceDefinition>(1); |
| INamespaceDefinition namespaceRef = NamespaceDefinition.createNamespaceDefinition(ns); |
| namespaceSet.add(namespaceRef); |
| asScope.getLocalProperty(project, definitions, name.getBaseName(), namespaceSet); |
| if (definitions.size() == 1) |
| def = definitions.get(0); |
| } |
| } |
| else |
| { |
| if (defName.isEmpty()) |
| { |
| def = ASScopeUtils.findDefinitionByName(scope, project, baseName, filterClassAndInterfaces); |
| |
| // this doesn't work but it should |
| if (def == null) |
| { |
| // final ASDefinitionFilter filterAll = new ASDefinitionFilter(ClassificationValue.ALL, SearchScopeValue.ALL_SCOPES, AccessValue.ALL, def); |
| // def = scope.findDefinitionByName(baseName, filterAll, cache ); |
| |
| // workaround for Falcon bug, because the code above is not finding any local variables. |
| if (def == null && scope instanceof ASScope) |
| { |
| final ASScope asScope = (ASScope)scope; |
| final INamespaceDefinition ins = getNamespaceDefinition(asScope, name); |
| String _name = removeParentheses(baseName); |
| |
| // TODO: revisit |
| if (_name.contains(".")) |
| _name = _name.substring(_name.lastIndexOf(".") + 1); |
| |
| if (ins != null) |
| def = asScope.findPropertyQualified(project, ins, _name, DependencyType.INHERITANCE); |
| else |
| def = asScope.findProperty(project, _name, DependencyType.INHERITANCE); |
| } |
| } |
| } |
| else |
| { |
| defName += "." + name.getBaseName(); |
| def = ASScopeUtils.findDefinitionByName(scope, project, defName, filterClassAndInterfaces); |
| // workaround for Falcon bug, because the code above is not finding any local variables. |
| if (def == null && scope instanceof ASScope) |
| { |
| final ASScope asScope = (ASScope)scope; |
| final INamespaceDefinition ins = getNamespaceDefinition(asScope, name); |
| String _name = removeParentheses(baseName); |
| |
| // TODO: revisit |
| if (_name.contains(".")) |
| _name = _name.substring(_name.lastIndexOf(".") + 1); |
| |
| if (ins != null) |
| def = asScope.findPropertyQualified(project, ins, _name, DependencyType.INHERITANCE); |
| else |
| def = asScope.findProperty(project, _name, DependencyType.INHERITANCE); |
| } |
| } |
| } |
| if (def != null) |
| break; |
| } |
| |
| /* |
| * if( scope instanceof ASScope ) { final ASScope asScope = |
| * (ASScope)scope; final INamespaceDefinition ins = |
| * getNamespaceDefinition(asScope, name); String _name = |
| * removeParentheses(getBasenameFromName(name)); // TODO: revisit if( |
| * _name.contains(".") ) _name = _name.substring( _name.lastIndexOf(".") |
| * + 1 ); if( ins != null ) def = asScope.findPropertyQualified(project, |
| * ins, _name, DependencyType.INHERITANCE ); else def = |
| * asScope.findProperty(project, _name, DependencyType.INHERITANCE ); } |
| * else if( scope instanceof ASProjectScope ) { final ASProjectScope |
| * projectScope = (ASProjectScope)scope; final IDefinition |
| * definitionContext = null; final ASDefinitionFilter filter = new |
| * ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, |
| * SearchScopeValue.ALL_SCOPES, AccessValue.ALL, definitionContext); |
| * final Nsset nsset = name.getQualifiers(); for( Namespace ns: nsset ) |
| * { String defName = ns.getName(); // find definition if( ns.getKind() |
| * == CONSTANT_PrivateNs && defName.startsWith(filePrivateNS) ) { // |
| * find the def that is the owner of filePrivateNS defName = |
| * defName.substring( filePrivateNS.length() ); def = |
| * projectScope.findDefinitionByName(defName, filter, cache ); final |
| * ASDefinitionFilter filterPrivate = new |
| * ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, |
| * SearchScopeValue.CONTAINING_SCOPES, AccessValue.ALL, def); def = |
| * projectScope.findDefinitionByName(name.getBaseName(), filterPrivate, |
| * cache ); if( def == null ) def = AmbiguousDefinition.get(); } else { |
| * if( !defName.isEmpty()) defName += "."; defName += |
| * name.getBaseName(); def = projectScope.findDefinitionByName(defName, |
| * filter, cache ); } if( def != null ) break; } } |
| */ |
| |
| // search for name in the currentScope |
| /* |
| * if( def == null && currentScope != null && currentScope.traitsVisitor |
| * != null && currentScope.traitsVisitor.getTraits() != null ) { for( |
| * Trait t : currentScope.traitsVisitor.getTraits() ) { Name n = |
| * t.getNameAttr("name"); if( getBasenameFromName(name).equals( |
| * getBasenameFromName(n)) ) { } } } |
| */ |
| |
| if (def instanceof AmbiguousDefinition) |
| { |
| final String fullName = getBasenameFromName(name); |
| |
| IDefinition foundDef = JSSharedData.instance.getDefinition(fullName); |
| if (foundDef == null) |
| foundDef = JSSharedData.instance.getDefinition(name); |
| if (foundDef != null) |
| def = foundDef; |
| } |
| |
| return def; |
| } |
| |
| public static IASScope getScopeFromNode(IASNode iNode) |
| { |
| NodeBase id = (NodeBase)iNode; |
| |
| // protected ASScope scope = id.getASScope(); |
| // This is the implementation from NodeBase::getASScope: |
| |
| IScopedNode scopeNode = id.getScopeNode(); // id.getContainingScope(); |
| IASScope scope = scopeNode != null ? scopeNode.getScope() : null; |
| // If the ScopedNode had a null scope, keep looking up the tree until we |
| // find one with a non-null scope. |
| // TODO: Is it a bug that an IScopedNode returns null for it's scope? |
| // TODO: this seems like a leftover from block scoping - for example, a |
| // TODO: ForLoopNode is an IScopedNode, but it doesn't really have a scope |
| while (scope == null && scopeNode != null) |
| { |
| scopeNode = scopeNode.getContainingScope(); |
| scope = scopeNode != null ? scopeNode.getScope() : null; |
| } |
| |
| return scope; |
| } |
| |
| public IDefinition getDefinitionForName(IASNode iNode, Name name) |
| { |
| final IASScope scope = getScopeFromNode(iNode); |
| return getDefinitionForNameByScope(currentScope.getProject(), scope, name); |
| } |
| |
| public static IDefinition getDefinitionForName(ICompilerProject project, Name name) |
| { |
| final IASScope scope = project.getScope(); |
| return getDefinitionForNameByScope(project, scope, name); |
| } |
| |
| private static Boolean isLocalName(IDefinition def) |
| { |
| if (!isGlobalName(def) |
| && (!(def instanceof ClassDefinition) && !(def.getParent() instanceof ClassDefinition)) |
| && (!(def instanceof InterfaceDefinition) && !(def.getParent() instanceof InterfaceDefinition))) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| private static Boolean isGlobalName(IDefinition def) |
| { |
| IDefinition parent = def.getParent(); |
| |
| return parent == null; |
| } |
| |
| private String getPackageNameFromName(IASNode iNode, Name name, Boolean withRoot, Boolean useQualifiers) |
| { |
| if (name == null) |
| return ""; |
| |
| IDefinition def = JSSharedData.instance.getDefinition(name); |
| if (def == null) |
| { |
| def = getDefinitionForNameByScope(currentScope.getProject(), currentScope.getProject().getScope(), name); |
| if (def != null) |
| JSSharedData.instance.registerDefinition(def); |
| } |
| |
| if (def == null) |
| { |
| JSSharedData.instance.getDefinition(name); |
| currentScope.addProblem(new JSInternalCompilerProblem(iNode, "cannot find definition for " + name.toString())); |
| } |
| |
| String packageName = ""; |
| if (!JSSharedData.instance.isExtern(def.getQualifiedName())) |
| { |
| if (withRoot) |
| packageName += JSSharedData.ROOT_NAME; |
| packageName += def.getPackageName(); |
| } |
| |
| /* |
| * Namespace ns = null; if( name.getQualifiers() != null ) { if( |
| * name.getQualifiers().length() == 1 ) { ns = |
| * name.getSingleQualifier(); } else if( useQualifiers ) { for( |
| * Namespace qualifier: name.getQualifiers() ) { if( qualifier.getKind() |
| * == ABCConstants.CONSTANT_PackageNs ) { ns = qualifier; break; } } } } |
| * if( ns == null ) return ""; // {PackageNs:"tests"}::TestBase if( |
| * ns.getKind() == ABCConstants.CONSTANT_Namespace || ns.getKind() == |
| * ABCConstants.CONSTANT_PrivateNs ) return ""; String packageName = |
| * ns.getName(); // File-private namespaces are not valid identifiers |
| * unless we strip out the ":" if |
| * (packageName.startsWith("FilePrivateNS:")) { packageName = |
| * packageName.replaceAll(":", "_"); } if( withRoot && |
| * !packageName.isEmpty() && !JSSharedData.ROOT_NAME.isEmpty() && |
| * !JSSharedData.instance.isExtern(name) ) packageName = |
| * JSSharedData.ROOT_NAME + packageName; |
| */ |
| return packageName; |
| } |
| |
| /* |
| * public String getFullNameFromName( Name name, Boolean withRoot, Boolean |
| * useQualifiers ) { if( name == null ) return null; String packageName = |
| * getPackageNameFromName(name,withRoot,useQualifiers); if( packageName == |
| * null || packageName.isEmpty() ) return getBasenameFromName(name); return |
| * packageName + "." + getBasenameFromName(name); } public String |
| * getFullNameFromName( Name name, Boolean withRoot ) { return |
| * getFullNameFromName(name, withRoot, false); } |
| */ |
| public JSEmitter getEmitter() |
| { |
| if (currentScope != null) |
| { |
| final JSEmitter emitter = (JSEmitter)currentScope.getEmitter(); |
| return emitter; |
| } |
| return null; |
| } |
| |
| public String getCurrentClassName() |
| { |
| final JSEmitter emitter = getEmitter(); |
| if (emitter != null) |
| return emitter.getCurrentClassName(); |
| return null; |
| } |
| |
| public String getCurrentSuperClassName() |
| { |
| final JSEmitter emitter = getEmitter(); |
| if (emitter != null) |
| return emitter.getCurrentSuperClassName(); |
| return null; |
| } |
| |
| public String getCurrentSuperClassFullName() |
| { |
| final JSEmitter emitter = getEmitter(); |
| if (emitter != null) |
| return emitter.getCurrentSuperClassFullName(); |
| return null; |
| } |
| |
| public String getCurrentPackageName() |
| { |
| final JSEmitter emitter = getEmitter(); |
| if (emitter != null) |
| return emitter.getCurrentPackageName(); |
| return null; |
| } |
| |
| public String getCurrentFullClassName() |
| { |
| final JSEmitter emitter = getEmitter(); |
| if (emitter != null) |
| { |
| final String packageName = getCurrentPackageName(); |
| if (packageName != null && !packageName.isEmpty()) |
| return getCurrentPackageName() + "." + emitter.getCurrentClassName(); |
| return emitter.getCurrentClassName(); |
| } |
| return null; |
| } |
| |
| public String getCurrentMethodName() |
| { |
| return currentScope.getMethodInfo().getMethodName(); |
| } |
| |
| public Boolean isCurrentFramework() |
| { |
| final String currentFullClassName = getCurrentFullClassName(); |
| return currentFullClassName != null && getCurrentFullClassName().equals(JSSharedData.JS_FRAMEWORK_NAME); |
| } |
| |
| public static String removeRootName(String fromThisName) |
| { |
| if (JSSharedData.ROOT_NAME.length() > 0) |
| return fromThisName.replaceFirst(JSSharedData.ROOT_NAME, ""); |
| return fromThisName; |
| } |
| |
| public static IFunctionDefinition getCurrentMethod(IASNode currentNode) |
| { |
| // TODO: would it be cleaner/faster to walk up the LexicalScopes instead? |
| IASNode node = currentNode; |
| while (node != null && !(node instanceof IFunctionNode)) |
| { |
| node = node.getParent(); |
| } |
| if (node != null) |
| return ((IFunctionNode)node).getDefinition(); |
| else |
| return null; |
| } |
| |
| private String createFullClassName(Boolean withRoot) |
| { |
| String s = ""; |
| if (withRoot && !JSSharedData.ROOT_NAME.isEmpty() && |
| !getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| { |
| s = JSSharedData.ROOT_NAME; |
| |
| // remove trailing dot |
| if (s.endsWith(".")) |
| s = s.substring(0, s.length() - 1); |
| } |
| String packageName = getCurrentPackageName(); |
| if (packageName != null && packageName.length() > 0) |
| { |
| if (s.length() > 0) |
| s += "."; |
| s += packageName; |
| } |
| |
| final String className = getCurrentClassName(); |
| if (className != null && className.length() > 0) |
| { |
| if (s.length() > 0) |
| s += "."; |
| s += className; |
| } |
| return s; |
| } |
| |
| private String createFullName(String tail, Boolean withRoot) |
| { |
| String s = createFullClassName(withRoot); |
| if (s.length() > 0) |
| s += "."; |
| s += tail; |
| return s; |
| } |
| |
| private String indent() |
| { |
| String s = ""; |
| for (int i = 0; i < m_indent; ++i) |
| s += " "; |
| return s; |
| } |
| |
| public static String indentBlock(String block, int indentBy) |
| { |
| String s = ""; |
| String[] parts = block.split("\n"); |
| for (String part : parts) |
| { |
| for (int i = 0; i < indentBy; ++i) |
| s += " "; |
| |
| s += part + "\n"; |
| } |
| return s; |
| } |
| |
| private String endl() |
| { |
| return "\n"; |
| } |
| |
| // Turns out that other Errors like EvalError |
| // behave like Error, see handleCast |
| private static boolean isErrorDataType(String typeName) |
| { |
| return typeName.equals("Error") || |
| typeName.equals("ArgumentError") || |
| typeName.equals("DefinitionError") || |
| typeName.equals("EvalError") || |
| typeName.equals("RangeError") || |
| typeName.equals("ReferenceError") || |
| typeName.equals("SecurityError") || |
| typeName.equals("SyntaxError") || |
| typeName.equals("TypeError") || |
| typeName.equals("UninitializedError") || |
| typeName.equals("URIError") || |
| typeName.equals("VerifyError"); |
| } |
| |
| private static boolean isComplexDataType(String typeName) |
| { |
| // Object, Array, Date, Error, Function, RegExp, XML, and XMLList. |
| return typeName.equals("Object") || |
| typeName.equals("Array") || |
| typeName.equals("Date") || |
| typeName.equals("Function") || |
| typeName.equals("Class") || |
| typeName.equals("RegExp") || |
| typeName.equals("XML") || |
| typeName.equals("XMLList") || |
| typeName.equals("Math") || |
| isErrorDataType(typeName); |
| } |
| |
| private static boolean isNumberDataType(String typeName) |
| { |
| // int, Number, uint |
| return typeName.equals("int") || |
| typeName.equals("Number") || |
| typeName.equals("uint"); |
| } |
| |
| private static boolean isPrimitveDataType(String typeName) |
| { |
| // Boolean, int, Null, Number, String, uint, and void. |
| return isNumberDataType(typeName) || |
| typeName.equals("Boolean") || |
| typeName.equals("Null") || |
| typeName.equals("IntClass") || |
| typeName.equals("UIntClass") || |
| typeName.equals("String") || |
| typeName.equals("*") || |
| typeName.equals("void"); |
| } |
| |
| public static boolean isReservedDataType(String typeName) |
| { |
| // Boolean, int, Null, Number, String, uint, and void. |
| return typeName.startsWith("com.jquery") || |
| typeName.equals("browser.MathGlue") || |
| typeName.equals("JQuery") || |
| typeName.equals("browser.JSError") || |
| typeName.equals("browser.JQEvent") || |
| typeName.equals("JQEvent") || |
| typeName.equals("jQuery.Event"); |
| } |
| |
| public static boolean isBrowserDataType(String qualifiedClassName) |
| { |
| return qualifiedClassName.startsWith("org.w3c.dom") || |
| qualifiedClassName.startsWith("com.jquery.") || |
| qualifiedClassName.startsWith("jQuery.") || |
| qualifiedClassName.startsWith("goog."); |
| } |
| |
| /* |
| * private boolean needsSelfParameter( IASNode iNode, String className ) { |
| * if( !JSSharedData.m_useSelfParameter ) return false; if( |
| * className.length() == 0 ) return true; if( |
| * className.equals(JSSharedData.JS_FRAMEWORK_NAME) ) return false; if( |
| * className.endsWith(".JSSharedData._SUPER") ) return true; final String |
| * typeName = getTypeOfInstance(iNode, className); if( typeName.length() == |
| * 0 ) return false; if( typeName.equals( "Function" ) ) return true; if( |
| * typeName.equals(JSSharedData.JS_FRAMEWORK_NAME) ) return false; if( |
| * isDataType( typeName ) ) return false; if( isBrowserDataType( typeName ) |
| * ) return false; if( isReservedDataType( typeName ) ) return false; return |
| * true; } |
| */ |
| |
| public static boolean isDataType(String typeName) |
| { |
| return isPrimitveDataType(typeName) || isComplexDataType(typeName) || isReservedDataType(typeName); |
| } |
| |
| public static boolean isDataType(IDefinition def) |
| { |
| // workaround for Falcon bug CMP-984 |
| // DefinitionBase::getContainingToplevelDefinition() runs into an NPE in test case source_level_function |
| try |
| { |
| final String packageName = def.getPackageName(); |
| if (!packageName.isEmpty()) |
| return false; |
| } |
| |
| catch (NullPointerException npe) |
| { |
| return false; |
| } |
| |
| // java.lang.AssertionError: This method should not be called until we have a containing scope.! |
| catch (AssertionError e) |
| { |
| return false; |
| } |
| |
| final String typeName = def.getBaseName(); |
| return isPrimitveDataType(typeName) || isComplexDataType(typeName) || isReservedDataType(typeName); |
| } |
| |
| private IDefinition getSuperClass(ICompilerProject project, IDefinition def) |
| { |
| if (def instanceof ClassDefinition) |
| { |
| final ClassDefinition classDef = (ClassDefinition)def; |
| return classDef.resolveBaseClass(project); |
| } |
| |
| return null; |
| } |
| |
| private String findClassWithMethod(String owner, String methodName) |
| { |
| String _class = owner; |
| while (_class != null && !isDataType(_class)) |
| { |
| String name = makeName(_class + "." + methodName).toString(); |
| if (m_sharedData.hasVarName(name)) |
| { |
| owner = _class; |
| // TODO: revisit |
| if (m_buildPhase.equals("CODEGEN") && owner.equals("void")) |
| return owner; |
| return owner; |
| } |
| |
| _class = getBasenameFromName(m_sharedData.getSuperClass(makeName(_class))); |
| } |
| |
| // TODO: revisit |
| if (m_buildPhase.equals("CODEGEN")) |
| return owner; |
| |
| return owner; |
| } |
| |
| private String generateNewCall(IASNode node, Binding class_name, Vector<String> args) |
| { |
| String _class = ""; |
| final Name _className = class_name.getName(); |
| final String baseName = getBasenameFromBinding(class_name); |
| |
| /* |
| * TODO: e15_4_2_2_2, ecma3/Array/e15_4_2_2_2.abc : expected failure: |
| * (new Array(new Number(1073741823))).length = 1 failed! expected: |
| * 1073741823 reason: JavaScript returns 'typeof(new Number()) = |
| * "object"' ecma3/Array/e15_4_2_2_2.abc : expected failure: (new |
| * Array(new Number(0))).length = 1 failed! expected: 0 reason: |
| * JavaScript returns 'typeof(new Number()) = "object"' |
| * ecma3/Array/e15_4_2_2_2.abc : expected failure: (new Array(new |
| * Number(1000))).length = 1 failed! expected: 1000 reason: JavaScript |
| * returns 'typeof(new Number()) = "object"' |
| */ |
| |
| if (isDataType(baseName)) |
| { |
| // "new int(0)" needs to be translated to "new Number(0)" |
| if (baseName.equals("int")) |
| _class = "Number"; |
| else if (baseName.equals("uint")) |
| _class = "Number"; |
| |
| } |
| else |
| { |
| final IDefinition def = getDefinitionForName(node, _className); |
| |
| // constant propagation for initialized members |
| if (!m_hasSideEffects && hasSideEffects(def)) |
| m_hasSideEffects = true; |
| |
| if (def instanceof ClassDefinition) |
| { |
| // Note: because getDefinitionForName() doesn't handle package qualfiers, ambiguous shortnames |
| // wind up in the catch-all "def != null" case below. |
| _class = createFullNameFromDefinition(currentScope.getProject(), def); |
| if (isExternalClass(currentScope.getProject(), def)) |
| { |
| // Invalid JavaScript code is generated for new DOMParser () |
| // http://watsonexp.corp.adobe.com/#bug=2985815 |
| final IMetaTag extern = def.getMetaTagByName(JSSharedData.EXTERN_METATAG); |
| if (extern != null && |
| extern.getAttributeValue("name") != null && |
| !extern.getAttributeValue("name").isEmpty()) |
| { |
| _class = extern.getAttributeValue("name"); |
| } |
| else |
| { |
| _class = baseName; |
| } |
| } |
| } |
| else if (def instanceof IGetterDefinition || def instanceof ISetterDefinition) |
| { |
| // Property (of type Class or Function) instead of literal type name - resolve into getter call |
| _class = "(" + bindingToString(node, class_name, true, true) + ")"; |
| } |
| else if (def instanceof IFunctionDefinition) |
| { |
| // Function call - "_className" was the original compiled function call expression |
| _class = baseName; |
| } |
| else if (def != null) |
| { |
| // Identifier needing "self." prefix, OR qualified classname where shortname is ambiguous (def is |
| // AmbiguousDefinition) - add "self." prefix and/or resolve into fully-qualified name. |
| _class = bindingToString(node, class_name, true, true); |
| } |
| else |
| { |
| // If we couldn't resolve a def or got one not of the above types, chances are baseName is |
| // actually not a name but an expression... so wrap it in parens to preserve predence vs. 'new' |
| // operator, and then just use the string verbatim. |
| _class = baseName; |
| } |
| } |
| |
| // TODO: do we ever hit this case other than when !isDataType()? (e.g. could this go in an 'else'?) |
| if (_class.isEmpty()) |
| _class = baseName; |
| |
| String result = ""; |
| |
| // final Boolean useNew = getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME) || isDataType(_class) || !m_sharedData.hasClass(removeRootName(_class)); /*|| !needsSelfParameter(_class)*/; |
| Boolean useNew = JSSharedData.m_useClosureLib || getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME) || isDataType(_class); // || !m_sharedData.hasClass(removeRootName(_class)); /*|| !needsSelfParameter(_class)*/; |
| |
| if (!useNew) |
| useNew = args.size() > 1; |
| if (useNew) |
| result += "(new " + _class + "("; |
| else |
| { |
| result += JSSharedData.JS_FRAMEWORK_NAME + ".newObject(" + _class + ", ["; |
| } |
| |
| result += collectCallParameters(node, "", null, args, false, ""); |
| |
| if (useNew) |
| { |
| result += ")"; // closes ctor parameters |
| result += ")"; // closes (new ...) |
| } |
| else |
| { |
| result += "]"; // closes ctor parameters |
| result += ")"; // closes newObject(...) |
| } |
| |
| // Symbol support |
| if (m_sharedData.hasSymbols() && !_class.isEmpty()) |
| { |
| final String symbolName = m_sharedData.getSymbol(_class); |
| if (symbolName != null && !symbolName.isEmpty()) |
| result = symbolName + "." + JSSharedData.SYMBOL_INSTANCE + "." + JSSharedData.SYMBOL_INIT + "(" + result + ")"; |
| } |
| |
| /* |
| * if( JSSharedData.m_useClosureLib && getCurrentClassName() != null && |
| * !getCurrentClassName().equals(JSSharedData.JS_FRAMEWORK_NAME) && |
| * !isDataType(_class)) { result = "adobe.addClassInfo(" + result + ", " |
| * + _class + ")"; } |
| */ |
| return result; |
| } |
| |
| private Boolean isConvertedFunction(IDefinition def) |
| { |
| if (def instanceof FunctionDefinition) |
| { |
| if (this.m_convertedMembers.contains(def.getBaseName())) |
| return true; |
| |
| final IDefinition parent = def.getParent(); |
| if (parent != null && parent instanceof FunctionDefinition) |
| { |
| final IMetaTag metaTag = parent.getMetaTagByName(JSSharedData.CONVERT_LOCAL_VARS_TO_MEMBERS); |
| if (metaTag != null) |
| { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| private String injectCreateProxy(IASNode node, IDefinition argDef, String arg) |
| { |
| if (isCurrentFramework()) |
| return arg; |
| |
| if (argDef == null) |
| { |
| // see test case createProxyForArrayItems(): |
| // const arr : Array = [new Test()]; |
| // callWithFunctionParameter( arr[0].createProxyForArrayItems ); |
| /* |
| * if( arg.startsWith("adobe.getProperty(" )) arg = |
| * arg.replaceFirst("getProperty", "getPropertyAsProxy" ); |
| */ |
| /* |
| * if( node instanceof IFunctionCallNode ) { final IFunctionCallNode |
| * fnode = (IFunctionCallNode)node; final IExpressionNode[] pnode = |
| * fnode.getArguments(); for( IExpressionNode anode: pnode ) { |
| * IDefinition def = anode.getDefinition(); if(def == null && anode |
| * instanceof IMemberAccessExpressionNode ) { |
| * IMemberAccessExpressionNode man = |
| * (IMemberAccessExpressionNode)anode; def = |
| * getDefinitionForNode(man.getLeft(),false); def = null; Object |
| * result = JSGenerator.generateParameterExpression(man.getLeft(), |
| * currentScope.getProject()); result = null; } } } |
| */ |
| return arg; |
| } |
| |
| // tamarin_ecma3_Array_e15_4_2_2_2() |
| final String argType = argDef.getTypeAsDisplayString(); |
| if (!argType.equals("Function")) |
| { |
| if (arg.contains("new Number(") && argDef == currentScope.getProject().getBuiltinType(BuiltinType.NUMBER)) |
| arg = "(" + arg + ").valueOf()"; |
| else if (arg.contains("new String(") && argDef == currentScope.getProject().getBuiltinType(BuiltinType.STRING)) |
| arg = "(" + arg + ").toString()"; |
| return arg; |
| } |
| |
| final String argWithoutThis = arg.replaceFirst(JSSharedData.THIS + ".", ""); |
| |
| IDefinition def = null; |
| String _stem = null; |
| String _member = null; |
| if (arg.startsWith("[") || arg.endsWith("]")) |
| { |
| /* |
| * Array params need to be ignored. Handles this test case: private |
| * function callFuncWithArrayParam( arr : Array ) : void { const |
| * localFunction = function():void { const num : uint = 13; |
| * funcWithArrayParam( [num] ); } } |
| */ |
| } |
| else if (arg.startsWith("/** @type {function(")) |
| { |
| /* |
| * Function params need to be ignored. i.e.: |
| * pgt.PagedView.moveBackward(); dl.then( |
| * function(gotToThePage:Boolean):void { ... } ); |
| */ |
| |
| } |
| else if (arg.startsWith("adobe.createProxy(")) |
| { |
| // adobe.createProxy() is currently a public method, |
| // which we need to exclude from proxying. |
| } |
| else if (arg.startsWith("adobe.getProperty(")) |
| { |
| // see test case createProxyForArrayItems(): |
| // const arr : Array = [new Test()]; |
| // callWithFunctionParameter( arr[0].createProxyForArrayItems ); |
| |
| arg = arg.replaceFirst("getProperty", "getPropertyAsProxy"); |
| } |
| else if (arg.equals(JSSharedData.JS_THIS)) |
| { |
| // we need to exclude "this" in anonymous functions from proxying. |
| } |
| else if (argWithoutThis.contains(".")) |
| { |
| _stem = argWithoutThis.substring(0, argWithoutThis.lastIndexOf(".")); |
| _member = argWithoutThis.substring(argWithoutThis.lastIndexOf(".") + 1); |
| def = this.getDefinitionForName(node, makeName(_stem)); |
| if (def instanceof ClassDefinition) |
| _stem = createFullNameFromDefinition(currentScope.getProject(), def); |
| } |
| else |
| { |
| def = this.getDefinitionForName(node, makeName(argWithoutThis)); |
| |
| if (_stem == null && _member == null) |
| { |
| // FalconJS needs to inject adobe.createProxy() for source-level functions, which get promoted to methods. |
| // (source level-functions and anonymous functions are usually local and should not be proxy'ed. But promoted SLFs are obviously an exception. |
| if (def == null || convertLocalVarsToMembers(node) || isConvertedFunction(def) || (!isLocalName(def) && !isGlobalName(def))) |
| { |
| _stem = JSSharedData.THIS; |
| _member = argWithoutThis; |
| } |
| } |
| } |
| |
| if (_stem != null && !_stem.equals(JSSharedData.JS_FRAMEWORK_NAME) && _member != null && !_member.contains("(")) |
| { |
| String type = this.getTypeOfInstance(node, argWithoutThis); |
| if (type.equals("Function")) |
| { |
| // we need to check for vectors, because vectors get translated to arrays. |
| // See tests/testceases/vectors. We now correctly identify foo.length as a function. |
| // But we don't want to inject createProxy(), because Array.length is native. |
| IDefinition idef = getDefinitionForInstance(node, _stem, ClassificationValue.CLASSES); |
| if (idef == null || !(idef instanceof AppliedVectorDefinition)) |
| { |
| idef = getDefinitionForInstance(node, _stem + "." + _member, ClassificationValue.MEMBERS_AND_TYPES); |
| if (idef == null || !isExternalClass(currentScope.getProject(), idef.getParent())) |
| { |
| if (def instanceof GetterDefinition) |
| arg = resolveGetterName(node, _stem, makeMemberBinding(node, _stem, _member), false); |
| else if (!isDataType(def)) |
| { |
| // arg = JSSharedData.JS_FRAMEWORK_NAME + ".createProxy(" + nameToString(node, makeName(_stem), true, true) + ",\"" + _member + "\")"; |
| if (!_stem.equals(JSSharedData.THIS) && !_stem.contains("(")) |
| { |
| _stem = nameToString(node, makeName(_stem), true, true); |
| } |
| arg = JSSharedData.JS_FRAMEWORK_NAME + ".createProxy(" + _stem + ", " + _stem + "." + _member + ")"; |
| } |
| } |
| } |
| } |
| } |
| |
| return arg; |
| } |
| |
| private String collectCallParameters(IASNode node, String stem, Binding method_name, Vector<String> args, Boolean addSelfParameter, String extraParams) |
| { |
| String result = ""; |
| |
| final String basename = getBasenameFromBinding(method_name); |
| |
| // FalconJS needs to inject adobe.createProxy() for source-level functions, which get promoted to methods. |
| // (source level-functions and anonymous functions are usually local and should not be proxy'ed. But promoted SLFs are obviously an exception. |
| // final Boolean convertLocalVarsToMembers = convertLocalVarsToMembers(node); |
| |
| if (args.size() == 2 && stem.equals(JSSharedData.JS_FRAMEWORK_NAME) && basename.equals("createProxy")) |
| { |
| result = args.get(0) + ", " + args.get(1); |
| return result; |
| } |
| |
| // TypeError: Result of expression 'self.m_displayObject' [undefined] is not an object. |
| // if( m_buildPhase.equals("CODEGEN") && stem.contains("sprite.get_graphics") && method_name != null && method_name.toString().contains("beginFill")) |
| // getTypeOfInstance(stem); |
| |
| // avmplus.test.acceptance.shell.AddTestCase |
| // TypeError: Result of expression '_self.getTestCaseResult' [undefined] is not a function. |
| |
| if (addSelfParameter) |
| result = JSSharedData.THIS; |
| /* |
| * if( addSelfParameter && needsSelfParameter(node, |
| * createFullClassName(false)) ) { final String methodName = method_name |
| * != null ? bindingToString(node, method_name,false,true) : ""; if( |
| * !methodName.equals( "new" ) && !methodName.equals( "delete" ) ) { if( |
| * stem.length() > 0 ) { if( needsSelfParameter(node, stem) ) { if( |
| * stem.startsWith( "self.JSSharedData._SUPER" ) ) result = |
| * stem.replaceFirst( "self.JSSharedData._SUPER", "self" ); else result |
| * = stem; } } else { if( needsSelfParameter(node, methodName) || |
| * method_name.toString().equals("{}::super") ) result = |
| * JSSharedData.THIS; else { String s = bindingToString(node, |
| * method_name,true,true); if( s.startsWith(JSSharedData.THIS +".")) s = |
| * s.replaceFirst( JSSharedData.THIS, createFullClassName(false) ); else |
| * s = removeRootName(methodName); // s = makeName(s).toString(); s = |
| * getTypeForMember( node, extractPackageName(s), extractClassName(s) ); |
| * if( s.length() > 0 ) result = JSSharedData.THIS; } } } } |
| */ |
| |
| if (args.size() == 0 && extraParams.isEmpty()) |
| return result; |
| |
| // using types from arg nodes. |
| if (!(node instanceof IFunctionCallNode)) |
| currentScope.addProblem(new JSInternalCompilerProblem(node, "this node is not a function node")); |
| final IFunctionCallNode fnode = (IFunctionCallNode)node; |
| final IDefinition calledDef = fnode.resolveCalledExpression(currentScope.getProject()); |
| final List<IDefinition> argDefs = new ArrayList<IDefinition>(); |
| /* |
| * if( calledDef instanceof VariableDefinition ) calledDef = |
| * calledDef.resolveType(currentScope.getDefinitionCache()); if( |
| * !(calledDef instanceof IFunctionDefinition) ) |
| * currentScope.getProblems().add( new JSInternalCompilerProblem(iNode, |
| * "Can't get called function of " + node.toString() ); final |
| * IFunctionDefinition fdef = (IFunctionDefinition)calledDef; for( |
| * IParameterDefinition param: fdef.getParameters() ) { IDefinition def |
| * = null; IVariableNode vnode = param.getVariableNode(); if( vnode != |
| * null ) def = vnode.getDefinition(); if( def == null ) { final String |
| * type = param.getType(); if( |
| * type.equals(IASLanguageConstants.ANY_TYPE) ) def = |
| * currentScope.getDefinitionCache |
| * ().getBuiltinType(BuiltinType.ANY_TYPE); else if( |
| * type.equals(IASLanguageConstants.Array) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.ARRAY); |
| * else if( type.equals(IASLanguageConstants.Boolean) ) def = |
| * currentScope |
| * .getDefinitionCache().getBuiltinType(BuiltinType.BOOLEAN); else if( |
| * type.equals(IASLanguageConstants.Class) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.CLASS); |
| * else if( type.equals(IASLanguageConstants.Function) ) def = |
| * currentScope |
| * .getDefinitionCache().getBuiltinType(BuiltinType.FUNCTION); else if( |
| * type.equals(IASLanguageConstants._int) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.INT); |
| * else if( type.equals(IASLanguageConstants.Namespace) ) def = |
| * currentScope |
| * .getDefinitionCache().getBuiltinType(BuiltinType.NAMESPACE); else if( |
| * type.equals(IASLanguageConstants.Null) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.NULL); |
| * else if( type.equals(IASLanguageConstants.Number) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.NUMBER); |
| * else if( type.equals(IASLanguageConstants.Object) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.OBJECT); |
| * else if( type.equals(IASLanguageConstants.QName) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.QNAME); |
| * else if( type.equals(IASLanguageConstants.RegExp) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.REGEXP); |
| * else if( type.equals(IASLanguageConstants.String) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.STRING); |
| * else if( type.equals(IASLanguageConstants.Undefined) ) def = |
| * currentScope |
| * .getDefinitionCache().getBuiltinType(BuiltinType.UNDEFINED); else if( |
| * type.equals(IASLanguageConstants.uint) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.UINT); |
| * else if( type.equals(IASLanguageConstants.Vector) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.VECTOR); |
| * else if( type.equals(IASLanguageConstants.void_) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.VOID); |
| * else if( type.equals(IASLanguageConstants.XML) ) def = |
| * currentScope.getDefinitionCache().getBuiltinType(BuiltinType.XML); |
| * else if( type.equals(IASLanguageConstants.XMLList) ) def = |
| * currentScope |
| * .getDefinitionCache().getBuiltinType(BuiltinType.XMLLIST); else def = |
| * null; } if( def != null ) def = |
| * def.resolveType(currentScope.getDefinitionCache()); if( def == null ) |
| * currentScope.getProblems().add( new JSInternalCompilerProblem(iNode, |
| * "Can't resolve type of parameter for " + node.toString() ); |
| * argDefs.add(def); } |
| */ |
| |
| IParameterDefinition[] params = null; |
| if (calledDef instanceof IFunctionDefinition) |
| { |
| final IFunctionDefinition fdef = (IFunctionDefinition)calledDef; |
| params = fdef.getParameters(); |
| } |
| |
| // poor man's type inference. |
| if (calledDef == null) |
| { |
| final Set<IFunctionDefinition> methods = m_sharedData.getMethods(currentScope.getProject(), basename); |
| if (methods.size() == 1) |
| params = methods.iterator().next().getParameters(); |
| } |
| |
| int nthArg = 0; |
| final IExpressionNode[] pnode = fnode.getArgumentNodes(); |
| for (IExpressionNode anode : pnode) |
| { |
| IDefinition def = anode.resolve(currentScope.getProject()); |
| if (def == null && params != null && nthArg < params.length) |
| { |
| final IVariableNode vnode = params[nthArg].getVariableNode(); |
| if (vnode != null) |
| { |
| def = vnode.getDefinition(); |
| } |
| } |
| |
| /* |
| * class method registered as eventListener gets global when class |
| * instance is in an array If def is null we were not able to |
| * identify the signature of this parameter. Our second attempt is |
| * trying to infer the type of the parameter by its value. |
| */ |
| if (def == null && anode instanceof IMemberAccessExpressionNode) |
| { |
| final IMemberAccessExpressionNode mnode = (IMemberAccessExpressionNode)anode; |
| final IASNode right = mnode.getRightOperandNode(); |
| if (right != null && right instanceof IIdentifierNode) |
| { |
| final IIdentifierNode inode = (IIdentifierNode)right; |
| final Set<IFunctionDefinition> methods = m_sharedData.getMethods(currentScope.getProject(), inode.getName()); |
| if (methods != null && methods.size() == 1) |
| def = methods.iterator().next(); |
| } |
| } |
| |
| argDefs.add(def); |
| ++nthArg; |
| } |
| |
| nthArg = 0; |
| boolean comma = result.length() > 0; |
| |
| if (!extraParams.isEmpty()) |
| { |
| if (comma) |
| result += ", "; |
| else |
| comma = true; |
| result += extraParams; |
| } |
| |
| for (String arg : args) |
| { |
| if (comma) |
| result += ", "; |
| else |
| comma = true; |
| |
| arg = injectCreateProxy(node, argDefs.get(nthArg), stripTabs(arg)); |
| result += arg; |
| ++nthArg; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Transform a list of qualified name parts into a name. Resolving the name |
| * may yield a QName or a multiname, depending on how it was resolved. TODO: |
| * To be replaced by annotations on reference ASTs. |
| * |
| * @return the resolved name. |
| */ |
| /* |
| * Name resolveName(Vector<? extends Object> qualifiers ) { if ( 1 == |
| * qualifiers.size() ) { // No qualifiers, just the base name. return |
| * currentScope.resolveName(qualifiers.firstElement().toString()); } else { |
| * StringBuffer buf = new StringBuffer(); for ( int i = 0; i < |
| * qualifiers.size() - 1; i++ ) { if ( buf.length() > 0 ) buf.append('.'); |
| * buf.append(qualifiers.elementAt(i).toString()); } String base_name = |
| * (String)qualifiers.lastElement(); return new Name(new Nsset(new |
| * Namespace(CONSTANT_PackageNs, buf.toString())), base_name); } } |
| */ |
| /* |
| * public String generateFunctionBody(String function_body) { return |
| * generateFunctionBody(function_body,null); } |
| */ |
| |
| /** |
| * Generate boilerplate function prolog/epilog code. |
| * |
| * @param block - the actual CFG. |
| * @param return_type - the function's return type. Not presently used. |
| */ |
| public String generateFunctionBody(IASNode iNode, String function_body, Binding returnType, Boolean uuu) |
| { |
| final Name return_type = returnType == null ? null : returnType.getName(); |
| final MethodInfo mi = currentScope.getMethodInfo(); |
| final String methodName = getMethodName(); |
| final Boolean isAnonymousFunction = isAnonymousFunction(methodName); |
| final Boolean isCtor = methodName != null && methodName.equals(getCurrentClassName()); |
| |
| if (mi != null && return_type != null && mi.getReturnType() == null) |
| { |
| mi.setReturnType(return_type); |
| } |
| |
| if (return_type != null) |
| usedTypes.add(getBasenameFromBinding(returnType)); |
| |
| String result = ""; // indent() + "{" + endl(); |
| |
| /* |
| * !isAnonymousFunction is necessary. AS and JS are different in the way |
| * "this" is being treated in anonymous functions. i.e. public function |
| * whoIsThis() : void { const localFunction : Function = funciton():void |
| * { this.callMe(); }; } In this example this.callMe() refers to the |
| * callMe method of the class. In JS "this" refers to the local |
| * function! By supressing "var self = this;" for anonymous functions |
| * we'll emulate the AS behavior. The JS result then looks like this: |
| * tests.Test.whoIsThis.prototype = function() { var self = this; const |
| * localFunction : Function = funciton():void { // NOT EMITTED: var self |
| * = this; self.callMe(); }; }; |
| */ |
| if (!JSSharedData.m_useSelfParameter && !isAnonymousFunction && |
| /* TODO: !needsSelfParameter(createFullClassName(false)) && */function_body.contains(JSSharedData.THIS)) |
| { |
| // final Binding fullClassName = makeBinding(createFullClassName(false)); |
| // this.registerLocalVariable(currentScope, makeBinding("self"), fullClassName, fullClassName); |
| |
| final Binding fullClassName = new Binding(iNode, makeName(createFullClassName(false)), null); |
| this.registerLocalVariable(currentScope, new Binding(iNode, makeName("self"), null), fullClassName, fullClassName); |
| result += indentBlock("/** @type {" + createFullClassName(true) + "} */" + endl(), 1); |
| result += indentBlock("var " + JSSharedData.THIS + " = this;" + endl(), 1); |
| } |
| |
| // Constructor-specific processing: add the instance initializers, |
| // add a constructsuper call if none exists. |
| if (haveAPrioriInstructions() && !isAnonymousFunction) |
| { |
| result += indentBlock(aPrioriInstructions, 1); |
| |
| // If this is a constructor and there's no explicit |
| // super() call, synthesize one. |
| // Note that this may be a semantic error if the |
| // superclass' constructor needs arguments. |
| /* |
| * if ( isCtor && !block.contains("super(") ) { // Call the |
| * superclass' constructor after the instance // init instructions; |
| * this doesn't seem like an abstractly // correct sequence, but |
| * it's what ASC does. result += "" // TODO: // |
| * result.addInstruction(OP_getlocal0); // |
| * result.addInstruction(OP_constructsuper, 0); } |
| */ |
| } |
| |
| // http://livedocs.adobe.com/flex/3/html/help.html?content=basic_as_2.html |
| // "If you define the constructor, but omit the call to super(), Flex automatically |
| // calls super() at the beginning of your constructor." |
| if (isCtor) |
| { |
| if (function_body.indexOf(JSSharedData.m_superCalledMarker) >= 0) |
| function_body = function_body.replaceAll(JSSharedData.m_superCalledMarker, ""); |
| else if (!function_body.contains("goog.base(this")) // we're only looking for super(), so ignore adobe.base() case |
| { |
| String derivedFrom = getCurrentSuperClassName(); |
| if (derivedFrom != null && !isDataType(derivedFrom) && !derivedFrom.equals("Globals")) |
| { |
| String superCall = indent(); |
| if (JSSharedData.m_useClosureLib) |
| { |
| // superCall += "goog.base(" + JSSharedData.ROOT_NAME + getCurrentFullClassName() + " /*generateFunctionBody*/"; |
| // superCall += ""goog.base(this"" + JSSharedData.THIS + " /*generateFunctionBody isCtor*/"; |
| superCall += "goog.base(this"; |
| } |
| else |
| { |
| if (m_convertToStatic) |
| { |
| String fullClassName = createFullClassName(false); |
| if (m_sharedData.hasClass(makeName(fullClassName))) |
| { |
| fullClassName = getCurrentSuperClassFullName(); |
| superCall += createFullName(fullClassName, true) + ".init("; |
| } |
| else |
| superCall += createFullClassName(true) + "." + JSSharedData._SUPER + ".init("; |
| } |
| else |
| superCall += JSSharedData.THIS + "." + JSSharedData._SUPER + "("; |
| if (JSSharedData.m_useSelfParameter) |
| superCall += JSSharedData.THIS; |
| } |
| superCall += "); /* Call to super() was missing in ctor! */" + endl(); |
| function_body = superCall + function_body; |
| } |
| } |
| } |
| |
| result += indentBlock(function_body, 1); |
| |
| if (isCtor) |
| { |
| if (!result.contains(JSSharedData.THIS)) |
| { |
| result += indentBlock("/** @type {" + createFullClassName(true) + "} */" + endl(), 1); |
| result += indentBlock("var " + JSSharedData.THIS + " = this;" + endl(), 1); |
| } |
| result += indentBlock("return " + JSSharedData.THIS + ";" + endl(), 1); |
| } |
| |
| // popIndent(); |
| // result += indent() + "}"; |
| return result; |
| } |
| |
| private boolean haveAPrioriInstructions() |
| { |
| return aPrioriInstructions != null && !aPrioriInstructions.isEmpty(); |
| } |
| |
| /* |
| * public JSTraits createTraitsVisitor() { return new JSTraits(); } |
| */ |
| |
| public String reduce_getModifier(IASNode iNode) |
| { |
| // Pattern getModifier |
| // KeywordGetID(void); |
| |
| return null; |
| } |
| |
| public String reduce_setModifier(IASNode iNode) |
| { |
| // Pattern setModifier |
| // KeywordSetID(void); |
| |
| return null; |
| } |
| |
| /* |
| * ****************** |
| * ** Declarations ** ****************** |
| */ |
| |
| public void prologue_typelessFunction(IASNode p, int iRule) throws java.lang.Exception |
| { |
| // Note: A named nested function does |
| // always need an activation record. |
| currentScope.setNeedsActivation(); |
| currentScope = currentScope.pushFrame(); |
| currentScope.declareNestedFunction(); |
| } |
| |
| public void prologue_typedFunction_to_statemen(IASNode p, int iRule) throws java.lang.Exception |
| { |
| // Note: A named nested function does |
| // always need an activation record. |
| currentScope.setNeedsActivation(); |
| currentScope = currentScope.pushFrame(); |
| currentScope.declareNestedFunction(); |
| } |
| |
| private Boolean addDependency(IDefinition fromDef, IDefinition toDef, DependencyType dt) |
| { |
| if (JSGeneratingReducer.isReservedDataType(fromDef.getBaseName())) |
| return false; |
| if (JSGeneratingReducer.isReservedDataType(toDef.getBaseName())) |
| return false; |
| |
| final CompilerProject compilerProject = (CompilerProject)currentScope.getProject(); |
| final ASProjectScope projectScope = compilerProject.getScope(); |
| |
| final ICompilationUnit fromCU = projectScope.getCompilationUnitForDefinition(fromDef); |
| if (fromCU == null) |
| return false; |
| |
| final ICompilationUnit toCU = projectScope.getCompilationUnitForDefinition(toDef); |
| if (toCU == null) |
| return false; |
| |
| if (fromCU == toCU) |
| return false; |
| |
| // sharedData.verboseMessage( "Adding dependency: " + fromDef.getName() + " to " + toDef.getName() ); |
| compilerProject.addDependency(fromCU, toCU, dt); |
| |
| return true; |
| } |
| |
| private String getJSDocForFunction(MethodInfo mi) |
| { |
| String s = "/** @type {function("; |
| Vector<PooledValue> defaultValues = mi.getDefaultValues(); |
| Vector<Name> paramTypes = mi.getParamTypes(); |
| List<String> paramNames = mi.getParamNames(); |
| |
| int nthParam = 0; |
| final int defaultsStartAt = paramTypes.size() - defaultValues.size(); |
| final Boolean needsRest = (mi.getFlags() & ABCConstants.NEED_REST) > 0; |
| final int restStartsAt = needsRest ? paramNames.size() - 1 : paramNames.size(); |
| Boolean emitComma = false; |
| |
| for (Name nextParam : paramTypes) |
| { |
| if (emitComma) |
| s += ", "; |
| else |
| emitComma = true; |
| |
| if (nthParam == restStartsAt) |
| { |
| // param is rest argument |
| s += "..."; |
| } |
| else if (nextParam == null) |
| { |
| /* |
| * internal compiler error generated with optimize enabled |
| * compiling as3_enumerate.fla and fails to release the JS file |
| * http://watsonexp.corp.adobe.com/#bug=3047880 We have to emit |
| * something, otherwise you'd get: |
| * @type {function(, , )} as that happened in as3_enumerate |
| * MainTimeline. In the original MainTimeline.as change() is a |
| * local function with untyped parameters: function |
| * change(identifier, object, correct) which gets promoted to a |
| * method via [ConvertLocalVarsToMembers] of frame0(). It seems |
| * that there are two bugs: 1. JSDoc comments for functions with |
| * untyped parameters should use "*" instead: |
| * @type {function(*, *, *)} 2. JSDoc comments for promoted |
| * local functions should be omitted. The change below addresses |
| * #1. |
| */ |
| |
| // param is untyped argument |
| s += "*"; |
| this.m_needsSecondPass = true; |
| } |
| else |
| { |
| final IDefinition paramDef = getDefinitionForName(currentScope.getProject(), nextParam); |
| if (paramDef == null) |
| { |
| this.m_needsSecondPass = true; |
| } |
| |
| final StringBuilder sb = new StringBuilder(); |
| if (!nameToJSDocType(currentScope.getProject(), nextParam, sb)) |
| m_needsSecondPass = true; |
| final String argType = sb.toString(); |
| if (nthParam < defaultsStartAt) |
| { |
| // param without default value |
| s += argType; |
| } |
| else |
| { |
| // param with default value |
| s += argType + "="; |
| } |
| } |
| |
| nthParam++; |
| } |
| |
| if (mi.getReturnType() != null && !getBasenameFromName(mi.getReturnType()).equals("void")) |
| { |
| final StringBuilder sb = new StringBuilder(); |
| if (!nameToJSDocType(currentScope.getProject(), mi.getReturnType(), sb)) |
| m_needsSecondPass = true; |
| s += " : " + sb.toString(); |
| } |
| |
| s += ")} */ "; |
| return s; |
| } |
| |
| private Boolean isSyntheticBinding(Binding b) |
| { |
| final String baseName = getBasenameFromBinding(b); |
| |
| if (!isAlphaNumString(baseName)) |
| return true; |
| |
| return false; |
| } |
| |
| private Boolean convertLocalVarsToMembers(IASNode iNode) |
| { |
| if (iNode != null && m_functionNode != null && !isAnonymousFunction(getMethodName())) |
| { |
| final IDefinition def = m_functionNode.getDefinition(); |
| final IMetaTag metaTag = def.getMetaTagByName(JSSharedData.CONVERT_LOCAL_VARS_TO_MEMBERS); |
| if (metaTag != null) |
| { |
| int countFunctionNodeParents = 0; |
| IASNode parent = iNode.getParent(); |
| while (parent != null) |
| { |
| if (parent instanceof IFunctionNode) |
| { |
| if (countFunctionNodeParents > 0) |
| return false; |
| ++countFunctionNodeParents; |
| } |
| else if (parent instanceof IClassNode) |
| { |
| return true; |
| } |
| parent = parent.getParent(); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public void startDeclareVariable(DefinitionBase varDef) |
| { |
| m_currentVariable = varDef; |
| m_hasSideEffects = false; |
| m_varInitValue = null; |
| } |
| |
| public void endDeclareVariable(DefinitionBase varDef) |
| { |
| m_currentVariable = null; |
| } |
| |
| private Boolean hasSideEffects(IDefinition def) |
| { |
| if (def != null && |
| !isExternalClass(currentScope.getProject(), def) && |
| !isDataType(def) |
| /* !isDataClass(currentScope.project, def) */) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| public String getVariableInitializer(DefinitionBase varDef) |
| { |
| if (varDef != m_currentVariable) |
| currentScope.addProblem(new JSInternalCompilerProblem(varDef.getNode(), "unknown variable definition")); |
| |
| if (m_hasSideEffects) |
| return null; |
| |
| return m_varInitValue; |
| } |
| |
| // called by JSGenerator::generateFunction |
| public void startFunction(FunctionNode func) |
| { |
| // DataClass classes must not declare methods. |
| if (isDataClass(currentScope.getProject(), this.m_currentClassDefinition)) |
| currentScope.addProblem(new JSDataClassMethodError(func)); |
| |
| final Boolean convertLocalVarsToMembers = convertLocalVarsToMembers(func); |
| if (convertLocalVarsToMembers) |
| { |
| final String func_name = func.getName(); |
| registerConvertedMember(func_name); |
| } |
| } |
| |
| // called by JSGenerator::generateFunction |
| public void endFunction(FunctionNode func) |
| { |
| if (!m_convertedMembers.isEmpty()) |
| { |
| for (String name : m_convertedMembers) |
| { |
| m_nameMap.remove(name); |
| } |
| m_convertedMembers.clear(); |
| } |
| } |
| |
| public void registerConvertedMember(String name) |
| { |
| if (!m_convertedMembers.contains(name) && !m_nameMap.containsKey(name)) |
| { |
| m_convertedMembers.add(name); |
| m_nameMap.put(name, JSSharedData.THIS + "." + name); |
| } |
| } |
| |
| public void registerAccessedPropertyName(IASNode iNode, String name) |
| { |
| m_sharedData.registerAccessedPropertyName(name); |
| if (inCodeGen()) |
| { |
| if (JSSharedData.usingAdvancedOptimizations() && !name.startsWith("\"")) |
| currentScope.addProblem(new JSWarnClosureAdvancedOptimizationsProblem(iNode)); |
| else if (JSSharedData.WARN_PERFORMANCE_LOSS || JSSharedData.WARN_RUNTIME_NAME_LOOKUP) |
| currentScope.addProblem(new JSWarnRuntimeNameLookupProblem(iNode)); |
| } |
| else |
| { |
| this.m_needsSecondPass = true; |
| } |
| } |
| |
| public void warnClassInitPerformance(IASNode iNode) |
| { |
| if (JSSharedData.WARN_PERFORMANCE_LOSS || JSSharedData.WARN_CLASS_INIT) |
| currentScope.addProblem(new JSWarnClassInitProblem(iNode)); |
| } |
| |
| /** |
| * Problem generated when JSSharedData.WARN_PERFORMANCE_LOSS is set and we |
| * emit adobe.get/set/callProperty |
| */ |
| public class JSWarnRuntimeNameLookupProblem extends CodegenProblem |
| { |
| public static final String DESCRIPTION = "PERFORMANCE: Forced to emit expensive runtime name lookup. Consider using strict types instead of generic objects and avoiding dynamic classes."; |
| |
| public static final int errCode = -1; |
| |
| public JSWarnRuntimeNameLookupProblem(IASNode site) |
| { |
| super(site); |
| } |
| } |
| |
| /** |
| * Problem generated when JSSharedData.WARN_PERFORMANCE_LOSS is set and we |
| * encounter any class init code. |
| */ |
| public class JSWarnClassInitProblem extends CodegenProblem |
| { |
| public static final String DESCRIPTION = "PERFORMANCE: Avoid loose class initialization statements. Consider using singletons instead."; |
| |
| public static final int errCode = -1; |
| |
| public JSWarnClassInitProblem(IASNode site) |
| { |
| super(site); |
| } |
| } |
| |
| /** |
| * Problem generated when JSSharedData.CLOSURE_compilation_level is set to |
| * "ADVANCED_OPTIMIZATIONS" and we emit adobe.get/set/callProperty |
| */ |
| public class JSWarnClosureAdvancedOptimizationsProblem extends CodegenProblem |
| { |
| public static final String DESCRIPTION = "WARNING: Optimizing with ADVANCED_OPTIMIZATIONS will fail because of accessing properties via unknown strings."; |
| |
| public static final int errCode = -1; |
| |
| public JSWarnClosureAdvancedOptimizationsProblem(IASNode site) |
| { |
| super(site); |
| } |
| } |
| |
| /** |
| * Problem generated when JSSharedData.WARN_PERFORMANCE_LOSS is set and we |
| * emit adobe.get/set/callProperty |
| */ |
| public class JSDataClassMethodError extends CodegenProblem |
| { |
| public static final String DESCRIPTION = "ERROR: DataClass classes must not declare methods."; |
| |
| public static final int errCode = -1; |
| |
| public JSDataClassMethodError(IASNode site) |
| { |
| super(site); |
| } |
| } |
| |
| /** |
| * {@link CompilerProblem} subclass for files in the source list that are |
| * not supported by the project. |
| */ |
| public class JSUnsupportedFeatureProblem extends CodegenProblem |
| { |
| public static final String DESCRIPTION = "Unsupported feature: ${feature}."; //$NON-NLS-1$ |
| |
| public JSUnsupportedFeatureProblem(IASNode site, String featureName) |
| { |
| super(site); |
| feature = featureName; |
| } |
| |
| public final String feature; |
| } |
| |
| /** |
| * {@link CompilerProblem} subclass for files in the source list that are |
| * not supported by the project. |
| */ |
| public class JSInternalCompilerProblem extends CodegenProblem |
| { |
| public static final String DESCRIPTION = "Internal compiler error: ${message}."; //$NON-NLS-1$ |
| |
| public JSInternalCompilerProblem(IASNode site, String errorMessage) |
| { |
| super(site); |
| message = errorMessage; |
| } |
| |
| public final String message; |
| } |
| |
| /** |
| * @return OP_unplus or OP_convert_d depending on if float support is |
| * enabled or not |
| */ |
| int op_unplus() |
| { |
| // This does not seem to matter for JSGeneratingReducer - each opcode has the same effect |
| return OP_unplus; |
| } |
| |
| public String reduce_arrayIndexExpr_to_mxmlDataBindingSetter(IASNode __p, |
| String stem, String index, boolean b) |
| { |
| assert false : "Data binding not supported!"; |
| return null; |
| } |
| |
| public String reduceName_to_mxmlDataBindingSetter(IASNode __p, Binding name) |
| { |
| assert false : "Data binding not supported!"; |
| return null; |
| } |
| |
| public String reduceRuntimeName_to_mxmlDataBindingSetter(IASNode __p, |
| RuntimeMultiname runtime_name_expression) |
| { |
| assert false : "Data binding not supported!"; |
| return null; |
| } |
| |
| public String reduce_memberAccessExpr_to_mxmlDataBindingSetter(IASNode __p, |
| String stem, Binding member) |
| { |
| assert false : "Data binding not supported!"; |
| return null; |
| } |
| |
| public String reduce_qualifiedMemberAccessExpr_to_mxmlDataBindingSetter( |
| IASNode __p, String stem, Binding qualifier, Binding member) |
| { |
| assert false : "Data binding not supported!"; |
| return null; |
| } |
| |
| public String reduce_qualifiedMemberRuntimeNameExpr_to_mxmlDataBindingSetter( |
| IASNode __p, String stem, Binding qualifier, String runtime_member) |
| { |
| assert false : "Data binding not supported!"; |
| return null; |
| } |
| |
| public Binding reduce_parameterizedName(IASNode node, Binding base, Binding param) |
| { |
| return reduce_parameterizedTypeName(node, base, param); |
| //return null; |
| } |
| |
| public String reduce_vectorLiteral(IASNode node, Binding type_param, Vector<String> elements) |
| { |
| return null; |
| } |
| } |