public void testOneDimensionPermWithCollapse() { ModuleDef md = new ModuleDef("testOneDimensionPerm"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("debug"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); bindingProperty.addCollapsedValues("true", "false"); } // Permutations and their values are in stable alphabetical order. // PropertyCombinations permutations = new PropertyCombinations(md.getProperties(), md.getActiveLinkerNames()); List<PropertyCombinations> collapsed = permutations.collapseProperties(); assertEquals("size", 1, collapsed.size()); permutations = collapsed.get(0); String[] permutation; Iterator<String[]> it = permutations.iterator(); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("false", permutation[0]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("true", permutation[0]); }
public void testTwoDimensionPermWithExtension() { // This is what you'd get with a conditional <extend-property> ModuleDef md = new ModuleDef("testTwoDimensionsWithConditions"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("user.agent"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "moz"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie6"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie8"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "opera"); } { BindingProperty bindingProperty = properties.createBinding("stackTraces"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); ConditionAny cond = new ConditionAny(); cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie6")); cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie8")); bindingProperty.addDefinedValue(cond, "true"); } validateTwoDimensionPerm(properties, md.getActiveLinkerNames()); }
private static String chooseDefault(BindingProperty property, String... candidates) { for (String candidate : candidates) { if (property.isAllowedValue(candidate)) { return candidate; } } return property.getFirstAllowedValue(); }
/** Sets a binding even if it's set to a different value in the GWT application. */ private static void overrideBinding(ModuleDef module, String propName, String newValue) { BindingProperty binding = module.getProperties().findBindingProp(propName); if (binding != null) { // This sets both allowed and generated values, which is needed since the module // might have explicitly disallowed the value. // It persists over multiple compiles but that's okay since we set it the same way // every time. binding.setValues(binding.getRootCondition(), newValue); } }
/** Make sure that a cycle doesn't cause an infinite loop. */ public void testCycle() { // This is what you'd get with a conditional <set-property value="false"> ModuleDef md = new ModuleDef("testCycle"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("A"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "a1"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "a2"); bindingProperty.addDefinedValue(new ConditionWhenPropertyIs("B", "b3"), "a3"); } { BindingProperty bindingProperty = properties.createBinding("B"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "b1"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "b2"); bindingProperty.addDefinedValue(new ConditionWhenPropertyIs("A", "a3"), "b3"); } try { new PropertyCombinations(properties, md.getActiveLinkerNames()); fail(); } catch (IllegalStateException e) { // OK } }
public void testTwoDimensionPerm() { ModuleDef md = new ModuleDef("testTwoDimensionPerm"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("user.agent"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "moz"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie6"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "opera"); } { BindingProperty bindingProperty = properties.createBinding("debug"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); } // String[]s and their values are in stable alphabetical order. // PropertyCombinations permutations = new PropertyCombinations(md.getProperties(), md.getActiveLinkerNames()); String[] permutation; Iterator<String[]> it = permutations.iterator(); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("false", permutation[0]); assertEquals("ie6", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("false", permutation[0]); assertEquals("moz", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("false", permutation[0]); assertEquals("opera", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("true", permutation[0]); assertEquals("ie6", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("true", permutation[0]); assertEquals("moz", permutation[1]); assertTrue(it.hasNext()); permutation = it.next(); assertEquals("true", permutation[0]); assertEquals("opera", permutation[1]); }
public void testTwoDimensionPermWithCollapse() { ModuleDef md = new ModuleDef("testTwoDimensionPerm"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("user.agent"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "moz"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie6"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "opera"); bindingProperty.addCollapsedValues("moz", "ie6", "opera"); } { BindingProperty bindingProperty = properties.createBinding("debug"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); } // String[]s and their values are in stable alphabetical order. // PropertyCombinations permutations = new PropertyCombinations(md.getProperties(), md.getActiveLinkerNames()); List<PropertyCombinations> collapsed = permutations.collapseProperties(); assertEquals("size", 2, collapsed.size()); Iterator<String[]> it = collapsed.get(0).iterator(); assertEquals(Arrays.asList("false", "ie6"), Arrays.asList(it.next())); assertEquals(Arrays.asList("false", "moz"), Arrays.asList(it.next())); assertEquals(Arrays.asList("false", "opera"), Arrays.asList(it.next())); assertFalse(it.hasNext()); it = collapsed.get(1).iterator(); assertEquals(Arrays.asList("true", "ie6"), Arrays.asList(it.next())); assertEquals(Arrays.asList("true", "moz"), Arrays.asList(it.next())); assertEquals(Arrays.asList("true", "opera"), Arrays.asList(it.next())); assertFalse(it.hasNext()); }
/** * Attempts to set a binding property to the given value. If the value is not allowed, see if we * can find a value that will work. There is a special case for "locale". * * @return the value actually set, or null if unable to set the property */ private static String maybeSetBinding( TreeLogger logger, ModuleDef module, String propName, String newValue) { logger = logger.branch(TreeLogger.Type.INFO, "binding: " + propName + "=" + newValue); BindingProperty binding = module.getProperties().findBindingProp(propName); if (binding == null) { logger.log(TreeLogger.Type.WARN, "undefined property: '" + propName + "'"); return null; } if (!binding.isAllowedValue(newValue)) { String[] allowedValues = binding.getAllowedValues(binding.getRootCondition()); logger.log( TreeLogger.Type.WARN, "property '" + propName + "' cannot be set to '" + newValue + "'"); logger.log(TreeLogger.Type.INFO, "allowed values: " + Joiner.on(", ").join(allowedValues)); // See if we can fall back on a reasonable default. if (allowedValues.length == 1) { // There is only one possibility, so use it. newValue = allowedValues[0]; } else if (binding.getName().equals("locale")) { // TODO: come up with a more general solution. Perhaps fail // the compile and give the user a way to override the property? newValue = chooseDefault(binding, "default", "en", "en_US"); } else { // There is more than one. Continue and possibly compile multiple permutations. logger.log( TreeLogger.Type.INFO, "continuing without " + propName + ". Sourcemaps may not work."); return null; } logger.log(TreeLogger.Type.INFO, "recovered with " + propName + "=" + newValue); } binding.setRootGeneratedValues(newValue); return newValue; }
/** * Given the source code to a Java class named <code>test.EntryPoint</code>, compiles it with * emulated stack traces turned on and returns the JavaScript. */ private JsProgram compileClass(String... lines) throws UnableToCompleteException { // Gather the Java source code to compile. final String code = Joiner.on("\n").join(lines); MockResourceOracle sourceOracle = new MockResourceOracle(); sourceOracle.addOrReplace( new MockJavaResource("test.EntryPoint") { @Override public CharSequence getContent() { return code; } }); sourceOracle.add(JavaAstConstructor.getCompilerTypes()); PrecompileTaskOptions options = new PrecompileTaskOptionsImpl(); options.setOutput(JsOutputOption.PRETTY); options.setRunAsyncEnabled(false); CompilerContext context = new CompilerContext.Builder() .options(options) .minimalRebuildCache(new MinimalRebuildCache()) .build(); ConfigurationProperties config = new ConfigurationProperties(Arrays.asList(recordFileNamesProp, recordLineNumbersProp)); CompilationState state = CompilationStateBuilder.buildFrom(logger, context, sourceOracle.getResources(), null); JProgram jProgram = AstConstructor.construct(logger, state, options, config); jProgram.addEntryMethod(findMethod(jProgram, "onModuleLoad")); if (inline) { MethodInliner.exec(jProgram); } CatchBlockNormalizer.exec(jProgram); // Construct the JavaScript AST. // These passes are needed by GenerateJavaScriptAST. ComputeCastabilityInformation.exec(jProgram, false); ImplementCastsAndTypeChecks.exec(jProgram, false); ArrayNormalizer.exec(jProgram); StringTypeMapper typeMapper = new StringTypeMapper(jProgram); ResolveRuntimeTypeReferences.exec(jProgram, typeMapper, TypeOrder.FREQUENCY); Map<StandardSymbolData, JsName> symbolTable = new TreeMap<StandardSymbolData, JsName>(new SymbolData.ClassIdentComparator()); BindingProperty stackMode = new BindingProperty("compiler.stackMode"); stackMode.addDefinedValue(new ConditionNone(), "EMULATED"); PermutationProperties properties = new PermutationProperties( Arrays.asList( new BindingProperties( new BindingProperty[] {stackMode}, new String[] {"EMULATED"}, config))); JsProgram jsProgram = new JsProgram(); JavaToJavaScriptMap jjsmap = GenerateJavaScriptAST.exec( logger, jProgram, jsProgram, context, typeMapper, symbolTable, properties) .getLeft(); // Finally, run the pass we care about. JsStackEmulator.exec(jProgram, jsProgram, properties, jjsmap); return jsProgram; }
public void testTwoDimensionPermWithExpansion() { ModuleDef md = new ModuleDef("testTwoDimensionsWithExpansion"); Properties properties = md.getProperties(); { BindingProperty bindingProperty = properties.createBinding("user.agent"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "moz"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie6"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "ie8"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "opera"); } { BindingProperty bindingProperty = properties.createBinding("stackTraces"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "false"); bindingProperty.addDefinedValue(bindingProperty.getRootCondition(), "true"); // <set-property name="stackTraces" value="false" /> bindingProperty.setValues(bindingProperty.getRootCondition(), "false"); /* * <set-property name="stackTraces" value="true,false"> <when user.agent * is ie6 or ie 8> </set-property> */ ConditionAny cond = new ConditionAny(); cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie6")); cond.getConditions().add(new ConditionWhenPropertyIs("user.agent", "ie8")); bindingProperty.setValues(cond, "true", "false"); } validateTwoDimensionPerm(properties, md.getActiveLinkerNames()); }
/** Loads the module and configures it for SuperDevMode. (Does not restrict permutations.) */ private ModuleDef loadModule(TreeLogger logger) throws UnableToCompleteException { // make sure we get the latest version of any modified jar ZipFileClassPathEntry.clearCache(); ResourceOracleImpl.clearCache(); ResourceLoader resources = ResourceLoaders.forClassLoader(Thread.currentThread()); resources = ResourceLoaders.forPathAndFallback(options.getSourcePath(), resources); this.resourceLoader.set(resources); // ModuleDefLoader.loadFromResources() checks for modified .gwt.xml files. ModuleDef moduleDef = ModuleDefLoader.loadFromResources( logger, compilerContext, inputModuleName, resources, true); compilerContext = compilerContextBuilder.module(moduleDef).build(); // Undo all permutation restriction customizations from previous compiles. for (BindingProperty bindingProperty : moduleDef.getProperties().getBindingProperties()) { String[] allowedValues = bindingProperty.getAllowedValues(bindingProperty.getRootCondition()); bindingProperty.setRootGeneratedValues(allowedValues); } // A snapshot of the module's configuration before we modified it. ConfigProps config = new ConfigProps(moduleDef); // We need a cross-site linker. Automatically replace the default linker. if (IFrameLinker.class.isAssignableFrom(moduleDef.getActivePrimaryLinker())) { moduleDef.addLinker("xsiframe"); } // Check that we have a compatible linker. Class<? extends Linker> linker = moduleDef.getActivePrimaryLinker(); if (!CrossSiteIframeLinker.class.isAssignableFrom(linker)) { logger.log( TreeLogger.ERROR, "linkers other than CrossSiteIFrameLinker aren't supported. Found: " + linker.getName()); throw new UnableToCompleteException(); } // Deactivate precompress linker. if (moduleDef.deactivateLinker("precompress")) { logger.log(TreeLogger.WARN, "Deactivated PrecompressLinker"); } // Print a nice error if the superdevmode hook isn't present if (config.getStrings("devModeRedirectEnabled").isEmpty()) { throw new RuntimeException( "devModeRedirectEnabled isn't set for module: " + moduleDef.getName()); } // Disable the redirect hook here to make sure we don't have an infinite loop. // (There is another check in the JavaScript, but just in case.) overrideConfig(moduleDef, "devModeRedirectEnabled", "false"); // Turn off "installCode" if it's on because it makes debugging harder. // (If it's already off, don't change anything.) if (config.getBoolean("installCode", true)) { overrideConfig(moduleDef, "installCode", "false"); // Make sure installScriptJs is set to the default for compiling without installCode. overrideConfig( moduleDef, "installScriptJs", "com/google/gwt/core/ext/linker/impl/installScriptDirect.js"); } // override computeScriptBase.js to enable the "Compile" button overrideConfig( moduleDef, "computeScriptBaseJs", "com/google/gwt/dev/codeserver/computeScriptBase.js"); // Fix bug with SDM and Chrome 24+ where //@ sourceURL directives cause X-SourceMap header to be // ignored // Frustratingly, Chrome won't canonicalize a relative URL overrideConfig( moduleDef, "includeSourceMapUrl", "http://" + serverPrefix + SourceHandler.sourceMapLocationTemplate(moduleDef.getName())); // If present, set some config properties back to defaults. // (Needed for Google's server-side linker.) maybeOverrideConfig(moduleDef, "includeBootstrapInPrimaryFragment", "false"); maybeOverrideConfig( moduleDef, "permutationsJs", "com/google/gwt/core/ext/linker/impl/permutations.js"); maybeOverrideConfig( moduleDef, "propertiesJs", "com/google/gwt/core/ext/linker/impl/properties.js"); if (options.isIncrementalCompileEnabled()) { // CSSResourceGenerator needs to produce stable, unique naming for its input. // Currently on default settings CssResourceGenerator's obfuscation depends on // whole world knowledge and thus will produce collision in obfuscated mode, since in // incremental compiles that information is not available. // // TODO(dankurka): Once we do proper stable hashing of classes in CssResourceGenerator, we // can probably replace / remove this. maybeOverrideConfig(moduleDef, "CssResource.style", "stable"); } overrideBinding(moduleDef, "compiler.useSourceMaps", "true"); overrideBinding(moduleDef, "compiler.useSymbolMaps", "false"); overrideBinding(moduleDef, "superdevmode", "on"); return moduleDef; }