blob: 6bc7bd465a96b4775db961a1b46d06db66c5e66f [file] [log] [blame]
/*
*
* 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 &amp;&amp;=, 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 &amp;&amp;=, 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 &amp;&amp;=, 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.&lt;String&gt; or * in
* Vector.&lt;*&gt;.
*
* @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;
}
}