JS backend: mangling JS reserved words.

This commit is contained in:
Zalim Bashorov
2014-09-24 00:44:49 +04:00
parent ef60a7f776
commit e91e724469
21 changed files with 129 additions and 71 deletions
+1
View File
@@ -8,6 +8,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="PROVIDED" name="intellij-core" level="project" />
<orderEntry type="library" name="kotlin-runtime" level="project" />
</component>
</module>
@@ -14,13 +14,13 @@ public class JsCatchScope extends JsScope {
private final JsName name;
public JsCatchScope(JsScope parent, String ident) {
super(parent, "Catch scope");
super(parent, "Catch scope", null);
name = new JsName(this, ident);
}
@Override
@NotNull
public JsName declareName(String identifier) {
public JsName declareName(@NotNull String identifier) {
// Declare into parent scope!
return getParent().declareName(identifier);
}
@@ -31,7 +31,7 @@ public class JsCatchScope extends JsScope {
}
@Override
protected JsName findOwnName(String ident) {
protected JsName findOwnName(@NotNull String ident) {
return name.getIdent().equals(ident) ? name : null;
}
}
@@ -16,21 +16,21 @@ public final class JsFunction extends JsLiteral implements HasName {
private JsBlock body;
private List<JsParameter> params;
@NotNull
private final JsScope scope;
private final JsFunctionScope scope;
private JsName name;
public JsFunction(JsScope parentScope) {
this(parentScope, (JsName) null);
public JsFunction(@NotNull JsScope parentScope, @NotNull String description) {
this(parentScope, description, null);
}
public JsFunction(JsScope parentScope, @NotNull JsBlock body) {
this(parentScope, (JsName) null);
public JsFunction(@NotNull JsScope parentScope, @NotNull JsBlock body, @NotNull String description) {
this(parentScope, description, null);
this.body = body;
}
private JsFunction(JsScope parentScope, @Nullable JsName name) {
private JsFunction(@NotNull JsScope parentScope, @NotNull String description, @Nullable JsName name) {
this.name = name;
scope = new JsScope(parentScope, name == null ? null : name.getIdent());
scope = new JsFunctionScope(parentScope, name == null ? description : name.getIdent());
}
@NotNull
@@ -57,7 +57,7 @@ public final class JsFunction extends JsLiteral implements HasName {
}
@NotNull
public JsScope getScope() {
public JsFunctionScope getScope() {
return scope;
}
@@ -29,11 +29,11 @@ public final class JsProgram extends SourceInfoAwareJsNode {
private final JsRootScope rootScope;
private final Map<String, JsStringLiteral> stringLiteralMap = new THashMap<String, JsStringLiteral>();
private final JsScope topScope;
private final JsObjectScope topScope;
public JsProgram(String unitId) {
rootScope = new JsRootScope(this);
topScope = new JsScope(rootScope, "Global", unitId);
topScope = new JsObjectScope(rootScope, "Global", unitId);
setFragmentCount(1);
emptyStatement = new JsEmpty();
@@ -94,7 +94,7 @@ public final class JsProgram extends SourceInfoAwareJsNode {
* Gets the top level scope. This is the scope of all the statements in the
* main program.
*/
public JsScope getScope() {
public JsObjectScope getScope() {
return topScope;
}
@@ -5,6 +5,7 @@
package com.google.dart.compiler.backend.js.ast;
import com.google.dart.compiler.backend.js.JsReservedIdentifiers;
import org.jetbrains.annotations.NotNull;
/**
* The root scope is the parent of every scope. All identifiers in this scope
@@ -25,7 +26,7 @@ public final class JsRootScope extends JsScope {
}
@Override
protected JsName findOwnName(String ident) {
protected JsName findOwnName(@NotNull String ident) {
JsName name = super.findOwnName(ident);
if (name == null) {
if (JsReservedIdentifiers.reservedGlobalSymbols.contains(ident)) {
@@ -11,6 +11,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Map;
import static com.google.dart.compiler.backend.js.ast.AstPackage.JsObjectScope;
/**
* A scope is a factory for creating and allocating
* {@link JsName}s. A JavaScript AST is
@@ -37,40 +39,32 @@ import java.util.Map;
* with a qualifier and could therefore never be confused with the global scope
* hierarchy.
*/
public class JsScope {
@Nullable
public abstract class JsScope {
@NotNull
private final String description;
private Map<String, JsName> names = Collections.emptyMap();
private final JsScope parent;
protected int tempIndex = 0;
private final String scopeId;
public JsScope(JsScope parent, @Nullable String description) {
this(parent, description, null);
}
public JsScope(JsScope parent) {
this(parent, null);
}
public JsScope(JsScope parent, @Nullable String description, @Nullable String scopeId) {
public JsScope(JsScope parent, @NotNull String description, @Nullable String scopeId) {
assert (parent != null);
this.scopeId = scopeId;
this.description = description;
this.parent = parent;
}
@NotNull
public JsScope innerScope(@Nullable String scopeName) {
return new JsScope(this, scopeName);
}
protected JsScope(@Nullable String description) {
protected JsScope(@NotNull String description) {
this.description = description;
parent = null;
scopeId = null;
}
@NotNull
public JsScope innerObjectScope(@NotNull String scopeName) {
return JsObjectScope(this, scopeName);
}
/**
* Gets a name object associated with the specified identifier in this scope,
* creating it if necessary.<br/>
@@ -81,7 +75,7 @@ public class JsScope {
* @param identifier An identifier that is unique within this scope.
*/
@NotNull
public JsName declareName(String identifier) {
public JsName declareName(@NotNull String identifier) {
JsName name = findOwnName(identifier);
return name != null ? name : doCreateName(identifier);
}
@@ -93,7 +87,7 @@ public class JsScope {
* (unless they use this function).
*/
@NotNull
public JsName declareFreshName(String suggestedName) {
public JsName declareFreshName(@NotNull String suggestedName) {
String name = suggestedName;
int counter = 0;
while (hasOwnName(name)) {
@@ -173,7 +167,7 @@ public class JsScope {
*
* @return <code>null</code> if the identifier has no associated name
*/
protected JsName findOwnName(String ident) {
protected JsName findOwnName(@NotNull String ident) {
return names.get(ident);
}
}
@@ -0,0 +1,56 @@
/*
* 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 com.google.dart.compiler.backend.js.ast
public fun JsObjectScope(parent: JsScope, description: String): JsObjectScope = JsObjectScope(parent, description, null)
public class JsObjectScope(parent: JsScope, description: String, scopeId: String?) : JsScope(parent, description, scopeId)
public class JsFunctionScope(parent: JsScope, description: String) : JsScope(parent, description, null) {
override fun declareName(identifier: String): JsName = super.declareFreshName(identifier)
override fun hasOwnName(name: String): Boolean = RESERVED_WORDS.contains(name) || super.hasOwnName(name)
public fun declareNameUnsafe(identifier: String): JsName = super.declareName(identifier)
class object {
public val RESERVED_WORDS: Set<String> = setOf(
// keywords
"await", "break", "case", "catch", "continue", "debugger", "default", "delete", "do", "else", "finally", "for", "function", "if",
"in", "instanceof", "new", "return", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with",
// future reserved words
"class", "const", "enum", "export", "extends", "import", "super",
// as future reserved words in strict mode
"implements", "interface", "let", "package", "private", "protected", "public", "static", "yield",
// additional reserved words
"null", "true", "false",
// disallowed as variable names in strict mode
"eval", "arguments",
// non-reserved words that act like reserved words
"NaN", "Infinity", "undefined",
// the special Kotlin object
"Kotlin"
)
}
}
@@ -19,10 +19,10 @@ package org.jetbrains.k2js.translate.context
import com.google.dart.compiler.backend.js.ast.JsExpression
import com.google.dart.compiler.backend.js.ast.JsPropertyInitializer
import com.google.dart.compiler.backend.js.ast.JsNameRef
import com.google.dart.compiler.backend.js.ast.JsScope
import com.google.dart.compiler.backend.js.ast.JsObjectScope
class DefinitionPlace(
private val scope: JsScope,
private val scope: JsObjectScope,
private val fqName: JsExpression,
val properties: MutableList<JsPropertyInitializer>
) {
@@ -31,4 +31,4 @@ class DefinitionPlace(
properties.add(JsPropertyInitializer(name.makeRef(), expression))
return JsNameRef(name, fqName)
}
}
}
@@ -23,6 +23,8 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.plugin.JetLanguage;
import static com.google.dart.compiler.backend.js.ast.AstPackage.JsObjectScope;
/**
* Encapuslates different types of constants and naming conventions.
*/
@@ -171,7 +173,7 @@ public final class Namer {
@NotNull
private final JsName kotlinName;
@NotNull
private final JsScope kotlinScope;
private final JsObjectScope kotlinScope;
@NotNull
private final JsName className;
@NotNull
@@ -208,7 +210,7 @@ public final class Namer {
private Namer(@NotNull JsScope rootScope) {
kotlinName = rootScope.declareName(KOTLIN_NAME);
kotlinScope = new JsScope(rootScope, "Kotlin standard object");
kotlinScope = JsObjectScope(rootScope, "Kotlin standard object");
traitName = kotlinScope.declareName(TRAIT_OBJECT_NAME);
definePackage = kotlin("definePackage");
@@ -318,7 +320,7 @@ public final class Namer {
}
@NotNull
/*package*/ JsScope getKotlinScope() {
/*package*/ JsObjectScope getKotlinScope() {
return kotlinScope;
}
@@ -18,7 +18,7 @@ package org.jetbrains.k2js.translate.context;
import com.google.common.collect.Maps;
import com.google.dart.compiler.backend.js.ast.JsName;
import com.google.dart.compiler.backend.js.ast.JsScope;
import com.google.dart.compiler.backend.js.ast.JsObjectScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
@@ -27,6 +27,7 @@ import org.jetbrains.jet.lang.resolve.name.Name;
import java.util.Map;
import static com.google.dart.compiler.backend.js.ast.AstPackage.JsObjectScope;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName;
/**
@@ -85,7 +86,7 @@ public final class StandardClasses {
}
@NotNull
public static StandardClasses bindImplementations(@NotNull JsScope kotlinObjectScope) {
public static StandardClasses bindImplementations(@NotNull JsObjectScope kotlinObjectScope) {
StandardClasses standardClasses = new StandardClasses(kotlinObjectScope);
declareKotlinStandardClasses(standardClasses);
return standardClasses;
@@ -107,24 +108,24 @@ public final class StandardClasses {
@NotNull
private final JsScope kotlinScope;
private final JsObjectScope kotlinScope;
@NotNull
private final Map<FqNameUnsafe, JsName> standardObjects = Maps.newHashMap();
@NotNull
private final Map<FqNameUnsafe, JsScope> scopeMap = Maps.newHashMap();
private final Map<FqNameUnsafe, JsObjectScope> scopeMap = Maps.newHashMap();
private StandardClasses(@NotNull JsScope kotlinScope) {
private StandardClasses(@NotNull JsObjectScope kotlinScope) {
this.kotlinScope = kotlinScope;
}
private void declareTopLevelObjectInScope(@NotNull JsScope scope, @NotNull Map<FqNameUnsafe, JsName> map,
private void declareTopLevelObjectInScope(@NotNull JsObjectScope scope, @NotNull Map<FqNameUnsafe, JsName> map,
@NotNull FqNameUnsafe fullQualifiedName, @NotNull String name) {
JsName declaredName = scope.declareName(name);
map.put(fullQualifiedName, declaredName);
scopeMap.put(fullQualifiedName, new JsScope(scope, "scope for " + name));
scopeMap.put(fullQualifiedName, JsObjectScope(scope, "scope for " + name));
}
private void declareKotlinObject(@NotNull FqNameUnsafe fullQualifiedName, @NotNull String kotlinLibName) {
@@ -134,7 +135,7 @@ public final class StandardClasses {
private void declareInner(@NotNull FqNameUnsafe fullQualifiedClassName,
@NotNull String shortMethodName,
@NotNull String javascriptName) {
JsScope classScope = scopeMap.get(fullQualifiedClassName);
JsObjectScope classScope = scopeMap.get(fullQualifiedClassName);
assert classScope != null;
FqNameUnsafe fullQualifiedMethodName = fullQualifiedClassName.child(Name.guess(shortMethodName));
standardObjects.put(fullQualifiedMethodName, classScope.declareName(javascriptName));
@@ -354,7 +354,7 @@ public final class StaticContext {
return null;
}
if (getSuperclass((ClassDescriptor) descriptor) == null) {
return getRootScope().innerScope("Scope for class " + descriptor.getName());
return getRootScope().innerObjectScope("Scope for class " + descriptor.getName());
}
return null;
}
@@ -369,7 +369,7 @@ public final class StaticContext {
if (superclass == null) {
return null;
}
return getScopeForDescriptor(superclass).innerScope("Scope for class " + descriptor.getName());
return getScopeForDescriptor(superclass).innerObjectScope("Scope for class " + descriptor.getName());
}
};
Rule<JsScope> generateNewScopesForPackageDescriptors = new Rule<JsScope>() {
@@ -378,7 +378,7 @@ public final class StaticContext {
if (!(descriptor instanceof PackageFragmentDescriptor)) {
return null;
}
return getRootScope().innerScope("Package " + descriptor.getName());
return getRootScope().innerObjectScope("Package " + descriptor.getName());
}
};
//TODO: never get there
@@ -386,7 +386,7 @@ public final class StaticContext {
@Override
public JsScope apply(@NotNull DeclarationDescriptor descriptor) {
JsScope enclosingScope = getEnclosingScope(descriptor);
return enclosingScope.innerScope("Scope for member " + descriptor.getName());
return enclosingScope.innerObjectScope("Scope for member " + descriptor.getName());
}
};
Rule<JsScope> createFunctionObjectsForCallableDescriptors = new Rule<JsScope>() {
@@ -19,15 +19,15 @@ package org.jetbrains.k2js.translate.context
import org.jetbrains.jet.lang.descriptors.*
import org.jetbrains.jet.lang.resolve.DescriptorUtils.isAncestor
import com.google.dart.compiler.backend.js.ast.JsName
import com.google.dart.compiler.backend.js.ast.JsScope
import org.jetbrains.k2js.translate.utils.TranslationUtils.getSuggestedName
import com.google.dart.compiler.backend.js.ast.JsFunctionScope
private val CAPTURED_RECEIVER_NAME_PREFIX : String = "this$"
class UsageTracker(
private val parent: UsageTracker?,
val containingDescriptor: MemberDescriptor,
private val scope: JsScope
private val scope: JsFunctionScope
) {
private val captured = linkedMapOf<CallableDescriptor, JsName>()
@@ -114,7 +114,7 @@ public final class ClassTranslator extends AbstractTranslator {
if (!descriptor.getKind().isSingleton() && !isAnonymousObject(descriptor)) {
qualifiedReference = declarationContext.getQualifiedReference(descriptor);
JsScope scope = context().getScopeForDescriptor(descriptor);
definitionPlace = new DefinitionPlace(scope, qualifiedReference, staticProperties);
definitionPlace = new DefinitionPlace((JsObjectScope) scope, qualifiedReference, staticProperties);
}
declarationContext = declarationContext.newDeclaration(descriptor, definitionPlace);
@@ -240,7 +240,7 @@ public final class ClassTranslator extends AbstractTranslator {
@NotNull
private JsExpression translateObjectInsideClass(@NotNull TranslationContext outerClassContext) {
JsFunction fun = new JsFunction(outerClassContext.scope(), new JsBlock());
JsFunction fun = new JsFunction(outerClassContext.scope(), new JsBlock(), "initializer for " + descriptor.getName().asString());
TranslationContext funContext = outerClassContext.newFunctionBodyWithUsageTracker(fun, descriptor);
fun.getBody().getStatements().add(new JsReturn(translate(funContext)));
@@ -109,6 +109,7 @@ public class DelegationTranslator(
val propertyName: String = descriptor.getName().asString()
fun generateDelegateGetterFunction(getterDescriptor: PropertyGetterDescriptor): JsFunction {
// TODO review: used wrong scope?
val delegateRefName = context().getScopeForDescriptor(getterDescriptor).declareName(delegateName)
val delegateRef = JsNameRef(delegateRefName, JsLiteral.THIS)
@@ -130,7 +131,8 @@ public class DelegationTranslator(
}
fun generateDelegateSetterFunction(setterDescriptor: PropertySetterDescriptor): JsFunction {
val jsFunction = JsFunction(context().getScopeForDescriptor(setterDescriptor.getContainingDeclaration()))
val jsFunction = JsFunction(context().getScopeForDescriptor(setterDescriptor.getContainingDeclaration()),
"setter for " + setterDescriptor.getName().asString())
assert(setterDescriptor.getValueParameters().size() == 1, "Setter must have 1 parameter")
val defaultParameter = JsParameter(jsFunction.getScope().declareTemporary())
@@ -169,8 +171,7 @@ public class DelegationTranslator(
return generateDelegateAccessor(setterDescriptor, generateDelegateSetterFunction(setterDescriptor))
}
properties.addGetterAndSetter(descriptor, context(), ::generateDelegateGetter, ::generateDelegateSetter
)
properties.addGetterAndSetter(descriptor, context(), ::generateDelegateGetter, ::generateDelegateSetter)
}
@@ -50,7 +50,7 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
@Override
public void generateCopyFunction(@NotNull FunctionDescriptor function, @NotNull List<JetParameter> constructorParameters) {
JsFunction functionObj = generateJsMethod(function);
JsScope funScope = functionObj.getScope();
JsFunctionScope funScope = functionObj.getScope();
assert function.getValueParameters().size() == constructorParameters.size();
@@ -145,7 +145,7 @@ class JsDataClassGenerator extends DataClassMethodGenerator {
assert !classProperties.isEmpty();
FunctionDescriptor prototypeFun = CodegenUtil.getAnyEqualsMethod();
JsFunction functionObj = generateJsMethod(prototypeFun);
JsScope funScope = functionObj.getScope();
JsFunctionScope funScope = functionObj.getScope();
JsName paramName = funScope.declareName("other");
functionObj.getParameters().add(new JsParameter(paramName));
@@ -42,7 +42,7 @@ final class PackageTranslator extends AbstractTranslator {
JsNameRef reference = context.getQualifiedReference(descriptor);
SmartList<JsPropertyInitializer> properties = new SmartList<JsPropertyInitializer>();
DefinitionPlace definitionPlace = new DefinitionPlace(scope, reference, properties);
DefinitionPlace definitionPlace = new DefinitionPlace((JsObjectScope) scope, reference, properties);
TranslationContext newContext = context.newDeclaration(descriptor, definitionPlace);
FileDeclarationVisitor visitor = new FileDeclarationVisitor(newContext, definitionPlace.getProperties());
@@ -127,7 +127,8 @@ private class PropertyTranslator(
}
private fun generateDefaultSetterFunction(setterDescriptor: PropertySetterDescriptor): JsFunction {
val jsFunction = JsFunction(context().getScopeForDescriptor(setterDescriptor.getContainingDeclaration()))
val jsFunction = JsFunction(context().getScopeForDescriptor(setterDescriptor.getContainingDeclaration()),
"setter for " + setterDescriptor.getName().asString())
assert(setterDescriptor.getValueParameters().size() == 1, "Setter must have 1 parameter")
val valueParameterDescriptor = setterDescriptor.getValueParameters().get(0)
@@ -351,8 +351,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
}
private static String getReferencedName(JetSimpleNameExpression expression) {
String name = expression.getReferencedName();
return name.charAt(0) == '@' ? name.substring(1) + '$' : name;
return expression.getReferencedName()
.replaceAll("^@", "")
.replaceAll("(?:^`(.*)`$)", "$1");
}
private static String getTargetLabel(JetExpressionWithLabel expression, TranslationContext context) {
@@ -24,7 +24,7 @@ public abstract class CommonUnitTester extends JSTester {
@Override
public void constructTestMethodInvocation(@NotNull JsExpression functionToTestCall,
@NotNull JsStringLiteral testName) {
JsFunction functionToTest = new JsFunction(getContext().scope());
JsFunction functionToTest = new JsFunction(getContext().scope(), "test function");
functionToTest.setBody(new JsBlock(functionToTestCall.makeStmt()));
getBlock().getStatements().add(new JsInvocation(getTestMethodRef(), testName, functionToTest).makeStmt());
}
@@ -278,7 +278,7 @@ public final class JsAstUtils {
@NotNull
public static JsFunction createFunctionWithEmptyBody(@NotNull JsScope parent) {
return new JsFunction(parent, new JsBlock());
return new JsFunction(parent, new JsBlock(), "<anonymous>");
}
@NotNull
@@ -322,10 +322,10 @@ public final class JsAstUtils {
}
@NotNull
public static JsFunction createPackage(@NotNull List<JsStatement> to, @NotNull JsScope scope) {
public static JsFunction createPackage(@NotNull List<JsStatement> to, @NotNull JsObjectScope scope) {
JsFunction packageBlockFunction = createFunctionWithEmptyBody(scope);
JsName kotlinObjectAsParameter = packageBlockFunction.getScope().declareName(Namer.KOTLIN_NAME);
JsName kotlinObjectAsParameter = packageBlockFunction.getScope().declareNameUnsafe(Namer.KOTLIN_NAME);
packageBlockFunction.getParameters().add(new JsParameter(kotlinObjectAsParameter));
to.add(new JsInvocation(packageBlockFunction, Namer.KOTLIN_OBJECT_REF).makeStmt());
@@ -65,7 +65,7 @@ public final class TranslationUtils {
@NotNull
public static JsFunction simpleReturnFunction(@NotNull JsScope functionScope, @NotNull JsExpression returnExpression) {
return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)));
return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)), "<simpleReturnFunction>");
}
@NotNull