in compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java [62:321]
public void emit(IMemberAccessExpressionNode node)
{
if (ASNodeUtils.hasParenOpen(node))
write(ASEmitterTokens.PAREN_OPEN);
JSRoyaleEmitter fjs = (JSRoyaleEmitter)getEmitter();
if (fjs.isDateProperty(node, false))
{
writeDateGetterMemberAccess(node);
return;
}
IExpressionNode leftNode = node.getLeftOperandNode();
IExpressionNode rightNode = node.getRightOperandNode();
IDefinition def = node.resolve(getProject());
//extra check to cope with e4x member access identifier nodes that resolve
//to instance member function definitions
//but should not be interpreted as such. e.g. xml.child.descendant
//should be output as xml.child('child').child('descendant')
//we also need to check we are not currently compiling the XML or XMLList class to avoid any
//possible internal references being treated incorrectly
boolean forceXmlCheck =(def != null
&& node.getRightOperandNode().getNodeID() == ASTNodeID.IdentifierID
&& SemanticUtils.isXMLish(def.getParent(), getProject())
&& def instanceof IFunctionDefinition
&& !def.isStatic()
&& !(getModel().getCurrentClass() != null && (getModel().getCurrentClass().getQualifiedName().equals("XML") || getModel().getCurrentClass().getQualifiedName().equals("XMLList")))
);
if (def == null || forceXmlCheck)
{
IASNode parentNode = node.getParent();
// could be XML
boolean isXML = false;
boolean isProxy = false;
if (leftNode instanceof MemberAccessExpressionNode)
isXML = fjs.isLeftNodeXMLish(leftNode);
else if (leftNode != null)
isXML = fjs.isXMLish(leftNode);
if (!isXML) {
if (leftNode instanceof MemberAccessExpressionNode)
isProxy = fjs.isProxy(leftNode);
else if (leftNode instanceof IExpressionNode)
isProxy = fjs.isProxy((IExpressionNode)leftNode);
}
if (isXML)
{
boolean descendant = (node.getOperator() == OperatorType.DESCENDANT_ACCESS);
boolean child = !descendant && (node.getOperator() == OperatorType.MEMBER_ACCESS) &&
(!(parentNode instanceof FunctionCallNode)) &&
rightNode.getNodeID() != ASTNodeID.Op_AtID &&
!((rightNode.getNodeID() == ASTNodeID.ArrayIndexExpressionID) &&
(((DynamicAccessNode)rightNode).getLeftOperandNode().getNodeID() == ASTNodeID.Op_AtID));
if (descendant || child) {
writeXmlDescendantOrChild(node, descendant, child);
return;
}
}
else if (isProxy)
{
boolean child = (node.getOperator() == OperatorType.MEMBER_ACCESS) &&
(!(parentNode instanceof FunctionCallNode)) &&
rightNode.getNodeID() != ASTNodeID.Op_AtID;
if (child)
{
writeProxyGetProperty(node);
return;
}
}
else if (rightNode instanceof NamespaceAccessExpressionNode)
{
// if you define a local variable with the same URI as a
// namespace that defines a namespaced property
// it doesn't resolve above so we handle it here
writeNullDefinitionRightSideNamespaceAccessExpressionNode(node);
return;
}
}
else if(def.getParent() instanceof IPackageDefinition)
{
//this is a fully qualified name, and we should output it directly
//because we don't want it to be treated as dynamic access
write(fjs.formatQualifiedName(def.getQualifiedName()));
return;
}
else if (def.getParent() != null &&
def.getParent().getQualifiedName().equals("Array"))
{
if (def.getBaseName().equals("removeAt"))
{
writeArrayRemoveAt(node);
return;
}
else if (def.getBaseName().equals("insertAt"))
{
writeArrayInsertAt(node);
return;
}
}
else if (rightNode instanceof NamespaceAccessExpressionNode)
{
writeRightSideNamespaceAccessExpressionNode(node, def);
return;
}
boolean isCustomNamespace = false;
if (def instanceof FunctionDefinition && node.getOperator() == OperatorType.MEMBER_ACCESS)
isCustomNamespace = fjs.isCustomNamespace((FunctionDefinition)def);
boolean isStatic = false;
if (def != null && def.isStatic())
isStatic = true;
boolean needClosure = false;
if (def instanceof FunctionDefinition && (!(def instanceof AccessorDefinition))
&& !def.getBaseName().equals("constructor")) // don't wrap references to obj.constructor
{
IASNode parentNode = node.getParent();
if (parentNode != null)
{
ASTNodeID parentNodeId = parentNode.getNodeID();
// we need a closure if this MAE is the top-level in a chain
// of MAE and not in a function call.
needClosure = !isStatic && parentNodeId != ASTNodeID.FunctionCallID &&
parentNodeId != ASTNodeID.MemberAccessExpressionID &&
parentNodeId != ASTNodeID.ArrayIndexExpressionID;
//If binding getterFunctions ever need closures, this seems to be where it would be done (so far not needed, @todo review and remove this when certain)
/*if (!needClosure && !isStatic && parentNodeId == ASTNodeID.FunctionCallID) {
if (node.getParent().getParent() instanceof IMXMLSingleDataBindingNode) {
needClosure = true;
}
}*/
if (needClosure
&& getEmitter().getDocEmitter() instanceof JSRoyaleDocEmitter
&& ((JSRoyaleDocEmitter)getEmitter().getDocEmitter()).getSuppressClosure())
needClosure = false;
}
}
boolean continueWalk = true;
if (!isStatic)
{
if (needClosure)
getEmitter().emitClosureStart();
continueWalk = writeLeftSide(node, leftNode, rightNode);
}
if (continueWalk)
{
boolean emitDynamicAccess = false;
boolean dynamicAccessUnknownMembers = false;
ICompilerProject project = getProject();
if(project instanceof RoyaleJSProject)
{
RoyaleJSProject fjsProject = (RoyaleJSProject) project;
if(fjsProject.config != null)
{
dynamicAccessUnknownMembers = fjsProject.config.getJsDynamicAccessUnknownMembers();
}
if (!dynamicAccessUnknownMembers) {
//for <fx:Object declarations in mxml, we need to do this by default, because initialization values are set this way already, as are destination bindings, for example.
IIdentifierNode checkNode = null;
if (leftNode instanceof IIdentifierNode) {
//we might be dealing with the direct child member access of an fx:Object
checkNode = (IIdentifierNode) leftNode;
} else {
if (leftNode instanceof MemberAccessExpressionNode) {
//if we are nested, check upwards for topmost Identifier node and verify that it is mxml variable of type Object, verifying that we are considered 'untyped' along the way
MemberAccessExpressionNode mae = (MemberAccessExpressionNode) leftNode;
while (mae != null) {
if (mae.getRightOperandNode().resolve(getProject()) == null) {
if (mae.getLeftOperandNode() instanceof IIdentifierNode) {
checkNode = (IIdentifierNode) mae.getLeftOperandNode();
break;
} else if (mae.getLeftOperandNode() instanceof MemberAccessExpressionNode) {
mae = (MemberAccessExpressionNode) mae.getLeftOperandNode();
} else {
mae = null;
}
} else mae = null;
}
}
}
if (checkNode != null &&
checkNode.resolve(getProject()) instanceof VariableDefinition) {
VariableDefinition varDef = ((VariableDefinition) (checkNode.resolve(getProject())));
if (varDef.isMXMLDeclared()) {
IDefinition type = varDef.resolveType(getProject());
if (type instanceof IClassDefinition && type.equals(getProject().getBuiltinType(IASLanguageConstants.BuiltinType.OBJECT))) {
dynamicAccessUnknownMembers = true;
}
}
}
}
}
if (dynamicAccessUnknownMembers && rightNode instanceof IIdentifierNode)
{
IIdentifierNode identifierNode = (IIdentifierNode) node.getRightOperandNode();
IDefinition resolvedDefinition = identifierNode.resolve(getProject());
if (resolvedDefinition == null) {
emitDynamicAccess = true;
IExpressionNode expressionNode = node.getLeftOperandNode();
while (expressionNode != null)
{
ITypeDefinition expressionType = expressionNode.resolveType(getProject());
if (SemanticUtils.isXMLish(expressionType, getProject())) {
emitDynamicAccess = false;
break;
}
if (expressionNode instanceof IMemberAccessExpressionNode)
{
IMemberAccessExpressionNode memberAccess = (IMemberAccessExpressionNode) expressionNode;
expressionNode = memberAccess.getLeftOperandNode();
}
else if (expressionNode instanceof IDynamicAccessNode)
{
IDynamicAccessNode dynamicAccess = (IDynamicAccessNode) expressionNode;
expressionNode = dynamicAccess.getLeftOperandNode();
}
else
{
expressionNode = null;
break;
}
}
}
}
if (emitDynamicAccess)
{
writeDynamicAccessForIdentifier(node);
}
else
{
if (!isStatic && !isCustomNamespace)
{
startMapping(node, node.getLeftOperandNode());
write(node.getOperator().getOperatorText());
endMapping(node);
}
getWalker().walk(node.getRightOperandNode());
}
}
if (needClosure)
{
write(ASEmitterTokens.COMMA);
write(ASEmitterTokens.SPACE);
if (leftNode.getNodeID() == ASTNodeID.SuperID)
write(ASEmitterTokens.THIS);
else
writeLeftSide(node, leftNode, rightNode);
getEmitter().emitClosureEnd(node, def);
}
if (ASNodeUtils.hasParenClose(node))
write(ASEmitterTokens.PAREN_CLOSE);
}