You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by jo...@apache.org on 2019/07/15 18:31:59 UTC

[royale-compiler] 06/06: Better detection of goog.provide() and externs classes

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

commit 7c5e83f4b3b166e9c9d2b78b8e30f94c00368a3b
Author: Josh Tynjala <jo...@apache.org>
AuthorDate: Mon Jul 15 11:31:44 2019 -0700

    Better detection of goog.provide() and externs classes
    
    This allows libraries to properly use external-library-path with other libraries, so that classes don't get added to the wrong SWCs.
    
    This also is part of a fix to the issue of CSS being added to apps from libraries that aren't actually used.
---
 compiler-externc/build.xml                         |  1 +
 .../apache/royale/compiler/clients/COMPJSC.java    |  7 +-
 .../royale/compiler/clients/COMPJSCNative.java     |  7 +-
 .../royale/compiler/clients/COMPJSCRoyale.java     |  7 +-
 .../codegen/js/jx/PackageHeaderEmitter.java        |  6 +-
 .../codegen/js/royale/JSRoyaleEmitter.java         | 11 ++--
 .../codegen/mxml/royale/MXMLRoyaleEmitter.java     | 75 +++++++++++++--------
 .../compiler/internal/graph/GoogDepsWriter.java    | 49 ++++++++++----
 .../internal/projects/RoyaleJSProject.java         | 76 ++++++++++++++++++++--
 .../compiler/internal/targets/RoyaleJSTarget.java  |  2 +
 compiler-jx/src/test/build.xml                     | 26 +++++++-
 compiler-jx/src/test/config/compile-js-config.xml  | 46 +++++++++++++
 .../internal/codegen/mxml/TestMXMLApplication.java |  7 ++
 .../mxml/royale/TestRoyaleMXMLApplication.java     |  5 ++
 .../royale/compiler/internal/test/ASTestBase.java  | 14 ++++
 .../compiler/internal/test/MXMLTestBase.java       | 16 +++++
 .../compiler/internal/test/RoyaleTestBase.java     |  4 ++
 .../royale/compiler/internal/test/TestBase.java    |  8 +--
 .../src/test/royale/XML.as                         |  0
 .../src/test/royale/XMLList.as                     |  0
 20 files changed, 299 insertions(+), 68 deletions(-)

diff --git a/compiler-externc/build.xml b/compiler-externc/build.xml
index 773a0ee..57b349f 100644
--- a/compiler-externc/build.xml
+++ b/compiler-externc/build.xml
@@ -161,6 +161,7 @@
 
     <target name="clean" description="clean">
         <delete dir="${compiler-externc}/target/classes"/>
+        <delete file="${compiler-externc}/target/js.swc"/>
     </target>
 
     <target name="wipe" depends="clean" description="Wipes everything that didn't come from Git.">
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSC.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSC.java
index 4625a41..23da56e 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSC.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSC.java
@@ -49,7 +49,6 @@ import org.apache.royale.compiler.driver.js.IJSApplication;
 import org.apache.royale.compiler.exceptions.ConfigurationException;
 import org.apache.royale.compiler.exceptions.ConfigurationException.IOError;
 import org.apache.royale.compiler.exceptions.ConfigurationException.MustSpecifyTarget;
-import org.apache.royale.compiler.internal.codegen.js.JSWriter;
 import org.apache.royale.compiler.internal.driver.js.goog.JSGoogCompcConfiguration;
 import org.apache.royale.compiler.internal.projects.CompilerProject;
 import org.apache.royale.compiler.internal.targets.RoyaleSWCTarget;
@@ -478,8 +477,10 @@ public class COMPJSC extends MXMLJSC
                             ByteArrayOutputStream sourceMapTemp = null;
 
                             boolean isExterns = false;
-	                        if (writer instanceof JSWriter)
-	                        	isExterns = ((JSWriter)writer).isExterns();
+                            if(cu.getDefinitionPromises().size() > 0)
+                            {
+                                isExterns = project.isExterns(cu.getDefinitionPromises().get(0).getQualifiedName());
+                            }
 
                             // if the file is @externs DON'T create source map file
                             if (project.config.getSourceMap() && !isExterns)
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCNative.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCNative.java
index e77bc1f..57d2ead 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCNative.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCNative.java
@@ -45,7 +45,6 @@ import org.apache.royale.compiler.driver.js.IJSApplication;
 import org.apache.royale.compiler.exceptions.ConfigurationException;
 import org.apache.royale.compiler.exceptions.ConfigurationException.IOError;
 import org.apache.royale.compiler.exceptions.ConfigurationException.MustSpecifyTarget;
