blob: add32607fca0a4161fa482d057cf22d637f7f04b [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.royale.compiler.internal.codegen.databinding;
import static org.apache.royale.abc.ABCConstants.*;
import java.util.LinkedList;
import org.apache.royale.abc.instructionlist.InstructionList;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.references.INamespaceReference;
import org.apache.royale.compiler.internal.as.codegen.Binding;
import org.apache.royale.compiler.internal.as.codegen.InstructionListNode;
import org.apache.royale.compiler.internal.as.codegen.MXMLClassDirectiveProcessor;
import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.IExpressionNode;
import org.apache.royale.compiler.tree.mxml.IMXMLDataBindingNode;
import org.apache.royale.compiler.tree.mxml.IMXMLModelNode;
import org.apache.royale.compiler.tree.mxml.IMXMLModelPropertyNode;
import org.apache.royale.compiler.tree.mxml.IMXMLModelRootNode;
import org.apache.royale.compiler.tree.mxml.IMXMLPropertySpecifierNode;
import org.apache.royale.compiler.tree.mxml.IMXMLSingleDataBindingNode;
/**
* Utility class for analyze binding destinations and making
* "destination functions" for them.
*
* Note that a "destination function" is passed as an argument to the
* constructor of the SDK's mx.binding.Binding object.
*
* A typical destination function looks like:
* function (_sourceFunctionReturnValue:*): void
* {
* some_complex_lvalue = _sourceFunctionReturnValue;
* }
*/
public class BindingDestinationMaker
{
public static class Destination
{
public Destination(IExpressionNode destinationASTNode, boolean transformationRequired)
{
this.destinationASTNode = destinationASTNode;
this.transformationRequired = transformationRequired;
}
public IExpressionNode destinationASTNode;
boolean transformationRequired;
}
/**
* Generate destination function from an IMXMLConcatenatedDataBindingNode
* or IMXMLDataBindingNode
*
* Do this by walking up and down the tree, building up instruction list
*/
public static IExpressionNode makeDestinationFunctionInstructionList(IMXMLDataBindingNode dbnode,
MXMLClassDirectiveProcessor host)
{
IExpressionNode ret = null;
final IASNode parent = dbnode.getParent();
// We only know how to make dest functions in very specific cases
// Case 1: we are in the <fx:Model> tag
if (parent instanceof IMXMLModelPropertyNode)
{
// search up until we find the model tag
// keep an ordered list of the nodes that will contribute to the instruction list
LinkedList<IASNode > nodes = new LinkedList<IASNode >();
IASNode tempNode = parent;
for (boolean done=false; !done; )
{
if (tempNode instanceof IMXMLModelNode)
{
// we found the root. terminate
done = true;
}
// Save the node in our array, if we care about it
if (!(tempNode instanceof IMXMLModelRootNode))
{
nodes.addFirst(tempNode);
}
tempNode = tempNode.getParent();
if (tempNode == null)
{
assert false; // no root. tree is mal-formed
return null;
}
}
// Now we can generate the instructions by going through our node list in order,
// which is like traversing the model tree from Model tag down to leaf
InstructionList insns = new InstructionList();
insns.addInstruction(OP_getlocal0);
int nodeIndex = 0;
for (IASNode n : nodes)
{
boolean last = nodeIndex == (nodes.size()-1);
generateOne(insns, n, last);
++nodeIndex;
}
ret = new InstructionListNode(insns); // Wrap the IL in a node and return it
}
else if (parent instanceof IMXMLPropertySpecifierNode && dbnode instanceof IMXMLSingleDataBindingNode)
{
IMXMLPropertySpecifierNode psn = (IMXMLPropertySpecifierNode)parent;
IDefinition d = psn.getDefinition();
//it's possible for the definition to be null if we're dealing with
//a dynamic property on a class like Object -JT
if (d != null)
{
Binding b = host.getInstanceScope().getBinding(d);
INamespaceReference ns = d.getNamespaceReference();
if (ns != NamespaceDefinition.getPublicNamespaceDefinition())
{
InstructionList insns = new InstructionList();
insns.addInstruction(OP_getlocal0);
insns.addInstruction(OP_getlocal1);
insns.addInstruction(OP_setproperty, b.getName());
ret = new InstructionListNode(insns); // Wrap the IL in a node and return it
}
}
}
return ret;
}
/**
* Generate the instructions for one level of the fx:Model tree
*
*/
private static void generateOne(InstructionList insns, IASNode node, boolean lastOne)
{
if (node instanceof IMXMLModelPropertyNode)
generateOneLevel(insns, (IMXMLModelPropertyNode)node, lastOne);
else if (node instanceof IMXMLModelNode)
generateModel(insns, (IMXMLModelNode)node, lastOne);
else assert false;
}
private static void generateOneLevel(InstructionList insns, IMXMLModelPropertyNode node, boolean lastOne)
{
if (!lastOne)
{
// get the next property in the destination chain
insns.addInstruction(OP_getproperty, new Name(node.getName()));
if (node.getIndex() >= 0)
{
// If it's an array, then get the correct element out of it
String nodeIndexName = Integer.toString(node.getIndex());
insns.addInstruction(OP_getproperty, new Name(nodeIndexName));
}
}
else if (node.getIndex() < 0)
{
// last node, not array
// get the passed in function parameter, and store it into
// the final property in the model node chain
insns.addInstruction(OP_getlocal1);
insns.addInstruction(OP_setproperty, new Name(node.getName()));
}
else
{
// last node, it's an array
// get the array...
insns.addInstruction(OP_getproperty, new Name(node.getName()));
// get the passed in function parameter, and store it into
// the correct element of the array
insns.addInstruction(OP_getlocal1);
String nodeIndexName = Integer.toString(node.getIndex());
insns.addInstruction(OP_setproperty, new Name(nodeIndexName));
}
}
private static void generateModel(InstructionList insns, IMXMLModelNode model, boolean lastOne)
{
if (!lastOne)
{
String id = model.getEffectiveID();
assert id != null;
insns.addInstruction(OP_getproperty, new Name(id));
}
else
assert false;// This would correspond to <Model><a>{}</a></Model>
// Since this is invalid, higher level code detects that
// and never calls us
}
}