Supported (Variable is Type expression) for user defined classes.

This commit is contained in:
Pavel Talanov
2011-11-17 14:14:47 +04:00
parent 9d848cb8a5
commit fec7539f8c
21 changed files with 348 additions and 44 deletions
@@ -268,4 +268,8 @@ public class AstUtil {
result.setStatements(statements);
return result;
}
public static JsPrefixOperation negation(JsExpression expression) {
return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
}
}
@@ -1,9 +1,7 @@
package org.jetbrains.k2js.translate;
import com.google.dart.compiler.backend.js.ast.JsProgram;
import com.google.dart.compiler.backend.js.ast.JsScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.resolve.BindingContext;
/**
* @author Talanov Pavel
@@ -26,14 +24,4 @@ public abstract class AbstractTranslator {
protected TranslationContext translationContext() {
return context;
}
@NotNull
protected BindingContext bindingContext() {
return context.bindingContext();
}
@NotNull
protected JsScope scope() {
return context.enclosingScope();
}
}
@@ -91,17 +91,23 @@ public final class BindingUtils {
Collection<? extends JetType> superclassTypes = classDescriptor.getTypeConstructor().getSupertypes();
List<ClassDescriptor> superClassDescriptors = new ArrayList<ClassDescriptor>();
for (JetType type : superclassTypes) {
DeclarationDescriptor superClassDescriptor =
type.getConstructor().getDeclarationDescriptor();
assert superClassDescriptor instanceof ClassDescriptor
: "Superclass descriptor of a type should be of type ClassDescriptor";
if (isNotAny(superClassDescriptor)) {
superClassDescriptors.add((ClassDescriptor) superClassDescriptor);
ClassDescriptor result = getClassDescriptorForType(type);
if (isNotAny(result)) {
superClassDescriptors.add(result);
}
}
return superClassDescriptors;
}
@NotNull
static public ClassDescriptor getClassDescriptorForType(@NotNull JetType type) {
DeclarationDescriptor superClassDescriptor =
type.getConstructor().getDeclarationDescriptor();
assert superClassDescriptor instanceof ClassDescriptor
: "Superclass descriptor of a type should be of type ClassDescriptor";
return (ClassDescriptor) superClassDescriptor;
}
static public boolean hasAncestorClass(@NotNull BindingContext context, @NotNull JetClass classDeclaration) {
List<ClassDescriptor> superclassDescriptors = getSuperclassDescriptors(context, classDeclaration);
return (findAncestorClass(superclassDescriptors) != null);
@@ -113,8 +119,22 @@ public final class BindingUtils {
return isStatement;
}
@NotNull
static public JetType getTypeByReference(@NotNull BindingContext context,
@NotNull JetTypeReference typeReference) {
JetType result = context.get(BindingContext.TYPE, typeReference);
assert result != null : "TypeReference should reference a type";
return result;
}
@NotNull
static public ClassDescriptor getClassDescriptorForTypeReference(@NotNull BindingContext context,
@NotNull JetTypeReference typeReference) {
return getClassDescriptorForType(getTypeByReference(context, typeReference));
}
//TODO better implementation?
private static boolean isNotAny(@NotNull DeclarationDescriptor superClassDescriptor) {
static private boolean isNotAny(@NotNull DeclarationDescriptor superClassDescriptor) {
return !superClassDescriptor.getName().equals("Any");
}
@@ -350,5 +350,19 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
}
// @Override
// @NotNull
// public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression,
// @NotNull TranslationContext context) {
// return Translation.typeOperationTranslator(context).translate(expression);
// }
@Override
@NotNull
public JsNode visitIsExpression(@NotNull JetIsExpression expression,
@NotNull TranslationContext context) {
return Translation.typeOperationTranslator(context).translateIsExpression(expression);
}
}
@@ -8,7 +8,7 @@ import com.google.dart.compiler.util.AstUtil;
*/
/*
* This class is a complete dummy and needs a lot of work
* This class is a complete dummy and should completely change in the future
*/
public final class Namer {
@@ -71,5 +71,9 @@ public final class Namer {
return AstUtil.newQualifiedNameRef("Trait.create");
}
public static JsNameRef isOperationReference() {
return AstUtil.newQualifiedNameRef("isType");
}
}
@@ -33,7 +33,7 @@ public final class NamespaceTranslator extends AbstractTranslator {
public JsBlock translate(@NotNull JetNamespace namespace) {
// TODO support multiple namespaces
JsBlock block = program().getFragmentBlock(0);
JsName namespaceName = scope().declareName(Namer.getNameForNamespace(namespace.getName()));
JsName namespaceName = translationContext().enclosingScope().declareName(Namer.getNameForNamespace(namespace.getName()));
block.addStatement(namespaceInitStatement(namespaceName));
TranslationContext newContext = translationContext().newNamespace(namespace);
JsFunction dummyFunction = JsFunction.getAnonymousFunctionWithScope
@@ -80,18 +80,21 @@ public final class OperationTranslator extends AbstractTranslator {
return setterCall;
}
@NotNull
private JetToken getOperationToken(@NotNull JetBinaryExpression expression) {
return (JetToken) expression.getOperationToken();
}
@NotNull
private JsExpression translateAsBinaryOperation(@NotNull JetBinaryExpression expression) {
JsExpression left = AstUtil.convertToExpression
(Translation.translateExpression(expression.getLeft(), translationContext()));
JsExpression right = translateRightExpression(expression);
JetToken jetOperationToken = getOperationToken(expression);
return new JsBinaryOperation(OperatorTable.getBinaryOperator(jetOperationToken), left, right);
JetToken token = getOperationToken(expression);
if (OperatorTable.hasCorrespondingBinaryOperator(token)) {
return new JsBinaryOperation(OperatorTable.getBinaryOperator(token), left, right);
}
if (OperatorTable.hasCorrespondingFunctionInvocation(token)) {
JsInvocation functionInvocation = OperatorTable.getCorrespondingFunctionInvocation(token);
functionInvocation.setArguments(Arrays.asList(left, right));
return functionInvocation;
}
throw new AssertionError("Unsupported token encountered: " + token.toString());
}
@NotNull
@@ -102,5 +105,10 @@ public final class OperationTranslator extends AbstractTranslator {
(Translation.translateExpression(rightExpression, translationContext()));
}
@NotNull
private JetToken getOperationToken(@NotNull JetBinaryExpression expression) {
return (JetToken) expression.getOperationToken();
}
}
@@ -1,7 +1,10 @@
package org.jetbrains.k2js.translate;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
import com.google.dart.compiler.backend.js.ast.JsInvocation;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import com.google.dart.compiler.backend.js.ast.JsUnaryOperator;
import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
@@ -14,9 +17,9 @@ import java.util.Map;
*/
public final class OperatorTable {
private static Map<JetToken, JsBinaryOperator> binaryOperatorsMap = new HashMap<JetToken, JsBinaryOperator>();
private static Map<JetToken, JsUnaryOperator> unaryOperatorsMap = new HashMap<JetToken, JsUnaryOperator>();
private static Map<JetToken, JsNameRef> operatorToFunctionNameReference = new HashMap<JetToken, JsNameRef>();
static {
unaryOperatorsMap.put(JetTokens.PLUSPLUS, JsUnaryOperator.INC); //++
@@ -43,6 +46,27 @@ public final class OperatorTable {
binaryOperatorsMap.put(JetTokens.PERC, JsBinaryOperator.MOD);
}
static {
operatorToFunctionNameReference.put(JetTokens.IS_KEYWORD, Namer.isOperationReference());
}
@NotNull
public static boolean hasCorrespondingBinaryOperator(@NotNull JetToken token) {
return binaryOperatorsMap.containsKey(token);
}
@NotNull
static boolean hasCorrespondingFunctionInvocation(@NotNull JetToken token) {
return operatorToFunctionNameReference.containsKey(token);
}
@NotNull
public static JsInvocation getCorrespondingFunctionInvocation(@NotNull JetToken token) {
JsNameRef functionReference = operatorToFunctionNameReference.get(token);
assert functionReference != null : "Token should represent a function call!";
return AstUtil.newInvocation(functionReference);
}
@NotNull
public static JsBinaryOperator getBinaryOperator(@NotNull JetToken token) {
assert JetTokens.OPERATIONS.contains(token) : "Token should represent an operation!";
@@ -59,4 +83,5 @@ public final class OperatorTable {
return (token == JetTokens.EQ);
}
}
@@ -46,6 +46,11 @@ public final class Translation {
return DeclarationTranslator.newInstance(context);
}
@NotNull
static public TypeOperationTranslator typeOperationTranslator(@NotNull TranslationContext context) {
return TypeOperationTranslator.newInstance(context);
}
@NotNull
static public JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) {
return expressionTranslator(context).translate(expression);
@@ -0,0 +1,90 @@
package org.jetbrains.k2js.translate;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsName;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import com.google.dart.compiler.util.AstUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.JetIsExpression;
import org.jetbrains.jet.lang.psi.JetTypePattern;
import org.jetbrains.jet.lang.psi.JetTypeReference;
/**
* @author Talanov Pavel
*/
public final class TypeOperationTranslator extends AbstractTranslator {
@NotNull
public static TypeOperationTranslator newInstance(@NotNull TranslationContext context) {
return new TypeOperationTranslator(context);
}
private TypeOperationTranslator(@NotNull TranslationContext context) {
super(context);
}
// @NotNull
// public JsExpression translate(@NotNull JetBinaryExpressionWithTypeRHS expression) {
// JsExpression left = AstUtil.convertToExpression
// (Translation.translateExpression(expression.getLeft(), translationContext()));
// JsNameRef right = getClassReference(expression);
// JetToken token = getOperationToken(expression);
// if (OperatorTable.hasCorrespondingFunctionInvocation(token)) {
// JsInvocation functionInvocation = OperatorTable.getCorrespondingFunctionInvocation(token);
// functionInvocation.setArguments(Arrays.asList(left, right));
// return functionInvocation;
// }
// throw new AssertionError("Unsupported token encountered: " + token.toString());
// }
@NotNull
JsExpression translateIsExpression(@NotNull JetIsExpression expression) {
JsExpression left = AstUtil.convertToExpression
(Translation.translateExpression(expression.getLeftHandSide(), translationContext()));
JsExpression resultingExpression = translateTypePattern(left, (JetTypePattern) expression.getPattern());
if (expression.isNegated()) {
return AstUtil.negation(resultingExpression);
}
return resultingExpression;
}
@NotNull
private JsExpression translateTypePattern(@NotNull JsExpression expressionToMatch,
@NotNull JetTypePattern pattern) {
return AstUtil.newInvocation(Namer.isOperationReference(), expressionToMatch, getClassReference(pattern));
}
@NotNull
private JsExpression getClassReference(@NotNull JetTypePattern pattern) {
JetTypeReference typeReference = pattern.getTypeReference();
assert typeReference != null : "Type pattern should contain a type reference";
return getClassNameReferenceForTypeReference(typeReference);
}
// @NotNull
// private JsNameRef getClassReference(@NotNull JetBinaryExpressionWithTypeRHS expression) {
// JetTypeReference typeReference = expression.getRight();
// assert typeReference != null : "Binary type expression should have a right expression";
// return getClassNameReferenceForTypeReference(typeReference);
// }
@NotNull
private JsNameRef getClassNameReferenceForTypeReference(@NotNull JetTypeReference typeReference) {
ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference
(translationContext().bindingContext(), typeReference);
//TODO should reference class by full name here
JsName className = translationContext().getNameForDescriptor(referencedClass);
return translationContext().getNamespaceQualifiedReference(className);
}
// @NotNull
// private JetToken getOperationToken(@NotNull JetBinaryExpressionWithTypeRHS expression) {
// JetSimpleNameExpression operationExpression = expression.getOperationSign();
// IElementType elementType = operationExpression.getReferencedNameElementType();
// assert elementType instanceof JetToken : "Binary type operation should have IElementType of type JetToken";
// return (JetToken) elementType;
// }
}
@@ -34,10 +34,12 @@ public class KotlinLibTest extends TranslationTest {
@Test
public void kotlinJsLibRunsWithRhino() throws Exception {
Context context = Context.enter();
Scriptable scope = context.initStandardObjects();
runFileWithRhino(kotlinLibraryPath(), context, scope);
Context.exit();
runRhinoTest(Arrays.asList(kotlinLibraryPath()), new RhinoResultChecker() {
@Override
public void runChecks(Context context, Scriptable scope) throws Exception {
//do nothing
}
});
}
@Test
@@ -64,4 +66,23 @@ public class KotlinLibTest extends TranslationTest {
new RhinoPropertyTypesChecker("foo", propertyToType));
}
@Test
public void isSameType() throws Exception {
runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isSameType.js")),
new RhinoFunctionResultChecker("test", true));
}
@Test
public void isAncestorType() throws Exception {
runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isAncestorType.js")),
new RhinoFunctionResultChecker("test", true));
}
@Test
public void isComplexTest() throws Exception {
runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isComplexTest.js")),
new RhinoFunctionResultChecker("test", true));
}
}
@@ -0,0 +1,22 @@
package org.jetbrains.k2js.test;
import org.junit.Test;
/**
* @author Talanov Pavel
*/
public class RTTITest extends AbstractClassTest {
final private static String MAIN = "rtti/";
@Override
protected String mainDirectory() {
return MAIN;
}
@Test
public void isSameClass() throws Exception {
testFooBoxIsTrue("isSameClass.kt");
}
}
@@ -1,5 +1,6 @@
package org.jetbrains.k2js.test;
import org.jetbrains.annotations.Nullable;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeObject;
@@ -16,27 +17,55 @@ public final class RhinoFunctionResultChecker implements RhinoResultChecker {
final String functionName;
final Object expectedResult;
public RhinoFunctionResultChecker(String namespaceName, String functionName, Object expectedResult) {
public RhinoFunctionResultChecker(@Nullable String namespaceName, String functionName, Object expectedResult) {
this.namespaceName = namespaceName;
this.functionName = functionName;
this.expectedResult = expectedResult;
}
public RhinoFunctionResultChecker(String functionName, Object expectedResult) {
this(null, functionName, expectedResult);
}
@Override
public void runChecks(Context context, Scriptable scope) throws Exception {
Object result = extractAndCallFunctionObject(namespaceName, functionName, context, scope);
assertTrue("Result is not what expected!", result.equals(expectedResult));
assertTrue("Result is not what expected! Exprected: " + expectedResult + " Evaluated : " + result,
result.equals(expectedResult));
String report = namespaceName + "." + functionName + "() = " + Context.toString(result);
System.out.println(report);
}
private Object extractAndCallFunctionObject(String namespaceName, String functionName,
Context cx, Scriptable scope) {
NativeObject namespaceObject = RhinoUtils.extractObject(namespaceName, scope);
Object box = namespaceObject.get(functionName, namespaceObject);
assertTrue("Function " + functionName + " not defined in namespace " + namespaceName, box instanceof Function);
Object functionObject;
if (namespaceName != null) {
functionObject = extractFunctionFromObject(namespaceName, functionName, scope);
} else {
functionObject = extractFunctionFromGlobalScope(functionName, scope);
}
return callFunctionAndCheckResults(cx, scope, (Function) functionObject);
}
private Object callFunctionAndCheckResults(Context cx, Scriptable scope, Function functionObject) {
Object functionArgs[] = {};
Function function = (Function) box;
return function.call(cx, scope, scope, functionArgs);
return functionObject.call(cx, scope, scope, functionArgs);
}
private Object extractFunctionFromGlobalScope(String functionName, Scriptable scope) {
Object functionObject;
functionObject = scope.get(functionName, scope);
assertTrue("Function " + functionName + " is not defined in global scope",
functionObject instanceof Function);
return functionObject;
}
private Object extractFunctionFromObject(String namespaceName, String functionName, Scriptable scope) {
Object functionObject;
NativeObject namespaceObject = RhinoUtils.extractObject(namespaceName, scope);
functionObject = namespaceObject.get(functionName, namespaceObject);
assertTrue("Function " + functionName + " is not defined in namespace " + namespaceName,
functionObject instanceof Function);
return functionObject;
}
}
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertTrue;
/**
* @author Talanov Pavel
*/
public class RhinoPropertyTypesChecker implements RhinoResultChecker {
public final class RhinoPropertyTypesChecker implements RhinoResultChecker {
final private String objectName;
final private Map<String, Class<? extends Scriptable>> propertyToType;
@@ -46,12 +46,16 @@ public abstract class TranslationTest {
protected void testFunctionOutput(String filename, String namespaceName,
String functionName, Object expectedResult) throws Exception {
translateFile(filename);
runRhinoTest(generateFilenameList(getOutputFilePath(filename)),
new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult));
}
private void translateFile(String filename) {
K2JSTranslator.Arguments args = new K2JSTranslator.Arguments();
args.src = getInputFilePath(filename);
args.outputDir = getOutputFilePath(filename);
K2JSTranslator.translate(args);
runRhinoTest(generateFilenameList(args.outputDir),
new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult));
}
abstract protected List<String> generateFilenameList(String inputfile);
@@ -0,0 +1,7 @@
var A = Class.create();
var B = Class.create(A);
var b = new B;
test = function() {
return (isType(b, A) && isType(b, B));
}
@@ -0,0 +1,24 @@
var A = Class.create();
var B = Class.create(A);
var b = new B;
var C = Class.create(B);
var c = new C;
var E = Class.create(A)
var e = new E;
test1 = function() {
b2 = b
return (isType(b, A) && isType(b, B));
}
test2 = function() {
return (isType(c, C) && isType(c, B) && isType(c, A) && (!isType(c, E)));
}
test3 = function() {
return isType(e, E) && !isType(e, B) && !isType(e, C) && isType(e, A)
}
test = function() {
return test1() && test2() && test3()
}
@@ -0,0 +1,8 @@
var A = Class.create();
var B = Class.create(A);
var C = Class.create();
var c = new C;
test = function() {
return ((!isType(c, A)) && !isType(c, B));
}
@@ -0,0 +1,6 @@
var A = Class.create();
var a = new A;
test = function() {
return isType(a, A);
}
+19 -1
View File
@@ -6,6 +6,17 @@ function $A(iterable) {
return results;
}
var isType = function(object, class) {
current = object.get_class();
while (current !== class) {
if (current === null) {
return false;
}
current = current.superclass;
}
return true;
}
var emptyFunction = function() {}
var Class = (function() {
@@ -32,10 +43,17 @@ var Class = (function() {
}
klass.addMethods(
{
get_class : function () {
return klass;
}
});
if (parent != null) {
klass.addMethods(
{
'super_init' : function () {
super_init : function () {
this.initializing = this.initializing.superclass;
this.initializing.prototype.initialize.apply(this, arguments)
}
@@ -0,0 +1,7 @@
namespace foo
class A() {
}
fun box() = (A() is A)