-import org.apache.royale.compiler.internal.codegen.js.JSWriter;
 import org.apache.royale.compiler.internal.driver.mxml.jsc.MXMLJSCJSSWCBackend;
 import org.apache.royale.compiler.internal.parsing.as.RoyaleASDocDelegate;
 import org.apache.royale.compiler.internal.projects.CompilerProject;
@@ -332,8 +331,10 @@ public class COMPJSCNative extends MXMLJSCNative
                             ByteArrayOutputStream sourceMapTemp = null;
                             
                             boolean isExterns = false;
-	                        if (writer instanceof JSWriter)
-	                        	isExterns = ((JSWriter)writer).isExterns();
+                            if(cu.getDefinitionPromises().size() > 0)
+                            {
+                                isExterns = project.isExterns(cu.getDefinitionPromises().get(0).getQualifiedName());
+                            }
 
                             // if the file is @externs DON'T create source map file
 	                        if (project.config.getSourceMap() && !isExterns)
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCRoyale.java b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCRoyale.java
index a83aae9..1ca37df 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCRoyale.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/clients/COMPJSCRoyale.java
@@ -48,7 +48,6 @@ import org.apache.royale.compiler.driver.js.IJSApplication;
 import org.apache.royale.compiler.exceptions.ConfigurationException;
 import org.apache.royale.compiler.exceptions.ConfigurationException.IOError;
 import org.apache.royale.compiler.exceptions.ConfigurationException.MustSpecifyTarget;
-import org.apache.royale.compiler.internal.codegen.js.JSWriter;
 import org.apache.royale.compiler.internal.driver.mxml.royale.MXMLRoyaleSWCBackend;
 import org.apache.royale.compiler.internal.parsing.as.RoyaleASDocDelegate;
 import org.apache.royale.compiler.internal.projects.CompilerProject;
@@ -372,8 +371,10 @@ public class COMPJSCRoyale extends MXMLJSCRoyale
                             ByteArrayOutputStream sourceMapTemp = null;
                             
                             boolean isExterns = false;
-	                        if (writer instanceof JSWriter)
-	                        	isExterns = ((JSWriter)writer).isExterns();
+                            if(cu.getDefinitionPromises().size() > 0)
+                            {
+                                isExterns = project.isExterns(cu.getDefinitionPromises().get(0).getQualifiedName());
+                            }
 	                        
                             // if the file is @externs DON'T create source map file
                             if (project.config.getSourceMap() && !isExterns)
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/PackageHeaderEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/PackageHeaderEmitter.java
index 57cfc76..d5ef151 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/PackageHeaderEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/jx/PackageHeaderEmitter.java
@@ -392,9 +392,13 @@ public class PackageHeaderEmitter extends JSSubEmitter implements
                     continue;
                 }
 
-                if (writtenRequires.indexOf(imp) == -1)
+                if(!project.isGoogProvided(imp))
                 {
+                    continue;
+                }
 
+                if (writtenRequires.indexOf(imp) == -1)
+                {
                     /* goog.require('x');\n */
                     write(JSGoogEmitterTokens.GOOG_REQUIRE);
                     write(ASEmitterTokens.PAREN_OPEN);
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 a5f43e6..d3a65ab 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
@@ -699,12 +699,12 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter
     	{
         	if (getModel().inStaticInitializer)
         		if (!staticUsedNames.contains(name) && !NativeUtils.isJSNative(name)
-        				&& !isExternal(name) && !getModel().getCurrentClass().getQualifiedName().equals(name)
+        				&& isGoogProvided(name) && !getModel().getCurrentClass().getQualifiedName().equals(name)
         				&& (getModel().primaryDefinitionQName == null
         					|| !getModel().primaryDefinitionQName.equals(name)))
         			staticUsedNames.add(name);
     		
-    		if (!usedNames.contains(name) && !isExternal(name))
+    		if (!usedNames.contains(name) && isGoogProvided(name))
     			usedNames.add(name);
     	}
         return name;
@@ -1524,13 +1524,10 @@ public class JSRoyaleEmitter extends JSGoogEmitter implements IJSRoyaleEmitter
         getModel().needLanguage = true;
     }
     
