This is an automated email from the ASF dual-hosted git repository. gregdove pushed a commit to branch improvements/Language in repository https://gitbox.apache.org/repos/asf/royale-compiler.git
commit 88b2c1f52e7b15d2573d1d40e6963de4aa15b24f Author: greg-dove <[email protected]> AuthorDate: Sat May 4 18:21:57 2019 +1200 Squashed commit of cumulative work on Language improvements --- .../royale/compiler/clients/JSConfiguration.java | 119 ++++++++- .../compiler/internal/codegen/js/JSEmitter.java | 204 ++++++++++++++- .../internal/codegen/js/jx/AsIsEmitter.java | 31 ++- .../codegen/js/jx/BinaryOperatorEmitter.java | 42 ++- .../codegen/js/jx/DynamicAccessEmitter.java | 57 +++++ .../codegen/js/jx/FunctionCallEmitter.java | 282 ++++++++++++++++----- .../internal/codegen/js/jx/IdentifierEmitter.java | 42 +-- .../codegen/js/jx/MemberAccessEmitter.java | 30 +-- .../internal/codegen/js/jx/MethodEmitter.java | 8 +- .../codegen/js/jx/UnaryOperatorEmitter.java | 29 +++ .../codegen/js/royale/JSRoyaleDocEmitter.java | 98 ++++++- .../codegen/js/royale/JSRoyaleEmitter.java | 40 +-- .../codegen/js/royale/JSRoyaleEmitterTokens.java | 9 +- .../internal/projects/RoyaleJSProject.java | 20 +- .../compiler/utils/JSClosureCompilerWrapper.java | 2 + .../apache/royale/compiler/utils/NativeUtils.java | 42 ++- .../codegen/js/royale/TestRoyaleExpressions.java | 8 +- .../codegen/js/royale/TestRoyaleGlobalClasses.java | 61 ++++- .../js/royale/TestRoyaleGlobalFunctions.java | 15 +- .../internal/tree/as/DynamicAccessNode.java | 7 +- 20 files changed, 972 insertions(+), 174 deletions(-) diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/JSConfiguration.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/JSConfiguration.java index c75b550..ae06bf7 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/JSConfiguration.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/JSConfiguration.java @@ -461,6 +461,121 @@ public class JSConfiguration extends Configuration { jsVectorEmulationClass = b; } - - + + + // + // 'js-no-complex-implicit-coercions' + // + + private boolean jsNoComplexImplicitCoercions = false; + + public boolean getJsNoComplexImplicitCoercions() + { + return jsNoComplexImplicitCoercions; + } + + /** + * Support for avoiding more complex implicit assignment coercions + * example + * var array:Array = [new MyClass()]; + * var myOtherClass:MyOtherClass = array[0]; + * + * In the above example, the compiler will (by default) output an implicit coercion + * that is equivalent in actionscript to: + * var myOtherClass:MyOtherClass = MyOtherClass(array[0]); + * + * By setting this configuration option to true, the implicit coercion code in situations similar to the above + * is not generated (other primitive implicit coercions, such as int/uint/Number/String and Boolean coercions remain) + * This is a global setting, it is possible to leave it on and specifically avoid it via doc + * settings. The doc comment compiler directive for that is: @royalesuppresscompleximplicitcoercion + * Another option is to add the explicit coercions in code and then avoid their output + * via specific @royaleignorecoercion doc comment directives. Doing so however may add extra unwanted output + * in other compiler targets (for example, swf bytecode) if the same source code is shared between targets. + */ + @Config(advanced = true) + @Mapping("js-no-complex-implicit-coercions") + public void setJsNoComplexImplicitCoercions(ConfigurationValue cv, boolean value) + throws ConfigurationException + { + jsNoComplexImplicitCoercions = value; + } + + // + // 'js-no-resolve-uncertain' + // + + private boolean jsNoResolveUncertain = false; + + public boolean getJsNoResolveUncertain() + { + return jsNoResolveUncertain; + } + + /** + * Support for avoiding more overhead of resolving instantiations from + * unknown constructors + * example + * var myClass:Class = String; + * var myString:* = new myClass("test"); + * + * In the above example, the compiler will (by default) output + * a call to a Language.resolveUncertain method which wraps the 'new myClass("test")' + * + * + * This normalizes the return value for some primitive constructors, so that (for example) + * strict equality and inequality operators provide the same results between compiler + * targets. + * In situations where it is certain that the resolveUncertain method is not needed, + * this option provides a way to switch it off 'globally'. + * It can also be switched off or on locally using the '@royalesuppressresolveuncertain' + * doc comment compiler directive. + */ + @Config(advanced = true) + @Mapping("js-no-resolve-uncertain") + public void setJsNoResolveUncertain(ConfigurationValue cv, boolean value) + throws ConfigurationException + { + jsNoResolveUncertain = value; + } + + // + // 'js-no-vector-index-checks' + // + + private boolean jsNoVectorIndexChecks = false; + + public boolean getJsNoVectorIndexChecks() + { + return jsNoVectorIndexChecks; + } + + /** + * Support for avoiding more overhead of adding checks into + * assignments via Vector index access + * example + * var myVector:Vector.<int> = new Vector.<int>(); + * myVector[0] = 42; + * + * In the above example, the compiler will (by default) wrap + * the '0' inside myVector[0] with a method call on the vector instance + * that checks to see if the index is valid for the Vector it is being used against + * + * This check will throw an error if the index is out of range, and the + * range checking differs if the Vector is 'fixed' or non-'fixed' + * + * In situations where it is certain that the index will always be valid for Vector instance + * being targeted, or where all cases in a given codebase are certain to be valid, it is possible + * to avoid the overhead of this check. This is especially important in loops. + * This config setting affects the global setting for the current compilation. + * It can be adjusted locally within code, using the '@royalesuppressvectorindexcheck' + * doc comment compiler directive. + */ + @Config(advanced = true) + @Mapping("js-no-vector-index-checks") + public void setJsNoVectorIndexChecks(ConfigurationValue cv, boolean value) + throws ConfigurationException + { + jsNoVectorIndexChecks = value; + } + } diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java index b9e80bb..5457ed1 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/JSEmitter.java @@ -23,6 +23,7 @@ import java.io.FilterWriter; import java.util.ArrayList; import java.util.List; +import org.apache.royale.compiler.codegen.IASGlobalFunctionConstants; import org.apache.royale.compiler.codegen.IDocEmitter; import org.apache.royale.compiler.codegen.IEmitter; import org.apache.royale.compiler.codegen.ISubEmitter; @@ -32,6 +33,8 @@ import org.apache.royale.compiler.common.ISourceLocation; import org.apache.royale.compiler.constants.IASLanguageConstants; import org.apache.royale.compiler.constants.IMetaAttributeConstants; import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType; +import org.apache.royale.compiler.definitions.IAppliedVectorDefinition; +import org.apache.royale.compiler.definitions.IClassDefinition; import org.apache.royale.compiler.definitions.IDefinition; import org.apache.royale.compiler.definitions.metadata.IMetaTag; import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute; @@ -64,8 +67,10 @@ import org.apache.royale.compiler.internal.codegen.js.jx.UnaryOperatorEmitter; import org.apache.royale.compiler.internal.codegen.js.jx.WhileLoopEmitter; import org.apache.royale.compiler.internal.codegen.js.jx.WithEmitter; import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleDocEmitter; +import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens; +import org.apache.royale.compiler.internal.projects.RoyaleJSProject; import org.apache.royale.compiler.internal.semantics.SemanticUtils; -import org.apache.royale.compiler.internal.tree.as.FunctionNode; +import org.apache.royale.compiler.internal.tree.as.*; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.tree.as.IASNode; import org.apache.royale.compiler.tree.as.ICatchNode; @@ -97,6 +102,9 @@ import org.apache.royale.compiler.tree.as.IWithNode; import com.google.debugging.sourcemap.FilePosition; +import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens; +import org.apache.royale.compiler.utils.NativeUtils; + /** * @author Michael Schmalle */ @@ -220,12 +228,12 @@ public class JSEmitter extends ASEmitter implements IJSEmitter public void emitClosureStart() { - + } public void emitClosureEnd(IASNode node, IDefinition nodeDef) { - + } public void emitSourceMapDirective(ITypeNode node) @@ -501,7 +509,7 @@ public class JSEmitter extends ASEmitter implements IJSEmitter /** * Adjusts the line numbers saved in the source map when a line should be * removed during post processing. - * + * * @param lineIndex */ protected void removeLineFromMappings(int lineIndex) @@ -546,6 +554,7 @@ public class JSEmitter extends ASEmitter implements IJSEmitter } String coercionStart = null; String coercionEnd = null; + boolean avoidCoercion = false; if (project.getBuiltinType(BuiltinType.INT).equals(definition)) { boolean needsCoercion = false; @@ -566,6 +575,10 @@ public class JSEmitter extends ASEmitter implements IJSEmitter endMapping(assignedNode); return; } + else if(assignedNode instanceof BinaryOperatorAsNode) + { + needsCoercion = true; + } else if(!project.getBuiltinType(BuiltinType.INT).equals(assignedTypeDef)) { needsCoercion = true; @@ -596,6 +609,10 @@ public class JSEmitter extends ASEmitter implements IJSEmitter endMapping(assignedNode); return; } + else if(assignedNode instanceof BinaryOperatorAsNode) + { + needsCoercion = true; + } else if(!project.getBuiltinType(BuiltinType.UINT).equals(assignedTypeDef)) { needsCoercion = true; @@ -631,6 +648,8 @@ public class JSEmitter extends ASEmitter implements IJSEmitter if (t.equals(IASLanguageConstants.Number)) { needsCoercion = false; + //explicitly prevent other coercion detection rules from picking this up + avoidCoercion = true; } } } @@ -695,6 +714,183 @@ public class JSEmitter extends ASEmitter implements IJSEmitter 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> + startMapping(assignedNode); + write(JSRoyaleEmitterTokens.SYNTH_VECTOR); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + //the element type of the Vector: + write(((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)){ + startMapping(assignedNode); + 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; + } + 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) + || project.getBuiltinType(BuiltinType.CLASS).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 = true; + + if (((RoyaleJSProject)project).config.getJsNoComplexImplicitCoercions()) { + needsCoercion = false; + } + + IDocEmitter docEmitter = getDocEmitter(); + if (docEmitter instanceof JSRoyaleDocEmitter) + { + JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter; + //check for local toggle + needsCoercion = royaleDocEmitter.getLocalSettingAsBoolean( + JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION, needsCoercion); + 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 */ " + + JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken() + + ASEmitterTokens.MEMBER_ACCESS.getToken() + + ASEmitterTokens.AS.getToken() + + ASEmitterTokens.PAREN_OPEN.getToken(); + String coercionTypeString = definition.getQualifiedName(); + if (NativeUtils.isSyntheticJSType(coercionTypeString)) { + String synthCall; + String synthethicType; + if (NativeUtils.isVector(coercionTypeString)) { + synthCall = JSRoyaleEmitterTokens.SYNTH_VECTOR.getToken(); + synthethicType = coercionTypeString.substring(8, coercionTypeString.length() -1); + } else { + synthCall = 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) { diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/AsIsEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/AsIsEmitter.java index e8ffe98..9ddea56 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/AsIsEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/AsIsEmitter.java @@ -22,6 +22,7 @@ package org.apache.royale.compiler.internal.codegen.js.jx; import org.apache.royale.compiler.asdoc.royale.ASDocComment; import org.apache.royale.compiler.codegen.js.IJSEmitter; import org.apache.royale.compiler.constants.IASLanguageConstants; +import org.apache.royale.compiler.definitions.IAppliedVectorDefinition; import org.apache.royale.compiler.definitions.IClassDefinition; import org.apache.royale.compiler.definitions.IDefinition; import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens; @@ -29,12 +30,14 @@ import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter; import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter; import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens; import org.apache.royale.compiler.internal.projects.RoyaleJSProject; +import org.apache.royale.compiler.parsing.IASToken; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.tree.ASTNodeID; import org.apache.royale.compiler.tree.as.IASNode; import org.apache.royale.compiler.tree.as.IBinaryOperatorNode; import org.apache.royale.compiler.tree.as.IExpressionNode; import org.apache.royale.compiler.tree.as.IFunctionNode; +import org.apache.royale.compiler.utils.NativeUtils; public class AsIsEmitter extends JSSubEmitter { @@ -53,7 +56,7 @@ public class AsIsEmitter extends JSSubEmitter .resolve(getProject()) : null; if (id != ASTNodeID.Op_IsID && dnode != null) { - boolean emit = coercion ? + boolean emit = coercion ? !((RoyaleJSProject)getProject()).config.getJSOutputOptimizations().contains(JSRoyaleEmitterTokens.SKIP_FUNCTION_COERCIONS.getToken()) : !((RoyaleJSProject)getProject()).config.getJSOutputOptimizations().contains(JSRoyaleEmitterTokens.SKIP_AS_COERCIONS.getToken()); @@ -147,7 +150,7 @@ public class AsIsEmitter extends JSSubEmitter getEmitter().getModel().needLanguage = true; if (node instanceof IBinaryOperatorNode) { - IBinaryOperatorNode binaryOperatorNode = (IBinaryOperatorNode) node; + IBinaryOperatorNode binaryOperatorNode = (IBinaryOperatorNode) node; startMapping(node, binaryOperatorNode.getLeftOperandNode()); } else @@ -181,7 +184,29 @@ public class AsIsEmitter extends JSSubEmitter if (dnode instanceof IClassDefinition) { startMapping(right); - write(getEmitter().formatQualifiedName(((JSRoyaleEmitter)getEmitter()).convertASTypeToJS(dnode.getQualifiedName()))); + if (NativeUtils.isSyntheticJSType(dnode.getQualifiedName())) { + JSRoyaleEmitterTokens langMethod; + String synthName; + if (NativeUtils.isVector(dnode.getQualifiedName()) && dnode instanceof IAppliedVectorDefinition) { + langMethod = JSRoyaleEmitterTokens.SYNTH_VECTOR; + synthName = getEmitter().formatQualifiedName(((IAppliedVectorDefinition) dnode).resolveElementType(project).getQualifiedName()); + } else { + //non-vector, e.g. int/uint + langMethod = JSRoyaleEmitterTokens.SYNTH_TYPE; + synthName = getEmitter().formatQualifiedName(dnode.getQualifiedName()); + } + write(langMethod); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + write(synthName); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + if (project instanceof RoyaleJSProject) + ((RoyaleJSProject)project).needLanguage = true; + getEmitter().getModel().needLanguage = true; + } else { + write(getEmitter().formatQualifiedName(((JSRoyaleEmitter)getEmitter()).convertASTypeToJS(dnode.getQualifiedName()))); + } endMapping(right); } else diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java index 1533bf4..4d02be3 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/BinaryOperatorEmitter.java @@ -21,6 +21,7 @@ package org.apache.royale.compiler.internal.codegen.js.jx; import org.apache.royale.compiler.codegen.ISubEmitter; import org.apache.royale.compiler.codegen.js.IJSEmitter; +import org.apache.royale.compiler.constants.IASLanguageConstants; import org.apache.royale.compiler.definitions.IDefinition; import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens; @@ -30,10 +31,9 @@ import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter; import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens; import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogEmitterTokens; import org.apache.royale.compiler.internal.definitions.AccessorDefinition; +import org.apache.royale.compiler.internal.definitions.AppliedVectorDefinition; import org.apache.royale.compiler.internal.semantics.SemanticUtils; -import org.apache.royale.compiler.internal.tree.as.DynamicAccessNode; -import org.apache.royale.compiler.internal.tree.as.MemberAccessExpressionNode; -import org.apache.royale.compiler.internal.tree.as.UnaryOperatorAtNode; +import org.apache.royale.compiler.internal.tree.as.*; import org.apache.royale.compiler.tree.ASTNodeID; import org.apache.royale.compiler.tree.as.IASNode; import org.apache.royale.compiler.tree.as.IBinaryOperatorNode; @@ -100,7 +100,7 @@ public class BinaryOperatorEmitter extends JSSubEmitter implements { IASNode lnode = leftSide.getChild(0); IASNode rnode = leftSide.getChild(1); - IDefinition rnodeDef = (rnode instanceof IIdentifierNode) ? + IDefinition rnodeDef = (rnode instanceof IIdentifierNode) ? ((IIdentifierNode) rnode).resolve(getWalker().getProject()) : null; boolean isDynamicAccess = rnode instanceof DynamicAccessNode; @@ -117,7 +117,7 @@ public class BinaryOperatorEmitter extends JSSubEmitter implements else write(getEmitter().formatQualifiedName( getModel().getCurrentClass().getQualifiedName())); - + write(ASEmitterTokens.MEMBER_ACCESS); write(JSGoogEmitterTokens.SUPERCLASS); write(ASEmitterTokens.MEMBER_ACCESS); @@ -391,10 +391,11 @@ public class BinaryOperatorEmitter extends JSSubEmitter implements } } - - super_emitBinaryOperator(node, isAssignment); + + super_emitBinaryOperator(node, isAssignment); } } + private void super_emitBinaryOperator(IBinaryOperatorNode node, boolean isAssignment) { @@ -431,8 +432,27 @@ public class BinaryOperatorEmitter extends JSSubEmitter implements } else { - getWalker().walk(node.getLeftOperandNode()); - + if (isAssignment + && node.getLeftOperandNode() instanceof MemberAccessExpressionNode + && ((MemberAccessExpressionNode) node.getLeftOperandNode()).getRightOperandNode() instanceof IdentifierNode + && ((IdentifierNode) ((MemberAccessExpressionNode) node.getLeftOperandNode()).getRightOperandNode()).getName().equals("length") + && ((MemberAccessExpressionNode) node.getLeftOperandNode()).getLeftOperandNode().resolveType(getProject()) instanceof AppliedVectorDefinition) + { + //for vectors, when setting length, we need to set it on the associated 'synthType' instance which tags the native + //Array representation of the Vector. This allows running 'setter' code because it is not possible to override the native length setter on Array + //unless using a different approach, like es6 Proxy. + //this code inserts the extra access name for setting length, e.g. myVectInstance['_synthType'].length = assignedValue + //the dynamic access field name is a constant on Language, so it can be different/shorter in release build + getWalker().walk(((MemberAccessExpressionNode) node.getLeftOperandNode()).getLeftOperandNode()); + write(ASEmitterTokens.SQUARE_OPEN); + write(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken()); + write(ASEmitterTokens.MEMBER_ACCESS); + write(JSRoyaleEmitterTokens.ROYALE_SYNTH_TAG_FIELD_NAME); + write(ASEmitterTokens.SQUARE_CLOSE); + write(ASEmitterTokens.MEMBER_ACCESS); + getWalker().walk(((MemberAccessExpressionNode) node.getLeftOperandNode()).getRightOperandNode()); + } + else getWalker().walk(node.getLeftOperandNode()); startMapping(node, node.getLeftOperandNode()); if (id != ASTNodeID.Op_CommaID) @@ -469,12 +489,14 @@ public class BinaryOperatorEmitter extends JSSubEmitter implements } else { + getWalker().walk(node.getRightOperandNode()); + if (node.getNodeID() == ASTNodeID.Op_InID && ((JSRoyaleEmitter)getEmitter()).isXML(node.getRightOperandNode())) { write(".elementNames()"); - } + } else if (node.getNodeID() == ASTNodeID.Op_InID && ((JSRoyaleEmitter)getEmitter()).isProxy(node.getRightOperandNode())) { diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/DynamicAccessEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/DynamicAccessEmitter.java index 2fd5085..9c65d4f 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/DynamicAccessEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/DynamicAccessEmitter.java @@ -19,20 +19,30 @@ package org.apache.royale.compiler.internal.codegen.js.jx; +import org.apache.royale.compiler.codegen.IDocEmitter; import org.apache.royale.compiler.codegen.ISubEmitter; import org.apache.royale.compiler.codegen.js.IJSEmitter; import org.apache.royale.compiler.constants.IASLanguageConstants; import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens; +import org.apache.royale.compiler.internal.codegen.js.JSEmitterTokens; import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter; +import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleDocEmitter; import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter; +import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens; +import org.apache.royale.compiler.internal.definitions.AppliedVectorDefinition; +import org.apache.royale.compiler.internal.projects.RoyaleJSProject; +import org.apache.royale.compiler.internal.tree.as.BinaryOperatorAssignmentNode; import org.apache.royale.compiler.internal.tree.as.FunctionCallNode; +import org.apache.royale.compiler.internal.tree.as.IdentifierNode; import org.apache.royale.compiler.internal.tree.as.MemberAccessExpressionNode; +import org.apache.royale.compiler.parsing.IASToken; import org.apache.royale.compiler.tree.ASTNodeID; import org.apache.royale.compiler.tree.as.IDynamicAccessNode; import org.apache.royale.compiler.tree.as.IExpressionNode; import org.apache.royale.compiler.tree.as.ILiteralNode; import org.apache.royale.compiler.tree.as.IOperatorNode.OperatorType; +import org.apache.royale.compiler.utils.NativeUtils; public class DynamicAccessEmitter extends JSSubEmitter implements ISubEmitter<IDynamicAccessNode> @@ -98,8 +108,55 @@ public class DynamicAccessEmitter extends JSSubEmitter implements startMapping(node, leftOperandNode); write(ASEmitterTokens.SQUARE_OPEN); endMapping(node); + boolean wrapVectorIndex = false; + if (getProject() instanceof RoyaleJSProject) { + if (node.getNodeID().equals(ASTNodeID.ArrayIndexExpressionID)){ + if (node.getParent() instanceof BinaryOperatorAssignmentNode) { + if (node.getLeftOperandNode().resolveType(getProject()) instanceof AppliedVectorDefinition) { + boolean suppressVectorIndexCheck = false; + if (((RoyaleJSProject)getProject()).config.getJsNoVectorIndexChecks()) { + //wrapVectorIndex = false; + suppressVectorIndexCheck = true; + } + IDocEmitter docEmitter = getEmitter().getDocEmitter(); + if (docEmitter instanceof JSRoyaleDocEmitter) + { + JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter; + //check for local toggle + suppressVectorIndexCheck = royaleDocEmitter.getLocalSettingAsBoolean( + JSRoyaleEmitterTokens.SUPPRESS_VECTOR_INDEX_CHECK, suppressVectorIndexCheck); + + if (!suppressVectorIndexCheck) { + //check for individual specified suppression, based on variable name + if (leftOperandNode instanceof IdentifierNode) { + if (royaleDocEmitter.getLocalSettingIncludesString( + JSRoyaleEmitterTokens.SUPPRESS_VECTOR_INDEX_CHECK, + ((IdentifierNode) leftOperandNode).getName() + )){ + suppressVectorIndexCheck = true; + } + } + } + } + if (!suppressVectorIndexCheck) { + getModel().needLanguage = true; + ((RoyaleJSProject) getProject()).needLanguage = true; + getWalker().walk(leftOperandNode); + write(ASEmitterTokens.SQUARE_OPEN); + write(JSRoyaleEmitterTokens.VECTOR_INDEX_CHECK_METHOD_NAME); + write(ASEmitterTokens.SQUARE_CLOSE); + write(ASEmitterTokens.PAREN_OPEN); + wrapVectorIndex = true; + } + } + } + } + } getWalker().walk(rightOperandNode); + if (wrapVectorIndex) { + write(ASEmitterTokens.PAREN_CLOSE); + } if (type != null && type.getQualifiedName().contentEquals(IASLanguageConstants.QName)) write(".objectAccessFormat()"); startMapping(node, rightOperandNode); diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/FunctionCallEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/FunctionCallEmitter.java index 1e0bae0..84e5f84 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/FunctionCallEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/FunctionCallEmitter.java @@ -20,8 +20,10 @@ package org.apache.royale.compiler.internal.codegen.js.jx; import org.apache.royale.compiler.codegen.IASGlobalFunctionConstants; +import org.apache.royale.compiler.codegen.IDocEmitter; import org.apache.royale.compiler.codegen.ISubEmitter; import org.apache.royale.compiler.codegen.js.IJSEmitter; +import org.apache.royale.compiler.common.SourceLocation; import org.apache.royale.compiler.constants.IASLanguageConstants; import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType; import org.apache.royale.compiler.definitions.IDefinition; @@ -29,28 +31,18 @@ import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens; import org.apache.royale.compiler.internal.codegen.js.JSSessionModel; import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter; +import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleDocEmitter; import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter; import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens; import org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils; -import org.apache.royale.compiler.internal.definitions.AppliedVectorDefinition; -import org.apache.royale.compiler.internal.definitions.ClassDefinition; -import org.apache.royale.compiler.internal.definitions.FunctionDefinition; -import org.apache.royale.compiler.internal.definitions.InterfaceDefinition; -import org.apache.royale.compiler.internal.definitions.NamespaceDefinition; +import org.apache.royale.compiler.internal.definitions.*; import org.apache.royale.compiler.internal.projects.RoyaleJSProject; -import org.apache.royale.compiler.internal.tree.as.ContainerNode; -import org.apache.royale.compiler.internal.tree.as.IdentifierNode; -import org.apache.royale.compiler.internal.tree.as.MemberAccessExpressionNode; -import org.apache.royale.compiler.internal.tree.as.NamespaceIdentifierNode; -import org.apache.royale.compiler.internal.tree.as.VectorLiteralNode; +import org.apache.royale.compiler.internal.tree.as.*; import org.apache.royale.compiler.problems.TooFewFunctionParametersProblem; import org.apache.royale.compiler.problems.TooManyFunctionParametersProblem; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.tree.ASTNodeID; -import org.apache.royale.compiler.tree.as.IASNode; -import org.apache.royale.compiler.tree.as.IContainerNode; -import org.apache.royale.compiler.tree.as.IExpressionNode; -import org.apache.royale.compiler.tree.as.IFunctionCallNode; +import org.apache.royale.compiler.tree.as.*; import org.apache.royale.compiler.utils.NativeUtils; public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFunctionCallNode> @@ -66,7 +58,6 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu { // TODO (mschmalle) will remove this cast as more things get abstracted JSRoyaleEmitter fjs = (JSRoyaleEmitter) getEmitter(); - IASNode cnode = node.getChild(0); if (cnode.getNodeID() == ASTNodeID.MemberAccessExpressionID) @@ -80,16 +71,42 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu def = nameNode.resolve(getProject()); boolean isClassCast = false; - + boolean wrapResolve = false; if (node.isNewExpression()) { - if (!(node.getChild(1) instanceof VectorLiteralNode)) + boolean omitNew = false; + if (nameNode instanceof IdentifierNode + && (((IdentifierNode) nameNode).getName().equals(IASLanguageConstants.String) + || ((IdentifierNode) nameNode).getName().equals(IASLanguageConstants.Boolean) + || ((IdentifierNode) nameNode).getName().equals(IASLanguageConstants.Number))) + { + omitNew = true; + } + + if (!((node.getChild(1) instanceof VectorLiteralNode))) { - if (def == null || !(def.getBaseName().equals(IASGlobalFunctionConstants._int) || - def.getBaseName().equals(IASGlobalFunctionConstants.uint) || - def instanceof AppliedVectorDefinition)) + if (!omitNew + && (def == null + || !(def.getBaseName().equals(IASGlobalFunctionConstants._int) + || def.getBaseName().equals(IASGlobalFunctionConstants.uint))) + ) { - startMapping(node.getNewKeywordNode()); + if (getProject() instanceof RoyaleJSProject + && nameNode.resolveType(getProject()) != null + && nameNode.resolveType(getProject()).getQualifiedName().equals("Class")) { + + wrapResolve = shouldResolveUncertain(nameNode, false); + + if (wrapResolve) { + ((RoyaleJSProject) getProject()).needLanguage = true; + getModel().needLanguage = true; + write(JSRoyaleEmitterTokens.LANGUAGE_QNAME); + write(ASEmitterTokens.MEMBER_ACCESS); + write("resolveUncertain"); + write(ASEmitterTokens.PAREN_OPEN); + } + } + startMapping(node.getNewKeywordNode()); writeToken(ASEmitterTokens.NEW); endMapping(node.getNewKeywordNode()); } @@ -98,13 +115,46 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu { VectorLiteralNode vectorLiteralNode = (VectorLiteralNode) node.getChild(1); String vectorClassName = (((RoyaleJSProject)fjs.getWalker().getProject()).config.getJsVectorEmulationClass()); + SourceLocation mappingLocation = null; if (vectorClassName != null) { writeToken(ASEmitterTokens.NEW); write(vectorClassName); write(ASEmitterTokens.PAREN_OPEN); + } else { + //no 'new' output in this case, just coercion, so map from the start of 'new' + startMapping(node); + write(JSRoyaleEmitterTokens.SYNTH_VECTOR); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + //the element type of the Vector: + write(((AppliedVectorDefinition)def).resolveElementType(getWalker().getProject()).getQualifiedName()); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + write(ASEmitterTokens.SQUARE_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + write("coerce"); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.SQUARE_CLOSE); + mappingLocation = new SourceLocation(vectorLiteralNode.getCollectionTypeNode()); + mappingLocation.setEndColumn(mappingLocation.getEndColumn() + 1); + endMapping(mappingLocation); + write(ASEmitterTokens.PAREN_OPEN); + if (getProject() instanceof RoyaleJSProject) + ((RoyaleJSProject)getProject()).needLanguage = true; + getEmitter().getModel().needLanguage = true; + } + mappingLocation = new SourceLocation(vectorLiteralNode.getContentsNode()); + if (mappingLocation.getColumn()>0) mappingLocation.setColumn(mappingLocation.getColumn() -1); + mappingLocation.setEndColumn(mappingLocation.getColumn()+1); + startMapping(mappingLocation); write("["); + + /*mappingLocation = new SourceLocation(vectorLiteralNode.getContentsNode()); + mappingLocation.setLine(vectorLiteralNode.getContentsNode().getLine()); + mappingLocation.setColumn(vectorLiteralNode.getContentsNode().getColumn() + 1);*/ + endMapping(mappingLocation); ContainerNode contentsNode = vectorLiteralNode.getContentsNode(); int len = contentsNode.getChildCount(); for (int i = 0; i < len; i++) @@ -115,7 +165,13 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu writeToken(ASEmitterTokens.COMMA); } } + mappingLocation = new SourceLocation(vectorLiteralNode.getContentsNode()); + mappingLocation.setLine(vectorLiteralNode.getContentsNode().getEndLine()); + mappingLocation.setColumn(vectorLiteralNode.getContentsNode().getEndColumn()); + mappingLocation.setEndColumn(mappingLocation.getColumn() + 1); + startMapping(mappingLocation); write("]"); + endMapping(mappingLocation); if (vectorClassName != null) { writeToken(ASEmitterTokens.COMMA); @@ -123,6 +179,8 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu write(((AppliedVectorDefinition)def).resolveElementType(getWalker().getProject()).getBaseName()); write(ASEmitterTokens.SINGLE_QUOTE); write(ASEmitterTokens.PAREN_CLOSE); + } else { + write(ASEmitterTokens.PAREN_CLOSE); } return; } @@ -131,12 +189,15 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu { def = node.getNameNode().resolve(getProject()); - isClassCast = (def instanceof ClassDefinition || def instanceof InterfaceDefinition) + isClassCast = def != null && (def instanceof ClassDefinition + || def instanceof InterfaceDefinition + || ( def instanceof VariableDefinition && ((VariableDefinition) def).resolveType(getProject()).getBaseName().equals("Class"))) && !(NativeUtils.isJSNative(def.getBaseName())) && !def.getBaseName().equals(IASLanguageConstants.XML) && !def.getBaseName().equals(IASLanguageConstants.XMLList); + } - + if (node.isNewExpression()) { def = node.resolveCalledExpression(getProject()); @@ -177,30 +238,23 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu if (nameNode.hasParenthesis()) write(ASEmitterTokens.PAREN_CLOSE); } - - if (def instanceof AppliedVectorDefinition) + if ( def instanceof AppliedVectorDefinition + && (fjs.getWalker().getProject() instanceof RoyaleJSProject) + && (((RoyaleJSProject)fjs.getWalker().getProject()).config.getJsVectorEmulationClass() != null)) { - ContainerNode args = node.getArgumentsNode(); - if (args.getChildCount() == 0) - { - String vectorClassName = (((RoyaleJSProject)fjs.getWalker().getProject()).config.getJsVectorEmulationClass()); - if (vectorClassName != null) - { - write(ASEmitterTokens.PAREN_OPEN); - write(ASEmitterTokens.SQUARE_OPEN); - write(ASEmitterTokens.SQUARE_CLOSE); - write(ASEmitterTokens.COMMA); - write(ASEmitterTokens.SPACE); - write(ASEmitterTokens.SINGLE_QUOTE); - write(((AppliedVectorDefinition)def).resolveElementType(getWalker().getProject()).getBaseName()); - write(ASEmitterTokens.SINGLE_QUOTE); - write(ASEmitterTokens.PAREN_CLOSE); - } - else - getEmitter().emitArguments(node.getArgumentsNode()); - } - else - { + ContainerNode args = node.getArgumentsNode(); + if (args.getChildCount() == 0) + { + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SQUARE_OPEN); + write(ASEmitterTokens.SQUARE_CLOSE); + write(ASEmitterTokens.COMMA); + write(ASEmitterTokens.SPACE); + write(ASEmitterTokens.SINGLE_QUOTE); + write(((AppliedVectorDefinition)def).resolveElementType(getWalker().getProject()).getQualifiedName()); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + } else { startMapping(node); write(ASEmitterTokens.PAREN_OPEN); endMapping(node); @@ -208,28 +262,32 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu write(ASEmitterTokens.COMMA); write(ASEmitterTokens.SPACE); write(ASEmitterTokens.SINGLE_QUOTE); - write(((AppliedVectorDefinition)def).resolveElementType(getWalker().getProject()).getBaseName()); + write(((AppliedVectorDefinition)def).resolveElementType(getWalker().getProject()).getQualifiedName()); write(ASEmitterTokens.SINGLE_QUOTE); if (args.getChildCount() == 2) { - IASNode second = args.getChild(1); - if (second instanceof IExpressionNode) - { - ITypeDefinition secondType = - ((IExpressionNode)second).resolveType(fjs.getWalker().getProject()); - if (fjs.getWalker().getProject().getBuiltinType(BuiltinType.BOOLEAN).equals(secondType)) - { + IASNode second = args.getChild(1); + if (second instanceof IExpressionNode) + { + ITypeDefinition secondType = + ((IExpressionNode)second).resolveType(fjs.getWalker().getProject()); + if (fjs.getWalker().getProject().getBuiltinType(BuiltinType.BOOLEAN).equals(secondType)) + { write(ASEmitterTokens.COMMA); write(ASEmitterTokens.SPACE); - getWalker().walk(second); - } - } + getWalker().walk(second); + } + } } write(ASEmitterTokens.PAREN_CLOSE); - } + } + } else { + getEmitter().emitArguments(node.getArgumentsNode()); + } + //end wrap resolve + if (wrapResolve) { + write(ASEmitterTokens.PAREN_CLOSE); } - else - getEmitter().emitArguments(node.getArgumentsNode()); } else if (!isClassCast) { @@ -300,7 +358,35 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu } } } - + else if ((def.getBaseName().equals("insertAt") + || def.getBaseName().equals("removeAt")) + && def.getParent() instanceof AppliedVectorDefinition) { + //unlike Array implementation of these methods, the synthetic Vector implementation supports these methods at runtime, + //and they behave differently with fixed length vectors compared to the native 'splice' method output which is used to + //support them in Array, however they are not protected from GCL renaming in release builds by any actual class definition, + //so we explicitly 'protect' them here + + ExpressionNodeBase leftSide = (ExpressionNodeBase)(((BinaryOperatorNodeBase) (node.getNameNode())).getLeftOperandNode()); + LiteralNode dynamicName = new LiteralNode(ILiteralNode.LiteralType.STRING, "'" + def.getBaseName() + "'"); + dynamicName.setSourceLocation(((BinaryOperatorNodeBase) (node.getNameNode())).getRightOperandNode()); + DynamicAccessNode replacement = new DynamicAccessNode(leftSide); + leftSide.setParent(replacement); + replacement.setSourceLocation(node.getNameNode()); + replacement.setRightOperandNode(dynamicName); + dynamicName.setParent(replacement); + + FunctionCallNode replacer = new FunctionCallNode(replacement) ; + replacement.setParent(replacer); + IExpressionNode[] args = node.getArgumentNodes(); + for (IExpressionNode arg : args) { + replacer.getArgumentsNode().addItem((NodeBase) arg); + } + replacer.getArgumentsNode().setParent(replacer); + replacer.getArgumentsNode().setSourceLocation(node.getArgumentsNode()); + replacer.setParent((NodeBase) node.getParent()); + //swap it out + node = replacer; + } else if (def instanceof AppliedVectorDefinition) { IExpressionNode[] argumentNodes = node.getArgumentNodes(); @@ -315,9 +401,25 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu } else { - IExpressionNode argumentNode = argumentNodes[0]; - getWalker().walk(argumentNode); - write(".slice()"); + startMapping(node.getNameNode()); + write(JSRoyaleEmitterTokens.SYNTH_VECTOR); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + //the element type of the Vector: + write(((TypedExpressionNode)nameNode).getTypeNode().resolve(getProject()).getQualifiedName()); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + write(ASEmitterTokens.SQUARE_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + write("coerce"); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.SQUARE_CLOSE); + endMapping(node.getNameNode()); + + getEmitter().emitArguments(node.getArgumentsNode()); + if (getProject() instanceof RoyaleJSProject) + ((RoyaleJSProject)getProject()).needLanguage = true; + getEmitter().getModel().needLanguage = true; } return; } @@ -327,6 +429,22 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu getEmitter().emitArguments(node.getArgumentsNode()); return; } + else if (def.getQualifiedName().equals(IASLanguageConstants.Object)) { + //'resolveUncertain' always output here + //unless a) there are no arguments + //or b) it is *explicitly* suppressed for 'Object' + if (node.getArgumentNodes().length > 0) { + if (shouldResolveUncertain(nameNode, true)) { + wrapResolve = true; + ((RoyaleJSProject) getProject()).needLanguage = true; + getModel().needLanguage = true; + write(JSRoyaleEmitterTokens.LANGUAGE_QNAME); + write(ASEmitterTokens.MEMBER_ACCESS); + write("resolveUncertain"); + write(ASEmitterTokens.PAREN_OPEN); + } + } + } else if (nameNode.getNodeID() == ASTNodeID.NamespaceAccessExpressionID && def instanceof FunctionDefinition) { if (fjs.isCustomNamespace((FunctionDefinition)def)) @@ -336,7 +454,7 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu NamespaceDefinition nsDef = (NamespaceDefinition)nin.resolve(getProject()); IdentifierNode idNode = (IdentifierNode)nameNode.getChild(1); String propName = idNode.getName(); - fjs.formatQualifiedName(nsDef.getQualifiedName()); // register with used names + fjs.formatQualifiedName(nsDef.getQualifiedName()); // register with used names String s = nsDef.getURI(); write(JSRoyaleEmitter.formatNamespacedProperty(s, propName, true)); getEmitter().emitArguments(node.getArgumentsNode()); @@ -367,6 +485,11 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu getWalker().walk(node.getNameNode()); getEmitter().emitArguments(node.getArgumentsNode()); + + //end wrap resolve + if (wrapResolve) { + write(ASEmitterTokens.PAREN_CLOSE); + } } else //function-style cast { @@ -378,5 +501,34 @@ public class FunctionCallEmitter extends JSSubEmitter implements ISubEmitter<IFu fjs.emitSuperCall(node, JSSessionModel.SUPER_FUNCTION_CALL); } } + + + private boolean shouldResolveUncertain(IExpressionNode nameNode, boolean forceExplicit) { + //default if not avoided globally + boolean should = true; + //just in case: + if (!(getProject() instanceof RoyaleJSProject)) return false; + if (((RoyaleJSProject)getProject()).config.getJsNoResolveUncertain()) { + //start with global toggle + should = false; + } + IDocEmitter docEmitter = getEmitter().getDocEmitter(); + if (docEmitter instanceof JSRoyaleDocEmitter) + { + JSRoyaleDocEmitter royaleDocEmitter = (JSRoyaleDocEmitter) docEmitter; + //look for local boolean toggle, unless forceExplicit is set + boolean suppress = !forceExplicit && royaleDocEmitter.getLocalSettingAsBoolean( + JSRoyaleEmitterTokens.SUPPRESS_RESOLVE_UNCERTAIN, !should); + //if it is still on, look for sepcific/named 'off' setting based on name node + if (!suppress && nameNode !=null) { + //check to suppress for indvidual named node + if (nameNode instanceof IdentifierNode) { + suppress = royaleDocEmitter.getLocalSettingIncludesString(JSRoyaleEmitterTokens.SUPPRESS_RESOLVE_UNCERTAIN, ((IdentifierNode) nameNode).getName()); + } + } + should = !suppress; + } + return should; + } } diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/IdentifierEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/IdentifierEmitter.java index 26d8649..4d42069 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/IdentifierEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/IdentifierEmitter.java @@ -34,18 +34,15 @@ import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens; import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter; import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitter; import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogEmitterTokens; +import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens; import org.apache.royale.compiler.internal.codegen.js.utils.EmitterUtils; -import org.apache.royale.compiler.internal.definitions.AccessorDefinition; -import org.apache.royale.compiler.internal.definitions.ClassDefinition; -import org.apache.royale.compiler.internal.definitions.FunctionDefinition; -import org.apache.royale.compiler.internal.definitions.TypeDefinitionBase; +import org.apache.royale.compiler.internal.definitions.*; +import org.apache.royale.compiler.internal.tree.as.BinaryOperatorAssignmentNode; +import org.apache.royale.compiler.internal.tree.as.BinaryOperatorDivisionAssignmentNode; +import org.apache.royale.compiler.internal.tree.as.MemberAccessExpressionNode; import org.apache.royale.compiler.internal.tree.as.NonResolvingIdentifierNode; import org.apache.royale.compiler.tree.ASTNodeID; -import org.apache.royale.compiler.tree.as.IASNode; -import org.apache.royale.compiler.tree.as.IFunctionNode; -import org.apache.royale.compiler.tree.as.IFunctionObjectNode; -import org.apache.royale.compiler.tree.as.IIdentifierNode; -import org.apache.royale.compiler.tree.as.IMemberAccessExpressionNode; +import org.apache.royale.compiler.tree.as.*; import org.apache.royale.compiler.utils.NativeUtils; public class IdentifierEmitter extends JSSubEmitter implements @@ -239,7 +236,7 @@ public class IdentifierEmitter extends JSSubEmitter implements { Namespace ns = (Namespace)((INamespaceResolvedReference)((FunctionDefinition)nodeDef).getNamespaceReference()).resolveAETNamespace(getProject()); INamespaceDefinition nsDef = ((FunctionDefinition)nodeDef).getNamespaceReference().resolveNamespaceReference(getProject()); - fjs.formatQualifiedName(nsDef.getQualifiedName()); // register with used names + fjs.formatQualifiedName(nsDef.getQualifiedName()); // register with used names String nsName = ns.getName(); write(JSRoyaleEmitter.formatNamespacedProperty(nsName, node.getName(), true)); } @@ -315,20 +312,31 @@ public class IdentifierEmitter extends JSSubEmitter implements } else if (identifierIsAccessorFunction && isStatic) { - write("[\"" +node.getName() + "\"]"); + write("[\"" +node.getName() + "\"]"); } else { qname = node.getName(); if (nodeDef != null && !isStatic && (nodeDef.getParent() instanceof ClassDefinition) && (!(nodeDef instanceof IParameterDefinition)) && nodeDef.isPrivate() && getProject().getAllowPrivateNameConflicts()) qname = getEmitter().formatPrivateName(nodeDef.getParent().getQualifiedName(), qname); - write(qname); + write(qname); } } else if (isPackageOrFileMember) write(getEmitter().formatQualifiedName(qname)); else if (nodeDef instanceof TypeDefinitionBase) - write(getEmitter().formatQualifiedName(qname)); + { + if (NativeUtils.isSyntheticJSType(qname) && !(parentNode instanceof IFunctionCallNode)) { + getEmitter().getModel().needLanguage = true; + write(JSRoyaleEmitterTokens.SYNTH_TYPE); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + write(getEmitter().formatQualifiedName(qname)); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + } + else write(getEmitter().formatQualifiedName(qname)); + } else if (isCustomNamespace) { String ns = ((INamespaceResolvedReference)((FunctionDefinition)nodeDef).getNamespaceReference()).resolveAETNamespace(getProject()).getName(); @@ -336,9 +344,9 @@ public class IdentifierEmitter extends JSSubEmitter implements } else if (identifierIsAccessorFunction && isStatic) { - write("[\"" + qname + "\"]"); + write("[\"" + qname + "\"]"); } - else + else { if (nodeDef != null && !isStatic && (nodeDef.getParent() instanceof ClassDefinition) && (!(nodeDef instanceof IParameterDefinition)) && nodeDef.isPrivate() && getProject().getAllowPrivateNameConflicts()) qname = getEmitter().formatPrivateName(nodeDef.getParent().getQualifiedName(), qname); @@ -353,7 +361,7 @@ public class IdentifierEmitter extends JSSubEmitter implements write("child('"); write(node.getName()); write("')"); - endMapping(node); + endMapping(node); } else { @@ -363,5 +371,7 @@ public class IdentifierEmitter extends JSSubEmitter implements } } } + + } diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java index 23e5ec0..968b78b 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MemberAccessEmitter.java @@ -34,22 +34,15 @@ import org.apache.royale.compiler.internal.definitions.AccessorDefinition; import org.apache.royale.compiler.internal.definitions.AppliedVectorDefinition; import org.apache.royale.compiler.internal.definitions.FunctionDefinition; import org.apache.royale.compiler.internal.projects.RoyaleJSProject; -import org.apache.royale.compiler.internal.tree.as.DynamicAccessNode; -import org.apache.royale.compiler.internal.tree.as.FunctionCallNode; -import org.apache.royale.compiler.internal.tree.as.GetterNode; -import org.apache.royale.compiler.internal.tree.as.IdentifierNode; -import org.apache.royale.compiler.internal.tree.as.MemberAccessExpressionNode; -import org.apache.royale.compiler.internal.tree.as.NamespaceAccessExpressionNode; +import org.apache.royale.compiler.internal.tree.as.*; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.tree.ASTNodeID; -import org.apache.royale.compiler.tree.as.IASNode; -import org.apache.royale.compiler.tree.as.IExpressionNode; -import org.apache.royale.compiler.tree.as.IIdentifierNode; -import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode; -import org.apache.royale.compiler.tree.as.IMemberAccessExpressionNode; +import org.apache.royale.compiler.tree.as.*; import org.apache.royale.compiler.tree.as.IOperatorNode.OperatorType; import org.apache.royale.compiler.utils.ASNodeUtils; +import javax.sound.midi.SysexMessage; + public class MemberAccessEmitter extends JSSubEmitter implements ISubEmitter<IMemberAccessExpressionNode> { @@ -199,21 +192,6 @@ public class MemberAccessEmitter extends JSSubEmitter implements return; } } - else if (def.getParent() instanceof AppliedVectorDefinition) - { - if (def.getBaseName().equals("removeAt")) - { - writeLeftSide(node, leftNode, rightNode); - write(".splice"); - return; - } - else if (def.getBaseName().equals("insertAt")) - { - writeLeftSide(node, leftNode, rightNode); - write(".splice"); - return; - } - } else if (rightNode instanceof NamespaceAccessExpressionNode) { boolean isStatic = false; diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MethodEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MethodEmitter.java index aad1184..0fc7b0b 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MethodEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/MethodEmitter.java @@ -108,8 +108,12 @@ public class MethodEmitter extends JSSubEmitter implements } endMapping(node.getNameExpressionNode()); } - - startMapping(node); + if (node.getMetaTags() != null) { + //offset mapping by any metadata tags that will be in the first child node + startMapping(node.getChild(1)); + } else { + startMapping(node); + } write(ASEmitterTokens.SPACE); writeToken(ASEmitterTokens.EQUAL); write(ASEmitterTokens.FUNCTION); diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/UnaryOperatorEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/UnaryOperatorEmitter.java index b575d55..bc040e7 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/UnaryOperatorEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/UnaryOperatorEmitter.java @@ -23,8 +23,12 @@ import org.apache.royale.compiler.codegen.ISubEmitter; import org.apache.royale.compiler.codegen.js.IJSEmitter; import org.apache.royale.compiler.internal.codegen.as.ASEmitterTokens; import org.apache.royale.compiler.internal.codegen.js.JSSubEmitter; +import org.apache.royale.compiler.internal.codegen.js.royale.JSRoyaleEmitterTokens; +import org.apache.royale.compiler.internal.definitions.AppliedVectorDefinition; +import org.apache.royale.compiler.internal.tree.as.*; import org.apache.royale.compiler.tree.ASTNodeID; import org.apache.royale.compiler.tree.as.IExpressionNode; +import org.apache.royale.compiler.tree.as.ILiteralNode; import org.apache.royale.compiler.tree.as.IUnaryOperatorNode; import org.apache.royale.compiler.utils.ASNodeUtils; @@ -41,6 +45,31 @@ public class UnaryOperatorEmitter extends JSSubEmitter implements { if (ASNodeUtils.hasParenOpen(node)) write(ASEmitterTokens.PAREN_OPEN); + + Boolean isAssignment = (node.getNodeID() == ASTNodeID.Op_PreIncrID + || node.getNodeID() == ASTNodeID.Op_PreDecrID + || node.getNodeID() == ASTNodeID.Op_PostIncrID + || node.getNodeID() == ASTNodeID.Op_PostDecrID); + + if (isAssignment && (node.getOperandNode() instanceof MemberAccessExpressionNode) + && (((MemberAccessExpressionNode)(node.getOperandNode())).getRightOperandNode() instanceof IdentifierNode) + && ((IdentifierNode)(((MemberAccessExpressionNode)(node.getOperandNode())).getRightOperandNode())).getName().equals("length") + && ((MemberAccessExpressionNode)(node.getOperandNode())).getLeftOperandNode().resolveType(getProject()) instanceof AppliedVectorDefinition + ) { + //support for output of alternate length setter, example: vectorInst.length++ as vectorInst['_synthType'].length++ + //likewise for pre/post increment/decrement + + String synthTagName = JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken() + ASEmitterTokens.MEMBER_ACCESS.getToken() + JSRoyaleEmitterTokens.ROYALE_SYNTH_TAG_FIELD_NAME.getToken(); + LiteralNode synthType = new LiteralNode(ILiteralNode.LiteralType.STRING, synthTagName); + synthType.setSynthetic(true); + DynamicAccessNode patchedVectorReference = new DynamicAccessNode(((ExpressionNodeBase)((MemberAccessExpressionNode) node.getOperandNode()).getLeftOperandNode())); + ((ExpressionNodeBase)((MemberAccessExpressionNode) node.getOperandNode()).getLeftOperandNode()).setParent(patchedVectorReference); + patchedVectorReference.setRightOperandNode(synthType); + synthType.setParent(patchedVectorReference); + patchedVectorReference.setParent((NodeBase) node.getOperandNode()); + patchedVectorReference.setSourceLocation(((MemberAccessExpressionNode) node.getOperandNode()).getLeftOperandNode()); + ((MemberAccessExpressionNode) node.getOperandNode()).setLeftOperandNode(patchedVectorReference); + } if (node.getNodeID() == ASTNodeID.Op_PreIncrID || node.getNodeID() == ASTNodeID.Op_PreDecrID diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleDocEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleDocEmitter.java index 8cfd1c9..1ab5268 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleDocEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleDocEmitter.java @@ -19,8 +19,7 @@ package org.apache.royale.compiler.internal.codegen.js.royale; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import org.apache.royale.compiler.asdoc.royale.ASDocComment; import org.apache.royale.compiler.codegen.as.IASEmitter; @@ -41,6 +40,7 @@ import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogDocEmitter; import org.apache.royale.compiler.internal.codegen.js.jx.BindableEmitter; import org.apache.royale.compiler.internal.projects.RoyaleJSProject; import org.apache.royale.compiler.internal.scopes.ASScope; +import org.apache.royale.compiler.parsing.IASToken; import org.apache.royale.compiler.problems.PublicVarWarningProblem; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.tree.ASTNodeID; @@ -53,6 +53,7 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter private List<String> classIgnoreList; private List<String> ignoreList; private List<String> coercionList; + private Map<String,List<String>> localSettings; public boolean emitStringConversions = true; private boolean emitExports = true; private boolean exportProtected = false; @@ -128,6 +129,7 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter coercionList = null; ignoreList = null; + localSettings = null; emitStringConversions = true; IClassDefinition classDefinition = resolveClassDefinition(node); @@ -228,6 +230,22 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter .getToken(); if (docText.contains(noStringToken)) emitStringConversions = false; + + String noImplicitComplexCoercion = JSRoyaleEmitterTokens.SUPPRESS_COMPLEX_IMPLICIT_COERCION + .getToken(); + if (docText.contains(noImplicitComplexCoercion)) + loadLocalSettings(docText, noImplicitComplexCoercion, "true"); + + String noResolveUncertain = JSRoyaleEmitterTokens.SUPPRESS_RESOLVE_UNCERTAIN + .getToken(); + if (docText.contains(noResolveUncertain)) + loadLocalSettings(docText,noResolveUncertain, "true"); + + String suppressVectorIndexCheck = JSRoyaleEmitterTokens.SUPPRESS_VECTOR_INDEX_CHECK + .getToken(); + if (docText.contains(suppressVectorIndexCheck)) + loadLocalSettings(docText,suppressVectorIndexCheck, "true"); + write(changeAnnotations(asDoc.commentNoEnd())); } else @@ -312,6 +330,82 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter end(); } } + + /** + * + */ + private void loadLocalSettings(String doc, String settingToken, String defaultSetting) + { + if (localSettings == null) localSettings = new HashMap<String, List<String>>(); + int index = doc.indexOf(settingToken); + List<String> settings = localSettings.containsKey(settingToken) ? localSettings.get(settingToken) : null; + while (index != -1) + { + String setting = doc.substring(index + settingToken.length()); + int endIndex = setting.indexOf("\n"); + setting = setting.substring(0, endIndex); + setting = setting.trim(); + if (settings == null) { + settings = new ArrayList<String>(); + localSettings.put(settingToken, settings); + } + List<String> settingItems = null; + if (setting.length() >0) { + settingItems = Arrays.asList(setting.split("\\s*(,\\s*)+")); + } else { + settingItems = Arrays.asList(defaultSetting); + } + for (String settingItem: settingItems) { + if (settings.contains(settingItem)) { + //change the order to reflect the latest addition + settings.remove(settingItem); + } + settings.add(settingItem); + System.out.println("---Adding setting "+settingToken+":"+settingItem); + } + + index = doc.indexOf(settingToken, index + endIndex); + } + } + + public boolean hasLocalSetting(String settingToken) { + if (localSettings == null) return false; + return (localSettings.keySet().contains(settingToken)); + } + + public boolean getLocalSettingAsBoolean(JSRoyaleEmitterTokens token, Boolean defaultValue) { + return getLocalSettingAsBoolean(token.getToken(), defaultValue); + } + + public boolean getLocalSettingAsBoolean(String settingToken, Boolean defaultValue) { + boolean setting = defaultValue; + if (hasLocalSetting(settingToken)) { + for (String stringVal: localSettings.get(settingToken)) { + //don't bail out after finding a boolean-ish string val + //'last one wins' + if (stringVal.equals("false")) setting = false; + else if (stringVal.equals("true")) setting = true; + } + } + return setting; + } + + public boolean getLocalSettingIncludesString(JSRoyaleEmitterTokens token, String searchValue) { + return getLocalSettingIncludesString(token.getToken(), searchValue); + } + + public boolean getLocalSettingIncludesString(String settingToken, String searchValue) { + boolean hasValue = false; + if (hasLocalSetting(settingToken)) { + for (String stringVal: localSettings.get(settingToken)) { + if (stringVal.equals(searchValue)) { + hasValue = true; + break; + } + } + } + return hasValue; + } private void loadIgnores(String doc) { diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitter.java index 834ce4d..2335b31 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitter.java @@ -718,12 +718,10 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter else if (name.equals(IASLanguageConstants._int) || name.equals(IASLanguageConstants.uint)) result = IASLanguageConstants.Number; - - boolean isBuiltinFunction = name.matches("Vector\\.<.*>"); - if (isBuiltinFunction) - { - result = IASLanguageConstants.Array; + else if (name.equals(IASLanguageConstants.Vector) || name.equals("__AS3__.vec.Vector")) { + result = JSRoyaleEmitterTokens.VECTOR.getToken(); } + return result; } @@ -925,10 +923,6 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter newNode = EmitterUtils.insertArgumentsAt(node, 1, new NumericLiteralNode("0")); } } - else if (def.getParent() != null && def.getParent() instanceof AppliedVectorDefinition) - { - newNode = EmitterUtils.insertArgumentsAt(node, 1, new NumericLiteralNode("0")); - } } } if (len == 1) @@ -947,10 +941,6 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter newNode = EmitterUtils.insertArgumentsAfter(node, new NumericLiteralNode("1")); } } - else if (def.getParent() != null && def.getParent() instanceof AppliedVectorDefinition) - { - newNode = EmitterUtils.insertArgumentsAfter(node, new NumericLiteralNode("1")); - } } else if (def != null && def.getBaseName().equals("parseInt")) { @@ -1479,12 +1469,32 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter String vectorClassName = ((RoyaleJSProject)project).config == null ? null : ((RoyaleJSProject)project).config.getJsVectorEmulationClass(); if (vectorClassName != null) { - writeToken(ASEmitterTokens.NEW); write(vectorClassName); return; } } - write(JSRoyaleEmitterTokens.VECTOR); + Boolean written = false; + if (node instanceof TypedExpressionNode) { + startMapping(node); + write(ASEmitterTokens.PAREN_OPEN); + /*write(JSRoyaleEmitterTokens.LANGUAGE_QNAME); + write(ASEmitterTokens.MEMBER_ACCESS); + write("synthVector");*/ + write(JSRoyaleEmitterTokens.SYNTH_VECTOR); + write(ASEmitterTokens.PAREN_OPEN); + write(ASEmitterTokens.SINGLE_QUOTE); + //the element type of the Vector: + write(node.getTypeNode().resolve(project).getQualifiedName()); + write(ASEmitterTokens.SINGLE_QUOTE); + write(ASEmitterTokens.PAREN_CLOSE); + write(ASEmitterTokens.PAREN_CLOSE); + endMapping(node); + written = true; + } + + if (!written) { + write(JSRoyaleEmitterTokens.VECTOR); + } if (getModel().inStaticInitializer) { if (!staticUsedNames.contains(JSRoyaleEmitterTokens.LANGUAGE_QNAME.getToken())) diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitterTokens.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitterTokens.java index 20bc298..1c61fb3 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitterTokens.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleEmitterTokens.java @@ -35,6 +35,7 @@ public enum JSRoyaleEmitterTokens implements IEmitterTokens ROYALE_CLASS_INFO_CLASS_KIND("class"), ROYALE_CLASS_INFO_INTERFACE_KIND("interface"), ROYALE_CLASS_INFO_IS_DYNAMIC("isDynamic"), + ROYALE_SYNTH_TAG_FIELD_NAME("SYNTH_TAG_FIELD"), GOOG_EXPORT_PROPERTY("goog.exportProperty"), GOOG_EXPORT_SYMBOL("goog.exportSymbol"), INDENT(" "), @@ -50,6 +51,9 @@ public enum JSRoyaleEmitterTokens implements IEmitterTokens IGNORE_IMPORT("@royaleignoreimport"), IGNORE_STRING_COERCION("@royalenoimplicitstringconversion"), SUPPRESS_PUBLIC_VAR_WARNING("@royalesuppresspublicvarwarning"), + SUPPRESS_COMPLEX_IMPLICIT_COERCION("@royalesuppresscompleximplicitcoercion"), + SUPPRESS_RESOLVE_UNCERTAIN("@royalesuppressresolveuncertain"), + SUPPRESS_VECTOR_INDEX_CHECK("@royalesuppressvectorindexcheck"), DEBUG_COMMENT("@royaledebug"), DEBUG_RETURN("if(!goog.DEBUG)return;"), PREINCREMENT("preincrement"), @@ -65,7 +69,10 @@ public enum JSRoyaleEmitterTokens implements IEmitterTokens SKIP_AS_COERCIONS("skipAsCoercions"), SKIP_FUNCTION_COERCIONS("skipFunctionCoercions"), JSX("JSX"), - VECTOR("org.apache.royale.utils.Language.Vector"), + VECTOR(LANGUAGE_QNAME.getToken() + ".Vector"), + SYNTH_TYPE(LANGUAGE_QNAME.getToken() + ".synthType"), + SYNTH_VECTOR(LANGUAGE_QNAME.getToken() + ".synthVector"), + VECTOR_INDEX_CHECK_METHOD_NAME(LANGUAGE_QNAME.getToken() + ".CHECK_INDEX"), ; private String token; diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleJSProject.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleJSProject.java index 06348a5..42e647c 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleJSProject.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleJSProject.java @@ -39,6 +39,7 @@ import org.apache.royale.compiler.config.Configurator; import org.apache.royale.compiler.css.ICSSMediaQueryCondition; import org.apache.royale.compiler.css.ICSSRule; import org.apache.royale.compiler.definitions.IDefinition; +import org.apache.royale.compiler.definitions.IFunctionDefinition; import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.definitions.metadata.IMetaTag; import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute; @@ -116,7 +117,7 @@ public class RoyaleJSProject extends RoyaleProject if (defNode instanceof IClassNode || defNode instanceof IInterfaceNode) { String defname = def.getQualifiedName(); - IASDocComment asDoc = (defNode instanceof IClassNode) ? + IASDocComment asDoc = (defNode instanceof IClassNode) ? (IASDocComment) ((IClassNode)defNode).getASDocComment() : (IASDocComment) ((IInterfaceNode)defNode).getASDocComment(); if (asDoc != null && (asDoc instanceof ASDocComment)) @@ -163,7 +164,7 @@ public class RoyaleJSProject extends RoyaleProject super.addDependency(from, to, dt, qname); } - private synchronized void updateRequiresMap(ICompilationUnit from, ICompilationUnit to, + private synchronized void updateRequiresMap(ICompilationUnit from, ICompilationUnit to, DependencyType dt, String qname) { HashMap<String, DependencyType> reqs; @@ -188,10 +189,10 @@ public class RoyaleJSProject extends RoyaleProject if (qname.equals("XML")) needXML = true; reqs.put(qname, dt); - } + } } - private synchronized void updateJSModulesMap(ICompilationUnit from, ICompilationUnit to, + private synchronized void updateJSModulesMap(ICompilationUnit from, ICompilationUnit to, DependencyType dt, String qname) { HashMap<String, DependencyType> reqs; @@ -219,7 +220,7 @@ public class RoyaleJSProject extends RoyaleProject } } - private synchronized void updateInterfacesMap(ICompilationUnit from, ICompilationUnit to, + private synchronized void updateInterfacesMap(ICompilationUnit from, ICompilationUnit to, DependencyType dt, String qname) { HashMap<String, String> interfacesArr; @@ -530,6 +531,15 @@ public class RoyaleJSProject extends RoyaleProject } return false; } + + @Override + public boolean isParameterCountMismatchAllowed(IFunctionDefinition func, + int formalCount, int actualCount) { + if ((func.getBaseName().equals("int") || func.getBaseName().equals("uint")) && func.isConstructor()) { + if (actualCount == 1) return true; + } + return super.isParameterCountMismatchAllowed(func, formalCount, actualCount); + } /** * List of compiler defines so it can be overridden diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/utils/JSClosureCompilerWrapper.java b/compiler-jx/src/main/java/org/apache/royale/compiler/utils/JSClosureCompilerWrapper.java index 74e1bb6..a305b4f 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/utils/JSClosureCompilerWrapper.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/utils/JSClosureCompilerWrapper.java @@ -356,6 +356,8 @@ public class JSClosureCompilerWrapper "playerversion", "langversion", "copy", "span", "para", "throw", "tiptext", "asparam", "asreturn", "asreturns", "asprivate", "royaleignoreimport", "royaleignorecoercion", "royaleemitcoercion", + "royalesuppresscompleximplicitcoercion","royalesuppressresolveuncertain", + "royalesuppressvectorindexcheck", "royalenoimplicitstringconversion","royaledebug"}; options_.setExtraAnnotationNames(Arrays.asList(asdocTags)); } diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/utils/NativeUtils.java b/compiler-jx/src/main/java/org/apache/royale/compiler/utils/NativeUtils.java index 0653542..51ea799 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/utils/NativeUtils.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/utils/NativeUtils.java @@ -122,8 +122,8 @@ public class NativeUtils unescape("unescape"), window("window"), - // (erikdebruin) These aren't strictly 'native' to JS, but the - // Publisher provides global functions, so, for all + // (erikdebruin) These aren't strictly 'native' to JS, but the + // Publisher provides global functions, so, for all // intends and purposes they behave like they are. _int("int"), trace("trace"), @@ -154,6 +154,25 @@ public class NativeUtils } } + public enum SyntheticJSType + { + _int("int"), + uint("uint"), + Vector("Vector"); + + private final String value; + + SyntheticJSType(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } + public static boolean isNative(String type) { for (NativeASType test : NativeASType.values()) @@ -175,5 +194,24 @@ public class NativeUtils } return false; } + + public static boolean isSyntheticJSType(String type) + { + for (SyntheticJSType test : SyntheticJSType.values()) + { + if (test.getValue().equals(type)) { + return true; + } + } + if (type.startsWith("Vector.<")) { + return true; + } + return false; + } + + public static boolean isVector(String type) + { + return type != null && type.startsWith("Vector.<"); + } } diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleExpressions.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleExpressions.java index 0b32b4a..0179d35 100644 --- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleExpressions.java +++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleExpressions.java @@ -1220,7 +1220,7 @@ public class TestRoyaleExpressions extends TestGoogExpressions "public class B {public function b() { function c(f:Function):void {}; var f:Array = [b]; c(f[0]); }}", IFunctionNode.class, WRAP_LEVEL_PACKAGE); asBlockWalker.visitFunction(node); - assertOut("/**\n * @export\n */\nB.prototype.b = function() {\n var self = this;\n function c(f) {\n };\n var /** @type {Array} */ f = [org.apache.royale.utils.Language.closure(this.b, this, 'b')];\n c(f[0]);\n}"); + assertOut("/**\n * @export\n */\nB.prototype.b = function() {\n var self = this;\n function c(f) {\n };\n var /** @type {Array} */ f = [org.apache.royale.utils.Language.closure(this.b, this, 'b')];\n c(/* implicit cast */ org.apache.royale.utils.Language.as(f[0], Function, true));\n}"); } @Test @@ -1512,7 +1512,7 @@ public class TestRoyaleExpressions extends TestGoogExpressions { IBinaryOperatorNode node = getBinaryNode("a as int"); asBlockWalker.visitBinaryOperator(node); - assertOut("org.apache.royale.utils.Language.as(a, Number)"); + assertOut("org.apache.royale.utils.Language.as(a, org.apache.royale.utils.Language.synthType('int'))"); } @Test @@ -1520,7 +1520,7 @@ public class TestRoyaleExpressions extends TestGoogExpressions { IBinaryOperatorNode node = getBinaryNode("a as uint"); asBlockWalker.visitBinaryOperator(node); - assertOut("org.apache.royale.utils.Language.as(a, Number)"); + assertOut("org.apache.royale.utils.Language.as(a, org.apache.royale.utils.Language.synthType('uint'))"); } @Test @@ -1530,7 +1530,7 @@ public class TestRoyaleExpressions extends TestGoogExpressions "public class B {private var memberVar:Class; public function b(o:Object):int { var a:B = null; a = o as memberVar; }}", IFunctionNode.class, WRAP_LEVEL_PACKAGE, true); asBlockWalker.visitFunction(node); - assertOut("/**\n * @export\n * @param {Object} o\n * @return {number}\n */\nfoo.bar.B.prototype.b = function(o) {\n var /** @type {foo.bar.B} */ a = null;\n a = org.apache.royale.utils.Language.as(o, this.memberVar);\n}"); + assertOut("/**\n * @export\n * @param {Object} o\n * @return {number}\n */\nfoo.bar.B.prototype.b = function(o) {\n var /** @type {foo.bar.B} */ a = null;\n a = /* implicit cast */ org.apache.royale.utils.Language.as(org.apache.royale.utils.Language.as(o, this.memberVar), foo.bar.B, true);\n}"); } @Test diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalClasses.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalClasses.java index 4f590d3..6957c18 100644 --- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalClasses.java +++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalClasses.java @@ -287,6 +287,16 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses asBlockWalker.visitVariable(node); assertOut("var /** @type {number} */ a = Math[\"PI\"]"); } + + @Override + @Test + public void testClass() + { + IVariableNode node = getVariable("var a:Class = String; var b:* = new a('test')"); + node = (IVariableNode)(node.getParent().getChild(1)); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {*} */ b = org.apache.royale.utils.Language.resolveUncertain(new a('test'))"); + } @Test public void testDateSetSeconds() @@ -379,7 +389,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses IVariableNode node = getVariable("var a:Vector.<String> = new Vector.<String>(['Hello', 'World']);"); asBlockWalker.visitVariable(node); //MXMLC does not report an error. Should we? - assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.Vector(['Hello', 'World'], 'String')"); + assertOut("var /** @type {Array} */ a = new (org.apache.royale.utils.Language.synthVector('String'))(['Hello', 'World'])"); } @Test @@ -387,7 +397,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses { IVariableNode node = getVariable("var a:Vector.<String> = new <String>[];"); asBlockWalker.visitVariable(node); - assertOut("var /** @type {Array} */ a = []"); + assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.synthVector('String')['coerce']([])"); } @Test @@ -395,7 +405,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses { IVariableNode node = getVariable("var a:Vector.<int> = new <int>[0, 1, 2, 3];"); asBlockWalker.visitVariable(node); - assertOut("var /** @type {Array} */ a = [0, 1, 2, 3]"); + assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.synthVector('int')['coerce']([0, 1, 2, 3])"); } @Test @@ -403,7 +413,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses { IVariableNode node = getVariable("var a:Vector.<String> = new <String>[\"one\", \"two\", \"three\";"); asBlockWalker.visitVariable(node); - assertOut("var /** @type {Array} */ a = [\"one\", \"two\", \"three\"]"); + assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.synthVector('String')['coerce']([\"one\", \"two\", \"three\"])"); } @Test @@ -411,7 +421,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses { IVariableNode node = getVariable("var a:Vector.<String> = new Vector.<String>();"); asBlockWalker.visitVariable(node); - assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.Vector()"); + assertOut("var /** @type {Array} */ a = new (org.apache.royale.utils.Language.synthVector('String'))()"); } @Test @@ -420,7 +430,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses IVariableNode node = getVariable("var a:Vector.<String> = new Vector.<String>('Hello', 'World');"); asBlockWalker.visitVariable(node); //MXMLC does not report an error. Should we? - assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.Vector('Hello', 'String')"); + assertOut("var /** @type {Array} */ a = new (org.apache.royale.utils.Language.synthVector('String'))('Hello', 'World')"); } @Test @@ -429,7 +439,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses IVariableNode node = getVariable("var a:Vector.<String> = new Vector.<String>('Hello', 'World', 'Three');"); asBlockWalker.visitVariable(node); //MXMLC does not report an error. Should we? - assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.Vector('Hello', 'String')"); + assertOut("var /** @type {Array} */ a = new (org.apache.royale.utils.Language.synthVector('String'))('Hello', 'World', 'Three')"); } @Test @@ -437,7 +447,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses { IVariableNode node = getVariable("var a:Vector.<String> = new Vector.<String>(30);"); asBlockWalker.visitVariable(node); - assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.Vector(30, 'String')"); + assertOut("var /** @type {Array} */ a = new (org.apache.royale.utils.Language.synthVector('String'))(30)"); } @Test @@ -446,7 +456,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses IVariableNode node = getVariable("var a:Vector.<String> = new Vector.<String>(30, 40);"); asBlockWalker.visitVariable(node); //MXMLC does not report an error. Should we? - assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.Vector(30, 'String')"); + assertOut("var /** @type {Array} */ a = new (org.apache.royale.utils.Language.synthVector('String'))(30, 40)"); } @Test @@ -455,7 +465,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses IVariableNode node = getVariable("var a:Vector.<String> = new Vector.<String>(['Hello', 'World']);"); asBlockWalker.visitVariable(node); //MXMLC does not report an error. Should we? - assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.Vector(['Hello', 'World'], 'String')"); + assertOut("var /** @type {Array} */ a = new (org.apache.royale.utils.Language.synthVector('String'))(['Hello', 'World'])"); } @Test @@ -468,7 +478,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses IBinaryOperatorNode node = getBinaryNode("var a:Vector.<String> = new Vector.<String>(); a.removeAt(2)"); IFunctionCallNode parentNode = (IFunctionCallNode)(node.getParent()); asBlockWalker.visitFunctionCall(parentNode); - assertOut("a.splice(2, 1)"); + assertOut("a['removeAt'](2)"); } } @@ -482,7 +492,7 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses IBinaryOperatorNode node = getBinaryNode("var a:Vector.<String> = new Vector.<String>(); a.insertAt(2, 'foo')"); IFunctionCallNode parentNode = (IFunctionCallNode)(node.getParent()); asBlockWalker.visitFunctionCall(parentNode); - assertOut("a.splice(2, 0, 'foo')"); + assertOut("a['insertAt'](2, 'foo')"); } } @@ -590,6 +600,33 @@ public class TestRoyaleGlobalClasses extends TestGoogGlobalClasses assertOut("var /** @type {CustomVector} */ a = new CustomVector(['Hello', 'World'], 'String')"); } + @Override + @Test + public void testBoolean() + { + IVariableNode node = getVariable("var a:Boolean = new Boolean(1);"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {boolean} */ a = Boolean(1)"); + } + + @Override + @Test + public void testNumber() + { + IVariableNode node = getVariable("var a:Number = new Number(\"1\");"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {number} */ a = Number(\"1\")"); + } + + @Override + @Test + public void testString() + { + IVariableNode node = getVariable("var a:String = new String(\"100\");"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {string} */ a = String(\"100\")"); + } + @Test public void testXML() { diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalFunctions.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalFunctions.java index 8f2f9d1..baef176 100644 --- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalFunctions.java +++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleGlobalFunctions.java @@ -100,6 +100,15 @@ public class TestRoyaleGlobalFunctions extends TestGoogGlobalFunctions asBlockWalker.visitVariable(node); assertOut("var /** @type {Array} */ a = Array(['Hello', 'World'])"); } + + @Override + @Test + public void testObject() + { + IVariableNode node = getVariable("var a:Object = Object(\"1\");"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {Object} */ a = org.apache.royale.utils.Language.resolveUncertain(Object(\"1\"))"); + } @Test public void testParseInt() @@ -159,7 +168,7 @@ public class TestRoyaleGlobalFunctions extends TestGoogGlobalFunctions { IVariableNode node = getVariable("var a:Vector.<String> = Vector.<String>(['Hello', 'World']);"); asBlockWalker.visitVariable(node); - assertOut("var /** @type {Array} */ a = ['Hello', 'World'].slice()"); + assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.synthVector('String')['coerce'](['Hello', 'World'])"); } @Test @@ -184,7 +193,7 @@ public class TestRoyaleGlobalFunctions extends TestGoogGlobalFunctions IVariableNode node = getVariable("var a:Vector.<String> = Vector.<String>(30);"); asBlockWalker.visitVariable(node); // MXMLC doesn't report an error either. Maybe we should. - assertOut("var /** @type {Array} */ a = 30.slice()"); + assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.synthVector('String')['coerce'](30)"); } @Test @@ -200,7 +209,7 @@ public class TestRoyaleGlobalFunctions extends TestGoogGlobalFunctions { IVariableNode node = getVariable("var a:Vector.<String> = Vector.<String>(['Hello', 'World']);"); asBlockWalker.visitVariable(node); - assertOut("var /** @type {Array} */ a = ['Hello', 'World'].slice()"); + assertOut("var /** @type {Array} */ a = org.apache.royale.utils.Language.synthVector('String')['coerce'](['Hello', 'World'])"); } @Override diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/DynamicAccessNode.java b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/DynamicAccessNode.java index dd974a7..7b9f34e 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/DynamicAccessNode.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/DynamicAccessNode.java @@ -21,6 +21,7 @@ package org.apache.royale.compiler.internal.tree.as; import org.apache.royale.compiler.definitions.IAppliedVectorDefinition; import org.apache.royale.compiler.definitions.ITypeDefinition; +import org.apache.royale.compiler.internal.semantics.SemanticUtils; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.tree.ASTNodeID; import org.apache.royale.compiler.tree.as.IDynamicAccessNode; @@ -81,8 +82,10 @@ public class DynamicAccessNode extends BinaryOperatorNodeBase implements IDynami ITypeDefinition leftType = getLeftOperandNode().resolveType(project); if (leftType instanceof IAppliedVectorDefinition) { - IAppliedVectorDefinition vectorDef = (IAppliedVectorDefinition) leftType; - return vectorDef.resolveElementType(project); + if (SemanticUtils.isNumericType(getRightOperandNode().resolveType(project), project)) { + IAppliedVectorDefinition vectorDef = (IAppliedVectorDefinition) leftType; + return vectorDef.resolveElementType(project); + } } return super.resolveType(project); }
