/* * Copyright 2014 Google Inc. * * Licensed 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 com.google.gwt.dev.js; import com.google.gwt.dev.jjs.SourceOrigin; import com.google.gwt.dev.jjs.ast.AccessModifier; import com.google.gwt.dev.jjs.ast.HasName; import com.google.gwt.dev.jjs.ast.JClassType; import com.google.gwt.dev.jjs.ast.JDeclaredType; import com.google.gwt.dev.jjs.ast.JField; import com.google.gwt.dev.jjs.ast.JField.Disposition; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JPrimitiveType; import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMapImpl; import com.google.gwt.dev.js.ast.JsName; import com.google.gwt.dev.js.ast.JsProgram; import com.google.gwt.dev.js.ast.JsStatement; import com.google.gwt.dev.js.ast.JsVisitor; import com.google.gwt.dev.util.DefaultTextOutput; import com.google.gwt.dev.util.TextOutput; import com.google.gwt.thirdparty.guava.common.collect.ImmutableList; import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap; import com.google.gwt.thirdparty.guava.common.collect.Maps; import junit.framework.TestCase; import java.io.IOException; import java.io.StringReader; import java.util.List; import java.util.Map; /** * Verifies that {@link JsNamespaceChooser} can put globals into namespaces. */ public class JsNamespaceChooserTest extends TestCase { private JsProgram program; // components of the jjsmap private JClassType comExampleFooClass = new JClassType(SourceOrigin.UNKNOWN, "com.example.Foo", false, false); private JClassType defaultPackageBarClass = new JClassType(SourceOrigin.UNKNOWN, "Bar", false, false); private Map<HasName, JsName> javaToName = Maps.newHashMap(); public void testMoveJavaField() throws Exception { program = parseJs("var x = 1; x = 2;"); mapJavaField("x"); checkResult("var ce={};ce.x=1;ce.x=2;"); checkNamespaceEquals("ce", "x"); } public void testMoveUninitializedJavaField() throws Exception { program = parseJs("var x; x = 1;"); mapJavaField("x"); checkResult("var ce={};ce.x=1;"); checkNamespaceEquals("ce", "x"); } public void testSkipDefaultPackageMember() throws Exception { program = parseJs("var x = 1; x = 2;"); mapJavaField("x", defaultPackageBarClass); checkResult("var x=1;x=2;"); checkNamespaceEquals(null, "x"); } public void testSkipNonJavaGlobal() throws Exception { program = parseJs("var x = 1; x = 2;"); checkResult("var x=1;x=2;"); checkNamespaceEquals(null, "x"); } public void testMoveJavaFunction() throws Exception { program = parseJs("function f() {} f();"); mapJavaMethod("f"); checkResult("var ce={};ce.f=function f(){};ce.f();"); checkNamespaceEquals("ce", "f"); } public void testSkipNonJavaFunction() throws Exception { program = parseJs("function f() {} f();"); checkResult("function f(){}\nf();"); checkNamespaceEquals(null, "f"); } public void testSkipPolymorphicJavaMethod() throws Exception { program = parseJs("var _ = {}; _.f = function f() {};"); mapJavaMethod("f"); checkResult("var _={};_.f=function f(){};"); checkNamespaceEquals(null, "_"); checkNamespaceEquals(null, "f"); } public void testSkipNamedFunctionExpression() throws Exception { // The name in a named function expression is not a global. // (The scope of the name is just the JavaScript function itself.) program = parseJs("var a; a = function f() {}; a();"); mapJavaMethod("f"); checkResult("var a;a=function f(){};a();"); } /** Adds a mapping from a JavaScript global to a Java field. */ private void mapJavaField(String name) { mapJavaField(name, comExampleFooClass); } /** Adds a mapping from a JavaScript global to a Java field. */ private void mapJavaField(String name, JClassType clazz) { JField field = new JField(SourceOrigin.UNKNOWN, name, clazz, JPrimitiveType.INT, true, Disposition.NONE); clazz.addField(field); javaToName.put(field, program.getScope().findExistingName(name)); } /** Adds a mapping from a JavaScript global to a Java method. */ private void mapJavaMethod(String name) { JMethod method = new JMethod(SourceOrigin.UNKNOWN, name, comExampleFooClass, JPrimitiveType.VOID, false, true, false, AccessModifier.DEFAULT); comExampleFooClass.addMethod(method); javaToName.put(method, program.getScope().findExistingName(name)); } private void checkResult(String expectedJs) { exec(); String actual = serializeJs(program); assertEquals(expectedJs, actual); } private void checkNamespaceEquals(String expectedNamespace, String global) { JsName globalName = program.getScope().findExistingName(global); assertNotNull("name doesn't exist: " + global, globalName); JsName actual = globalName.getNamespace(); assertEquals("namespace is different", expectedNamespace, actual == null ? null : actual.getIdent()); } private void exec() { // Prerequisite: resolve name references. JsSymbolResolver.exec(program); // Build the jjsmap. List<JDeclaredType> types = ImmutableList.<JDeclaredType>of(comExampleFooClass, defaultPackageBarClass); Map<JsStatement, JDeclaredType> typeForStatement = ImmutableMap.of(); Map<JsStatement, JMethod> vtableInitForMethod = ImmutableMap.of(); JavaToJavaScriptMapImpl jjsmap = new JavaToJavaScriptMapImpl(types, javaToName, typeForStatement, vtableInitForMethod); // Run it. JsNamespaceChooser.exec(null, program, jjsmap); } private static JsProgram parseJs(String js) throws IOException, JsParserException { JsProgram program = new JsProgram(); List<JsStatement> statements = JsParser.parse(SourceOrigin.UNKNOWN, program.getScope(), new StringReader(js)); program.getGlobalBlock().getStatements().addAll(statements); return program; } private static String serializeJs(JsProgram program1) { TextOutput text = new DefaultTextOutput(true); JsVisitor generator = new JsSourceGenerationVisitor(text); generator.accept(program1); return text.toString(); } }