-	boolean isExternal(String className)
+	boolean isGoogProvided(String className)
 	{
         ICompilerProject project = getWalker().getProject();
-		ICompilationUnit cu = project.resolveQNameToCompilationUnit(className);
-		if (cu == null) return false; // unit testing
-		
-		return ((RoyaleJSProject)project).isExternalLinkage(cu);
+		return ((RoyaleJSProject)project).isGoogProvided(className);
 	}
 
 }
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyaleEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyaleEmitter.java
index 826f44e..f5e7cba 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyaleEmitter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/mxml/royale/MXMLRoyaleEmitter.java
@@ -249,11 +249,15 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
                             if (subDocumentNames.contains(usedName)) continue;
                             if (royaleProject != null)
                             {
+                                if (!isGoogProvided(usedName))
+                                {
+                                    continue;
+                                }
                             	ICompilationUnit cu = royaleProject.resolveQNameToCompilationUnit(usedName);
-                            	if (cu != null && royaleProject.isExternalLinkage(cu))
-                            		continue;
-                            	if (cu == null)
-                            		System.out.println("didn't find CompilationUnit for " + usedName);
+                                if (cu == null)
+                                {
+                                    System.out.println("didn't find CompilationUnit for " + usedName);
+                                }
                             }
                             namesToAdd.add(usedName);
                         }
@@ -311,10 +315,14 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
 		            	boolean firstOne = true;
 		            	for (String mixin : mixins)
 		            	{
-		            		if (isExternal(mixin))
-		            			continue;
-		            		if (!firstOne)
-		            			mixinInject += ", ";
+                            if (!isGoogProvided(mixin))
+                            {
+                                continue;
+                            }
+                            if (!firstOne)
+                            {
+                                mixinInject += ", ";
+                            }
 		            		mixinInject += mixin;
 		            		firstOne = false;
 		                    StringBuilder appendString = new StringBuilder();
@@ -340,10 +348,14 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
 		            	boolean firstOne = true;
 		            	for (String className : aliases.keySet())
 		            	{
-		            		if (isExternal(className))
-		            			continue;
-		            		if (!firstOne)
-		            			aliasInject += ", ";
+                            if (!isGoogProvided(className))
+                            {
+                                continue;
+                            }
+                            if (!firstOne)
+                            {
+                                aliasInject += ", ";
+                            }
 		            		aliasInject += "\"" + className + "\": ";
 		            		String alias = aliases.get(className);
 		            		aliasInject += "\"" + alias + "\"";
@@ -373,8 +385,10 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
 		            	locales.toArray(localeNames);
 		            	for (String locale : localeNames)
 		            	{
-		            		if (!firstOne)
-		            			localeInject += ", ";
+                            if (!firstOne)
+                            {
+                                localeInject += ", ";
+                            }
 		            		localeInject += "\"" + locale + "\"";
 		            		firstOne = false;
 		            	}
@@ -390,8 +404,10 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
 		            	boolean firstOne = true;
 		            	for (String bundle : bundles)
 		            	{
-		            		if (!firstOne)
-		            			bundleInject += ", ";
+                            if (!firstOne)
+                            {
+                                bundleInject += ", ";
+                            }
 		            		bundleInject += "\"" + bundle + "\"";
 		            		firstOne = false;
 		            	}
@@ -1535,7 +1551,9 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
             for (IExpressionNode param : params)
             {
                 if (!firstone)
+                {
                     sb.append(ASEmitterTokens.COMMA.getToken());
+                }
                 firstone = false;
                 sb.append(asEmitter.stringifyNode(param));
             }
@@ -3012,12 +3030,18 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
     	if (subDocumentNames.contains(name))
     		return documentDefinition.getQualifiedName() + "." + name;
         if (NativeUtils.isJSNative(name)) return name;
-    	if (inStaticInitializer)
-    		if (!staticUsedNames.contains(name) && !NativeUtils.isJSNative(name) && !isExternal(name))
-    			staticUsedNames.add(name);
+        if (inStaticInitializer)
+        {
+            if (!staticUsedNames.contains(name) && !NativeUtils.isJSNative(name) && isGoogProvided(name))
+            {
+                staticUsedNames.add(name);
+            }
+        }
 
-		if (useName && !usedNames.contains(name) && !isExternal(name))
-			usedNames.add(name);
+        if (useName && !usedNames.contains(name) && isGoogProvided(name))
+        {
+            usedNames.add(name);
+        }
      	return name;
     }
 
@@ -3242,14 +3266,11 @@ public class MXMLRoyaleEmitter extends MXMLEmitter implements
         //System.out.println("mxml implements "+list);
         interfaceList = list.toString();
     }
