support for apply and call (both returning the actual return value of
the method where that is called on), bind (returning hte actual parent
method) and Object.create() now returning an object of the type given
including the properties
added tests
diff --git a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/ti/TypeInferencerVisitor.java b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/ti/TypeInferencerVisitor.java
index 4c2a4c2..e70a726 100644
--- a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/ti/TypeInferencerVisitor.java
+++ b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/ti/TypeInferencerVisitor.java
@@ -13,6 +13,7 @@
import java.io.StringReader;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
@@ -125,6 +126,7 @@
import org.eclipse.dltk.javascript.typeinfo.IRMethod;
import org.eclipse.dltk.javascript.typeinfo.IRProperty;
import org.eclipse.dltk.javascript.typeinfo.IRRecordMember;
+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.IRTypeDeclaration;
@@ -533,6 +535,11 @@
reference, IRMethod.class);
if (methods != null && methods.size() == 1) {
final IRMethod method = methods.get(0);
+ IValueReference ref = checkSpecialJavascriptFunctionCalls(
+ reference,
+ arguments, method);
+ if (ref != null)
+ return ref;
if (method.isGeneric()) {
final IRType type = evaluateGenericCall(method, arguments);
return ConstantValue.of(type);
@@ -567,6 +574,62 @@
}
}
+ protected IValueReference checkSpecialJavascriptFunctionCalls(
+ final IValueReference reference, final IValueReference[] arguments,
+ final IRMethod method) {
+ if (method.getName() != null) {
+ if (reference.getParent() != null
+ && RTypes.FUNCTION.getDeclaration().equals(
+ method.getDeclaringType())) {
+
+ if ("call".equals(method.getName())
+ || "apply".equals(method.getName())) {
+ Object x = reference.getParent().getAttribute(
+ IReferenceAttributes.ELEMENT);
+ if (x instanceof IRMethod) {
+ return ConstantValue.of(((IRMethod) x).getType());
+ }
+ } else if ("bind".equals(method.getName())) {
+ return reference.getParent();
+ }
+ } else if (method.getName().equals("create")
+ && RTypes.OBJECT.getDeclaration().equals(
+ method.getDeclaringType())) {
+ if (arguments.length == 1)
+ return arguments[0];
+ else if (arguments.length == 2) {
+ AnonymousValue value = new AnonymousValue();
+ value.addValue(arguments[0], false);
+ JSTypeSet types = arguments[1].getTypes();
+ for (IRType type : types) {
+ if (type instanceof IRRecordType) {
+ List<IRRecordMember> newMembers = new ArrayList<IRRecordMember>();
+ Collection<IRRecordMember> members = ((IRRecordType) type)
+ .getMembers();
+ for (IRRecordMember member : members) {
+ if (member.getType() instanceof IRRecordType) {
+ IRRecordMember valueMember = ((IRRecordType) member
+ .getType()).getMember("value");
+ if (valueMember != null) {
+ newMembers.add(new RRecordMember(member
+ .getName(), valueMember
+ .getType(), valueMember
+ .getSource()));
+ }
+ }
+ }
+ value.addValue(ConstantValue.of(RTypes
+ .recordType(newMembers)), true);
+ }
+ }
+
+ return value;
+ }
+ }
+ }
+ return null;
+ }
+
protected IRType evaluateGenericCall(IRMethod rMethod,
IValueReference[] arguments) {
assert rMethod.isGeneric();
diff --git a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/validation/TypeInfoValidator.java b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/validation/TypeInfoValidator.java
index 80645cc..a532eb5 100644
--- a/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/validation/TypeInfoValidator.java
+++ b/plugins/org.eclipse.dltk.javascript.core/src/org/eclipse/dltk/internal/javascript/validation/TypeInfoValidator.java
@@ -959,6 +959,10 @@
reference, IRMethod.class);
if (methods != null && methods.size() == 1) {
final IRMethod method = methods.get(0);
+ IValueReference ref = checkSpecialJavascriptFunctionCalls(
+ reference, arguments, method);
+ if (ref != null)
+ return ref;
if (method.isGeneric()) {
if (!JavaScriptValidations.checkParameterCount(method,
args.size())) {
diff --git a/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/validation/TypeInfoValidationTests.java b/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/validation/TypeInfoValidationTests.java
index 36babc0..6d871b7 100644
--- a/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/validation/TypeInfoValidationTests.java
+++ b/tests/org.eclipse.dltk.javascript.core.tests/src/org/eclipse/dltk/javascript/core/tests/validation/TypeInfoValidationTests.java
@@ -3706,4 +3706,85 @@
final List<IProblem> problems = validate(code.toString());
assertEquals(problems.toString(), 0, problems.size());
}
+
+ public void testPrototypeThroughObjectCreate() {
+ final StringList code = new StringList();
+ code.add("function Base(name) {");
+ code.add(" this.name = name");
+ code.add("}");
+ code.add("Base.prototype = {");
+ code.add(" baseMethod: function(a,b,c) {");
+ code.add(" return 'baseMethod called'");
+ code.add(" }");
+ code.add("}");
+ code.add("/**");
+ code.add(" * @extends {Base}");
+ code.add(" */");
+ code.add("function Sub(name, age) {");
+ code.add(" Base.call(this, name)");
+ code.add(" this.age = age");
+ code.add("}");
+ code.add("Sub.prototype = Object.create(Base.prototype, {");
+ code.add(" subMethod: {");
+ code.add(" value: function(a,b) {");
+ code.add(" return 'subMethod called'");
+ code.add(" },");
+ code.add(" enumerable: true");
+ code.add(" }");
+ code.add(" })");
+ code.add("Sub.prototype.subMethod2 = function(a,b,c) {");
+ code.add(" return 'subMethod2 called'");
+ code.add("}");
+ code.add("function test2() {");
+ code.add(" var x = new Sub('DLTK', 11)");
+ code.add(" var name = x.name;");
+ code.add(" var age = x.age;");
+ code.add(" x.baseMethod(name,age,age);");
+ code.add(" x.subMethod(name,age)");
+ code.add(" x.subMethod2(name,age,age);");
+ code.add("}");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+
+ }
+
+ public void testApplyCall() {
+ final StringList code = new StringList();
+ code.add("function test() {var args = Array.prototype.slice.apply(arguments, [1]);");
+ code.add("args.slice(1);}");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+
+ public void testCallCall() {
+ final StringList code = new StringList();
+ code.add("function test() {");
+ code.add("var args = Array.prototype.slice.call(arguments, 1);");
+ code.add("args.slice(1);");
+ code.add("}");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
+
+ public void testBindCall() {
+ final StringList code = new StringList();
+ code.add("function A(nr) {");
+ code.add(" this.nr = nr;");
+ code.add(" /**");
+ code.add(" * @return {Number}");
+ code.add(" */");
+ code.add(" this.getNr = function()");
+ code.add(" {");
+ code.add(" return this.nr;");
+ code.add(" }");
+ code.add("}");
+ code.add("var a1 = new A(1);");
+ code.add("var a2 = new A(2);");
+ code.add("var a = a1.getNr();");
+ code.add("a.toExponential();");
+ code.add("var b = a1.getNr.bind(a2)();");
+ code.add("b.toExponential();");
+ final List<IProblem> problems = validate(code.toString());
+ assertEquals(problems.toString(), 0, problems.size());
+ }
}