implement better this aliasing, implement anti shadowing.
This commit is contained in:
Generated
+18
@@ -0,0 +1,18 @@
|
||||
<component name="ArtifactManager">
|
||||
<artifact type="jar" name="k2js:jar">
|
||||
<output-path>$PROJECT_DIR$/out/artifacts/k2js_jar</output-path>
|
||||
<root id="archive" name="k2js.jar">
|
||||
<element id="module-output" name="backend" />
|
||||
<element id="module-output" name="frontend" />
|
||||
<element id="module-output" name="frontend.java" />
|
||||
<element id="module-output" name="stdlib" />
|
||||
<element id="module-output" name="jetTests" />
|
||||
<element id="module-output" name="translator" />
|
||||
<element id="module-output" name="js" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/translator/lib/js.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/translator/lib/junit-4.10.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/translator/lib/guava-10.0.1.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/../../Kotlin/jet/lib/asm-util-3.3.1.jar" path-in-jar="/" />
|
||||
</root>
|
||||
</artifact>
|
||||
</component>
|
||||
@@ -3,13 +3,13 @@ package org.jetbrains.k2js.translate.context;
|
||||
import com.google.dart.compiler.backend.js.ast.JsName;
|
||||
import com.google.dart.compiler.backend.js.ast.JsNameRef;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
//TODO: implement aliases stack for this need TESTS
|
||||
//TODO: implement aliasesForDescriptors stack for this need TESTS
|
||||
public class Aliaser {
|
||||
|
||||
public static Aliaser newInstance() {
|
||||
@@ -17,50 +17,53 @@ public class Aliaser {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private final Map<DeclarationDescriptor, JsName> aliases = new HashMap<DeclarationDescriptor, JsName>();
|
||||
@Nullable
|
||||
private JsName aliasForThis = null;
|
||||
private final Map<DeclarationDescriptor, JsName> aliasesForDescriptors = new HashMap<DeclarationDescriptor, JsName>();
|
||||
@NotNull
|
||||
private Stack<JsName> thisAliases = new Stack<JsName>();
|
||||
|
||||
private Aliaser() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsNameRef getAliasForThis() {
|
||||
assert aliasForThis != null : "Alias is null. Use hasAliasForThis function to check.";
|
||||
return aliasForThis.makeRef();
|
||||
assert !thisAliases.empty() : "No alias. Use hasAliasForThis function to check.";
|
||||
return thisAliases.peek().makeRef();
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
public void setAliasForThis(@NotNull JsName alias) {
|
||||
aliasForThis = alias;
|
||||
thisAliases.push(alias);
|
||||
}
|
||||
|
||||
public void removeAliasForThis() {
|
||||
aliasForThis = null;
|
||||
//NOTE: here we are passing alias to check that they are set and removed in consistent order.
|
||||
public void removeAliasForThis(@NotNull JsName aliasToRemove) {
|
||||
assert !thisAliases.empty() : "Error: removing alias, when it is not set.";
|
||||
JsName lastAlias = thisAliases.pop();
|
||||
assert lastAlias.equals(aliasToRemove) : "Error: inconsistent alias removing.";
|
||||
}
|
||||
|
||||
public boolean hasAliasForThis() {
|
||||
return (aliasForThis != null);
|
||||
return (!thisAliases.empty());
|
||||
}
|
||||
|
||||
public boolean hasAliasForDeclaration(@NotNull DeclarationDescriptor declaration) {
|
||||
return aliases.containsKey(declaration.getOriginal());
|
||||
return aliasesForDescriptors.containsKey(declaration.getOriginal());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsName getAliasForDeclaration(@NotNull DeclarationDescriptor declaration) {
|
||||
JsName alias = aliases.get(declaration.getOriginal());
|
||||
JsName alias = aliasesForDescriptors.get(declaration.getOriginal());
|
||||
assert alias != null : "Use has alias for declaration to check.";
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAliasForDescriptor(@NotNull DeclarationDescriptor declaration, @NotNull JsName alias) {
|
||||
assert (!hasAliasForDeclaration(declaration.getOriginal())) : "This declaration already has an alias!";
|
||||
aliases.put(declaration.getOriginal(), alias);
|
||||
aliasesForDescriptors.put(declaration.getOriginal(), alias);
|
||||
}
|
||||
|
||||
public void removeAliasForDescriptor(@NotNull DeclarationDescriptor declaration) {
|
||||
assert (hasAliasForDeclaration(declaration.getOriginal())) : "This declaration does not has an alias!";
|
||||
aliases.remove(declaration.getOriginal());
|
||||
aliasesForDescriptors.remove(declaration.getOriginal());
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,9 @@ public final class StandardClasses {
|
||||
}
|
||||
|
||||
private void declareStandardTopLevelObject(@NotNull String fullQualifiedName, @NotNull String kotlinLibName) {
|
||||
nameMap.put(fullQualifiedName, kotlinScope.declareName(kotlinLibName));
|
||||
JsName declaredName = kotlinScope.declareName(kotlinLibName);
|
||||
declaredName.setObfuscatable(false);
|
||||
nameMap.put(fullQualifiedName, declaredName);
|
||||
scopeMap.put(fullQualifiedName, new JsScope(kotlinScope, "standard object " + kotlinLibName));
|
||||
}
|
||||
|
||||
@@ -143,7 +145,9 @@ public final class StandardClasses {
|
||||
@NotNull String kotlinLibName) {
|
||||
JsScope classScope = scopeMap.get(fullQualifiedClassName);
|
||||
String fullQualifiedMethodName = fullQualifiedClassName + "." + shortMethodName;
|
||||
nameMap.put(fullQualifiedMethodName, classScope.declareName(kotlinLibName));
|
||||
JsName declaredName = classScope.declareName(kotlinLibName);
|
||||
declaredName.setObfuscatable(false);
|
||||
nameMap.put(fullQualifiedMethodName, declaredName);
|
||||
}
|
||||
|
||||
public boolean isStandardObject(@NotNull DeclarationDescriptor descriptor) {
|
||||
|
||||
@@ -27,7 +27,7 @@ public final class TemporaryVariable {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
/*package*/ JsName name() {
|
||||
public JsName name() {
|
||||
return variableName;
|
||||
}
|
||||
|
||||
|
||||
@@ -157,13 +157,13 @@ public final class TranslationContext {
|
||||
|
||||
@NotNull
|
||||
public TemporaryVariable newAliasForThis() {
|
||||
TemporaryVariable aliasForThis = dynamicContext.declareTemporaryWithName("that", new JsThisRef());
|
||||
TemporaryVariable aliasForThis = dynamicContext.declareTemporary(new JsThisRef());
|
||||
aliaser().setAliasForThis(aliasForThis.name());
|
||||
return aliasForThis;
|
||||
}
|
||||
|
||||
public void removeAliasForThis() {
|
||||
aliaser().removeAliasForThis();
|
||||
public void removeAliasForThis(@NotNull JsName aliasToRemove) {
|
||||
aliaser().removeAliasForThis(aliasToRemove);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -25,6 +25,15 @@ import static org.jetbrains.k2js.translate.utils.TranslationUtils.functionWithSc
|
||||
*/
|
||||
public final class FunctionTranslator extends AbstractTranslator {
|
||||
|
||||
@NotNull
|
||||
public static FunctionTranslator newInstance(@NotNull JetDeclarationWithBody function,
|
||||
@NotNull TranslationContext context) {
|
||||
return new FunctionTranslator(function, context);
|
||||
}
|
||||
|
||||
//TODO: implement more generic and safe way
|
||||
private static final String RECEIVER_PARAMETER_NAME = "receiver";
|
||||
|
||||
@NotNull
|
||||
private final JetDeclarationWithBody functionDeclaration;
|
||||
@NotNull
|
||||
@@ -32,11 +41,6 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
@NotNull
|
||||
private final TranslationContext functionBodyContext;
|
||||
|
||||
@NotNull
|
||||
public static FunctionTranslator newInstance(@NotNull JetDeclarationWithBody function,
|
||||
@NotNull TranslationContext context) {
|
||||
return new FunctionTranslator(function, context);
|
||||
}
|
||||
|
||||
private FunctionTranslator(@NotNull JetDeclarationWithBody functionDeclaration,
|
||||
@NotNull TranslationContext context) {
|
||||
@@ -58,7 +62,7 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
public JsExpression translateAsLiteral() {
|
||||
TemporaryVariable aliasForThis = context().newAliasForThis();
|
||||
JsFunction function = generateFunctionObject();
|
||||
context().removeAliasForThis();
|
||||
context().removeAliasForThis(aliasForThis.name());
|
||||
return AstUtil.newSequence(aliasForThis.assignmentExpression(), function);
|
||||
}
|
||||
|
||||
@@ -72,7 +76,8 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
|
||||
private void restoreContext() {
|
||||
if (isExtensionFunction()) {
|
||||
functionBodyContext.removeAliasForThis();
|
||||
JsName receiverAlias = functionBodyContext.jsScope().findExistingName(RECEIVER_PARAMETER_NAME);
|
||||
functionBodyContext.removeAliasForThis(receiverAlias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,19 +170,14 @@ public final class FunctionTranslator extends AbstractTranslator {
|
||||
}
|
||||
|
||||
private void mayBeAddThisParameterForExtensionFunction(@NotNull List<JsParameter> jsParameters) {
|
||||
boolean isExtensionFunction = isExtensionFunction();
|
||||
if (isExtensionFunction) {
|
||||
JsName receiver = functionBodyContext.jsScope().declareName("receiver");
|
||||
if (isExtensionFunction()) {
|
||||
JsName receiver = functionBodyContext.jsScope().declareName(RECEIVER_PARAMETER_NAME);
|
||||
context().aliaser().setAliasForThis(receiver);
|
||||
jsParameters.add(new JsParameter(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExtensionFunction() {
|
||||
// if (!(functionDeclaration instanceof JetNamedFunction)) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
FunctionDescriptor functionDescriptor = getFunctionDescriptor(context().bindingContext(), functionDeclaration);
|
||||
return DescriptorUtils.isExtensionFunction(functionDescriptor);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.jetbrains.k2js.translate.general;
|
||||
|
||||
import com.google.dart.compiler.backend.js.JsNamer;
|
||||
import com.google.dart.compiler.backend.js.JsPrettyNamer;
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import com.google.dart.compiler.util.AstUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -98,6 +100,10 @@ public final class Translation {
|
||||
JsBlock block = staticContext.getProgram().getFragmentBlock(0);
|
||||
TranslationContext context = TranslationContext.rootContext(staticContext);
|
||||
block.addStatement(Translation.translateNamespace(namespace, context));
|
||||
|
||||
JsNamer namer = new JsPrettyNamer();
|
||||
namer.exec(context.program());
|
||||
|
||||
return context.program();
|
||||
}
|
||||
|
||||
|
||||
@@ -47,4 +47,9 @@ public final class ExtensionFunctionTest extends TranslationTest {
|
||||
public void extensionLiteralPassedToFunction() throws Exception {
|
||||
testFooBoxIsTrue("extensionLiteralPassedToFunction.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extensionInsideFunctionLiteral() throws Exception {
|
||||
testFooBoxIsTrue("extensionInsideFunctionLiteral.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace foo
|
||||
|
||||
class M() {
|
||||
var m = 0
|
||||
|
||||
fun eval() {
|
||||
var d = {
|
||||
var c = {Int.() => this + 3}
|
||||
m += 3.c()
|
||||
}
|
||||
d();
|
||||
}
|
||||
}
|
||||
|
||||
fun box() : Boolean {
|
||||
var a = M()
|
||||
if (a.m != 0) return false;
|
||||
a.eval()
|
||||
if (a.m != 6) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user