-
-	boolean isExternal(String className)
+    
+	boolean isGoogProvided(String className)
 	{
         ICompilerProject project = getMXMLWalker().getProject();
-		ICompilationUnit cu = project.resolveQNameToCompilationUnit(className);
-		if (cu == null) return false; // unit testing
-
-		return ((RoyaleJSProject)project).isExternalLinkage(cu);
+		return ((RoyaleJSProject)project).isGoogProvided(className);
 	}
 
 	@Override
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/graph/GoogDepsWriter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/graph/GoogDepsWriter.java
index bfb08cf..66ef9a3 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/graph/GoogDepsWriter.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/graph/GoogDepsWriter.java
@@ -196,15 +196,19 @@ public class GoogDepsWriter {
 			ArrayList<String> restOfDeps = new ArrayList<String>();
 			for (String dep: mainDep.deps)
 			{
-				if (!isExternal(dep))
+				if (isGoogProvided(dep))
+				{
 					restOfDeps.add(dep);
+				}
 			}
 			if (mainDep.fileInfo.impls != null)
 			{
 				for (String dep: mainDep.fileInfo.impls)
 				{
-					if (!isExternal(dep))
+					if (isGoogProvided(dep))
+					{
 						restOfDeps.add(dep);
+					}
 				}				
 			}
 	        DependencyTypeSet dependencyTypes = DependencyTypeSet.allOf();
@@ -220,8 +224,10 @@ public class GoogDepsWriter {
 					{
 						for (String d : gd.fileInfo.impls)
 						{
-							if (!restOfDeps.contains(d) && !gd.fileInfo.isExtern && !isExternal(d) && !usedDeps.contains(d))
+							if (!restOfDeps.contains(d) && !gd.fileInfo.isExtern && isGoogProvided(d) && !usedDeps.contains(d))
+							{
 								restOfDeps.add(d);
+							}
 						}
 					}
 					continue;
@@ -229,15 +235,19 @@ public class GoogDepsWriter {
 				ICompilationUnit unit = requireMap.get(gd.className);
 				if (unit == null)
 				{
-					if (!restOfDeps.contains(gd.className) && !gd.fileInfo.isExtern && !isExternal(gd.className) && !usedDeps.contains(gd.className))
+					if (!restOfDeps.contains(gd.className) && !gd.fileInfo.isExtern && isGoogProvided(gd.className) && !usedDeps.contains(gd.className))
+					{
 						restOfDeps.add(gd.className);
+					}
 					continue;
 				}
 				Set<ICompilationUnit> deps = graph.getDirectReverseDependencies(unit, dependencyTypes);
 				if (deps.size() == 0)
 				{
-					if (!restOfDeps.contains(gd.className) && !gd.fileInfo.isExtern && !isExternal(gd.className) && !usedDeps.contains(gd.className))
+					if (!restOfDeps.contains(gd.className) && !gd.fileInfo.isExtern && isGoogProvided(gd.className) && !usedDeps.contains(gd.className))
+					{
 						restOfDeps.add(gd.className);
+					}
 				}
 			}
 			appendDependencies(restOfDeps, mainDeps);
@@ -307,7 +317,7 @@ public class GoogDepsWriter {
 			problems.add(new MainDefinitionQNameProblem("Google Closure Library", mainName));
 			return false;
 		}
-		if (isExternal(mainName))
+		if (!isGoogProvided(mainName))
 		{
 			problems.add(new MainDefinitionQNameProblem("External Libraries", mainName));
 			return false;
@@ -411,7 +421,7 @@ public class GoogDepsWriter {
 				GoogDep dep = depMap.get(name);
 				if (dep == null)
 				{
-					if (!isExternal(name))
+					if (isGoogProvided(name))
 					{
 						System.out.println("No GoogDep for " + name);
 						//added this to prevent a NullPointerException when the
@@ -516,7 +526,7 @@ public class GoogDepsWriter {
 		ArrayList<String> deps = current.deps;
 		for (String className : deps)
 		{
-			if (!isGoogClass(className) && !isExternal(className))
+			if (!isGoogClass(className) && isGoogProvided(className))
 			{
 				GoogDep gd = depMap.get(className);
 				if (gd == null)
@@ -563,20 +573,24 @@ public class GoogDepsWriter {
 			// first scan requires in case this is a module and some have been externed
 			int j = main.fileInfo.googProvideLine + 1;
 			while (j < fileLines.size() && !fileLines.get(j).contains(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()))
+			{
 				j++;
+			}
 			while (j < fileLines.size() && fileLines.get(j).contains(JSGoogEmitterTokens.GOOG_REQUIRE.getToken()))
 			{
 				String line = fileLines.get(j);
 				int c = line.indexOf(JSGoogEmitterTokens.GOOG_REQUIRE.getToken());
 				int c2 = line.indexOf(")");
                 String s = line.substring(c + 14, c2 - 1);
-                if (isExternal(s))
+                if (!isGoogProvided(s))
                 {
                 	fileLines.remove(j);
 					sourceMapConsumer = removeLineFromSourceMap(sourceMapConsumer, mainFile.getName(), j);
                 }
-                else
-                	j++;
+				else
+				{
+					j++;
+				}
 			}
 			
 			int n = restOfDeps.size();
@@ -615,8 +629,10 @@ public class GoogDepsWriter {
 	
 	private void addDeps(String className)
 	{
-		if (depMap.containsKey(className) || isGoogClass(className) || isExternal(className))
+		if (depMap.containsKey(className) || isGoogClass(className) || !isGoogProvided(className))
+		{
 			return;
+		}
 		
 		// build goog dependency list
 		GoogDep gd = new GoogDep();
@@ -734,7 +750,7 @@ public class GoogDepsWriter {
                         String s = line.substring(c + 14, c2 - 1);
                         if (((gd.fileInfo.impls == null || !gd.fileInfo.impls.contains(s)) &&
                         		(gd.fileInfo.staticDeps == null || !gd.fileInfo.staticDeps.contains(s))) ||
-                        		isExternal(s))
+                        		!isGoogProvided(s))
                         {
                         	// don't remove the require if some class needs it at static initialization
                         	// time
@@ -768,7 +784,7 @@ public class GoogDepsWriter {
 					lastRequireLine = gd.fileInfo.googProvideLine + 1;
             	for (String dep : gd.fileInfo.staticDeps)
             	{
-            		if (!writtenRequires.contains(dep) && !isExternal(dep))
+            		if (!writtenRequires.contains(dep) && isGoogProvided(dep))
             		{
 						StringBuilder lineBuilder = new StringBuilder();
 						lineBuilder.append(JSGoogEmitterTokens.GOOG_REQUIRE.getToken())
@@ -1530,6 +1546,11 @@ public class GoogDepsWriter {
 		return path;
 	}
 	
+	boolean isGoogProvided(String className)
+	{
+		return ((RoyaleJSProject)project).isGoogProvided(className);
+	}
+	
 	boolean isExternal(String className)
 	{
 		ICompilationUnit cu = project.resolveQNameToCompilationUnit(className);
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 42e647c..231a4c0 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
@@ -64,9 +64,11 @@ import org.apache.royale.compiler.targets.ITargetSettings;
 import org.apache.royale.compiler.tree.as.IASNode;
 import org.apache.royale.compiler.tree.as.IClassNode;
 import org.apache.royale.compiler.tree.as.IDefinitionNode;
+import org.apache.royale.compiler.tree.as.IDocumentableDefinitionNode;
 import org.apache.royale.compiler.tree.as.IInterfaceNode;
 import org.apache.royale.compiler.units.ICompilationUnit;
 import org.apache.royale.compiler.units.ICompilationUnit.UnitType;
+import org.apache.royale.swc.ISWC;
 
 import com.google.common.collect.ImmutableList;
 
@@ -180,14 +182,18 @@ public class RoyaleJSProject extends RoyaleProject
             // inheritance is important so remember it
             if (reqs.get(qname) != DependencyType.INHERITANCE)
             {
-                if (!isExternalLinkage(to))
+                if (isGoogProvided(qname))
+                {
                     reqs.put(qname, dt);
+                }
             }
         }
-        else if (!isExternalLinkage(to) || qname.equals("Namespace"))
+        else if (isGoogProvided(qname) || qname.equals("Namespace"))
         {
             if (qname.equals("XML"))
+            {
                 needXML = true;
+            }
             reqs.put(qname, dt);
         }
     }
@@ -236,10 +242,13 @@ public class RoyaleJSProject extends RoyaleProject
 
         if (!interfacesArr.containsKey(qname))
         {
-            if (!isExternalLinkage(to))
-            	interfacesArr.put(qname, qname);
+            if (isGoogProvided(qname))
+            {
+                interfacesArr.put(qname, qname);
+            }
         }
     }
+
     public boolean needLanguage;
     public boolean needCSS;
     public boolean needXML;
@@ -274,6 +283,65 @@ public class RoyaleJSProject extends RoyaleProject
         return null;
     }
 
+    public boolean isExterns(String qname)
+    {
+		ICompilationUnit cu = resolveQNameToCompilationUnit(qname);
+        if (cu == null)
+        {
+            return false;
+        }
+        if (cu.getCompilationUnitType().equals(ICompilationUnit.UnitType.SWC_UNIT))
+        {
+            return !isGoogProvided(qname);
+        }
+        else if (!cu.getCompilationUnitType().equals(ICompilationUnit.UnitType.AS_UNIT))
+        {
+            return false;
+        }
+
+        IDefinition def = resolveQNameToDefinition(qname);
+        if (def == null)
+        {
+            return false;
+        }
+
+        IDefinitionNode node = def.getNode();
+        if (!(node instanceof IDocumentableDefinitionNode))
+        {
+            return false;
+        }
+
+        IDocumentableDefinitionNode docNode = (IDocumentableDefinitionNode) node;
+        IASDocComment comment = docNode.getASDocComment();
+        if (!(comment instanceof ASDocComment))
+        {
+            return false;
+        }
+        ASDocComment royaleComment = (ASDocComment) comment;
+        return royaleComment.commentNoEnd().contains(JSRoyaleEmitterTokens.EXTERNS.getToken());
+    }
+
+    public boolean isGoogProvided(String qname)
+    {
+		ICompilationUnit cu = resolveQNameToCompilationUnit(qname);
+        if (cu == null)
+        {
+            //TODO: maybe this this should be false because we can't actually
+            //check whether it's a goog.provide() object or not
+            return true;
+        }
+        
+        if (cu.getCompilationUnitType().equals(ICompilationUnit.UnitType.SWC_UNIT))
+        {
+            SWCCompilationUnit swcUnit = (SWCCompilationUnit) cu;
+            ISWC swc = swcUnit.getSWC();
+            String qnameFilePath = "js/out/" + qname.replace('.', '/') + ".js";
+            return swc.getFile(qnameFilePath) != null;
+        }
+
+        return !isExterns(qname);
+    }
+
     public boolean isExternalLinkage(ICompilationUnit cu)
     {
         if (linkageChecker == null)
diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/targets/RoyaleJSTarget.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/targets/RoyaleJSTarget.java
index 6155005..0fea71d 100644
--- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/targets/RoyaleJSTarget.java
+++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/targets/RoyaleJSTarget.java
@@ -311,7 +311,9 @@ public class RoyaleJSTarget extends JSTarget implements IJSTarget
                 final File swcFile = new File(compilationUnit.getAbsoluteFilename());
                 final ICSSDocument defaultCSS = cssManager.getDefaultCSS(swcFile);
                 if (defaultCSS != null)
+                {
                     result.put(defaultCSS, swcFile);
+                }
             }
         }
         return result;
diff --git a/compiler-jx/src/test/build.xml b/compiler-jx/src/test/build.xml
index b57aba0..532436e 100644
--- a/compiler-jx/src/test/build.xml
+++ b/compiler-jx/src/test/build.xml
@@ -31,11 +31,33 @@
 	<property name="maxmem" value="512" />
 	
     <property name="compiler" value="${compiler.tests}/../.."/>
+    <property name="compiler-externc" value="${compiler}/../compiler-externc"/>
 
     <target name="download" description="Downloads third-party JARs">
         <ant antfile="${compiler.tests}/downloads.xml" dir="${compiler.tests}"/>
     	<delete dir="${compiler.tests}/in"/>
     </target>
+    
+    <target name="js.swc">
+        <copy file="${compiler-externc}/src/test/config/compile-as-config.xml"
+        todir="${compiler-externc}/target" />
+        <java jar="${compiler}/lib/compc.jar" fork="true"
+            failonerror="true">
+            <arg value="-targets=SWF"/>
+            <arg value="-load-config=${compiler-externc}/target/compile-as-config.xml" />
+            <arg value="-output=${compiler-externc}/target/js.swc" />
+        </java>
+    </target>
+
+    <target name="custom.swc">
+        <copy file="config/compile-js-config.xml"
+        todir="${compiler}/target" />
+        <java jar="${compiler}/lib/compc.jar" fork="true"
+            failonerror="true">
+            <arg value="-load-config=${compiler}/target/compile-js-config.xml" />
+            <arg value="-output=${compiler}/target/custom.swc" />
+        </java>
+    </target>
 
     <target name="compile.unit.tests">
     	<delete dir="${compiler}/target/test-classes"/>
@@ -182,8 +204,8 @@
         </junit>
     </target>
      
-    <target name="main" depends="unit.tests, integration.tests"/>
-    <target name="all" depends="unit.tests, integration.tests, typedef.tests, integration.tests.asjs"/>
+    <target name="main" depends="js.swc, custom.swc, unit.tests, integration.tests"/>
+    <target name="all" depends="main, typedef.tests, integration.tests.asjs"/>
 
     <target name="clean">
         <delete dir="${compiler.tests}/bin"/>
diff --git a/compiler-jx/src/test/config/compile-js-config.xml b/compiler-jx/src/test/config/compile-js-config.xml
new file mode 100644
index 0000000..68a3341
--- /dev/null
+++ b/compiler-jx/src/test/config/compile-js-config.xml
@@ -0,0 +1,46 @@
+<!--
+
+  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.
+
+-->
+<royale-config>
+
+    <compiler>
+        <accessible>true</accessible>
+
+		<targets>
+			<target>SWF</target>
+			<target>JSRoyale</target>
+		</targets>
+        
+        <source-path>
+            <path-element>../../compiler/src/test/royale</path-element>
+        </source-path>
+        
+        <external-library-path>
+            <path-element>../../compiler-externc/target/js.swc</path-element>
+        </external-library-path>
+        
+        <warn-no-constructor>false</warn-no-constructor>
+    </compiler>
+
+	<warn-public-vars>false</warn-public-vars>
+    
+    <include-sources>
+        <path-element>../../compiler/src/test/royale</path-element>
+    </include-sources>
+
+</royale-config>
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/mxml/TestMXMLApplication.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/mxml/TestMXMLApplication.java
index 0aa5601..0e39169 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/mxml/TestMXMLApplication.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/mxml/TestMXMLApplication.java
@@ -18,6 +18,8 @@
  */
 package org.apache.royale.compiler.internal.codegen.mxml;
 
+import static org.junit.Assert.assertNotNull;
+
 import org.apache.royale.compiler.internal.test.MXMLTestBase;
 import org.apache.royale.compiler.tree.mxml.IMXMLFileNode;
 import org.junit.Test;
@@ -33,6 +35,7 @@ public class TestMXMLApplication extends MXMLTestBase
                 + "</custom:TestInstance>";
 
         IMXMLFileNode node = compileMXML(code);
+        assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
 
@@ -48,6 +51,7 @@ public class TestMXMLApplication extends MXMLTestBase
                 + "</custom:TestInstance>";
 
         IMXMLFileNode node = compileMXML(code);
+        assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
 
@@ -64,6 +68,7 @@ public class TestMXMLApplication extends MXMLTestBase
                 + "</custom:TestInstance>";
 
         IMXMLFileNode node = compileMXML(code);
+        assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
 
@@ -80,6 +85,7 @@ public class TestMXMLApplication extends MXMLTestBase
                 + "    ]]></fx:Script>" + "</custom:TestInstance>";
 
         IMXMLFileNode node = compileMXML(code);
+        assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
 
@@ -99,6 +105,7 @@ public class TestMXMLApplication extends MXMLTestBase
                 + "    </fx:Declarations>" + "</custom:TestInstance>";
 
         IMXMLFileNode node = compileMXML(code);
+        assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
 
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/mxml/royale/TestRoyaleMXMLApplication.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/mxml/royale/TestRoyaleMXMLApplication.java
index 19f524a..deec96c 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/mxml/royale/TestRoyaleMXMLApplication.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/mxml/royale/TestRoyaleMXMLApplication.java
@@ -19,6 +19,7 @@
 package org.apache.royale.compiler.internal.codegen.mxml.royale;
 
 import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 
 import org.apache.royale.compiler.clients.MXMLJSC;
@@ -57,6 +58,7 @@ public class TestRoyaleMXMLApplication extends RoyaleTestBase
 
         IMXMLFileNode node = compileMXML(fileName, true,
                 new File(testAdapter.getUnitTestBaseDir(), "royale/files").getPath(), false);
+		assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
         
@@ -79,6 +81,7 @@ public class TestRoyaleMXMLApplication extends RoyaleTestBase
 
         IMXMLFileNode node = compileMXML(fileName, true,
                 new File(testAdapter.getUnitTestBaseDir(), "royale/files").getPath(), false);
+		assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
         
@@ -94,6 +97,7 @@ public class TestRoyaleMXMLApplication extends RoyaleTestBase
 
         IMXMLFileNode node = compileMXML(fileName, true,
                 new File(testAdapter.getUnitTestBaseDir(), "royale/files").getPath(), false);
+		assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
 
@@ -109,6 +113,7 @@ public class TestRoyaleMXMLApplication extends RoyaleTestBase
 
         IMXMLFileNode node = compileMXML(fileName, true,
                 new File(testAdapter.getUnitTestBaseDir(), "royale/files").getPath(), false);
+		assertNotNull(node);
 
         mxmlBlockWalker.visitFile(node);
 
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/ASTestBase.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/ASTestBase.java
index 88ea788..807c950 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/ASTestBase.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/ASTestBase.java
@@ -35,6 +35,7 @@ import org.apache.royale.compiler.tree.as.IInterfaceNode;
 import org.apache.royale.compiler.tree.as.INamespaceAccessExpressionNode;
 import org.apache.royale.compiler.tree.as.IUnaryOperatorNode;
 import org.apache.royale.compiler.tree.as.IVariableNode;
+import org.apache.royale.utils.FilenameNormalization;
 import org.apache.royale.utils.ITestAdapter;
 import org.apache.royale.utils.TestAdapterFactory;
 import org.junit.Ignore;
@@ -57,6 +58,19 @@ public class ASTestBase extends TestBase
     }
 
     @Override
+    protected void addDependencies()
+    {
+        if (libraries.size() == 0)
+        {
+        	String jsSwcPath = FilenameNormalization.normalize("../compiler-externc/target/js.swc");
+    		libraries.add(new File(jsSwcPath));
+        	String customSwcPath = FilenameNormalization.normalize("../compiler-jx/target/custom.swc");
+    		libraries.add(new File(customSwcPath));
+        }
+        super.addDependencies();
+    }
+
+    @Override
     protected void addSourcePaths(List<File> sourcePaths)
     {
         //sourcePaths.add(new File(FilenameNormalization.normalize(
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/MXMLTestBase.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/MXMLTestBase.java
index 5235ffc..7f8ba79 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/MXMLTestBase.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/MXMLTestBase.java
@@ -18,6 +18,8 @@
  */
 package org.apache.royale.compiler.internal.test;
 
+import static org.junit.Assert.assertNotNull;
+
 import java.io.File;
 import java.util.List;
 
@@ -54,6 +56,19 @@ public class MXMLTestBase extends TestBase
     }
 
     @Override
+    protected void addDependencies()
+    {
+        if (libraries.size() == 0)
+        {
+        	String jsSwcPath = FilenameNormalization.normalize("../compiler-externc/target/js.swc");
+    		libraries.add(new File(jsSwcPath));
+        	String customSwcPath = FilenameNormalization.normalize("../compiler-jx/target/custom.swc");
+    		libraries.add(new File(customSwcPath));
+        }
+        super.addDependencies();
+    }
+
+    @Override
     protected void addLibraries(List<File> libraries)
     {
         //libraries.addAll(testAdapter.getLibraries(true));
@@ -103,6 +118,7 @@ public class MXMLTestBase extends TestBase
                     + "</custom:TestInstance>";
 
         IMXMLFileNode node = compileMXML(code);
+        assertNotNull(node);
 
         if (wrapLevel >= WRAP_LEVEL_NODE) // for now: attributes
         {
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/RoyaleTestBase.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/RoyaleTestBase.java
index ae810bc..5f7a8bd 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/RoyaleTestBase.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/RoyaleTestBase.java
@@ -18,6 +18,8 @@
  */
 package org.apache.royale.compiler.internal.test;
 
+import static org.junit.Assert.assertNotNull;
+
 import java.io.File;
 import java.util.List;
 
@@ -98,6 +100,7 @@ public class RoyaleTestBase extends TestBase
                 + code + "]]></fx:Script></basic:Application>";
 
         IMXMLFileNode node = compileMXML(code);
+        assertNotNull(node);
 
         return findFirstASDescendantOfType(node, type);
     }
@@ -113,6 +116,7 @@ public class RoyaleTestBase extends TestBase
         }
 
         IMXMLFileNode node = compileMXML(code);
+        assertNotNull(node);
 
         return findFirstDescendantOfType(node, type);
     }
diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/TestBase.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/TestBase.java
index a89aa85..c436fd9 100644
--- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/TestBase.java
+++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/TestBase.java
@@ -102,9 +102,9 @@ public class TestBase implements ITestBase
 
     protected File tempDir;
 
-    private List<File> sourcePaths = new ArrayList<File>();
-    private List<File> libraries = new ArrayList<File>();
-    private List<IMXMLNamespaceMapping> namespaceMappings = new ArrayList<IMXMLNamespaceMapping>();
+    protected List<File> sourcePaths = new ArrayList<File>();
+    protected List<File> libraries = new ArrayList<File>();
+    protected List<IMXMLNamespaceMapping> namespaceMappings = new ArrayList<IMXMLNamespaceMapping>();
 
     @Before
     public void setUp()
@@ -695,7 +695,7 @@ public class TestBase implements ITestBase
         	String jsSwcPath = FilenameNormalization.normalize("../compiler-externc/target/js.swc");
     		libraries.add(new File(jsSwcPath));
         	String customSwcPath = FilenameNormalization.normalize("../compiler/target/custom.swc");
-    		libraries.add(new File(customSwcPath));        	
+    		libraries.add(new File(customSwcPath));
         }
         addNamespaceMappings(namespaceMappings);
 
diff --git a/compiler-externc/src/test/royale/XML.as b/compiler/src/test/royale/XML.as
similarity index 100%
rename from compiler-externc/src/test/royale/XML.as
rename to compiler/src/test/royale/XML.as
diff --git a/compiler-externc/src/test/royale/XMLList.as b/compiler/src/test/royale/XMLList.as
similarity index 100%
rename from compiler-externc/src/test/royale/XMLList.as
rename to compiler/src/test/royale/XMLList.as