aboutsummaryrefslogtreecommitdiff
path: root/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt
diff options
context:
space:
mode:
Diffstat (limited to 'atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt')
-rw-r--r--atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt104
1 files changed, 78 insertions, 26 deletions
diff --git a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt
index 91f3037..de8f94a 100644
--- a/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt
+++ b/atomicfu-transformer/src/main/kotlin/kotlinx/atomicfu/transformer/AtomicFUTransformerJS.kt
@@ -52,8 +52,8 @@ class AtomicFUTransformerJS(
outputDir: File
) : AtomicFUTransformerBase(inputDir, outputDir) {
private val atomicConstructors = mutableSetOf<String>()
- private val fieldDelegates = mutableMapOf<String, String>()
- private val delegatedProperties = mutableMapOf<String, String>()
+ private val delegateToOriginalAtomicField = mutableMapOf<String, Name>()
+ private val topLevelDelegatedFieldAccessorToOriginalField = mutableMapOf<String, Name>()
private val atomicArrayConstructors = mutableMapOf<String, String?>()
private val traceConstructors = mutableSetOf<String>()
private val traceFormatObjects = mutableSetOf<String>()
@@ -81,6 +81,7 @@ class AtomicFUTransformerJS(
root.visit(AtomicConstructorDetector())
root.visit(FieldDelegatesVisitor())
root.visit(DelegatedPropertyAccessorsVisitor())
+ root.visit(TopLevelDelegatedFieldsAccessorVisitor())
root.visit(TransformVisitor())
root.visit(AtomicOperationsInliner())
return root.eraseGetValue().toByteArray()
@@ -283,12 +284,17 @@ class AtomicFUTransformerJS(
if (stmt is ExpressionStatement) {
if (stmt.expression is Assignment) {
val delegateAssignment = stmt.expression as Assignment
- if (delegateAssignment.right is PropertyGet) {
- val initializer = delegateAssignment.right as PropertyGet
- if (initializer.toSource() == atomicField.toSource()) {
- // register field delegate and the original atomic field
- fieldDelegates[(delegateAssignment.left as PropertyGet).property.toSource()] =
- (atomicField as PropertyGet).property.toSource()
+ val initializer = delegateAssignment.right
+ if (initializer.toSource() == atomicField.toSource()) {
+ if (delegateAssignment.right is PropertyGet) { // initialization of a class field
+ // delegate${owner_class} to original atomic field
+ val delegateFieldName = (delegateAssignment.left as PropertyGet).property.toSource()
+ val ownerClassName = constructorBlock.enclosingFunction.functionName.identifier
+ delegateToOriginalAtomicField["$delegateFieldName\$$ownerClassName"] =
+ (atomicField as PropertyGet).property
+ } else { // top-level delegated fields
+ val delegateFieldName = delegateAssignment.left.toSource()
+ delegateToOriginalAtomicField[delegateFieldName] = atomicField as Name
}
}
}
@@ -303,22 +309,74 @@ class AtomicFUTransformerJS(
inner class DelegatedPropertyAccessorsVisitor : NodeVisitor {
override fun visit(node: AstNode?): Boolean {
- if (node is PropertyGet) {
- if (node.target is PropertyGet) {
- if ((node.target as PropertyGet).property.toSource() in fieldDelegates && node.property.toSource() == MANGLED_VALUE_PROP) {
- if (node.parent is ReturnStatement) {
- val getter = ((((node.parent.parent as? Block)?.parent as? FunctionNode)?.parent as? ObjectProperty)?.parent as? ObjectLiteral)
- ?: abort("Incorrect tree structure of the accessor for the property delegated " +
- "to the atomic field ${fieldDelegates[node.target.toSource()]}")
- val definePropertyCall = getter.parent as FunctionCall
- val stringLiteral = definePropertyCall.arguments[1] as? StringLiteral
- ?: abort ("Object.defineProperty invocation should take a property name as the second argument")
- val delegatedProperty = stringLiteral.value.toString()
- delegatedProperties[delegatedProperty] = (node.target as PropertyGet).property.toSource()
+ // find ObjectLiteral with accessors of the delegated field (get: FunctionNode, set: FunctionNode)
+ // redirect getter/setter from generated delegate field to the original atomic field
+ if (node is ObjectLiteral && node.parent is FunctionCall &&
+ ((node.elements.size == 2 && node.elements[1].left.toSource() == "get") ||
+ (node.elements.size == 3 && node.elements[1].left.toSource() == "get" && node.elements[2].left.toSource() == "set"))) {
+ // check that these are accessors of the atomic delegate field (check only getter)
+ if (node.elements[1].right is FunctionNode) {
+ val getter = node.elements[1].right as FunctionNode
+ if (getter.body.hasChildren() && getter.body.firstChild is ReturnStatement) {
+ val returnStmt = getter.body.firstChild as ReturnStatement
+ if (returnStmt.returnValue is PropertyGet && (returnStmt.returnValue as PropertyGet).property.toSource() == MANGLED_VALUE_PROP) {
+ val delegateField = ((returnStmt.returnValue as PropertyGet).target as PropertyGet).property.toSource()
+ val ownerClassName = ((node.parent as FunctionCall).arguments[0] as PropertyGet).target.toSource()
+ val key = "$delegateField\$$ownerClassName"
+ delegateToOriginalAtomicField[key]?.let { atomicField ->
+ // get() = a$delegate.value -> _a.value
+ getter.replaceAccessedField(true, atomicField)
+ if (node.elements.size == 3) {
+ // set(v: T) { a$delegate.value = v } -> { _a.value = v }
+ val setter = node.elements[2].right as FunctionNode
+ setter.replaceAccessedField(false, atomicField)
+ }
+ }
+ }
+ }
+ }
+ }
+ if (node is ObjectLiteral && node.parent is FunctionCall && ((node.elements.size == 1 && node.elements[0].left.toSource() == "get") ||
+ node.elements.size == 2 && node.elements[0].left.toSource() == "get" && node.elements[1].left.toSource() == "set")) {
+ val parent = node.parent as FunctionCall
+ if (parent.arguments.size == 3 && parent.arguments[1] is StringLiteral) {
+ val topLevelDelegatedFieldName = (parent.arguments[1] as StringLiteral).value
+ if (topLevelDelegatedFieldName in delegateToOriginalAtomicField) {
+ val originalAtomicFieldName = delegateToOriginalAtomicField[topLevelDelegatedFieldName]!!
+ val getterName = node.elements[0].right.toSource()
+ topLevelDelegatedFieldAccessorToOriginalField[getterName] = originalAtomicFieldName
+ if (node.elements.size == 2) {
+ val setterName = node.elements[1].right.toSource()
+ topLevelDelegatedFieldAccessorToOriginalField[setterName] = originalAtomicFieldName
}
}
}
+ }
+ return true
+ }
+ }
+ private fun FunctionNode.replaceAccessedField(isGetter: Boolean, newField: Name) {
+ val propertyGet = if (isGetter) {
+ (body.firstChild as ReturnStatement).returnValue as PropertyGet
+ } else {
+ ((body.firstChild as ExpressionStatement).expression as Assignment).left as PropertyGet
+ }
+ if (propertyGet.target is PropertyGet) { // class member
+ (propertyGet.target as PropertyGet).property = newField
+ } else { // top-level field
+ propertyGet.target = newField
+ }
+ }
+
+ inner class TopLevelDelegatedFieldsAccessorVisitor : NodeVisitor {
+ override fun visit(node: AstNode?): Boolean {
+ if (node is FunctionNode && node.name.toString() in topLevelDelegatedFieldAccessorToOriginalField) {
+ val accessorName = node.name.toString()
+ val atomicField = topLevelDelegatedFieldAccessorToOriginalField[accessorName]!!
+ // function get_topLevelDelegatedField() = a.value -> _a.value
+ // function set_topLevelDelegatedField(v: T) { a.value = v } -> { _a.value = v }
+ node.replaceAccessedField(accessorName.startsWith("get"), atomicField)
}
return true
}
@@ -388,12 +446,6 @@ class AtomicFUTransformerJS(
rr.receiver?.let { node.target = it }
}
}
- if (node.property.toSource() in delegatedProperties) {
- // replace delegated property name with the name of the original atomic field
- val fieldDelegate = delegatedProperties[node.property.toSource()]
- val originalField = fieldDelegates[fieldDelegate]!!
- node.property = Name().apply { identifier = originalField }
- }
// replace Atomic*Array.size call with `length` property on the pure type js array
if (node.property.toSource() == ARRAY_SIZE) {
node.property = Name().also { it.identifier = LENGTH }