JS backend: support for reflection

This commit is contained in:
Michael Nedzelsky
2014-07-22 16:11:37 +04:00
parent c42a9840de
commit cc98664832
6 changed files with 286 additions and 12 deletions
@@ -7,6 +7,7 @@ package com.google.dart.compiler.backend.js.ast;
import gnu.trove.TDoubleObjectHashMap;
import gnu.trove.THashMap;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
@@ -91,6 +92,7 @@ public final class JsProgram extends SourceInfoAwareJsNode {
/**
* Creates or retrieves a JsStringLiteral from an interned object pool.
*/
@NotNull
public JsStringLiteral getStringLiteral(String value) {
JsStringLiteral literal = stringLiteralMap.get(value);
if (literal == null) {
@@ -34,6 +34,8 @@ import org.jetbrains.k2js.translate.reference.CallArgumentTranslator
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor
import org.jetbrains.jet.lang.descriptors.Visibilities
import org.jetbrains.jet.lang.psi.Call.CallType
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils
import org.jetbrains.jet.lang.resolve.DescriptorUtils
public fun addReceiverToArgs(receiver: JsExpression, arguments: List<JsExpression>): List<JsExpression> {
if (arguments.isEmpty())
@@ -143,10 +145,15 @@ object InvokeIntrinsic : FunctionCallCase {
return false
val parameterCount = callInfo.callableDescriptor.getValueParameters().size()
val funDeclaration = callInfo.callableDescriptor.getContainingDeclaration()
return funDeclaration == ((if (callInfo.callableDescriptor.getReceiverParameter() == null)
KotlinBuiltIns.getInstance().getFunction(parameterCount)
val reflectionTypes = callInfo.context.getReflectionTypes()
return if (callInfo.callableDescriptor.getReceiverParameter() == null)
funDeclaration == KotlinBuiltIns.getInstance().getFunction(parameterCount) ||
funDeclaration == reflectionTypes.getKFunction(parameterCount)
else
KotlinBuiltIns.getInstance().getExtensionFunction(parameterCount)))
funDeclaration == KotlinBuiltIns.getInstance().getExtensionFunction(parameterCount) ||
funDeclaration == reflectionTypes.getKExtensionFunction(parameterCount) ||
funDeclaration == reflectionTypes.getKMemberFunction(parameterCount)
}
override fun FunctionCallInfo.thisObject(): JsExpression {
@@ -43,6 +43,12 @@ public final class Namer {
private static final String TRAIT_OBJECT_NAME = "createTrait";
private static final String OBJECT_OBJECT_NAME = "createObject";
private static final String ENUM_ENTRIES_NAME = "createEnumEntries";
private static final String CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME = "getCallableRefForMemberFunction";
private static final String CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME = "getCallableRefForExtensionFunction";
private static final String CALLABLE_REF_FOR_CONSTRUCTOR_NAME = "getCallableRefForConstructor";
private static final String CALLABLE_REF_FOR_TOP_LEVEL_PROPERTY = "getCallableRefForTopLevelProperty";
private static final String CALLABLE_REF_FOR_MEMBER_PROPERTY = "getCallableRefForMemberProperty";
private static final String CALLABLE_REF_FOR_EXTENSION_PROPERTY = "getCallableRefForExtensionProperty";
private static final String SETTER_PREFIX = "set_";
private static final String GETTER_PREFIX = "get_";
@@ -177,6 +183,18 @@ public final class Namer {
@NotNull
private final JsName enumEntriesName;
@NotNull
private final JsName callableRefForMemberFunctionName;
@NotNull
private final JsName callableRefForExtensionFunctionName;
@NotNull
private final JsName callableRefForConstructorName;
@NotNull
private final JsName callableRefForTopLevelProperty;
@NotNull
private final JsName callableRefForMemberProperty;
@NotNull
private final JsName callableRefForExtensionProperty;
@NotNull
private final JsExpression undefinedExpression;
@NotNull
private final JsExpression callGetProperty;
@@ -200,6 +218,12 @@ public final class Namer {
className = kotlinScope.declareName(CLASS_OBJECT_NAME);
enumEntriesName = kotlinScope.declareName(ENUM_ENTRIES_NAME);
objectName = kotlinScope.declareName(OBJECT_OBJECT_NAME);
callableRefForMemberFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME);
callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME);
callableRefForConstructorName = kotlinScope.declareName(CALLABLE_REF_FOR_CONSTRUCTOR_NAME);
callableRefForTopLevelProperty = kotlinScope.declareName(CALLABLE_REF_FOR_TOP_LEVEL_PROPERTY);
callableRefForMemberProperty = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_PROPERTY);
callableRefForExtensionProperty = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_PROPERTY);
isTypeName = kotlinScope.declareName("isType");
@@ -236,6 +260,36 @@ public final class Namer {
return kotlin(objectName);
}
@NotNull
public JsExpression callableRefForMemberFunctionReference() {
return kotlin(callableRefForMemberFunctionName);
}
@NotNull
public JsExpression callableRefForExtensionFunctionReference() {
return kotlin(callableRefForExtensionFunctionName);
}
@NotNull
public JsExpression callableRefForConstructorReference() {
return kotlin(callableRefForConstructorName);
}
@NotNull
public JsExpression callableRefForTopLevelPropertyReference() {
return kotlin(callableRefForTopLevelProperty);
}
@NotNull
public JsExpression callableRefForMemberPropertyReference() {
return kotlin(callableRefForMemberProperty);
}
@NotNull
public JsExpression callableRefForExtensionPropertyReference() {
return kotlin(callableRefForExtensionProperty);
}
@NotNull
public JsExpression throwNPEFunctionRef() {
return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject());
@@ -19,9 +19,7 @@ package org.jetbrains.k2js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
@@ -39,12 +37,8 @@ import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.general.TranslatorVisitor;
import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator;
import org.jetbrains.k2js.translate.reference.AccessTranslationUtils;
import org.jetbrains.k2js.translate.reference.CallExpressionTranslator;
import org.jetbrains.k2js.translate.reference.QualifiedExpressionTranslator;
import org.jetbrains.k2js.translate.reference.ReferenceTranslator;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import org.jetbrains.k2js.translate.reference.*;
import org.jetbrains.k2js.translate.utils.*;
import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
import java.util.List;
@@ -168,6 +162,12 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return newVar(name, initializer).source(expression);
}
@Override
@NotNull
public JsNode visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression, @NotNull TranslationContext context) {
return CallableReferenceTranslator.INSTANCE$.translate(expression, context);
}
@Override
@NotNull
public JsNode visitCallExpression(@NotNull JetCallExpression expression,
@@ -0,0 +1,158 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.reference
import com.google.dart.compiler.backend.js.ast.JsExpression
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
import org.jetbrains.k2js.translate.context.TranslationContext
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import com.google.dart.compiler.backend.js.ast.JsInvocation
import org.jetbrains.jet.lang.descriptors.Visibilities
import org.jetbrains.k2js.translate.utils.AnnotationsUtils
import org.jetbrains.jet.lang.psi.JetCallableReferenceExpression
import org.jetbrains.k2js.translate.utils.BindingUtils
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor
import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor
import com.google.dart.compiler.backend.js.ast.JsLiteral
import java.util.ArrayList
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import com.google.dart.compiler.backend.js.ast.JsNameRef
import org.jetbrains.k2js.translate.context.Namer
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
object CallableReferenceTranslator {
fun translate(expression: JetCallableReferenceExpression, context: TranslationContext): JsExpression {
val descriptor = BindingUtils.getDescriptorForReferenceExpression(context.bindingContext(), expression.getCallableReference())
return when (descriptor) {
is PropertyDescriptor ->
translateForProperty(descriptor as PropertyDescriptor, context)
is FunctionDescriptor ->
translateForFunction(descriptor as FunctionDescriptor, context)
else ->
throw IllegalArgumentException("Expected property or function: ${descriptor}, expression=${expression.getText()}")
}
}
private fun translateForFunction(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression {
return when {
// TODO Support for callable reference to builtin functions and members
JsDescriptorUtils.isBuiltin(descriptor) ->
throw UnsupportedOperationException("callable references for builtin functions are not supported yet")
isConstructor(descriptor) ->
translateForConstructor(descriptor, context)
isExtension(descriptor) ->
translateForExtensionFunction(descriptor, context)
isMember(descriptor) ->
translateForMemberFunction(descriptor, context)
else ->
ReferenceTranslator.translateAsFQReference(descriptor, context)
}
}
private fun translateForProperty(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression {
return when {
// TODO Support for callable reference to builtin properties
JsDescriptorUtils.isBuiltin(descriptor) ->
throw UnsupportedOperationException("callable references for builtin properties are not supported yet")
isExtension(descriptor) ->
translateForExtensionProperty(descriptor, context)
isMember(descriptor) ->
translateForMemberProperty(descriptor, context)
else ->
translateForTopLevelProperty(descriptor, context)
}
}
private fun isConstructor(descriptor: CallableDescriptor): Boolean = descriptor is ConstructorDescriptor
private fun isExtension(descriptor: CallableDescriptor): Boolean = JsDescriptorUtils.isExtension(descriptor)
private fun isMember(descriptor: CallableDescriptor): Boolean = JsDescriptorUtils.getContainingDeclaration(descriptor) is ClassDescriptor
private fun isVar(descriptor: PropertyDescriptor): JsExpression = if (descriptor.isVar()) JsLiteral.TRUE else JsLiteral.FALSE
private fun translateForTopLevelProperty(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression {
val packageDescriptor = JsDescriptorUtils.getContainingDeclaration(descriptor)
assert(packageDescriptor is PackageFragmentDescriptor, "Expected PackageFragmentDescriptor: ${packageDescriptor}")
val jsPackageNameRef = context.getQualifiedReference(packageDescriptor)
val jsPropertyName = context.getNameForDescriptor(descriptor)
val jsPropertyNameAsString = context.program().getStringLiteral(jsPropertyName.toString())
return JsInvocation(context.namer().callableRefForTopLevelPropertyReference(), jsPackageNameRef, jsPropertyNameAsString, isVar(descriptor))
}
private fun translateForMemberProperty(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression {
val jsPropertyName = context.getNameForDescriptor(descriptor)
val jsPropertyNameAsString = context.program().getStringLiteral(jsPropertyName.toString())
return JsInvocation(context.namer().callableRefForMemberPropertyReference(), jsPropertyNameAsString, isVar(descriptor))
}
private fun translateForExtensionProperty(descriptor: PropertyDescriptor, context: TranslationContext): JsExpression {
val jsGetterNameRef = context.getQualifiedReference(descriptor.getGetter()!!)
val propertyName = descriptor.getName()
val jsPropertyNameAsString = context.program().getStringLiteral(propertyName.asString())
val argumentList = ArrayList<JsExpression>(3)
argumentList.add(jsPropertyNameAsString)
argumentList.add(jsGetterNameRef)
if (descriptor.isVar()) {
val jsSetterNameRef = context.getQualifiedReference(descriptor.getSetter()!!)
argumentList.add(jsSetterNameRef)
}
if (AnnotationsUtils.isNativeObject(descriptor))
return translateForMemberProperty(descriptor, context)
else
return JsInvocation(context.namer().callableRefForExtensionPropertyReference(), argumentList)
}
private fun translateForConstructor(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression {
val jsFunctionRef = ReferenceTranslator.translateAsFQReference(descriptor, context)
return JsInvocation(context.namer().callableRefForConstructorReference(), jsFunctionRef)
}
private fun translateForExtensionFunction(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression {
val receiverParameterDescriptor = descriptor.getReceiverParameter()
assert(receiverParameterDescriptor != null, "receiverParameter for extension should not be null")
val jsFunctionRef = ReferenceTranslator.translateAsFQReference(descriptor, context)
if (descriptor.getVisibility() == Visibilities.LOCAL)
return jsFunctionRef
else if (AnnotationsUtils.isNativeObject(descriptor)) {
val jetType = receiverParameterDescriptor!!.getType()
val receiverClassDescriptor = DescriptorUtils.getClassDescriptorForType(jetType)
return translateAsMemberFunctionReference(descriptor, receiverClassDescriptor, context)
}
else
return JsInvocation(context.namer().callableRefForExtensionFunctionReference(), jsFunctionRef)
}
private fun translateForMemberFunction(descriptor: FunctionDescriptor, context: TranslationContext): JsExpression {
val classDescriptor = JsDescriptorUtils.getContainingDeclaration(descriptor) as? ClassDescriptor ?: throw IllegalArgumentException("Expected ClassDescriptor: ${descriptor}")
return translateAsMemberFunctionReference(descriptor, classDescriptor, context)
}
private fun translateAsMemberFunctionReference(descriptor: CallableDescriptor, classDescriptor: ClassDescriptor, context: TranslationContext): JsExpression {
val jsClassNameRef = context.getQualifiedReference(classDescriptor)
val funName = context.getNameForDescriptor(descriptor)
val funNameAsString = context.program().getStringLiteral(funName.toString())
return JsInvocation(context.namer().callableRefForMemberFunctionReference(), jsClassNameRef, funNameAsString)
}
}
@@ -294,7 +294,60 @@ var Kotlin = {};
}
};
// TODO Store callable references for members in class
Kotlin.getCallableRefForMemberFunction = function (klass, memberName) {
return function () {
return this[memberName].apply(this, arguments);
};
};
// TODO Store callable references for extension functions in class
// extFun expected receiver as the first argument
Kotlin.getCallableRefForExtensionFunction = function (extFun) {
return function () {
var args = [this];
Array.prototype.push.apply(args, arguments);
return extFun.apply(null, args);
};
};
Kotlin.getCallableRefForConstructor = function (klass) {
return function () {
var obj = Object.create(klass.prototype);
klass.apply(obj, arguments);
return obj;
};
};
Kotlin.getCallableRefForTopLevelProperty = function(packageName, name, isVar) {
var obj = {};
obj.name = name;
obj.get = function() { return packageName[name]; };
if (isVar) {
obj.set_za3rmp$ = function(value) { packageName[name] = value; };
}
return obj;
};
Kotlin.getCallableRefForMemberProperty = function(name, isVar) {
var obj = {};
obj.name = name;
obj.get_za3rmp$ = function(receiver) { return receiver[name]; };
if (isVar) {
obj.set_wn2jw4$ = function(receiver, value) { receiver[name] = value; };
}
return obj;
};
Kotlin.getCallableRefForExtensionProperty = function(name, getFun, setFun) {
var obj = {};
obj.name = name;
obj.get_za3rmp$ = getFun;
if (setFun !== undefined) {
obj.set_wn2jw4$ = setFun;
}
return obj;
};
////////////////////////////////// packages & modules //////////////////////////////
Kotlin.modules = {};