/******************************************************************************* * Copyright (c) 2010 xored software, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * xored software, Inc. - initial API and Implementation (Alex Panchenko) *******************************************************************************/ package org.eclipse.dltk.javascript.core.tests.typeinference; import static org.eclipse.dltk.javascript.typeinfo.RModelBuilder.createParameter; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import junit.framework.TestCase; import org.eclipse.dltk.compiler.env.ModuleSource; import org.eclipse.dltk.compiler.problem.ProblemCollector; import org.eclipse.dltk.core.tests.TestSupport; import org.eclipse.dltk.core.tests.util.StringList; import org.eclipse.dltk.internal.javascript.ti.IReferenceAttributes; import org.eclipse.dltk.internal.javascript.ti.TypeInferencer2; import org.eclipse.dltk.internal.javascript.validation.JavaScriptValidations; import org.eclipse.dltk.javascript.ast.Script; import org.eclipse.dltk.javascript.core.Types; import org.eclipse.dltk.javascript.parser.JavaScriptParser; import org.eclipse.dltk.javascript.typeinference.IValueCollection; import org.eclipse.dltk.javascript.typeinference.IValueReference; import org.eclipse.dltk.javascript.typeinference.ReferenceLocation; import org.eclipse.dltk.javascript.typeinference.ValueReferenceUtil; import org.eclipse.dltk.javascript.typeinfo.IRClassType; import org.eclipse.dltk.javascript.typeinfo.IRFunctionType; import org.eclipse.dltk.javascript.typeinfo.IRMember; import org.eclipse.dltk.javascript.typeinfo.IRMethod; import org.eclipse.dltk.javascript.typeinfo.IRRecordType; import org.eclipse.dltk.javascript.typeinfo.IRSimpleType; import org.eclipse.dltk.javascript.typeinfo.IRType; import org.eclipse.dltk.javascript.typeinfo.ITypeNames; import org.eclipse.dltk.javascript.typeinfo.ITypeSystem; import org.eclipse.dltk.javascript.typeinfo.JSTypeSet; import org.eclipse.dltk.javascript.typeinfo.RTypes; import org.eclipse.dltk.javascript.typeinfo.model.Member; import org.eclipse.dltk.javascript.typeinfo.model.ParameterKind; import org.eclipse.dltk.javascript.typeinfo.model.Type; import org.eclipse.dltk.javascript.typeinfo.model.TypeInfoModelLoader; @SuppressWarnings({ "nls", "restriction" }) public class TypeInferenceTests extends TestCase implements ITypeNames { private static Script parse(String code) { final JavaScriptParser parser = new JavaScriptParser(); final ProblemCollector reporter = new ProblemCollector(); final Script script = parser.parse(new ModuleSource(code), reporter); if (reporter.hasErrors()) { fail(reporter.getErrors().toString()); } return script; } private static final String STATIC_PREFIX = "static:"; private JSTypeSet getTypes(String... names) { final JSTypeSet types = JSTypeSet.create(); for (String name : names) { final boolean isStatic = name.startsWith(STATIC_PREFIX); final Type type = TypeInfoModelLoader.getInstance().getType( isStatic ? name.substring(STATIC_PREFIX.length()) : name); assertNotNull(type); types.add(isStatic ? RTypes.classType(ts(), type) : RTypes.simple( ts(), type)); } return types; } private TypeInferencer2 inferencer; private IValueCollection inference(final String code) { inferencer = new TestTypeInferencer2(); // return inferencer.doInferencing(parse(code)); inferencer.doInferencing(parse(code)); return inferencer.getCollection(); } private ITypeSystem ts() { return inferencer; } public void testNewNamedFunction() throws Exception { List<String> lines = new StringList(); lines.add("var test = new function Test() {"); lines.add("this.p = 10;"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference a = collection.getChild("test"); assertEquals(1, a.getTypes().size()); assertEquals("Test", a.getTypes().iterator().next().getName()); assertEquals(1, a.getDirectChildren().size()); assertEquals("p", a.getDirectChildren().iterator().next()); } public void testNewFunction() throws Exception { List<String> lines = new StringList(); lines.add("var test = new function() {"); lines.add("this.p = 10;"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference a = collection.getChild("test"); assertEquals(1, a.getTypes().size()); assertEquals(ITypeNames.OBJECT, a.getTypes().iterator().next() .getName()); assertEquals(1, a.getDirectChildren().size()); assertEquals("p", a.getDirectChildren().iterator().next()); } public void testNewType() throws Exception { List<String> lines = new StringList(); lines.add("function Test() {"); lines.add("this.p = 10;"); lines.add("}"); lines.add("var test = new Test();"); IValueCollection collection = inference(lines.toString()); IValueReference a = collection.getChild("test"); assertEquals(1, a.getTypes().size()); assertEquals("Test", a.getTypes().iterator().next().getName()); assertEquals(1, a.getDirectChildren().size()); assertEquals("p", a.getDirectChildren().iterator().next()); } public void testNestedFunctionType() throws Exception { List<String> lines = new StringList(); lines.add("function Test() {"); lines.add(" /** @type Node */"); lines.add(" this.newNode = function newNode() {"); lines.add(" return new Node();"); lines.add(" }"); lines.add(" function Node(){"); lines.add(" this.a = 10;"); lines.add(" /** @type String */"); lines.add(" this.toString = function toString() {"); lines.add(" return 'Node';"); lines.add(" }"); lines.add(" }"); lines.add("}"); lines.add("var test = new Test();"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); assertEquals(true, test.exists()); assertEquals(1, test.getDirectChildren().size()); assertEquals(1, test.getTypes().size()); assertEquals("Test", test.getTypes().iterator().next().getName()); assertEquals("newNode", test.getDirectChildren().iterator().next()); test = test.getChild("newNode"); assertEquals(true, test.exists()); assertEquals(1, test.getTypes().size()); assertEquals("Function", typename(test.getTypes())); test = test.getChild(IValueReference.FUNCTION_OP); assertEquals(2, test.getDirectChildren().size()); assertNotNull(test.getDeclaredType()); assertEquals("Node", test.getDeclaredType().getName()); IValueReference a = test.getChild("a"); assertEquals(true, a.exists()); assertEquals(1, a.getTypes().size()); assertEquals("Number", typename(a.getTypes())); IValueReference toString = test.getChild("toString"); assertEquals(true, toString.exists()); assertEquals(1, toString.getTypes().size()); assertEquals(FUNCTION, typename(toString.getTypes())); toString = toString.getChild(IValueReference.FUNCTION_OP); assertEquals(true, toString.exists()); assertEquals(1, toString.getTypes().size()); assertEquals("String", typename(toString.getTypes())); } protected String typename(JSTypeSet types) { assertEquals(1, types.size()); return types.toRType().getName(); } public void testNestedFunctionTypeWithoutDeclaration() throws Exception { List<String> lines = new StringList(); lines.add("function Test() {"); lines.add(" this.newNode = function newNode() {"); lines.add(" return new Node();"); lines.add(" }"); lines.add(" function Node(){"); lines.add(" this.a = 10;"); lines.add(" this.toString = function toString() {"); lines.add(" return 'Node';"); lines.add(" }"); lines.add(" }"); lines.add("}"); lines.add("var test = new Test();"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); assertEquals(true, test.exists()); assertEquals(1, test.getDirectChildren().size()); assertEquals(1, test.getTypes().size()); assertEquals("Test", test.getTypes().iterator().next().getName()); assertEquals("newNode", test.getDirectChildren().iterator().next()); test = test.getChild("newNode"); assertEquals(true, test.exists()); assertEquals(1, test.getTypes().size()); assertEquals("Function", typename(test.getTypes())); test = test.getChild(IValueReference.FUNCTION_OP); assertEquals(2, test.getDirectChildren().size()); assertNotNull(test.getDeclaredType()); assertEquals("Node", test.getDeclaredType().getName()); IValueReference a = test.getChild("a"); assertEquals(true, a.exists()); assertEquals(1, a.getTypes().size()); assertEquals("Number", a.getTypes().iterator().next().getName()); IValueReference toString = test.getChild("toString"); assertEquals(true, toString.exists()); assertEquals(1, toString.getTypes().size()); assertEquals("Function", typename(toString.getTypes())); toString = toString.getChild(IValueReference.FUNCTION_OP); assertEquals(true, toString.exists()); assertEquals(1, toString.getTypes().size()); assertEquals("String", typename(toString.getTypes())); } public void testNestedFunctionTypeWithScopeReturn() throws Exception { List<String> lines = new StringList(); lines.add("function Test() {"); lines.add("this.newScope = function() {"); lines.add("return {"); lines.add("x:10,"); lines.add("y:'10',"); lines.add("z:function(){return 'test';}"); lines.add("}"); lines.add("}"); lines.add("}"); lines.add("var test = new Test();"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); assertEquals(true, test.exists()); assertEquals(1, test.getDirectChildren().size()); assertEquals(1, test.getTypes().size()); assertEquals("Test", test.getTypes().iterator().next().getName()); assertEquals("newScope", test.getDirectChildren().iterator().next()); test = test.getChild("newScope"); assertEquals(true, test.exists()); assertEquals(1, test.getTypes().size()); assertEquals("Function", test.getTypes().iterator().next().getName()); test = test.getChild(IValueReference.FUNCTION_OP); final IRRecordType testType = (IRRecordType) JavaScriptValidations .typeOf(test); assertEquals(3, testType.getMembers().size()); assertNull(test.getDeclaredType()); IValueReference x = test.getChild("x"); assertEquals(true, x.exists()); assertEquals(1, x.getDeclaredTypes().size()); assertEquals(0, x.getTypes().size()); assertEquals(RTypes.NUMBER, x.getDeclaredType()); IValueReference y = test.getChild("y"); assertEquals(true, y.exists()); assertEquals(1, y.getDeclaredTypes().size()); assertEquals(0, y.getTypes().size()); assertEquals(RTypes.STRING, y.getDeclaredType()); IValueReference z = test.getChild("z"); assertEquals(true, z.exists()); assertEquals(1, z.getDeclaredTypes().size()); assertEquals(0, z.getTypes().size()); assertTrue(z.getDeclaredType() instanceof IRFunctionType); IValueReference zResult = z.getChild(IValueReference.FUNCTION_OP); assertEquals(true, zResult.exists()); assertEquals(1, zResult.getTypes().size()); assertEquals("String", zResult.getTypes().iterator().next().getName()); } public void testNumberVar() { IValueCollection collection = inference("var a = 1"); IValueReference a = collection.getChild("a"); assertEquals(getTypes(NUMBER), a.getTypes()); } public void testStringVar() { IValueCollection collection = inference("var a = \"Hello\""); IValueReference a = collection.getChild("a"); assertEquals(getTypes(STRING), a.getTypes()); } public void testBooleanVar() { IValueCollection collection = inference("var a = false"); IValueReference a = collection.getChild("a"); assertEquals(getTypes(BOOLEAN), a.getTypes()); } public void testXmlVar() { IValueCollection collection = inference("var a = <hello/>"); IValueReference a = collection.getChild("a"); assertEquals(getTypes(XML), a.getTypes()); } public void testXmlVarHidesType() { IValueCollection collection = inference("var XML = 1"); IValueReference xml = collection.getChild("XML"); assertEquals(getTypes(NUMBER), xml.getTypes()); } public void testConditionalOperator() { IValueCollection collection = inference("var a = isActive ? 1 : 0"); IValueReference a = collection.getChild("a"); assertEquals(getTypes(NUMBER), a.getTypes()); } public void testReturnNumber() { List<String> lines = new StringList(); lines.add("var s = 1"); lines.add("s.execute = function() {"); lines.add(" return 1"); lines.add("}"); lines.add("var z = s.execute()"); IValueCollection collection = inference(lines.toString()); IValueReference z = collection.getChild("z"); assertEquals(getTypes(NUMBER), z.getTypes()); } public void testReturnObjectProperties() { List<String> lines = new StringList(); lines.add("var s = 1"); lines.add("s.execute = function() {"); lines.add(" return { a: 1, b: true }"); lines.add("}"); lines.add("var z = s.execute()"); IValueCollection collection = inference(lines.toString()); IValueReference z = collection.getChild("z"); assertTrue(z.getTypes().toRType() instanceof IRRecordType); final IValueReference a = z.getChild("a"); assertTrue(a.exists()); assertEquals(getTypes(NUMBER), a.getDeclaredTypes()); final IValueReference b = z.getChild("b"); assertTrue(b.exists()); assertEquals(getTypes(BOOLEAN), b.getDeclaredTypes()); } public void testInlineFunctionStatementCall() { List<String> lines = new StringList(); lines.add("var str = (function() {"); lines.add(" return 'Hello'"); lines.add("})()"); IValueCollection collection = inference(lines.toString()); IValueReference str = collection.getChild("str"); assertEquals(getTypes(STRING), str.getTypes()); } public void testToStringMethod() { List<String> lines = new StringList(); lines.add("var num = 1"); lines.add("var str = num.toString()"); IValueCollection collection = inference(lines.toString()); IValueReference str = collection.getChild("str"); assertEquals(getTypes(STRING), str.getTypes()); } public void testStringLengthProperty() { List<String> lines = new StringList(); lines.add("var str = 'STRING'"); lines.add("var len = str.length"); IValueCollection collection = inference(lines.toString()); IValueReference len = collection.getChild("len"); assertEquals(getTypes(NUMBER), len.getTypes()); } public void testFunctionCall() { List<String> lines = new StringList(); lines.add("function hello() {"); lines.add(" return 'Hello'"); lines.add("}"); lines.add("var str = hello()"); IValueCollection collection = inference(lines.toString()); IValueReference str = collection.getChild("str"); assertEquals(getTypes(STRING), str.getTypes()); } public void testIf() { List<String> lines = new StringList(); lines.add("if (1 == 2) {"); lines.add(" x = 1"); lines.add("}"); lines.add("else {"); lines.add(" x = 'No'"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference x = collection.getChild("x"); assertEquals(getTypes(STRING, NUMBER), x.getTypes()); } public void testIf2() { List<String> lines = new StringList(); lines.add("if (1 == 2) {"); lines.add(" x = 1"); lines.add("}"); lines.add("else {"); lines.add(" y = 'No'"); lines.add("}"); IValueCollection collection = inference(lines.toString()); assertEquals(getTypes(NUMBER), collection.getChild("x").getTypes()); assertEquals(getTypes(STRING), collection.getChild("y").getTypes()); } public void testFor() { List<String> lines = new StringList(); lines.add("for (var i = 0; i < 10; ++i) {"); lines.add(" x = 'No'"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference i = collection.getChild("i"); assertEquals(getTypes(NUMBER), i.getTypes()); IValueReference x = collection.getChild("x"); assertEquals(getTypes(STRING), x.getTypes()); } public void testForIn() { List<String> lines = new StringList(); lines.add("for (var i in objectWithIterator) {"); lines.add(" print(objectWithIterator[i])"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference i = collection.getChild("i"); assertEquals(getTypes(STRING), i.getTypes()); } public void testWhile() { List<String> lines = new StringList(); lines.add("while (1 == 2) {"); lines.add(" x = 'No'"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference x = collection.getChild("x"); assertEquals(getTypes(STRING), x.getTypes()); } public void testUnknownVar() { List<String> lines = new StringList(); lines.add("var x = y"); IValueCollection collection = inference(lines.toString()); IValueReference x = collection.getChild("x"); assertEquals(getTypes(), x.getTypes()); assertFalse(collection.hasChild("y")); } public void testUnknownProperty() { List<String> lines = new StringList(); lines.add("var x = {a:1}"); lines.add("var y = x.b"); IValueCollection collection = inference(lines.toString()); IValueReference x = collection.getChild("x"); final IRRecordType xType = (IRRecordType) x.getTypes().toRType(); assertEquals(Collections.singleton("a"), RTypes.memberNames(xType)); IValueReference y = collection.getChild("y"); assertEquals(getTypes(), y.getTypes()); } public void testUnknownProperty3() { List<String> lines = new StringList(); lines.add("x.y.z = 1"); IValueCollection collection = inference(lines.toString()); IValueReference x = collection.getChild("x"); IValueReference y = x.getChild("y"); IValueReference z = y.getChild("z"); assertEquals(getTypes(NUMBER), z.getTypes()); } public void testSwitch() { List<String> lines = new StringList(); lines.add("switch(n) {"); lines.add("case 0: str = 'Zero'; break;"); lines.add("case 1: str = 'One'; break;"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference x = collection.getChild("str"); assertEquals(getTypes(STRING), x.getTypes()); } public void testWith() { if (TestSupport.notYetImplemented(this)) return; List<String> lines = new StringList(); lines.add("var a = {name:1}"); lines.add("with (a) {"); lines.add(" name = 'Alex'"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference a = collection.getChild("a"); assertTrue(a.getTypes().toRType() instanceof IRRecordType); IValueReference name = a.getChild("name"); assertTrue(name.getTypes().containsAll(getTypes(STRING))); } public void testNew() { List<String> lines = new StringList(); lines.add("var str = new String()"); lines.add("var num = new Number()"); lines.add("var bool = new Boolean()"); lines.add("var arr = new Array()"); IValueCollection collection = inference(lines.toString()); IValueReference str = collection.getChild("str"); assertEquals(getTypes(STRING), str.getTypes()); IValueReference num = collection.getChild("num"); assertEquals(getTypes(NUMBER), num.getTypes()); IValueReference bool = collection.getChild("bool"); assertEquals(getTypes(BOOLEAN), bool.getTypes()); IValueReference arr = collection.getChild("arr"); assertEquals(JSTypeSet.singleton(RTypes.arrayOf(ts(), RTypes.any())), arr.getTypes()); } public void testRecursion1() { List<String> lines = new StringList(); lines.add("context = { 'index': 1, 'prev': context }"); lines.add("doSomethind()"); lines.add("context = context['prev']"); IValueCollection collection = inference(lines.toString()); IValueReference context = collection.getChild("context"); IValueReference index = context.getChild("index"); assertTrue(index.exists()); IValueReference prev = context.getChild("prev"); assertTrue(prev.exists()); // (alex) not sure what is expected here } public void testRecursion2() { List<String> lines = new StringList(); lines.add("var a = function() { return 1 }"); lines.add("a.operation2 = a"); IValueCollection collection = inference(lines.toString()); IValueReference a = collection.getChild("a"); assertTrue(a.exists()); } public void testRecursion3() { List<String> lines = new StringList(); lines.add("var a = function() { return 1 }"); lines.add("a.operation2 = a"); lines.add("var z = a.operation2()"); IValueCollection collection = inference(lines.toString()); IValueReference z = collection.getChild("z"); assertEquals(getTypes(NUMBER), z.getTypes()); } public void testExampleTypeProvider1() { List<String> lines = new StringList(); lines.add("/** @type ExampleService */"); lines.add("var a"); lines.add("var status = a.execute().status"); IValueCollection collection = inference(lines.toString()); IValueReference status = collection.getChild("status"); assertEquals(getTypes(NUMBER), status.getTypes()); } public void testExampleTypeProvider2() { List<String> lines = new StringList(); lines.add("/** @type ExampleService */"); lines.add("var a"); lines.add("var name = a.execute().service.name"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("name"); assertEquals(getTypes(STRING), name.getTypes()); } public void testExampleTypeProvider3() { List<String> lines = new StringList(); lines.add("/** @type ExampleService2 */"); lines.add("var a"); lines.add("var name = a.execute().service.name"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("name"); assertTrue(name.exists()); assertTrue(name.getTypes().isEmpty()); } public void testExampleTypeWithCollection() { List<String> lines = new StringList(); lines.add("/** @type ExampleService2 */"); lines.add("var a;"); lines.add("/** @type " + ExampleTypeProvider.TYPE_WITH_COLLECTION + " */"); lines.add("var b = a.execute();"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("b"); assertTrue(name.exists()); assertEquals(ExampleTypeProvider.TYPE_WITH_COLLECTION, name .getDeclaredType().getName()); Set<String> directChildren = name.getDirectChildren(); assertEquals(1, directChildren.size()); assertTrue(name.getChild("service").exists()); assertTrue(name.getChild("test").exists()); } public void testExampleTypeWithCollectionInArray() { List<String> lines = new StringList(); lines.add("/** @type Array<" + ExampleTypeProvider.TYPE_WITH_COLLECTION + ">" + " */"); lines.add("var b = new Array()"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("b"); assertTrue(name.exists()); assertEquals("Array<" + ExampleTypeProvider.TYPE_WITH_COLLECTION + ">", name.getDeclaredType().getName()); IValueReference array = name.getChild("[]"); assertTrue(array.getChild("service").exists()); assertTrue(array.getChild("test").exists()); } public void testGenericArrayTypeMethod() { List<String> lines = new StringList(); lines.add("/** @type " + ExampleTypeProvider.TYPE_GENERIC_ARRAY_METHOD + " */"); lines.add("var a"); lines.add("var name = a.execute();"); lines.add("var name2 = a.execute()[0];"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("name"); assertTrue(name.exists()); assertEquals(1, name.getTypes().size()); assertEquals(JSTypeSet.singleton(RTypes.arrayOf(ts(), RTypes.STRING)), name.getTypes()); IValueReference name2 = collection.getChild("name2"); assertTrue(name2.exists()); assertEquals(1, name2.getTypes().size()); assertEquals("String", typename(name2.getTypes())); } public void testGenericArrayResolverMethod() { List<String> lines = new StringList(); lines.add("var name = myGenericArrayTest.execute()[0];"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("name"); assertTrue(name.exists()); assertEquals(1, name.getTypes().size()); assertEquals("String", name.getTypes().iterator().next().getName()); } public void testGenericArrayResolverProperty() { List<String> lines = new StringList(); lines.add("var name = myGenericArrayTest.genericArrayProperty[0];"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("name"); assertTrue(name.exists()); assertEquals(getTypes(ITypeNames.STRING), name.getTypes()); } public void testExampleElementResolver1() { List<String> lines = new StringList(); lines.add("var name = ExampleGlobal.execute().service.name"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("name"); assertEquals(getTypes(STRING), name.getTypes()); } public void testExampleElementResolver2() { List<String> lines = new StringList(); lines.add("var name = executeExampleGlobal().service.name"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("name"); assertEquals(getTypes(STRING), name.getTypes()); } public void testSelfReferenceAssignment() throws Exception { List<String> lines = new StringList(); lines.add("var str = '10';"); lines.add("str = str.big();"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("str"); assertEquals(getTypes(STRING), name.getTypes()); } public void testStaticTypeAssignment() throws Exception { List<String> lines = new StringList(); lines.add("var num = Number;"); IValueCollection collection = inference(lines.toString()); IValueReference name = collection.getChild("num"); assertEquals(getTypes(STATIC_PREFIX + NUMBER), name.getTypes()); assertEquals(NUMBER, ((IRClassType) name.getTypes().toRType()) .getTarget().getName()); // TODO should a static reference getchild really return existing none // static childs? // assertEquals(true, name.getChild("prototype").exists()); // assertEquals(false, name.getChild("toFixed").exists()); } public void testAssignToResolvedProperty() { List<String> lines = new StringList(); lines.add("ExampleGlobal.abcdef = 1"); inference(lines.toString()); } public void testJavaClzIntegrationWithPackagesPrefix() { List<String> lines = new StringList(); lines.add("var str = Packages.java.lang.String;"); lines.add("var x = new str()"); IValueCollection collection = inference(lines.toString()); IValueReference strClz = collection.getChild("str"); assertEquals(1, strClz.getTypes().size()); IRType type = strClz.getTypes().toRType(); assertEquals("Class<java.lang.String>", type.getName()); final Type stringType = ((IRClassType) type).getTarget(); final Member valueOf = stringType.findDirectMember("valueOf"); assertNotNull(valueOf); assertEquals(true, valueOf.isStatic()); IValueReference str = collection.getChild("x"); assertEquals(1, str.getTypes().size()); assertEquals(stringType, ((IRSimpleType) str.getTypes().toRType()).getTarget()); final Member toString = stringType.findDirectMember("toString"); assertNotNull(toString); assertEquals(false, toString.isStatic()); } public void testJavaClzIntegrationWithJavaPrefix() { List<String> lines = new StringList(); lines.add("var str = java.lang.String;"); lines.add("var x = new str()"); IValueCollection collection = inference(lines.toString()); IValueReference strClz = collection.getChild("str"); assertEquals(1, strClz.getTypes().size()); IRType type = strClz.getTypes().toRType(); assertEquals("Class<java.lang.String>", type.getName()); final Type stringType = ((IRClassType) type).getTarget(); final Member valueOf = stringType.findDirectMember("valueOf"); assertNotNull(valueOf); assertEquals(true, valueOf.isStatic()); IValueReference str = collection.getChild("x"); assertEquals(1, str.getTypes().size()); assertEquals(stringType, ((IRSimpleType) str.getTypes().toRType()).getTarget()); final Member toString = stringType.findDirectMember("toString"); assertNotNull(toString); assertEquals(false, toString.isStatic()); } public void testJavaIntegrationWithJavaPrefix() { List<String> lines = new StringList(); lines.add("var str = new java.lang.String()"); IValueCollection collection = inference(lines.toString()); IValueReference strClz = collection.getChild("str"); assertEquals(1, strClz.getTypes().size()); IRType type = strClz.getTypes().iterator().next(); assertEquals("java.lang.String", type.getName()); boolean toStringFound = false; for (Member member : ((IRSimpleType) type).getTarget().getMembers()) { toStringFound = member.getName().equals("toString"); if (toStringFound) { assertEquals(false, member.isStatic()); break; } } assertEquals(true, toStringFound); } public void testJavaIntegrationWithPackagesPrefix() { List<String> lines = new StringList(); lines.add("var str = new Packages.java.lang.String()"); IValueCollection collection = inference(lines.toString()); IValueReference strClz = collection.getChild("str"); assertEquals(1, strClz.getTypes().size()); IRType type = strClz.getTypes().iterator().next(); assertEquals("java.lang.String", type.getName()); boolean toStringFound = false; for (Member member : ((IRSimpleType) type).getTarget().getMembers()) { toStringFound = member.getName().equals("toString"); if (toStringFound) { assertEquals(false, member.isStatic()); break; } } assertEquals(true, toStringFound); } public void testJSDocParamWithDefaultProperties() throws Exception { List<String> lines = new StringList(); lines.add("/**"); lines.add(" * @param node a nice node"); lines.add(" * @param node.name the name of the node"); lines.add(" * @param node.value the value of the node"); lines.add(" */"); lines.add("function addChild(node) {"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference addChild = collection.getChild("addChild"); assertEquals(1, addChild.getDirectChildren().size()); assertEquals(IValueReference.FUNCTION_OP, addChild.getDirectChildren() .iterator().next()); IValueCollection functionCollection = (IValueCollection) addChild .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); assertNotNull(functionCollection); IValueReference node = functionCollection.getChild("node"); assertEquals(0, node.getDirectChildren().size()); assertNotNull(node.getDeclaredType()); final IRRecordType declaredType = (IRRecordType) node.getDeclaredType(); assertEquals(2, declaredType.getMembers().size()); assertNotNull(declaredType.getMember("name")); assertNotNull(declaredType.getMember("value")); } public void testJSDocTypeTagFunction() throws Exception { List<String> lines = new StringList(); lines.add("/**"); lines.add(" * @type String"); lines.add(" */"); lines.add("function getChild() {"); lines.add("}"); lines.add("function test() {"); lines.add("var x = getChild();"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) test .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference x = functionScope.getChild("x"); assertEquals(1, x.getTypes().size()); assertEquals("String", x.getTypes().iterator().next().getName()); } public void testJSDocReturnsTagFunction() throws Exception { List<String> lines = new StringList(); lines.add("/**"); lines.add(" * @returns {String} a nice string"); lines.add(" */"); lines.add("function getChild() {"); lines.add("}"); lines.add("function test() {"); lines.add("var x = getChild();"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) test .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference x = functionScope.getChild("x"); assertEquals(1, x.getTypes().size()); assertEquals("String", x.getTypes().iterator().next().getName()); } public void testJSDocReturnsTagLazyFunction() throws Exception { List<String> lines = new StringList(); lines.add("function test() {"); lines.add("var x = getChild();"); lines.add("}"); lines.add("/**"); lines.add(" * @returns {String} a nice string"); lines.add(" */"); lines.add("function getChild() {"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) test .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference x = functionScope.getChild("x"); assertEquals(1, x.getTypes().size()); assertEquals("String", x.getTypes().iterator().next().getName()); } public void testJSDocTypeTagVariable() throws Exception { List<String> lines = new StringList(); lines.add("/**"); lines.add(" * @type String"); lines.add(" */"); lines.add("var str;"); lines.add("function test() {"); lines.add("var x = str;"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) test .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference x = functionScope.getChild("x"); assertNotNull(x.getDeclaredType()); assertEquals("String", x.getDeclaredType().getName()); } public void testJSDocTypeTagMultiVar1() throws Exception { List<String> lines = new StringList(); lines.add("/** @type String */"); lines.add("var var1,"); lines.add("\t" + "/** @type Number */"); lines.add("\t" + "var2"); IValueCollection collection = inference(lines.toString()); assertEquals("String", typename(collection.getChild("var1") .getDeclaredTypes())); assertEquals("Number", typename(collection.getChild("var2") .getDeclaredTypes())); } public void testJSDocTypeTagMultiVar2() throws Exception { List<String> lines = new StringList(); lines.add("var"); lines.add("\t" + "/** @type String */"); lines.add("\t" + "var1,"); lines.add("\t" + "/** @type Number */"); lines.add("\t" + "var2"); IValueCollection collection = inference(lines.toString()); assertEquals("String", typename(collection.getChild("var1") .getDeclaredTypes())); assertEquals("Number", typename(collection.getChild("var2") .getDeclaredTypes())); } public void testOrAssignmentOf2VariablesOfSameType() throws Exception { List<String> lines = new StringList(); lines.add("/**"); lines.add(" * @param {String} a"); lines.add(" * @param {String} b"); lines.add(" */"); lines.add("function test(a,b) {"); lines.add("var x = a || b;"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) test .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference x = functionScope.getChild("x"); assertEquals(x.getTypes().toString(), 1, x.getTypes().size()); assertTrue(x.getTypes().toString(), x.getTypes().equals(getTypes("String"))); } public void testOrAssignmentOf2VariablesOfDifferentType() throws Exception { List<String> lines = new StringList(); lines.add("/**"); lines.add(" * @param {Number} a"); lines.add(" * @param {String} b"); lines.add(" */"); lines.add("function test(a,b) {"); lines.add("var x = a || b;"); lines.add("}"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) test .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference x = functionScope.getChild("x"); assertEquals(x.getTypes().toString(), 2, x.getTypes().size()); assertTrue(x.getTypes().toString(), x.getTypes().equals(getTypes("Number", "String"))); } public void testJSDocTypeTagVariableLazy() throws Exception { List<String> lines = new StringList(); lines.add("function test() {"); lines.add("var x = str;"); lines.add("}"); lines.add("/**"); lines.add(" * @type String"); lines.add(" */"); lines.add("var str;"); IValueCollection collection = inference(lines.toString()); IValueReference test = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) test .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference x = functionScope.getChild("x"); assertNotNull(x.getDeclaredType()); assertEquals("String", x.getDeclaredType().getName()); } public void testCallExternalFunctionViaReferenceCopy() { StringList code = new StringList(); code.add("var p = parseInt"); code.add("var n = p('1')"); IValueCollection collection = inference(code.toString()); IValueReference n = collection.getChild("n"); assertEquals(getTypes(ITypeNames.NUMBER), n.getTypes()); } public void testMapWithOnlyValueDeclaration() { StringList code = new StringList(); code.add("/** @param {Object<String>} param */"); code.add("function test(param) {"); code.add(" var x = param['test'];"); code.add("}"); IValueCollection collection = inference(code.toString()); IValueReference function = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) function .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference variable = functionScope.getChild("x"); assertEquals(getTypes(ITypeNames.STRING), variable.getTypes()); } public void testMapWithKeyValueDeclaration() { StringList code = new StringList(); code.add("/** @param {Object<String,String>} param */"); code.add("function test(param) {"); code.add(" var x = param['test'];"); code.add("}"); IValueCollection collection = inference(code.toString()); IValueReference function = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) function .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference variable = functionScope.getChild("x"); assertEquals(getTypes(ITypeNames.STRING), variable.getTypes()); } public void testMapWithKeyValueDeclaration2() { StringList code = new StringList(); code.add("/** @param {Object<String,Number>} param */"); code.add("function test(param) {"); code.add(" var x = param['test'];"); code.add("}"); IValueCollection collection = inference(code.toString()); IValueReference function = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) function .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); IValueReference variable = functionScope.getChild("x"); assertEquals(getTypes(ITypeNames.NUMBER), variable.getTypes()); } public void testGenericArrayPop() throws Exception { StringList code = new StringList(); code.add("/** @type {Array<String>} */"); code.add("var x = [];"); code.add("var y = x.pop();"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("y"); assertEquals(getTypes(ITypeNames.STRING), child.getTypes()); } public void testJSDocWithArrayInArrayType() throws Exception { StringList code = new StringList(); code.add("/** @type {String[][]} */"); code.add("var x = null;"); code.add("var y = x[0][0];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("y"); assertEquals(getTypes(ITypeNames.STRING), child.getTypes()); } public void testJSDocWithGenericArrayInArrayType() throws Exception { StringList code = new StringList(); code.add("/** @type {Array<String>[]} */"); code.add("var x = null;"); code.add("var y = x[0][0];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("y"); assertEquals(getTypes(ITypeNames.STRING), child.getTypes()); } public void testArrayInitializerWithLiteralNumbers() { StringList code = new StringList(); code.add("var x = [1,2,3];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("x"); IRType type = JavaScriptValidations.typeOf(child); assertEquals(RTypes.arrayOf(ts(), RTypes.simple(ts(), Types.NUMBER)), type); } public void testArrayInitializerWithLiteralStrings() { StringList code = new StringList(); code.add("var x = ['1','2','3'];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("x"); IRType type = JavaScriptValidations.typeOf(child); assertEquals(RTypes.arrayOf(ts(), RTypes.simple(ts(), Types.STRING)), type); } public void testArrayInitializerWithLiteralMixed() { StringList code = new StringList(); code.add("var x = ['1','2',3];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("x"); IRType type = JavaScriptValidations.typeOf(child); assertEquals(RTypes.arrayOf(ts(), RTypes.union(Arrays.<IRType>asList(RTypes.STRING,RTypes.NUMBER))), type); } public void testArrayInitializerWithVariableNumbers() { StringList code = new StringList(); code.add("var y = 1;"); code.add("var x = [y,y,y];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("x"); IRType type = JavaScriptValidations.typeOf(child); assertEquals(RTypes.arrayOf(ts(), RTypes.simple(ts(), Types.NUMBER)), type); } public void testArrayInitializerWithVariableAndLiteralNumbers() { StringList code = new StringList(); code.add("var y = 1;"); code.add("var x = [y,1,y];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("x"); IRType type = JavaScriptValidations.typeOf(child); assertEquals(RTypes.arrayOf(ts(), RTypes.simple(ts(), Types.NUMBER)), type); } public void testArrayInitializerWithVariableStrings() { StringList code = new StringList(); code.add("var y = '1';"); code.add("var x = [y,y,y];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("x"); IRType type = JavaScriptValidations.typeOf(child); assertEquals(RTypes.arrayOf(ts(), RTypes.simple(ts(), Types.STRING)), type); } public void testArrayInitializerWithVariableAndLiteralStrings() { StringList code = new StringList(); code.add("var y = '1';"); code.add("var x = [y,'2',y];"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("x"); IRType type = JavaScriptValidations.typeOf(child); assertEquals(RTypes.arrayOf(ts(), RTypes.simple(ts(), Types.STRING)), type); } public void testArrayInRecordTypeVariableLookup() { StringList code = new StringList(); code.add("/** @type {{layout_id:String,panels:Array<{foundset:String, index:Number, selectedTab:Number, view:Number}>}} */"); code.add("var vInfoObject = null;"); code.add("var panel = vInfoObject.panels[0];"); code.add("var fs = panel.foundset;"); IValueCollection collection = inference(code.toString()); IValueReference child = collection.getChild("fs"); IRType type = JavaScriptValidations.typeOf(child); assertEquals(RTypes.simple(ts(), Types.STRING), type); } public void testDeleteOperator() { List<String> lines = new StringList(); lines.add("var point = {x:1, y:2}"); lines.add("var result = delete point.x"); IValueCollection collection = inference(lines.toString()); IValueReference len = collection.getChild("result"); assertEquals(getTypes(BOOLEAN), len.getTypes()); } public void testDateAsFunction() { List<String> lines = new StringList(); lines.add("var d = Date()"); IValueCollection collection = inference(lines.toString()); IValueReference len = collection.getChild("d"); assertEquals(getTypes(STRING), len.getTypes()); } public void testFunctionTypeMethodCaching() { final StringList code = new StringList(); code.add("/** @type {function(Number,Number):Date} */"); code.add("var f;"); code.add("var fcall = f.call;"); code.add("fcall({}, 1, 2);"); code.add(""); code.add("/** @type {function(Number,Number):Date} */"); code.add("var g;"); code.add("var gcall = g.call;"); code.add("gcall({}, 3, 4);"); final IValueCollection collection = inference(code.toString()); final IRMethod fcall = ValueReferenceUtil.extractElement( collection.getChild("fcall"), IRMethod.class); assertNotNull(fcall); final IRMember gcall = ValueReferenceUtil.extractElement( collection.getChild("gcall"), IRMethod.class); assertNotNull(gcall); assertSame(fcall, fcall); } public void testStaticConstructors() { { final IValueCollection collection = inference("var s = String('text')"); assertEquals(RTypes.STRING, JavaScriptValidations.typeOf(collection.getChild("s"))); } { final IValueCollection collection = inference("var n = Number('1')"); assertEquals(RTypes.NUMBER, JavaScriptValidations.typeOf(collection.getChild("n"))); } { final IValueCollection collection = inference("var b = Boolean('true')"); assertEquals(RTypes.BOOLEAN, JavaScriptValidations.typeOf(collection.getChild("b"))); } } public void testObjectLiteral() { final StringList code = new StringList(); code.add("var person = { name:'John', age: 18 }"); final IValueCollection collection = inference(code.toString()); final IRRecordType point = (IRRecordType) JavaScriptValidations .typeOf(collection.getChild("person")); assertEquals(RTypes.STRING, point.getMember("name").getType()); assertEquals(RTypes.NUMBER, point.getMember("age").getType()); } public void testObjectLiteralFunctionMember() { final StringList code = new StringList(); code.add("/**"); code.add(" * @param {Number} x"); code.add(" * @param {Number} y"); code.add(" */"); code.add("function drawRect(x,y) {}"); code.add("var figure = { draw: drawRect }"); final IValueCollection collection = inference(code.toString()); final IRRecordType figure = (IRRecordType) JavaScriptValidations .typeOf(collection.getChild("figure")); final IRFunctionType expected = RTypes.functionType(ts(), Arrays .asList(createParameter("x", RTypes.NUMBER, ParameterKind.NORMAL), createParameter("y", RTypes.NUMBER, ParameterKind.NORMAL)), null); assertEquals(expected, figure.getMember("draw").getType()); } public void testObjectLiteralFunctionMemberTypeOverride() { final StringList code = new StringList(); code.add("/**"); code.add(" * @param {Number} x"); code.add(" * @param {Number} y"); code.add(" */"); code.add("function drawRect(x,y) {}"); code.add("var figure = { /** @type {String} */ draw: drawRect }"); final IValueCollection collection = inference(code.toString()); final IRRecordType figure = (IRRecordType) JavaScriptValidations .typeOf(collection.getChild("figure")); assertEquals(RTypes.STRING, figure.getMember("draw").getType()); } public void testTryCatchDefaultVariableType() { final StringList code = new StringList(); code.add("var o;"); code.add("try {"); code.add("}"); code.add("catch (e) { o = e; }"); final IValueCollection collection = inference(code.toString()); final IRSimpleType o = (IRSimpleType) JavaScriptValidations .typeOf(collection.getChild("o")); assertEquals(ITypeNames.ERROR, o.getName()); } public void testTryCatchVariableType() { final StringList code = new StringList(); code.add("var o;"); code.add("try {"); code.add(" throw 1"); code.add("}"); code.add("catch (/** @type {Number} */ e) { o = e; }"); final IValueCollection collection = inference(code.toString()); final IRSimpleType o = (IRSimpleType) JavaScriptValidations .typeOf(collection.getChild("o")); assertEquals(ITypeNames.NUMBER, o.getName()); } public void testMapTypeLookup() { final StringList code = new StringList(); code.add("/**"); code.add("* @type {Object<String>}"); code.add("*/"); code.add("var MapTypeIds = {"); code.add("}"); code.add("function test() {"); code.add(" var x = MapTypeIds.test;"); code.add("}"); final IValueCollection collection = inference(code.toString()); IValueReference test = collection.getChild("test"); IValueCollection functionScope = (IValueCollection) test .getAttribute(IReferenceAttributes.FUNCTION_SCOPE); final IRSimpleType o = (IRSimpleType) JavaScriptValidations .typeOf(functionScope.getChild("x")); assertNotNull(o); assertEquals(ITypeNames.STRING, o.getName()); } public void testBaseTypeWith2SubClasses() { final StringList code = new StringList(); code.add("/** @constructor */"); code.add("function base() {"); code.add(" this.baseVar = 10;"); code.add(" this.baseMethod = function(){};"); code.add("}"); code.add("/** @constructor "); code.add(" * @extends {base} */"); code.add("function A1() {};"); code.add("A1.prototype = new base();"); code.add("A1.prototype.constructor = A1;"); code.add("A1.prototype.methodAddedToA1Prototype = function() {};"); code.add("/** @constructor "); code.add(" * @extends {base} */"); code.add("function A2() {};"); code.add("A2.prototype = new base();"); code.add("A2.prototype.constructor = A2;"); code.add("var x = new A2();"); final IValueCollection collection = inference(code.toString()); IValueReference test = collection.getChild("x"); Set<String> directChildren = test.getDirectChildren(); assertEquals(2, directChildren.size()); } public void test2InstancesOfTheSameFunctionAddingAfieldTo1() { final StringList code = new StringList(); code.add("function cust(){}"); code.add("var x = new cust();"); code.add("x.test = 10;"); code.add("var y = new cust();"); final IValueCollection collection = inference(code.toString()); IValueReference test = collection.getChild("y"); Set<String> directChildren = test.getDirectChildren(); assertFalse(directChildren.contains("test")); } public void testReferenceToPropertyAssignedFunction() { final StringList code = new StringList(); code.add("var p ={};"); code.add("p.object.myfunc = function(){};"); code.add("p.object.myfunc();"); final IValueCollection collection = inference(code.toString()); IValueReference func = collection.getChild("p").getChild("object").getChild("myfunc"); assertTrue(func.exists()); ReferenceLocation location = func.getLocation(); assertEquals(20, location.getNameStart()); assertEquals(26, location.getNameEnd()); } public void testObjectCreateCallWithNull() { final StringList code = new StringList(); code.add("var p = Object.create(null)"); final IValueCollection collection = inference(code.toString()); IValueReference func = collection.getChild("p").getChild("hasOwnProperty"); assertTrue(func.exists()); } }