This is an automated email from the ASF dual-hosted git repository.
joshtynjala pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-compiler.git
The following commit(s) were added to refs/heads/develop by this push:
new 62d8ba6 optional private constructors (closes #82)
62d8ba6 is described below
commit 62d8ba6a093d5ec20abfa49b38c2f9aae0479376
Author: Josh Tynjala <[email protected]>
AuthorDate: Thu Apr 25 11:01:10 2019 -0700
optional private constructors (closes #82)
Enable with --allow-private-constructors compiler option. When enabled,
constructors may be private instead of public and classes with private
constructors cannot be instantiated outside of their parent class at compile
time.
---
.../royale/compiler/config/Configuration.java | 22 +
.../royale/compiler/projects/ICompilerProject.java | 7 +-
.../constants/IMetaAttributeConstants.java | 4 +
.../as/codegen/ClassDirectiveProcessor.java | 19 +-
.../internal/definitions/ClassDefinition.java | 10 +
.../internal/definitions/FunctionDefinition.java | 23 +
.../internal/parsing/as/ConfigProcessor.java | 6 +
.../compiler/internal/projects/ASCProject.java | 6 +
.../compiler/internal/projects/RoyaleProject.java | 15 +
.../projects/RoyaleProjectConfigurator.java | 1 +
.../semantics/MethodBodySemanticChecker.java | 65 ++-
.../compiler/internal/tree/as/FunctionNode.java | 44 +-
.../ConstructorMustBePublicOrPrivateProblem.java | 42 ++
.../InaccessibleConstructorReferenceProblem.java | 43 ++
.../test/java/as/ASPrivateConstructorTests.java | 631 +++++++++++++++++++++
15 files changed, 925 insertions(+), 13 deletions(-)
diff --git
a/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java
b/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java
index 9b8d0de..fba2c0a 100644
---
a/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java
+++
b/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java
@@ -1530,6 +1530,28 @@ public class Configuration
}
//
+ // 'compiler.allow-private-constructors' option
+ //
+
+ private boolean allowPrivateConstructors = false;
+
+ public boolean getCompilerAllowPrivateConstructors()
+ {
+ return allowPrivateConstructors;
+ }
+
+ /**
+ * Whether the compiler will allow constructors to be private.
+ */
+ @Config
+ @Mapping({ "compiler", "allow-private-constructors" })
+ @RoyaleOnly
+ public void setCompilerAllowPrivateConstructors(ConfigurationValue cv,
boolean allow)
+ {
+ this.allowPrivateConstructors = allow;
+ }
+
+ //
// 'compiler.actionscript-file-encoding' option
//
diff --git
a/compiler-common/src/main/java/org/apache/royale/compiler/projects/ICompilerProject.java
b/compiler-common/src/main/java/org/apache/royale/compiler/projects/ICompilerProject.java
index 61d7489..00042f9 100644
---
a/compiler-common/src/main/java/org/apache/royale/compiler/projects/ICompilerProject.java
+++
b/compiler-common/src/main/java/org/apache/royale/compiler/projects/ICompilerProject.java
@@ -275,6 +275,11 @@ public interface ICompilerProject
/**
* @return True if abstract classes are allowed.
*/
- boolean getAllowAbstractClasses();
+ boolean getAllowAbstractClasses();
+
+ /**
+ * @return True if private constructors are allowed.
+ */
+ boolean getAllowPrivateConstructors();
}
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/constants/IMetaAttributeConstants.java
b/compiler/src/main/java/org/apache/royale/compiler/constants/IMetaAttributeConstants.java
index 189f8ea..4aba20b 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/constants/IMetaAttributeConstants.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/constants/IMetaAttributeConstants.java
@@ -213,6 +213,9 @@ public interface IMetaAttributeConstants
// [RoyaleAbstract]
static final String ATTRIBUTE_ABSTRACT = "RoyaleAbstract";
+
+ // [RoyalePrivateConstructor]
+ static final String ATTRIBUTE_PRIVATE_CONSTRUCTOR =
"RoyalePrivateConstructor";
/**
* List of metadata tags that do not inherit
@@ -226,6 +229,7 @@ public interface IMetaAttributeConstants
ATTRIBUTE_DISCOURAGED_FOR_PROFILE,
ATTRIBUTE_EXCLUDECLASS,
ATTRIBUTE_ABSTRACT,
+ ATTRIBUTE_PRIVATE_CONSTRUCTOR,
})));
}
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ClassDirectiveProcessor.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ClassDirectiveProcessor.java
index 7be6c46..08f2704 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ClassDirectiveProcessor.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ClassDirectiveProcessor.java
@@ -67,6 +67,7 @@ import
org.apache.royale.compiler.problems.CircularTypeReferenceProblem;
import
org.apache.royale.compiler.problems.ConstructorCannotHaveReturnTypeProblem;
import org.apache.royale.compiler.problems.ConstructorIsGetterSetterProblem;
import org.apache.royale.compiler.problems.ConstructorIsStaticProblem;
+import
org.apache.royale.compiler.problems.ConstructorMustBePublicOrPrivateProblem;
import org.apache.royale.compiler.problems.ConstructorMustBePublicProblem;
import org.apache.royale.compiler.problems.DuplicateClassDefinitionProblem;
import org.apache.royale.compiler.problems.DynamicNotOnClassProblem;
@@ -982,9 +983,21 @@ class ClassDirectiveProcessor extends DirectiveProcessor
// It is ok to omit the namespace
// We must check the AST, as CM treats all ctors as public no
matter what the user typed in
// so the FunctionDefinition will always be in the public namespace
- if( node.getActualNamespaceNode() != null &&
- node.getActualNamespaceNode().getName() !=
IASKeywordConstants.PUBLIC)
- problems.add(new
ConstructorMustBePublicProblem(node.getActualNamespaceNode()));
+ if (classScope.getProject().getAllowPrivateConstructors())
+ {
+ if (node.getActualNamespaceNode().getName() !=
IASKeywordConstants.PUBLIC
+ && !func.isPrivate())
+ {
+ problems.add(new
ConstructorMustBePublicOrPrivateProblem(node.getActualNamespaceNode()));
+ }
+ }
+ else if( node.getActualNamespaceNode() != null )
+ {
+ if (node.getActualNamespaceNode().getName() !=
IASKeywordConstants.PUBLIC || func.isPrivate())
+ {
+ problems.add(new
ConstructorMustBePublicProblem(node.getActualNamespaceNode()));
+ }
+ }
// A constructor cannot be static
if( func.isStatic() )
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/ClassDefinition.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/ClassDefinition.java
index f92749a..9e3f63f 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/ClassDefinition.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/ClassDefinition.java
@@ -313,6 +313,16 @@ public class ClassDefinition extends ClassDefinitionBase
implements IClassDefini
protected void setConstructor(IFunctionDefinition constructor)
{
this.constructor = constructor;
+
+ if(this.constructor.isPrivate()
+ &&
this.constructor.getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_PRIVATE_CONSTRUCTOR)
== null)
+ {
+ // ensures that the constructor remains private when compiled into
+ // a library because the metadata is how private constructors are
+ // stored in the bytecode
+ MetaTag privateMetaTag = new MetaTag(this,
IMetaAttributeConstants.ATTRIBUTE_PRIVATE_CONSTRUCTOR, new
IMetaTagAttribute[0]);
+ addMetaTag(privateMetaTag);
+ }
}
@Override
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/FunctionDefinition.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/FunctionDefinition.java
index 5b71d87..9f0e546 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/FunctionDefinition.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/FunctionDefinition.java
@@ -296,6 +296,29 @@ public class FunctionDefinition extends
ScopedDefinitionBase implements IFunctio
}
@Override
+ public boolean isPrivate()
+ {
+ if (super.isPrivate())
+ {
+ return true;
+ }
+ if (isConstructor())
+ {
+ IDefinition parent = getParent();
+ if (parent == null)
+ {
+ return false;
+ }
+ // if the construcutor does not have a private namespace, the
parent
+ // class may have [RoyalePrivateConstructor] metadata instead. this
+ // is how private constructors are stored in bytecode.
+ IMetaTag[] metaTags =
parent.getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_PRIVATE_CONSTRUCTOR);
+ return metaTags != null && metaTags.length > 0;
+ }
+ return false;
+ }
+
+ @Override
public boolean overridesAncestor(ICompilerProject project)
{
return (resolveOverriddenFunction(project) != null);
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/ConfigProcessor.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/ConfigProcessor.java
index af2dd3b..89fcd51 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/ConfigProcessor.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/ConfigProcessor.java
@@ -170,6 +170,12 @@ public class ConfigProcessor
// TODO Auto-generated method stub
return false;
}
+
+ @Override
+ public boolean getAllowPrivateConstructors() {
+ // TODO Auto-generated method stub
+ return false;
+ }
}
/**
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/ASCProject.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/ASCProject.java
index de04bb2..f542cf3 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/ASCProject.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/ASCProject.java
@@ -92,4 +92,10 @@ public class ASCProject extends CompilerProject implements
IASCProject
// TODO Auto-generated method stub
return false;
}
+
+ @Override
+ public boolean getAllowPrivateConstructors() {
+ // TODO Auto-generated method stub
+ return false;
+ }
}
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProject.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProject.java
index c99f5bb..c0a1285 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProject.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProject.java
@@ -2460,6 +2460,21 @@ public class RoyaleProject extends ASProject implements
IRoyaleProject, ICompile
allowAbstractClasses = allow;
}
+ private boolean allowPrivateConstructors = false;
+
+ /**
+ * Indicates if private constructors are allowed.
+ */
+ @Override
+ public boolean getAllowPrivateConstructors()
+ {
+ return allowPrivateConstructors;
+ }
+ public void setAllowPrivateConstructors(boolean allow)
+ {
+ allowPrivateConstructors = allow;
+ }
+
@Override
public boolean isPlatformRule(ICSSRule rule) {
return true;
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java
index cb3a35b..46ccade 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java
@@ -265,6 +265,7 @@ public class RoyaleProjectConfigurator extends Configurator
project.setAllowImportAliases(configuration.getCompilerAllowImportAliases());
project.setAllowAbstractClasses(configuration.getCompilerAllowAbstractClasses());
+
project.setAllowPrivateConstructors(configuration.getCompilerAllowPrivateConstructors());
DataTranscoder.embedClassName =
configuration.getByteArrayEmbedClass();
}
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/MethodBodySemanticChecker.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/MethodBodySemanticChecker.java
index 52a5bd4..067c2fe 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/MethodBodySemanticChecker.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/MethodBodySemanticChecker.java
@@ -70,6 +70,7 @@ import
org.apache.royale.compiler.internal.definitions.AmbiguousDefinition;
import org.apache.royale.compiler.internal.definitions.VariableDefinition;
import org.apache.royale.compiler.problems.*;
import org.apache.royale.compiler.projects.ICompilerProject;
+import org.apache.royale.compiler.scopes.IASScope;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.IASNode;
import org.apache.royale.compiler.tree.as.IBinaryOperatorNode;
@@ -85,6 +86,7 @@ import
org.apache.royale.compiler.tree.as.INamespaceDecorationNode;
import org.apache.royale.compiler.tree.as.INumericLiteralNode;
import org.apache.royale.compiler.tree.as.IParameterNode;
import org.apache.royale.compiler.tree.as.IReturnNode;
+import org.apache.royale.compiler.tree.as.IScopedNode;
import org.apache.royale.compiler.tree.as.IUnaryOperatorNode;
import org.apache.royale.compiler.tree.as.IVariableNode;
import org.apache.royale.compiler.internal.as.codegen.ABCGeneratingReducer;
@@ -2025,7 +2027,8 @@ public class MethodBodySemanticChecker
}
else if ( def instanceof ClassDefinition )
{
- // pass
+ IClassDefinition classDef = (IClassDefinition) def;
+ checkPrivateConstructorNewExpr(call_node, null, classDef,
classDef.getConstructor());
}
else if ( def instanceof GetterDefinition )
{
@@ -2041,6 +2044,12 @@ public class MethodBodySemanticChecker
addProblem(new
MethodCannotBeConstructorProblem(call_node));
break;
}
+
+ if (func_def.isConstructor())
+ {
+ IDefinition class_def = func_def.getParent();
+ checkPrivateConstructorNewExpr(call_node, null, class_def,
func_def);
+ }
}
else if (def == null)
{
@@ -2087,8 +2096,10 @@ public class MethodBodySemanticChecker
if ( ctor instanceof FunctionDefinition )
{
- FunctionDefinition func = (FunctionDefinition)ctor;
- checkFormalsVsActuals(iNode, func, args);
+ FunctionDefinition func_def = (FunctionDefinition)ctor;
+ checkFormalsVsActuals(iNode, func_def, args);
+
+ checkPrivateConstructorNewExpr(iNode, class_binding,
class_def, func_def);
}
}
else if ( def instanceof GetterDefinition )
@@ -2100,7 +2111,12 @@ public class MethodBodySemanticChecker
IFunctionDefinition.FunctionClassification func_type =
func_def.getFunctionClassification();
- if (
func_type.equals(IFunctionDefinition.FunctionClassification.CLASS_MEMBER) ||
func_type.equals(IFunctionDefinition.FunctionClassification.INTERFACE_MEMBER) )
+ if (func_def.isConstructor())
+ {
+ IDefinition class_def = func_def.getParent();
+ checkPrivateConstructorNewExpr(iNode, class_binding,
class_def, func_def);
+ }
+ else if (
func_type.equals(IFunctionDefinition.FunctionClassification.CLASS_MEMBER) ||
func_type.equals(IFunctionDefinition.FunctionClassification.INTERFACE_MEMBER) )
{
addProblem(new MethodCannotBeConstructorProblem(
roundUpUsualSuspects(class_binding, iNode)
@@ -2133,6 +2149,47 @@ public class MethodBodySemanticChecker
checkReference(class_binding);
}
+ private void checkPrivateConstructorNewExpr(IASNode iNode, Binding
class_binding, IDefinition classDef, IFunctionDefinition funcDef)
+ {
+ if (!project.getAllowPrivateConstructors() || !funcDef.isPrivate())
+ {
+ return;
+ }
+
+ IScopedNode enclosingScope = iNode.getContainingScope();
+
+ if (enclosingScope != null)
+ {
+ boolean needsProblem = true;
+ IASScope currentScope = enclosingScope.getScope();
+ while (currentScope != null)
+ {
+ IDefinition currentDef = currentScope.getDefinition();
+ if (currentDef instanceof IClassDefinition)
+ {
+ needsProblem =
!classDef.equals(currentScope.getDefinition());
+ break;
+ }
+ currentScope = currentScope.getContainingScope();
+ }
+ if(needsProblem)
+ {
+ if(class_binding != null)
+ {
+ addProblem(new InaccessibleConstructorReferenceProblem(
+ roundUpUsualSuspects(class_binding, iNode),
classDef.getQualifiedName()
+ ));
+ }
+ else
+ {
+ addProblem(new InaccessibleConstructorReferenceProblem(
+ iNode, classDef.getQualifiedName()
+ ));
+ }
+ }
+ }
+ }
+
/**
* Check a return expression that returns a value.
* @param iNode - the return statement.
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
index c07a8be..64c0b2b 100644
---
a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
+++
b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/FunctionNode.java
@@ -36,8 +36,10 @@ import org.apache.royale.compiler.common.ASImportTarget;
import org.apache.royale.compiler.common.IImportTarget;
import org.apache.royale.compiler.config.CompilerDiagnosticsConstants;
import org.apache.royale.compiler.constants.IASLanguageConstants;
+import org.apache.royale.compiler.constants.IMetaAttributeConstants;
import org.apache.royale.compiler.constants.INamespaceConstants;
import org.apache.royale.compiler.definitions.IDefinition;
+import org.apache.royale.compiler.definitions.INamespaceDefinition;
import
org.apache.royale.compiler.definitions.IFunctionDefinition.FunctionClassification;
import org.apache.royale.compiler.definitions.IParameterDefinition;
import org.apache.royale.compiler.definitions.references.IReference;
@@ -74,6 +76,7 @@ import org.apache.royale.compiler.tree.as.IParameterNode;
import org.apache.royale.compiler.tree.as.IScopedNode;
import org.apache.royale.compiler.tree.as.ITypeNode;
import org.apache.royale.compiler.tree.as.IVariableNode;
+import org.apache.royale.compiler.tree.metadata.IMetaTagNode;
import org.apache.royale.compiler.workspaces.IWorkspace;
import com.google.common.base.Predicate;
@@ -364,8 +367,30 @@ public class FunctionNode extends BaseTypedDefinitionNode
implements IFunctionNo
if (isConstructor())
{
- if (namespaceNode != null &&
namespaceNode.getName().equals(INamespaceConstants.public_))
+ if (namespaceNode != null
+ &&
(namespaceNode.getName().equals(INamespaceConstants.public_) ||
namespaceNode.getName().equals(INamespaceConstants.private_)))
+ {
+ // if the existing node is already public or private, return it
return namespaceNode;
+ }
+ IASNode parentNode = getParent();
+ if (parentNode instanceof IDefinitionNode)
+ {
+ IDefinitionNode defNode = (IDefinitionNode) parentNode;
+ IMetaTagNode[] metaTagNodes =
defNode.getMetaTags().getTagsByName(IMetaAttributeConstants.ATTRIBUTE_PRIVATE_CONSTRUCTOR);
+ if (metaTagNodes != null && metaTagNodes.length > 0)
+ {
+ // if the parent class has [RoyalePrivateConstructor]
+ // metadata, the constructor should be considered private
+ // and we should generate a fake namespace node
+ NamespaceIdentifierNode priv = new
NamespaceIdentifierNode(INamespaceConstants.private_);
+ priv.span(-1, -1, -1, -1);
+ priv.setDecorationTarget(this);
+ return priv;
+ }
+ }
+ // if there is no namespace node, the namespace defaults to public
+ // and we'll generate a fake node
NamespaceIdentifierNode pub = new
NamespaceIdentifierNode(INamespaceConstants.public_);
pub.span(-1, -1, -1, -1);
pub.setDecorationTarget(this);
@@ -382,11 +407,12 @@ public class FunctionNode extends BaseTypedDefinitionNode
implements IFunctionNo
if (ns != null)
{
String nameString = ns.getName();
- // If public, just return it.
- if (nameString.equals(INamespaceConstants.public_))
+ // If public or private, just return it.
+ if (nameString.equals(INamespaceConstants.public_) ||
nameString.equals(INamespaceConstants.private_))
return nameString;
- // Otherwise, check to see if we are a constructor.
+ // Otherwise, check to see if we are a constructor and always
return
+ // public
if (isConstructor())
return INamespaceConstants.public_;
@@ -649,8 +675,16 @@ public class FunctionNode extends BaseTypedDefinitionNode
implements IFunctionNo
classNode.constructorNode = this;
}
}
+ // if the namespace reference is private, don't change it
+ if(!(funcDef.getNamespaceReference() instanceof
INamespaceDefinition.IPrivateNamespaceDefinition))
+ {
+
funcDef.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace());
+ }
+ }
+ else
+ {
+
funcDef.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace());
}
-
funcDef.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace());
}
}
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/problems/ConstructorMustBePublicOrPrivateProblem.java
b/compiler/src/main/java/org/apache/royale/compiler/problems/ConstructorMustBePublicOrPrivateProblem.java
new file mode 100644
index 0000000..93e8755
--- /dev/null
+++
b/compiler/src/main/java/org/apache/royale/compiler/problems/ConstructorMustBePublicOrPrivateProblem.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.royale.compiler.problems;
+
+import org.apache.royale.compiler.tree.as.IASNode;
+
+/**
+ * Diagnostic emitted when a constructor is not public or private
+ */
+public final class ConstructorMustBePublicOrPrivateProblem extends
CodegenProblem
+{
+ public static final String DESCRIPTION =
+ "A constructor can only be declared ${PUBLIC} or ${PRIVATE}";
+
+ public static final int errorCode = 1153;
+
+ public ConstructorMustBePublicOrPrivateProblem(IASNode site)
+ {
+ super(site);
+ }
+
+ // Prevent these from being localized.
+ public final String PUBLIC = "public";
+ public final String PRIVATE = "private";
+}
diff --git
a/compiler/src/main/java/org/apache/royale/compiler/problems/InaccessibleConstructorReferenceProblem.java
b/compiler/src/main/java/org/apache/royale/compiler/problems/InaccessibleConstructorReferenceProblem.java
new file mode 100644
index 0000000..761717a
--- /dev/null
+++
b/compiler/src/main/java/org/apache/royale/compiler/problems/InaccessibleConstructorReferenceProblem.java
@@ -0,0 +1,43 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.royale.compiler.problems;
+
+import org.apache.royale.compiler.tree.as.IASNode;
+
+/**
+ * Diagnostic emitted when the semantic analyzer detects
+ * an attempt to call an inaccessible constructor (e.g., a
+ * private constructor call outside the class).
+ */
+public final class InaccessibleConstructorReferenceProblem extends
StrictSemanticsProblem
+{
+ public static final String DESCRIPTION =
+ "Attempted access of inaccessible constructor through a reference with
static type ${className}.";
+
+ public static final int errorCode = 1195;
+
+ public InaccessibleConstructorReferenceProblem(IASNode site, final String
className)
+ {
+ super(site);
+ this.className = className;
+ }
+
+ public final String className;
+}
diff --git a/compiler/src/test/java/as/ASPrivateConstructorTests.java
b/compiler/src/test/java/as/ASPrivateConstructorTests.java
new file mode 100644
index 0000000..7981155
--- /dev/null
+++ b/compiler/src/test/java/as/ASPrivateConstructorTests.java
@@ -0,0 +1,631 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package as;
+
+import java.io.File;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ASPrivateConstructorTests extends ASFeatureTestsBase
+{
+ @Test
+ public void
testConstructorMustBePublicProblem_withPrivateConstructor_andAllowPrivateConstructorsDisabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ //error because private constructors have not been enabled
+ "private function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=false"
+ };
+ compileAndExpectErrors(source, false, false, false, options, "A
constructor can only be declared public\n");
+ }
+
+ @Test
+ public void
testConstructorMustBePublicProblem_withProtectedConstructor_andAllowPrivateConstructorsDisabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ //error because protected constructors are not allowed
+ "protected function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=false"
+ };
+ compileAndExpectErrors(source, false, false, false, options, "A
constructor can only be declared public\n");
+ }
+
+ @Test
+ public void
testConstructorMustBePublicProblem_withInternalConstructor_andAllowPrivateConstructorsDisabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ //error because internal constructors are not allowed
+ "internal function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=false"
+ };
+ compileAndExpectErrors(source, false, false, false, options, "A
constructor can only be declared public\n");
+ }
+
+ @Test
+ public void
testConstructorMustBePublicProblem_withMetadata_andAllowPrivateConstructorsDisabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ //error because private constructors have not been
enabled
+ "[RoyalePrivateConstructor]",
+ "class A {",
+ "public function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=false"
+ };
+ compileAndExpectErrors(source, false, false, false, options, "A
constructor can only be declared public\n");
+ }
+
+ @Test
+ public void
testConstructorMustBePublicProblemAndInaccessibleConstructorReferenceProblem_withPrivateConstructor_andAllowPrivateConstructorsDisabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ //no error because the constructor cannot be private
+ "new A()"
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ //error because private constructors have not been enabled
+ "private function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=false"
+ };
+ compileAndExpectErrors(source, false, false, false, options, "A
constructor can only be declared public\n");
+ }
+
+ @Test
+ public void
testConstructorMustBePublicProblemAndInaccessibleConstructorReferenceProblem_withMetadata_andAllowPrivateConstructorsDisabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ //no error because the constructor cannot be private
+ "new A()"
+ };
+ String[] extra = new String[]
+ {
+ "[RoyalePrivateConstructor]",
+ "class A {",
+ //error because private constructors have not been enabled
+ "private function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=false"
+ };
+ compileAndExpectErrors(source, false, false, false, options, "A
constructor can only be declared public\n");
+ }
+
+ @Test
+ public void
testNoConstructorMustBePublicOrPrivateProblem_withPrivateConstructor_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ "private function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ File tempASFile = generateTempFile(source);
+ String result = compile(tempASFile, source, false, false, false,
options, true);
+ Assert.assertEquals("", result);
+ }
+
+ @Test
+ public void
testConstructorMustBePublicOrPrivateProblem_withProtectedConstructor_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ //error because protected constructors are not allowed
+ "protected function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ compileAndExpectErrors(source, false, false, false, options, "A
constructor can only be declared public or private\n");
+ }
+
+ @Test
+ public void
testConstructorMustBePublicOrPrivateProblem_withInternalConstructor_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ //error because internal constructors are not allowed
+ "internal function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ compileAndExpectErrors(source, false, false, false, options, "A
constructor can only be declared public or private\n");
+ }
+
+ @Test
+ public void
testNoConstructorMustBePublicOrPrivateProblem_withMetadata_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ //error because private constructors have not been
enabled
+ "[RoyalePrivateConstructor]",
+ "class A {",
+ "public function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ File tempASFile = generateTempFile(source);
+ String result = compile(tempASFile, source, false, false, false,
options, true);
+ Assert.assertEquals("", result);
+ }
+
+ @Test
+ public void
testInaccessibleConstructorReferenceProblem_withPrivateConstructor_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ //error because the constructor is private
+ "new A()"
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ "private function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ compileAndExpectErrors(source, false, false, false, options,
"Attempted access of inaccessible constructor through a reference with static
type A.\n");
+ }
+
+ @Test
+ public void
testInaccessibleConstructorReferenceProblem_withMetadata_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ //error because the constructor is private
+ "new A()"
+ };
+ String[] extra = new String[]
+ {
+ "[RoyalePrivateConstructor]",
+ "class A {",
+ "private function A() {",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ compileAndExpectErrors(source, false, false, false, options,
"Attempted access of inaccessible constructor through a reference with static
type A.\n");
+ }
+
+ @Test
+ public void
testInaccessibleConstructorReferenceProblem_withPrivateConstructor_inFilePrivateArea_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ "private function A() {",
+ "}",
+ "}",
+ //error because the constructor is private
+ "new A()"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ compileAndExpectErrors(source, false, false, false, options,
"Attempted access of inaccessible constructor through a reference with static
type A.\n");
+ }
+
+ @Test
+ public void
testInaccessibleConstructorReferenceProblem_withMetadata_inFilePrivateArea_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "[RoyalePrivateConstructor]",
+ "class A {",
+ "private function A() {",
+ "}",
+ "}",
+ //error because the constructor is private
+ "new A()"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ compileAndExpectErrors(source, false, false, false, options,
"Attempted access of inaccessible constructor through a reference with static
type A.\n");
+ }
+
+ @Test
+ public void
testNoInaccessibleConstructorReferenceProblem_withPrivateConstructor_inMethodOfSameClass_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ "private function A() {",
+ "}",
+ "public function method():Object {",
+ "return new A()",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ File tempASFile = generateTempFile(source);
+ String result = compile(tempASFile, source, false, false, false,
options, true);
+ Assert.assertEquals("", result);
+ }
+
+ @Test
+ public void
testNoInaccessibleConstructorReferenceProblem_withMetadata_inMethodOfSameClass_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "[RoyalePrivateConstructor]",
+ "class A {",
+ "private function A() {",
+ "}",
+ "public function method():Object {",
+ "return new A()",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ File tempASFile = generateTempFile(source);
+ String result = compile(tempASFile, source, false, false, false,
options, true);
+ Assert.assertEquals("", result);
+ }
+
+ @Test
+ public void
testNoInaccessibleConstructorReferenceProblem_withPrivateConstructor_inStaticMethodOfSameClass_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ "private function A() {",
+ "}",
+ "public static function method():Object {",
+ "return new A()",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ File tempASFile = generateTempFile(source);
+ String result = compile(tempASFile, source, false, false, false,
options, true);
+ Assert.assertEquals("", result);
+ }
+
+ @Test
+ public void
testNoInaccessibleConstructorReferenceProblem_withMetadata_inStaticMethodOfSameClass_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "[RoyalePrivateConstructor]",
+ "class A {",
+ "private function A() {",
+ "}",
+ "public static function method():Object {",
+ "return new A()",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ File tempASFile = generateTempFile(source);
+ String result = compile(tempASFile, source, false, false, false,
options, true);
+ Assert.assertEquals("", result);
+ }
+
+ @Test
+ public void
testNoInaccessibleConstructorReferenceProblem_withPrivateConstructor_inGetterOfSameClass_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "class A {",
+ "private function A() {",
+ "}",
+ "public function get getter():Object {",
+ "return new A()",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ File tempASFile = generateTempFile(source);
+ String result = compile(tempASFile, source, false, false, false,
options, true);
+ Assert.assertEquals("", result);
+ }
+
+ @Test
+ public void
testNoInaccessibleConstructorReferenceProblem_withMetadata_inGetterOfSameClass_andAllowPrivateConstructorsEnabled()
+ {
+ String[] imports = new String[]
+ {
+ };
+ String[] declarations = new String[]
+ {
+ };
+ String[] testCode = new String[]
+ {
+ };
+ String[] extra = new String[]
+ {
+ "[RoyalePrivateConstructor]",
+ "class A {",
+ "private function A() {",
+ "}",
+ "public function get getter():Object {",
+ "return new A()",
+ "}",
+ "}"
+ };
+ String source = getAS(imports, declarations, testCode, extra);
+ String[] options = new String[]
+ {
+ "-allow-private-constructors=true"
+ };
+ File tempASFile = generateTempFile(source);
+ String result = compile(tempASFile, source, false, false, false,
options, true);
+ Assert.assertEquals("", result);
+ }
+}
\ No newline at end of file