Fixes for cases where unexpected declaration descriptor hierarchy caused invalid scoping structure in js backend.

Minor refactorings.
This commit is contained in:
Pavel V. Talanov
2012-03-15 19:13:40 +04:00
parent e4a658fbeb
commit 090fd1b7f6
17 changed files with 186 additions and 139 deletions
@@ -93,7 +93,23 @@ public final class MiscTest extends AbstractExpressionTest {
checkFooBoxIsTrue("funInConstructor.kt");
}
public void testFunInConstructorBlock() throws Exception {
checkFooBoxIsTrue("funInConstructorBlock.kt");
}
public void testPropertyAsFunCalledOnConstructor() throws Exception {
checkFooBoxIsTrue("propertyAsFunCalledOnConstructor.kt");
}
public void testNamespacePropertyCalledAsFun() throws Exception {
checkFooBoxIsTrue("namespacePropertyCalledAsFun.kt");
}
public void testExtensionLiteralCreatedAtNamespaceLevel() throws Exception {
checkFooBoxIsTrue("extensionLiteralCreatedAtNamespaceLevel.kt");
}
public void testTemporaryVariableCreatedInNamespaceInitializer() throws Exception {
checkFooBoxIsTrue("temporaryVariableCreatedInNamespaceInitializer.kt");
}
}
@@ -26,54 +26,48 @@ public class DynamicContext {
@NotNull
public static DynamicContext rootContext(@NotNull NamingScope rootScope, @NotNull JsBlock globalBlock) {
return new DynamicContext(rootScope, rootScope, globalBlock);
return new DynamicContext(rootScope, globalBlock);
}
@NotNull
public static DynamicContext contextWithScope(@NotNull NamingScope scope) {
return new DynamicContext(scope, scope, new JsBlock());
public static DynamicContext newContext(@NotNull NamingScope scope, @NotNull JsBlock block) {
return new DynamicContext(scope, block);
}
//TODO: current/block scope logic unclear. is it necessary?
@NotNull
private final NamingScope currentScope;
@NotNull
private final NamingScope blockScope;
@NotNull
private final JsBlock currentBlock;
private DynamicContext(@NotNull NamingScope scope, @NotNull NamingScope blockScope, @NotNull JsBlock block) {
private DynamicContext(@NotNull NamingScope scope, @NotNull JsBlock block) {
this.currentScope = scope;
this.currentBlock = block;
this.blockScope = blockScope;
}
@NotNull
public DynamicContext innerScope(@NotNull JsScope scope) {
return new DynamicContext(currentScope.innerScope(scope), blockScope, currentBlock);
}
@NotNull
public DynamicContext innerBlock(@NotNull JsBlock block) {
return new DynamicContext(currentScope, currentScope, block);
return new DynamicContext(currentScope, block);
}
@NotNull
public TemporaryVariable declareTemporary(@NotNull JsExpression initExpression) {
JsName temporaryName = blockScope.declareTemporary();
JsName temporaryName = currentScope.declareTemporary();
JsVars temporaryDeclaration = newVar(temporaryName, /*no init expression in var statement*/ null);
addVarDeclaration(jsBlock(), temporaryDeclaration);
return new TemporaryVariable(temporaryName, initExpression);
}
@NotNull
public JsScope jsScope() {
return currentScope.jsScope();
}
@NotNull
public NamingScope getScope() {
return currentScope;
}
@NotNull
public JsBlock jsBlock() {
return currentBlock;
@@ -27,6 +27,7 @@ import org.jetbrains.k2js.translate.context.generator.Generator;
import org.jetbrains.k2js.translate.context.generator.Rule;
import org.jetbrains.k2js.translate.intrinsic.Intrinsics;
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.PredefinedAnnotation;
import java.util.Map;
@@ -84,7 +85,6 @@ public final class StaticContext {
private final Generator<JsNameRef> qualifiers = new QualifierGenerator();
@NotNull
private final Generator<Boolean> qualifierIsNull = new QualifierIsNullGenerator();
@NotNull
private final Map<NamingScope, JsFunction> scopeToFunction = Maps.newHashMap();
@@ -135,7 +135,7 @@ public final class StaticContext {
@NotNull
public NamingScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) {
NamingScope namingScope = scopes.get(descriptor);
NamingScope namingScope = scopes.get(descriptor.getOriginal());
assert namingScope != null : "Must have a scope for descriptor";
return namingScope;
}
@@ -337,8 +337,9 @@ public final class StaticContext {
return null;
}
NamingScope enclosingScope = getEnclosingScope(descriptor);
JsFunction correspondingFunction = new JsFunction(enclosingScope.jsScope());
JsFunction correspondingFunction = JsAstUtils.createFunctionWithEmptyBody(enclosingScope.jsScope());
NamingScope newScope = enclosingScope.innerScope(correspondingFunction.getScope());
assert (!scopeToFunction.containsKey(newScope)) : "Scope to function value overriden for " + descriptor;
scopeToFunction.put(newScope, correspondingFunction);
return newScope;
}
@@ -53,14 +53,8 @@ public final class TranslationContext {
}
@NotNull
public TranslationContext contextWithScope(@NotNull NamingScope newScope) {
return new TranslationContext(staticContext, DynamicContext.contextWithScope(newScope));
}
// Note: Should be used ONLY if scope has no corresponding descriptor
@NotNull
public TranslationContext innerJsScope(@NotNull JsScope enclosingScope) {
return new TranslationContext(staticContext, dynamicContext.innerScope(enclosingScope));
public TranslationContext contextWithScope(@NotNull NamingScope newScope, @NotNull JsBlock block) {
return new TranslationContext(staticContext, DynamicContext.newContext(newScope, block));
}
@NotNull
@@ -70,7 +64,23 @@ public final class TranslationContext {
@NotNull
public TranslationContext newDeclaration(@NotNull DeclarationDescriptor descriptor) {
return contextWithScope(getScopeForDescriptor(descriptor));
return contextWithScope(getScopeForDescriptor(descriptor), getBlockForDescriptor(descriptor));
}
//TODO: consider passing a function here
@NotNull
public TranslationContext innerContextWithGivenScopeAndBlock(@NotNull JsScope scope, @NotNull JsBlock block) {
return contextWithScope(dynamicContext.getScope().innerScope(scope), block);
}
@NotNull
public JsBlock getBlockForDescriptor(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof CallableDescriptor) {
return getFunctionObject((CallableDescriptor) descriptor).getBody();
}
else {
return new JsBlock();
}
}
@NotNull
@@ -125,7 +125,7 @@ public final class PropertyTranslator extends AbstractTranslator {
private JsFunction generateDefaultGetterFunction(@NotNull PropertyGetterDescriptor descriptor) {
JsReturn returnExpression = new JsReturn(backingFieldReference(context(), property));
JsFunction getterFunction = context().getFunctionObject(descriptor);
getterFunction.setBody(convertToBlock(returnExpression));
getterFunction.getBody().getStatements().add(returnExpression);
return getterFunction;
}
@@ -144,7 +144,7 @@ public final class PropertyTranslator extends AbstractTranslator {
new JsParameter(propertyAccessContext(propertySetterDescriptor).jsScope().declareTemporary());
JsStatement assignment = assignmentToBackingField(context(), property, defaultParameter.getName().makeRef()).makeStmt();
setParameters(result, defaultParameter);
result.setBody(convertToBlock(assignment));
result.getBody().getStatements().add(assignment);
return result;
}
@@ -52,6 +52,8 @@ import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.m
/**
* @author Pavel Talanov
*/
//TODO: class has convoluted code. REFACTOR before changing
public final class FunctionTranslator extends AbstractTranslator {
@NotNull
@@ -75,10 +77,10 @@ public final class FunctionTranslator extends AbstractTranslator {
private FunctionTranslator(@NotNull JetDeclarationWithBody functionDeclaration,
@NotNull TranslationContext context) {
super(context);
this.functionBody = new JsBlock();
this.descriptor = getFunctionDescriptor(context.bindingContext(), functionDeclaration);
this.functionDeclaration = functionDeclaration;
this.functionObject = createFunctionObject();
this.functionObject = context().getFunctionObject(descriptor);
this.functionBody = functionObject.getBody();
this.functionBodyContext = functionBodyContext().innerBlock(functionBody);
}
@@ -147,7 +149,6 @@ public final class FunctionTranslator extends AbstractTranslator {
private void generateFunctionObject() {
setParameters(functionObject, translateParameters());
translateBody();
functionObject.setBody(functionBody);
restoreContext();
}
@@ -159,11 +160,6 @@ public final class FunctionTranslator extends AbstractTranslator {
}
}
@NotNull
private JsFunction createFunctionObject() {
return context().getFunctionObject(descriptor);
}
private void translateBody() {
JetExpression jetBodyExpression = functionDeclaration.getBodyExpression();
if (jetBodyExpression == null) {
@@ -203,12 +199,7 @@ public final class FunctionTranslator extends AbstractTranslator {
@NotNull
private TranslationContext functionBodyContext() {
if (isLiteral()) {
return context().innerJsScope(functionObject.getScope());
}
else {
return context().newDeclaration(functionDeclaration);
}
return context().newDeclaration(functionDeclaration);
}
@NotNull
@@ -100,13 +100,15 @@ public final class Translation {
@NotNull
public static JsPropertyInitializer generateClassInitializerMethod(@NotNull JetClassOrObject classDeclaration,
@NotNull TranslationContext context) {
return (new ClassInitializerTranslator(classDeclaration, context)).generateInitializeMethod();
final ClassInitializerTranslator classInitializerTranslator = new ClassInitializerTranslator(classDeclaration, context);
return classInitializerTranslator.generateInitializeMethod();
}
@NotNull
public static JsPropertyInitializer generateNamespaceInitializerMethod(@NotNull NamespaceDescriptor namespace,
@NotNull TranslationContext context) {
return (new NamespaceInitializerTranslator(namespace, context)).generateInitializeMethod();
final NamespaceInitializerTranslator namespaceInitializerTranslator = new NamespaceInitializerTranslator(namespace, context);
return namespaceInitializerTranslator.generateInitializeMethod();
}
@NotNull
@@ -1,68 +0,0 @@
/*
* Copyright 2010-2012 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.initializer;
import com.google.dart.compiler.backend.js.ast.JsFunction;
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer;
import com.google.dart.compiler.backend.js.ast.JsStatement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.psi.JetClassOrObject;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.NamingScope;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import java.util.List;
/**
* @author Pavel Talanov
*/
public abstract class AbstractInitializerTranslator extends AbstractTranslator {
@NotNull
private final InitializerVisitor visitor;
@NotNull
protected final NamingScope initializerMethodScope;
protected AbstractInitializerTranslator(@NotNull NamingScope scope, @NotNull TranslationContext context) {
super(context.contextWithScope(scope));
this.visitor = new InitializerVisitor();
this.initializerMethodScope = scope;
}
abstract protected JsFunction generateInitializerFunction();
@NotNull
public JsPropertyInitializer generateInitializeMethod() {
JsPropertyInitializer initializer = new JsPropertyInitializer();
initializer.setLabelExpr(Namer.initializeMethodReference());
initializer.setValueExpr(generateInitializerFunction());
return initializer;
}
@NotNull
protected List<JsStatement> translateClassInitializers(@NotNull JetClassOrObject declaration) {
return visitor.traverseClass(declaration, context());
}
@NotNull
protected List<JsStatement> translateNamespaceInitializers(@NotNull NamespaceDescriptor namespace) {
return visitor.traverseNamespace(namespace, context());
}
}
@@ -28,6 +28,7 @@ import org.jetbrains.jet.lang.psi.JetDelegatorToSuperCall;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import java.util.ArrayList;
import java.util.List;
@@ -41,7 +42,7 @@ import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateArgum
/**
* @author Pavel Talanov
*/
public final class ClassInitializerTranslator extends AbstractInitializerTranslator {
public final class ClassInitializerTranslator extends AbstractTranslator {
@NotNull
private final JetClassOrObject classDeclaration;
@@ -51,13 +52,12 @@ public final class ClassInitializerTranslator extends AbstractInitializerTransla
public ClassInitializerTranslator(@NotNull JetClassOrObject classDeclaration, @NotNull TranslationContext context) {
// Note: it's important we use scope for class descriptor because anonymous function used in property initializers
// belong to the properties themselves
super(context.getScopeForDescriptor(getClassDescriptor(context.bindingContext(), classDeclaration)), context);
super(context.newDeclaration(getConstructor(context.bindingContext(), classDeclaration)));
this.classDeclaration = classDeclaration;
}
@Override
@NotNull
protected JsFunction generateInitializerFunction() {
public JsPropertyInitializer generateInitializeMethod() {
//TODO: it's inconsistent that we scope for class and function for constructor, currently have problems implementing better way
ConstructorDescriptor primaryConstructor = getConstructor(bindingContext(), classDeclaration);
JsFunction result = context().getFunctionObject(primaryConstructor);
@@ -65,14 +65,9 @@ public final class ClassInitializerTranslator extends AbstractInitializerTransla
// for properties declared as constructor parameters
setParameters(result, translatePrimaryConstructorParameters());
mayBeAddCallToSuperMethod();
result.setBody(generateInitializerMethodBody());
return result;
}
@NotNull
private JsBlock generateInitializerMethodBody() {
initializerStatements.addAll(translateClassInitializers(classDeclaration));
return newBlock(initializerStatements);
initializerStatements.addAll((new InitializerVisitor()).traverseClass(classDeclaration, context()));
result.getBody().getStatements().addAll(initializerStatements);
return InitializerUtils.generateInitializeMethod(result);
}
private void mayBeAddCallToSuperMethod() {
@@ -85,7 +80,7 @@ public final class ClassInitializerTranslator extends AbstractInitializerTransla
private void addCallToSuperMethod(@NotNull JetDelegatorToSuperCall superCall) {
//TODO: can be problematic to maintain
JsName superMethodName = initializerMethodScope.jsScope().declareName(Namer.superMethodName());
JsName superMethodName = context().jsScope().declareName(Namer.superMethodName());
superMethodName.setObfuscatable(false);
List<JsExpression> arguments = translateArguments(superCall);
initializerStatements.add(convertToStatement(newInvocation(thisQualifiedReference(superMethodName), arguments)));
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2012 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.initializer;
import com.google.dart.compiler.backend.js.ast.JsFunction;
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
/**
* @author Pavel Talanov
*/
public final class InitializerUtils {
private InitializerUtils() {
}
@NotNull
public static JsPropertyInitializer generateInitializeMethod(@NotNull JsFunction initializerFunction) {
JsPropertyInitializer initializer = new JsPropertyInitializer();
initializer.setLabelExpr(Namer.initializeMethodReference());
initializer.setValueExpr(initializerFunction);
return initializer;
}
}
@@ -17,33 +17,39 @@
package org.jetbrains.k2js.translate.initializer;
import com.google.dart.compiler.backend.js.ast.JsFunction;
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer;
import com.google.dart.compiler.backend.js.ast.JsStatement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.newBlock;
import java.util.List;
/**
* @author Pavel Talanov
*/
public final class NamespaceInitializerTranslator extends AbstractInitializerTranslator {
public final class NamespaceInitializerTranslator {
@NotNull
private final NamespaceDescriptor namespace;
@NotNull
private final TranslationContext namespaceContext;
public NamespaceInitializerTranslator(@NotNull NamespaceDescriptor namespace, @NotNull TranslationContext context) {
//NOTE:
super(context.getScopeForDescriptor(namespace), context);
this.namespace = namespace;
this.namespaceContext = context;
}
@Override
@NotNull
protected JsFunction generateInitializerFunction() {
//NOTE: namespace has no constructor
JsFunction result = new JsFunction(initializerMethodScope.jsScope());
result.setBody(newBlock(translateNamespaceInitializers(namespace)));
return result;
public JsPropertyInitializer generateInitializeMethod() {
JsFunction result = JsAstUtils.createFunctionWithEmptyBody(namespaceContext.jsScope());
TranslationContext namespaceInitializerContext
= namespaceContext.innerContextWithGivenScopeAndBlock(result.getScope(), result.getBody());
List<JsStatement> initializerStatements =
(new InitializerVisitor()).traverseNamespace(namespace, namespaceInitializerContext);
result.getBody().getStatements().addAll(initializerStatements);
return InitializerUtils.generateInitializeMethod(result);
}
@@ -347,7 +347,7 @@ public final class BindingUtils {
return resolvedCall;
}
public static ConstructorDescriptor getConstructor(BindingContext bindingContext,
public static ConstructorDescriptor getConstructor(@NotNull BindingContext bindingContext,
@NotNull JetClassOrObject declaration) {
ConstructorDescriptor primaryConstructor =
getClassDescriptor(bindingContext, declaration).getUnsubstitutedPrimaryConstructor();
@@ -264,4 +264,11 @@ public final class JsAstUtils {
}
return result;
}
@NotNull
public static JsFunction createFunctionWithEmptyBody(@NotNull JsScope parent) {
JsFunction correspondingFunction = new JsFunction(parent);
correspondingFunction.setBody(new JsBlock());
return correspondingFunction;
}
}
@@ -0,0 +1,19 @@
package foo
class A() {
fun foo() = 1
fun a(f : A.()->Int) : Int {
return f()
}
}
var d = 0
val p : A.()->Int = {
d = foo()
d++
}
val c = A().a(p)
fun box() = (c == 1)
@@ -0,0 +1,19 @@
package foo
class A() {
fun lold() = true
val p : ()->Boolean
{
$p = {{lold()}()}
}
}
fun box() : Boolean {
return A().p()
}
fun main(arg : Array<String>) {
println(box())
}
@@ -0,0 +1,7 @@
package foo
fun lold() = true
val p = {{lold()}()}
fun box() = p() && foo.p()
@@ -0,0 +1,6 @@
package foo
var p = 0
val c = p++ // creates temporary value
fun box() = (p == 1) && (c == 0)