in compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java [539:949]
public void emitAssignmentCoercion(IExpressionNode assignedNode, IDefinition definition)
{
IDefinition assignedDef = null;
IDefinition assignedTypeDef = null;
ICompilerProject project = getWalker().getProject();
boolean isXML = false;
if (assignedNode != null)
{
assignedDef = assignedNode.resolve(project);
assignedTypeDef = assignedNode.resolveType(project);
if (assignedTypeDef == null || project.getBuiltinType(BuiltinType.ANY_TYPE).equals(assignedTypeDef))
{
IDefinition resolvedXMLDef = SemanticUtils.resolveXML(assignedNode, project);
if (resolvedXMLDef != null)
{
assignedDef = resolvedXMLDef;
assignedTypeDef = SemanticUtils.resolveTypeXML(assignedNode, project);
isXML = true;
}
} else if (SemanticUtils.isXMLish(assignedTypeDef, project)) {
isXML = true;
}
}
String coercionStart = null;
String coercionEnd = null;
boolean avoidCoercion = false;
if (project.getBuiltinType(BuiltinType.INT).equals(definition))
{
boolean needsCoercion = false;
if (assignedNode instanceof INumericLiteralNode)
{
INumericLiteralNode numericLiteral = (INumericLiteralNode) assignedNode;
INumericLiteralNode.INumericValue numericValue = numericLiteral.getNumericValue();
startMapping(assignedNode);
if(numericValue.toString().startsWith("0x"))
{
//for readability, keep the same formatting
write("0x" + Integer.toHexString(numericValue.toInt32()));
}
else
{
write(Integer.toString(numericValue.toInt32()));
}
endMapping(assignedNode);
return;
}
else if(assignedNode instanceof BinaryOperatorAsNode)
{
needsCoercion = true;
}
else if(!project.getBuiltinType(BuiltinType.INT).equals(assignedTypeDef))
{
needsCoercion = true;
}
if (needsCoercion)
{
coercionStart = "(";
coercionEnd = ") >> 0";
}
}
else if (project.getBuiltinType(BuiltinType.UINT).equals(definition))
{
boolean needsCoercion = false;
if (assignedNode instanceof INumericLiteralNode)
{
INumericLiteralNode numericLiteral = (INumericLiteralNode) assignedNode;
INumericLiteralNode.INumericValue numericValue = numericLiteral.getNumericValue();
startMapping(assignedNode);
if(numericValue.toString().startsWith("0x"))
{
//for readability, keep the same formatting
write("0x" + Long.toHexString(numericValue.toUint32()));
}
else
{
write(Long.toString(numericValue.toUint32()));
}
endMapping(assignedNode);
return;
}
else if(assignedNode instanceof BinaryOperatorAsNode)
{
needsCoercion = true;
}
else if(!project.getBuiltinType(BuiltinType.UINT).equals(assignedTypeDef))
{
needsCoercion = true;
}
if (needsCoercion)
{
coercionStart = "(";
coercionEnd = ") >>> 0";
}
}
else if (project.getBuiltinType(BuiltinType.NUMBER).equals(definition)
&& !project.getBuiltinType(BuiltinType.NUMBER).equals(assignedTypeDef)
&& !project.getBuiltinType(BuiltinType.INT).equals(assignedTypeDef)
&& !project.getBuiltinType(BuiltinType.UINT).equals(assignedTypeDef))
{
boolean needsCoercion = true;
if (assignedNode instanceof IDynamicAccessNode)
{
IDynamicAccessNode dynamicAccess = (IDynamicAccessNode) assignedNode;
IDefinition dynamicAccessIndexDef = dynamicAccess.getRightOperandNode().resolveType(project);
if (project.getBuiltinType(BuiltinType.NUMBER).equals(dynamicAccessIndexDef))
{
IDefinition leftDef = dynamicAccess.getLeftOperandNode().resolveType(project);
if (leftDef != null) {
IMetaTag[] metas = leftDef.getAllMetaTags();
for (IMetaTag meta : metas)
{
if (meta.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_ARRAYELEMENTTYPE))
{
IMetaTagAttribute[] attrs = meta.getAllAttributes();
for (IMetaTagAttribute attr : attrs)
{
String t = attr.getValue();
if (t.equals(IASLanguageConstants.Number))
{
needsCoercion = false;
//explicitly prevent other coercion detection rules from picking this up
avoidCoercion = true;
}
}
}
}
}
}
}
if (needsCoercion)
{
coercionStart = "Number(";
}
}
else if (project.getBuiltinType(BuiltinType.BOOLEAN).equals(definition)
&& !project.getBuiltinType(BuiltinType.BOOLEAN).equals(assignedTypeDef))
{
if (project.getBuiltinType(BuiltinType.NULL).equals(assignedTypeDef)
|| (assignedDef != null && assignedDef.getQualifiedName().equals(IASLanguageConstants.UNDEFINED)))
{
//null and undefined are coerced to false
startMapping(assignedNode);
write(IASLanguageConstants.FALSE);
endMapping(assignedNode);
return;
}
if (assignedNode instanceof INumericLiteralNode)
{
INumericLiteralNode numericLiteral = (INumericLiteralNode) assignedNode;
INumericLiteralNode.INumericValue numericValue = numericLiteral.getNumericValue();
//zero is coerced to false, and everything else is true
String booleanValue = numericValue.toNumber() == 0.0
? IASLanguageConstants.FALSE
: IASLanguageConstants.TRUE;
startMapping(assignedNode);
write(booleanValue);
endMapping(assignedNode);
return;
}
coercionStart = "!!(";
}
else if (project.getBuiltinType(BuiltinType.STRING).equals(definition)
&& !project.getBuiltinType(BuiltinType.STRING).equals(assignedTypeDef)
&& !project.getBuiltinType(BuiltinType.NULL).equals(assignedTypeDef)
&& !(project.getBuiltinType(BuiltinType.ANY_TYPE).equals(assignedTypeDef)
&& SemanticUtils.isToStringFunctionCall(assignedNode, project)))
{
if(assignedDef != null && assignedDef.getQualifiedName().equals(IASLanguageConstants.UNDEFINED))
{
//undefined is coerced to null
startMapping(assignedNode);
write(IASLanguageConstants.NULL);
endMapping(assignedNode);
return;
}
boolean emitStringCoercion = true;
IDocEmitter docEmitter = getDocEmitter();
if (docEmitter instanceof JSRoyaleDocEmitter)
{
JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter;
emitStringCoercion = royaleDocEmitter.emitStringConversions;
}
if (emitStringCoercion)
{
if (isXML) {
if (EmitterUtils.xmlRequiresNullCheck((NodeBase) assignedNode, project)) {
//if it could be a null reference use the XMLList.coerce_string method, which retains null
coercionStart = "XMLList.coerce_string(";
} else {
coercionStart = "String(";
}
}
else coercionStart = "org.apache.royale.utils.Language.string(";
}
}
if ( assignedDef != null
&& assignedDef instanceof IAppliedVectorDefinition
&& assignedNode instanceof TypedExpressionNode) {
//assign a Vector class as the assigned value, e.g. var c:Class = Vector.<int>
if (project instanceof RoyaleJSProject
&& ((RoyaleJSProject)project).config.getJsVectorEmulationClass() != null) {
startMapping(assignedNode);
write(((RoyaleJSProject)project).config.getJsVectorEmulationClass());
endMapping(assignedNode);
} else {
startMapping(assignedNode);
write(formatQualifiedName(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken()));
write(ASEmitterTokens.MEMBER_ACCESS);
write(JSRoyaleEmitterTokens.SYNTH_VECTOR);
write(ASEmitterTokens.PAREN_OPEN);
write(ASEmitterTokens.SINGLE_QUOTE);
//the element type of the Vector:
write(formatQualifiedName(((TypedExpressionNode)assignedNode).getTypeNode().resolve(project).getQualifiedName()));
write(ASEmitterTokens.SINGLE_QUOTE);
write(ASEmitterTokens.PAREN_CLOSE);
endMapping(assignedNode);
if (project instanceof RoyaleJSProject)
((RoyaleJSProject)project).needLanguage = true;
getModel().needLanguage = true;
}
return;
}
if (assignedDef instanceof IClassDefinition
&& assignedNode instanceof IdentifierNode
&& ((IdentifierNode)assignedNode).getName().equals(IASGlobalFunctionConstants.Vector)
&& project instanceof RoyaleJSProject
&& ((RoyaleJSProject)project).config.getJsVectorEmulationClass() == null ){
startMapping(assignedNode);
write(formatQualifiedName(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken()));
write(ASEmitterTokens.MEMBER_ACCESS);
write(JSRoyaleEmitterTokens.SYNTH_VECTOR);
write(ASEmitterTokens.PAREN_OPEN);
//null to signify not a valid constructor
write(ASEmitterTokens.NULL);
write(ASEmitterTokens.PAREN_CLOSE);
endMapping(assignedNode);
if (project instanceof RoyaleJSProject)
((RoyaleJSProject)project).needLanguage = true;
getModel().needLanguage = true;
return;
}
//special case for 'rewritten' multicatch support - we avoid coercion because it is assured via an injected 'is' check immediately prior to declaration/assignment
if (DefinitionUtils.isRewrittenMultiCatchParam(assignedDef)) {
avoidCoercion = true;
}
if (coercionStart == null
&& !avoidCoercion
&& assignedTypeDef !=null
&& definition !=null
&& (project.getBuiltinType(BuiltinType.ANY_TYPE).equals(assignedTypeDef)
|| project.getBuiltinType(BuiltinType.OBJECT).equals(assignedTypeDef))
&& !(project.getBuiltinType(BuiltinType.ANY_TYPE).equals(definition)
|| project.getBuiltinType(BuiltinType.OBJECT).equals(definition))) {
//catch leftovers: remaining implicit coercion of loosely typed assigned values to strongly typed context
//assignment to Class definitions is excluded because there is no 'Class' type in JS
//Possibility: 'Class' could be implemented as a synthType
boolean needsCoercion = ((RoyaleJSProject)project).config.getJsComplexImplicitCoercions();
IDocEmitter docEmitter = getDocEmitter();
if (docEmitter instanceof JSRoyaleDocEmitter)
{
JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter;
//check for local toggle
if (needsCoercion) needsCoercion = !(royaleDocEmitter.getLocalSettingAsBoolean(
JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION, false));
else {
if (royaleDocEmitter.hasLocalSetting(JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION.getToken())) {
needsCoercion = !(royaleDocEmitter.getLocalSettingAsBoolean(
JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION, false));
}
}
if (needsCoercion) {
//check for individual specified suppression
String definitionName = definition.getQualifiedName();
//for Vectors, use the unqualified name to match the source code
if (NativeUtils.isVector(definitionName)) {
definitionName = definition.getBaseName();
}
if (royaleDocEmitter.getLocalSettingIncludesString(
JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION,
definitionName))
{
needsCoercion = false;
}
}
}
//Avoid specific compile-time 'fake' class(es)
if (needsCoercion && definition.getQualifiedName().equals("org.apache.royale.core.WrappedHTMLElement")) {
//*actual* coercion fails here, because this is not actually instantiated, it is
//simply a type definition representing the 'wrapped' (or tagged) HTMLElement
needsCoercion = false;
}
//Avoid XML/XMLList:
if (needsCoercion && project.getBuiltinType(BuiltinType.XML) != null) {
if (project.getBuiltinType(BuiltinType.XML).equals(definition)
|| project.getBuiltinType(BuiltinType.XMLLIST).equals(definition)) {
//XML/XMLList has complex output and would need more work
needsCoercion = false;
}
}
//avoid scenario with ArrayElementType specified as metadata definition type - assume it is 'typed'
if (needsCoercion && assignedNode instanceof IDynamicAccessNode)
{
IDynamicAccessNode dynamicAccess = (IDynamicAccessNode) assignedNode;
IDefinition dynamicAccessIndexDef = dynamicAccess.getRightOperandNode().resolveType(project);
if (project.getBuiltinType(BuiltinType.NUMBER).equals(dynamicAccessIndexDef))
{
IDefinition leftDef = dynamicAccess.getLeftOperandNode().resolveType(project);
if (leftDef != null) {
IMetaTag[] metas = leftDef.getAllMetaTags();
for (IMetaTag meta : metas)
{
if (meta.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_ARRAYELEMENTTYPE))
{
IMetaTagAttribute[] attrs = meta.getAllAttributes();
for (IMetaTagAttribute attr : attrs)
{
String t = attr.getValue();
if (t.equals(definition.getQualifiedName()))
{
needsCoercion = false;
}
}
}
}
}
}
}
if (needsCoercion && project.getBuiltinType(BuiltinType.STRING).equals(definition)) {
//explicit suppression of String coercion
if (docEmitter instanceof JSRoyaleDocEmitter)
{
JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter;
needsCoercion = royaleDocEmitter.emitStringConversions;
}
if (needsCoercion
&& assignedNode instanceof FunctionCallNode
&& ((FunctionCallNode) assignedNode).getNameNode() instanceof MemberAccessExpressionNode
&& ((MemberAccessExpressionNode)((FunctionCallNode) assignedNode).getNameNode()).getRightOperandNode() instanceof IdentifierNode
&& ((IdentifierNode)(((MemberAccessExpressionNode)((FunctionCallNode) assignedNode).getNameNode()).getRightOperandNode())).getName().equals("toString")) {
//even if toString() is called in an untyped way, assume a call to a method named 'toString' is actually providing a String
needsCoercion = false;
}
}
if (needsCoercion) {
//add a comment tag leader, so implicit casts are identifiable in the output
coercionStart = "/* implicit cast */ "
+ formatQualifiedName(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken())
+ ASEmitterTokens.MEMBER_ACCESS.getToken()
+ ASEmitterTokens.AS.getToken()
+ ASEmitterTokens.PAREN_OPEN.getToken();
String coercionTypeString = formatQualifiedName(definition.getQualifiedName());
if (NativeUtils.isSyntheticJSType(coercionTypeString)) {
String synthCall;
String synthethicType;
if (NativeUtils.isVector(coercionTypeString)) {
synthCall = formatQualifiedName(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken())
+ ASEmitterTokens.MEMBER_ACCESS.getToken()
+ JSRoyaleEmitterTokens.SYNTH_VECTOR.getToken();
synthethicType = formatQualifiedName(coercionTypeString.substring(8, coercionTypeString.length() -1));
} else {
synthCall = formatQualifiedName(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken())
+ ASEmitterTokens.MEMBER_ACCESS.getToken()
+ JSRoyaleEmitterTokens.SYNTH_TYPE.getToken();
synthethicType = coercionTypeString;
}
coercionTypeString = synthCall
+ ASEmitterTokens.PAREN_OPEN.getToken()
+ ASEmitterTokens.SINGLE_QUOTE.getToken()
+ synthethicType
+ ASEmitterTokens.SINGLE_QUOTE.getToken()
+ ASEmitterTokens.PAREN_CLOSE.getToken();
}
coercionEnd = ASEmitterTokens.COMMA.getToken()
+ ASEmitterTokens.SPACE.getToken()
+ coercionTypeString
+ ASEmitterTokens.COMMA.getToken()
+ ASEmitterTokens.SPACE.getToken()
+ ASEmitterTokens.TRUE.getToken()
+ ASEmitterTokens.PAREN_CLOSE.getToken();
if (project instanceof RoyaleJSProject)
((RoyaleJSProject)project).needLanguage = true;
getModel().needLanguage = true;
}
}
if (coercionStart != null)
{
write(coercionStart);
}
emitAssignedValue(assignedNode);
if (coercionStart != null)
{
if (coercionEnd != null)
{
write(coercionEnd);
}
else
{
write(")");
}
}
}