diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/AbstractNode.java b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/AbstractNode.java index fb99016a30b..b2961fd793a 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/AbstractNode.java +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/AbstractNode.java @@ -16,16 +16,30 @@ package org.jetbrains.kotlin.js.backend.ast; +import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor; import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadata; +import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadataImpl; import org.jetbrains.kotlin.js.util.TextOutputImpl; -import java.util.LinkedList; +import java.util.Collections; import java.util.List; +import java.util.Map; -abstract class AbstractNode extends HasMetadata implements JsNode { - private List commentsBefore = null; - private List commentsAfter = null; +abstract class AbstractNode implements JsNode, HasMetadata { + private static class Internals extends HasMetadataImpl { + List commentsBefore = null; + List commentsAfter = null; + } + + private Internals internals = null; + + private Internals getInternals() { + if (internals == null) { + internals = new Internals(); + } + return internals; + } @Override public String toString() { @@ -48,21 +62,56 @@ abstract class AbstractNode extends HasMetadata implements JsNode { @Override public List getCommentsBeforeNode() { - return commentsBefore; + return internals == null ? null : internals.commentsBefore; } @Override public List getCommentsAfterNode() { - return commentsAfter; + return internals == null ? null : internals.commentsAfter; } @Override public void setCommentsBeforeNode(List comments) { - commentsBefore = comments; + getInternals().commentsBefore = comments; } @Override public void setCommentsAfterNode(List comments) { - commentsAfter = comments; + getInternals().commentsAfter = comments; + } + + @Override + public T getData(@NotNull String key) { + return getInternals().getData(key); + } + + @Override + public void setData(@NotNull String key, T value) { + getInternals().setData(key, value); + } + + @Override + public boolean hasData(@NotNull String key) { + return internals != null && internals.hasData(key); + } + + @Override + public void removeData(@NotNull String key) { + if (internals != null) { + internals.removeData(key); + } + } + + @Override + public void copyMetadataFrom(@NotNull HasMetadata other) { + if (!other.getRawMetadata().isEmpty()) { + getInternals().copyMetadataFrom(other); + } + } + + @NotNull + @Override + public Map getRawMetadata() { + return internals != null ? internals.getRawMetadata() : Collections.emptyMap(); } } diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsName.java b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsName.java index 3c4cf7b8052..bee0fff2dfd 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsName.java +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsName.java @@ -5,13 +5,13 @@ package org.jetbrains.kotlin.js.backend.ast; import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadata; +import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadataImpl; import org.jetbrains.kotlin.js.common.Symbol; /** * An abstract base class for named JavaScript objects. */ -public class JsName extends HasMetadata implements Symbol { +public class JsName extends HasMetadataImpl implements Symbol { @NotNull private final String ident; diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/HasMetadata.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/HasMetadata.kt index f9c5ab23262..f89b453aa37 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/HasMetadata.kt +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/HasMetadata.kt @@ -16,27 +16,45 @@ package org.jetbrains.kotlin.js.backend.ast.metadata -abstract class HasMetadata { - private val metadata: MutableMap = hashMapOf() +interface HasMetadata { + fun getData(key: String): T + fun setData(key: String, value: T) + fun hasData(key: String): Boolean + fun removeData(key: String) + fun copyMetadataFrom(other: HasMetadata) + fun getRawMetadata(): Map +} - fun getData(key: String): T { - @Suppress("UNCHECKED_CAST") - return metadata[key] as T +open class HasMetadataImpl : HasMetadata { + private var metadataImpl: MutableMap? = null + + private val metadata: MutableMap + get() = metadataImpl ?: hashMapOf().also { metadataImpl = it } + + final override fun getData(key: String): T { + @Suppress("UNCHECKED_CAST") return metadata[key] as T } - fun setData(key: String, value: T) { + final override fun setData(key: String, value: T) { metadata[key] = value } - fun hasData(key: String): Boolean { - return metadata.containsKey(key) + final override fun hasData(key: String): Boolean { + return metadataImpl?.containsKey(key) == true } - fun removeData(key: String) { - metadata.remove(key) + final override fun removeData(key: String) { + metadataImpl?.remove(key) } - fun copyMetadataFrom(other: HasMetadata) { - metadata.putAll(other.metadata) + final override fun copyMetadataFrom(other: HasMetadata) { + val otherRawMetadata = other.getRawMetadata() + if (otherRawMetadata.isNotEmpty()) { + metadata.putAll(otherRawMetadata) + } + } + + final override fun getRawMetadata(): Map { + return metadataImpl ?: emptyMap() } }