blob: ee15c55313d1450016d9c562c28f830aacf06574 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 xored software, Inc.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* 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());
}
}