1 /* <lambda>null2 * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.atomicfu.transformer 6 7 import org.mozilla.javascript.* 8 import org.mozilla.javascript.ast.* 9 import java.io.File 10 import java.io.FileReader 11 import org.mozilla.javascript.Token 12 import java.util.regex.* 13 14 private const val ATOMIC_CONSTRUCTOR = """(atomic\$(ref|int|long|boolean)\$|Atomic(Ref|Int|Long|Boolean))""" 15 private const val ATOMIC_CONSTRUCTOR_BINARY_COMPATIBILITY = """(atomic\$(ref|int|long|boolean)\$1)""" // mangled names for declarations left for binary compatibility 16 private const val ATOMIC_ARRAY_CONSTRUCTOR = """(atomicfu)\$(Atomic(Ref|Int|Long|Boolean)Array)\$(ref|int|long|boolean|ofNulls)""" 17 private const val MANGLED_VALUE_PROP = "kotlinx\$atomicfu\$value" 18 19 private const val TRACE_CONSTRUCTOR = "atomicfu\\\$Trace" 20 private const val TRACE_BASE_CLASS = "atomicfu\\\$TraceBase" 21 private const val TRACE_APPEND = """(atomicfu)\$(Trace)\$(append)\$([1234])""" // [1234] is the number of arguments in the append overload 22 private const val TRACE_NAMED = "atomicfu\\\$Trace\\\$named" 23 private const val TRACE_FORMAT = "TraceFormat" 24 private const val TRACE_FORMAT_CONSTRUCTOR = "atomicfu\\\$$TRACE_FORMAT" 25 private const val TRACE_FORMAT_FORMAT = "atomicfu\\\$$TRACE_FORMAT\\\$format" 26 27 private const val RECEIVER = """(\$(receiver)(_\d+)?)""" 28 private const val SCOPE = "scope" 29 private const val FACTORY = "factory" 30 private const val REQUIRE = "require" 31 private const val PROTOTYPE = "prototype" 32 private const val KOTLINX_ATOMICFU = "'kotlinx-atomicfu'" 33 private const val KOTLINX_ATOMICFU_PACKAGE = "kotlinx.atomicfu" 34 private const val KOTLIN_TYPE_CHECK = "Kotlin.isType" 35 private const val ATOMIC_REF = "AtomicRef" 36 private const val MODULE_KOTLINX_ATOMICFU = "\\\$module\\\$kotlinx_atomicfu" 37 private const val ARRAY = "Array" 38 private const val FILL = "fill" 39 private const val GET_ELEMENT = "atomicfu\\\$get" 40 private const val ARRAY_SIZE = "atomicfu\$size" 41 private const val LENGTH = "length" 42 private const val LOCKS = "locks" 43 private const val REENTRANT_LOCK_ATOMICFU_SINGLETON = "$LOCKS.atomicfu\\\$reentrantLock" 44 45 46 private val MANGLE_VALUE_REGEX = Regex(".${Pattern.quote(MANGLED_VALUE_PROP)}") 47 // matches index until the first occurence of ')', parenthesised index expressions not supported 48 private val ARRAY_GET_ELEMENT_REGEX = Regex(".$GET_ELEMENT\\((.*)\\)") 49 50 class AtomicFUTransformerJS( 51 inputDir: File, 52 outputDir: File 53 ) : AtomicFUTransformerBase(inputDir, outputDir) { 54 private val atomicConstructors = mutableSetOf<String>() 55 private val delegateToOriginalAtomicField = mutableMapOf<String, Name>() 56 private val topLevelDelegatedFieldAccessorToOriginalField = mutableMapOf<String, Name>() 57 private val atomicArrayConstructors = mutableMapOf<String, String?>() 58 private val traceConstructors = mutableSetOf<String>() 59 private val traceFormatObjects = mutableSetOf<String>() 60 61 override fun transform() { 62 info("Transforming to $outputDir") 63 inputDir.walk().filter { it.isFile }.forEach { file -> 64 val outBytes = if (file.isJsFile()) { 65 println("Transforming file: ${file.canonicalPath}") 66 transformFile(file) 67 } else { 68 file.readBytes() 69 } 70 file.toOutputFile().mkdirsAndWrite(outBytes) 71 } 72 } 73 74 private fun File.isJsFile() = 75 name.endsWith(".js") && !name.endsWith(".meta.js") 76 77 private fun transformFile(file: File): ByteArray { 78 val p = Parser(CompilerEnvirons()) 79 val root = p.parse(FileReader(file), null, 0) 80 root.visit(DependencyEraser()) 81 root.visit(AtomicConstructorDetector()) 82 root.visit(FieldDelegatesVisitor()) 83 root.visit(DelegatedPropertyAccessorsVisitor()) 84 root.visit(TopLevelDelegatedFieldsAccessorVisitor()) 85 root.visit(TransformVisitor()) 86 root.visit(AtomicOperationsInliner()) 87 return root.eraseGetValue().toByteArray() 88 } 89 90 // erase getting value of atomic field 91 private fun AstNode.eraseGetValue(): String { 92 var res = this.toSource() 93 val primitiveGetValue = MANGLE_VALUE_REGEX 94 val arrayGetElement = ARRAY_GET_ELEMENT_REGEX 95 while (res.contains(arrayGetElement)) { 96 res = res.replace(arrayGetElement) { matchResult -> 97 val greedyToLastClosingParen = matchResult.groupValues[1] 98 var balance = 1 99 var indexEndPos = 0 100 for (i in 0 until greedyToLastClosingParen.length) { 101 val c = greedyToLastClosingParen[i] 102 if (c == '(') balance++ 103 if (c == ')') balance-- 104 if (balance == 0) { 105 indexEndPos = i 106 break 107 } 108 } 109 val closingParen = indexEndPos == greedyToLastClosingParen.lastIndex 110 if (balance == 1) { 111 "[$greedyToLastClosingParen]" 112 } else { 113 "[${greedyToLastClosingParen.substring(0, indexEndPos)}]${greedyToLastClosingParen.substring(indexEndPos + 1)}${if (!closingParen) ")" else ""}" 114 } 115 } 116 } 117 return res.replace(primitiveGetValue) { "" } 118 } 119 120 inner class DependencyEraser : NodeVisitor { 121 private fun isAtomicfuDependency(node: AstNode) = 122 (node.type == Token.STRING && node.toSource() == KOTLINX_ATOMICFU) 123 124 private fun isAtomicfuModule(node: AstNode) = 125 (node.type == Token.NAME && node.toSource().matches(Regex(MODULE_KOTLINX_ATOMICFU))) 126 127 override fun visit(node: AstNode): Boolean { 128 when (node.type) { 129 Token.ARRAYLIT -> { 130 // erasing 'kotlinx-atomicfu' from the list of defined dependencies 131 val elements = (node as ArrayLiteral).elements as MutableList 132 val it = elements.listIterator() 133 while (it.hasNext()) { 134 val arg = it.next() 135 if (isAtomicfuDependency(arg)) { 136 it.remove() 137 } 138 } 139 } 140 Token.FUNCTION -> { 141 if (node is FunctionNode) { 142 val it = node.params.listIterator() 143 while (it.hasNext()) { 144 // erasing 'kotlinx-atomicfu' module passed as parameter 145 if (isAtomicfuModule(it.next())) { 146 it.remove() 147 } 148 } 149 } 150 } 151 Token.CALL -> { 152 if (node is FunctionCall && node.target.toSource() == FACTORY) { 153 val it = node.arguments.listIterator() 154 while (it.hasNext()) { 155 val arg = it.next() 156 when (arg.type) { 157 Token.GETELEM -> { 158 // erasing 'kotlinx-atomicfu' dependency as factory argument 159 if (isAtomicfuDependency((arg as ElementGet).element)) { 160 it.remove() 161 } 162 } 163 Token.CALL -> { 164 // erasing require of 'kotlinx-atomicfu' dependency 165 if ((arg as FunctionCall).target.toSource() == REQUIRE) { 166 if (isAtomicfuDependency(arg.arguments[0])) { 167 it.remove() 168 } 169 } 170 } 171 } 172 } 173 } 174 } 175 Token.GETELEM -> { 176 if (isAtomicfuDependency((node as ElementGet).element)) { 177 val enclosingNode = node.parent 178 // erasing the check whether 'kotlinx-atomicfu' is defined 179 if (enclosingNode.type == Token.TYPEOF) { 180 if (enclosingNode.parent.parent.type == Token.IF) { 181 val ifStatement = enclosingNode.parent.parent as IfStatement 182 val falseKeyword = KeywordLiteral() 183 falseKeyword.type = Token.FALSE 184 ifStatement.condition = falseKeyword 185 val oneLineBlock = Block() 186 oneLineBlock.addStatement(EmptyLine()) 187 ifStatement.thenPart = oneLineBlock 188 } 189 } 190 191 } 192 } 193 Token.BLOCK -> { 194 // erasing importsForInline for 'kotlinx-atomicfu' 195 for (stmt in node) { 196 if (stmt is ExpressionStatement) { 197 val expr = stmt.expression 198 if (expr is Assignment && expr.left is ElementGet) { 199 if (isAtomicfuDependency((expr.left as ElementGet).element)) { 200 node.replaceChild(stmt, EmptyLine()) 201 } 202 } 203 } 204 } 205 } 206 } 207 return true 208 } 209 } 210 211 inner class AtomicConstructorDetector : NodeVisitor { 212 private fun kotlinxAtomicfuModuleName(name: String) = "$MODULE_KOTLINX_ATOMICFU.$KOTLINX_ATOMICFU_PACKAGE.$name" 213 214 override fun visit(node: AstNode?): Boolean { 215 if (node is Block) { 216 for (stmt in node) { 217 if (stmt is VariableDeclaration) { 218 val varInit = stmt.variables[0] as VariableInitializer 219 if (varInit.initializer is PropertyGet) { 220 val initializer = varInit.initializer.toSource() 221 if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($ATOMIC_CONSTRUCTOR|$ATOMIC_CONSTRUCTOR_BINARY_COMPATIBILITY)""")))) { 222 atomicConstructors.add(varInit.target.toSource()) 223 node.replaceChild(stmt, EmptyLine()) 224 } else if (initializer.matches(Regex(kotlinxAtomicfuModuleName(TRACE_CONSTRUCTOR)))) { 225 traceConstructors.add(varInit.target.toSource()) 226 node.replaceChild(stmt, EmptyLine()) 227 } else if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($LOCKS|$TRACE_FORMAT_CONSTRUCTOR|$TRACE_BASE_CLASS|$TRACE_NAMED)""")))) { 228 node.replaceChild(stmt, EmptyLine()) 229 } 230 } 231 } 232 } 233 } 234 if (node is PropertyGet && node.property.toSource().matches(Regex(TRACE_FORMAT_FORMAT))) { 235 val target = node.target 236 node.property = Name().also { it.identifier = "emptyProperty" } 237 if (target is PropertyGet && target.property.toSource().matches(Regex(PROTOTYPE))) { 238 traceFormatObjects.add(target.target.toSource()) 239 } 240 } 241 if (node is VariableInitializer && node.initializer is PropertyGet) { 242 val initializer = node.initializer.toSource() 243 if (initializer.matches(Regex(REENTRANT_LOCK_ATOMICFU_SINGLETON))) { 244 node.initializer = null 245 } 246 if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($ATOMIC_CONSTRUCTOR|$ATOMIC_CONSTRUCTOR_BINARY_COMPATIBILITY)""")))) { 247 atomicConstructors.add(node.target.toSource()) 248 node.initializer = null 249 } 250 if (initializer.matches(Regex(kotlinxAtomicfuModuleName(ATOMIC_ARRAY_CONSTRUCTOR)))) { 251 val initialValue = when (initializer.substringAfterLast('$')) { 252 "int" -> "0" 253 "long" -> "0" 254 "boolean" -> "false" 255 else -> null 256 } 257 atomicArrayConstructors[node.target.toSource()] = initialValue 258 node.initializer = null 259 } 260 return false 261 } else if (node is Assignment && node.right is PropertyGet) { 262 val initializer = node.right.toSource() 263 if (initializer.matches(Regex(REENTRANT_LOCK_ATOMICFU_SINGLETON))) { 264 node.right = Name().also { it.identifier = "null" } 265 return false 266 } 267 } 268 return true 269 } 270 } 271 272 inner class FieldDelegatesVisitor : NodeVisitor { 273 override fun visit(node: AstNode?): Boolean { 274 if (node is FunctionCall) { 275 val functionName = node.target.toSource() 276 if (atomicConstructors.contains(functionName)) { 277 if (node.parent is Assignment) { 278 val assignment = node.parent as Assignment 279 val atomicField = assignment.left 280 val constructorBlock = ((node.parent.parent as? ExpressionStatement)?.parent as? Block) 281 ?: abort("Incorrect tree structure of the constructor block initializing ${node.parent.toSource()}") 282 // check if there is a delegate field initialized by the reference to this atomic 283 for (stmt in constructorBlock) { 284 if (stmt is ExpressionStatement) { 285 if (stmt.expression is Assignment) { 286 val delegateAssignment = stmt.expression as Assignment 287 val initializer = delegateAssignment.right 288 if (initializer.toSource() == atomicField.toSource()) { 289 if (delegateAssignment.right is PropertyGet) { // initialization of a class field 290 // delegate${owner_class} to original atomic field 291 val delegateFieldName = (delegateAssignment.left as PropertyGet).property.toSource() 292 val ownerClassName = constructorBlock.enclosingFunction.functionName.identifier 293 delegateToOriginalAtomicField["$delegateFieldName\$$ownerClassName"] = 294 (atomicField as PropertyGet).property 295 } else { // top-level delegated fields 296 val delegateFieldName = delegateAssignment.left.toSource() 297 delegateToOriginalAtomicField[delegateFieldName] = atomicField as Name 298 } 299 } 300 } 301 } 302 } 303 } 304 } 305 } 306 return true 307 } 308 } 309 310 inner class DelegatedPropertyAccessorsVisitor : NodeVisitor { 311 override fun visit(node: AstNode?): Boolean { 312 // find ObjectLiteral with accessors of the delegated field (get: FunctionNode, set: FunctionNode) 313 // redirect getter/setter from generated delegate field to the original atomic field 314 if (node is ObjectLiteral && node.parent is FunctionCall && 315 ((node.elements.size == 2 && node.elements[1].left.toSource() == "get") || 316 (node.elements.size == 3 && node.elements[1].left.toSource() == "get" && node.elements[2].left.toSource() == "set"))) { 317 // check that these are accessors of the atomic delegate field (check only getter) 318 if (node.elements[1].right is FunctionNode) { 319 val getter = node.elements[1].right as FunctionNode 320 if (getter.body.hasChildren() && getter.body.firstChild is ReturnStatement) { 321 val returnStmt = getter.body.firstChild as ReturnStatement 322 if (returnStmt.returnValue is PropertyGet && (returnStmt.returnValue as PropertyGet).property.toSource() == MANGLED_VALUE_PROP) { 323 val delegateField = ((returnStmt.returnValue as PropertyGet).target as PropertyGet).property.toSource() 324 val ownerClassName = ((node.parent as FunctionCall).arguments[0] as PropertyGet).target.toSource() 325 val key = "$delegateField\$$ownerClassName" 326 delegateToOriginalAtomicField[key]?.let { atomicField -> 327 // get() = a$delegate.value -> _a.value 328 getter.replaceAccessedField(true, atomicField) 329 if (node.elements.size == 3) { 330 // set(v: T) { a$delegate.value = v } -> { _a.value = v } 331 val setter = node.elements[2].right as FunctionNode 332 setter.replaceAccessedField(false, atomicField) 333 } 334 } 335 } 336 } 337 } 338 } 339 if (node is ObjectLiteral && node.parent is FunctionCall && ((node.elements.size == 1 && node.elements[0].left.toSource() == "get") || 340 node.elements.size == 2 && node.elements[0].left.toSource() == "get" && node.elements[1].left.toSource() == "set")) { 341 val parent = node.parent as FunctionCall 342 if (parent.arguments.size == 3 && parent.arguments[1] is StringLiteral) { 343 val topLevelDelegatedFieldName = (parent.arguments[1] as StringLiteral).value 344 if (topLevelDelegatedFieldName in delegateToOriginalAtomicField) { 345 val originalAtomicFieldName = delegateToOriginalAtomicField[topLevelDelegatedFieldName]!! 346 val getterName = node.elements[0].right.toSource() 347 topLevelDelegatedFieldAccessorToOriginalField[getterName] = originalAtomicFieldName 348 if (node.elements.size == 2) { 349 val setterName = node.elements[1].right.toSource() 350 topLevelDelegatedFieldAccessorToOriginalField[setterName] = originalAtomicFieldName 351 } 352 } 353 } 354 } 355 return true 356 } 357 } 358 359 private fun FunctionNode.replaceAccessedField(isGetter: Boolean, newField: Name) { 360 val propertyGet = if (isGetter) { 361 (body.firstChild as ReturnStatement).returnValue as PropertyGet 362 } else { 363 ((body.firstChild as ExpressionStatement).expression as Assignment).left as PropertyGet 364 } 365 if (propertyGet.target is PropertyGet) { // class member 366 (propertyGet.target as PropertyGet).property = newField 367 } else { // top-level field 368 propertyGet.target = newField 369 } 370 } 371 372 inner class TopLevelDelegatedFieldsAccessorVisitor : NodeVisitor { 373 override fun visit(node: AstNode?): Boolean { 374 if (node is FunctionNode && node.name.toString() in topLevelDelegatedFieldAccessorToOriginalField) { 375 val accessorName = node.name.toString() 376 val atomicField = topLevelDelegatedFieldAccessorToOriginalField[accessorName]!! 377 // function get_topLevelDelegatedField() = a.value -> _a.value 378 // function set_topLevelDelegatedField(v: T) { a.value = v } -> { _a.value = v } 379 node.replaceAccessedField(accessorName.startsWith("get"), atomicField) 380 } 381 return true 382 } 383 } 384 385 inner class TransformVisitor : NodeVisitor { 386 override fun visit(node: AstNode): Boolean { 387 // remove atomic constructors from classes fields 388 if (node is FunctionCall) { 389 val functionName = node.target.toSource() 390 if (atomicConstructors.contains(functionName)) { 391 if (node.parent is Assignment) { 392 val valueNode = node.arguments[0] 393 (node.parent as Assignment).right = valueNode 394 } 395 return true 396 } else if (atomicArrayConstructors.contains(functionName)) { 397 val arrayConstructor = Name() 398 arrayConstructor.identifier = ARRAY 399 node.target = arrayConstructor 400 atomicArrayConstructors[functionName]?.let { 401 val arrayConsCall = FunctionCall() 402 arrayConsCall.target = node.target 403 arrayConsCall.arguments = node.arguments 404 val target = PropertyGet() 405 val fill = Name() 406 fill.identifier = FILL 407 target.target = arrayConsCall 408 target.property = fill 409 node.target = target 410 val initialValue = Name() 411 initialValue.identifier = it 412 node.arguments = listOf(initialValue) 413 } 414 return true 415 } else if (node.target is PropertyGet) { 416 if ((node.target as PropertyGet).target is FunctionCall) { 417 val atomicOperationTarget = node.target as PropertyGet 418 val funcCall = atomicOperationTarget.target as FunctionCall 419 if (funcCall.target is PropertyGet) { 420 val getterCall = (funcCall.target as PropertyGet).property 421 if (Regex(GET_ELEMENT).matches(getterCall.toSource())) { 422 val getter = getArrayElement(funcCall) 423 atomicOperationTarget.target = getter 424 } 425 } 426 } 427 } 428 } 429 // remove value property call 430 if (node is PropertyGet) { 431 if (node.property.toSource() == MANGLED_VALUE_PROP) { 432 // check whether atomic operation is performed on the type casted atomic field 433 node.target.eraseAtomicFieldFromUncheckedCast()?.let { node.target = it } 434 // A.a.value 435 if (node.target.type == Token.GETPROP) { 436 val clearField = node.target as PropertyGet 437 val targetNode = clearField.target 438 val clearProperety = clearField.property 439 node.setLeftAndRight(targetNode, clearProperety) 440 } 441 // other cases with $receiver.kotlinx$atomicfu$value in inline functions 442 else if (node.target.toSource().matches(Regex(RECEIVER))) { 443 val receiverName = node.target.toSource() // $receiver_i 444 val rr = ReceiverResolver(receiverName) 445 node.enclosingFunction.visit(rr) 446 rr.receiver?.let { node.target = it } 447 } 448 } 449 // replace Atomic*Array.size call with `length` property on the pure type js array 450 if (node.property.toSource() == ARRAY_SIZE) { 451 node.property = Name().also { it.identifier = LENGTH } 452 } 453 } 454 if (node is Block) { 455 for (stmt in node) { 456 if (stmt is ExpressionStatement) { 457 if (stmt.expression is Assignment) { 458 // erase field initialisation 459 val assignment = stmt.expression as Assignment 460 if (assignment.right is FunctionCall) { 461 val functionName = (assignment.right as FunctionCall).target.toSource() 462 if (traceConstructors.contains(functionName)) { 463 node.replaceChild(stmt, EmptyLine()) 464 } 465 } 466 } 467 if (stmt.expression is FunctionCall) { 468 // erase append(text) call 469 val funcNode = (stmt.expression as FunctionCall).target 470 if (funcNode is PropertyGet && funcNode.property.toSource().matches(Regex(TRACE_APPEND))) { 471 node.replaceChild(stmt, EmptyLine()) 472 } 473 } 474 } 475 } 476 } 477 if (node is Assignment && node.left is PropertyGet) { 478 val left = node.left as PropertyGet 479 if (traceFormatObjects.contains(left.target.toSource())) { 480 if (node.right is FunctionCall) { 481 // TraceFormatObject initialization 482 (node.right as FunctionCall).arguments = listOf(Name().also { it.identifier = "null" }) 483 } 484 } 485 } 486 // remove TraceFormatObject constructor definition 487 if (node is FunctionNode && traceFormatObjects.contains(node.name)) { 488 val body = node.body 489 for (stmt in body) { body.replaceChild(stmt, EmptyLine()) } 490 } 491 // remove TraceFormat from TraceFormatObject interfaces 492 if (node is Assignment && node.left is PropertyGet && node.right is ObjectLiteral) { 493 val left = node.left as PropertyGet 494 val metadata = node.right as ObjectLiteral 495 if (traceFormatObjects.contains(left.target.toSource())) { 496 for (e in metadata.elements) { 497 if (e.right is ArrayLiteral) { 498 val array = (e.right as ArrayLiteral).toSource() 499 if (array.contains(TRACE_FORMAT)) { 500 (e.right as ArrayLiteral).elements = emptyList() 501 } 502 } 503 } 504 } 505 } 506 return true 507 } 508 509 private fun getArrayElement(getterCall: FunctionCall): AstNode { 510 val index = getterCall.arguments[0] 511 val arrayField = (getterCall.target as PropertyGet).target 512 // whether this field is static or not 513 val isStatic = arrayField !is PropertyGet 514 val arrName = if (isStatic) arrayField else (arrayField as PropertyGet).property 515 val getter = ElementGet(arrName, index) 516 return if (isStatic) { //intArr[index] 517 getter 518 } else { //A.intArr[0] 519 val call = PropertyGet() 520 call.target = (arrayField as PropertyGet).target 521 val name = Name() 522 name.identifier = getter.toSource() 523 call.property = name 524 call 525 } 526 } 527 } 528 529 530 // receiver data flow 531 inner class ReceiverResolver(private val receiverName: String) : NodeVisitor { 532 var receiver: AstNode? = null 533 override fun visit(node: AstNode): Boolean { 534 if (node is VariableInitializer) { 535 if (node.target.toSource() == receiverName) { 536 receiver = node.initializer 537 return false 538 } 539 } 540 return true 541 } 542 } 543 544 inner class AtomicOperationsInliner : NodeVisitor { 545 override fun visit(node: AstNode?): Boolean { 546 // inline atomic operations 547 if (node is FunctionCall) { 548 if (node.target is PropertyGet) { 549 val funcName = (node.target as PropertyGet).property 550 var field = (node.target as PropertyGet).target 551 if (field.toSource().matches(Regex(RECEIVER))) { 552 val receiverName = field.toSource() // $receiver_i 553 val rr = ReceiverResolver(receiverName) 554 node.enclosingFunction.visit(rr) 555 if (rr.receiver != null) { 556 field = rr.receiver 557 } 558 } 559 field.eraseAtomicFieldFromUncheckedCast()?.let { field = it } 560 val args = node.arguments 561 val inlined = node.inlineAtomicOperation(funcName.toSource(), field, args) 562 return !inlined 563 } 564 } 565 return true 566 } 567 } 568 569 private fun AstNode.eraseAtomicFieldFromUncheckedCast(): AstNode? { 570 if (this is ParenthesizedExpression && expression is ConditionalExpression) { 571 val testExpression = (expression as ConditionalExpression).testExpression 572 if (testExpression is FunctionCall && testExpression.target.toSource() == KOTLIN_TYPE_CHECK) { 573 // type check 574 val typeToCast = testExpression.arguments[1] 575 if ((typeToCast as Name).identifier == ATOMIC_REF) { 576 // unchecked type cast -> erase atomic field itself 577 return (testExpression.arguments[0] as Assignment).right 578 } 579 } 580 } 581 return null 582 } 583 584 private fun AstNode.isThisNode(): Boolean { 585 return when(this) { 586 is PropertyGet -> { 587 target.isThisNode() 588 } 589 is FunctionCall -> { 590 target.isThisNode() 591 } 592 else -> { 593 (this.type == Token.THIS) 594 } 595 } 596 } 597 598 private fun PropertyGet.resolvePropName(): String { 599 val target = this.target 600 return if (target is PropertyGet) { 601 "${target.resolvePropName()}.${property.toSource()}" 602 } else { 603 property.toSource() 604 } 605 } 606 607 private fun AstNode.scopedSource(): String { 608 if (this.isThisNode()) { 609 if (this is PropertyGet) { 610 val property = resolvePropName() 611 return "$SCOPE.$property" 612 } else if (this is FunctionCall && this.target is PropertyGet) { 613 // check that this function call is getting array element 614 if (this.target is PropertyGet) { 615 val funcName = (this.target as PropertyGet).property.toSource() 616 if (Regex(GET_ELEMENT).matches(funcName)) { 617 val property = (this.target as PropertyGet).resolvePropName() 618 return "$SCOPE.$property(${this.arguments[0].toSource()})" 619 } 620 } 621 } else if (this.type == Token.THIS) { 622 return SCOPE 623 } 624 } 625 return this.toSource() 626 } 627 628 private fun FunctionCall.inlineAtomicOperation( 629 funcName: String, 630 field: AstNode, 631 args: List<AstNode> 632 ): Boolean { 633 val f = field.scopedSource() 634 val code = when (funcName) { 635 "atomicfu\$getAndSet" -> { 636 val arg = args[0].toSource() 637 "(function($SCOPE) {var oldValue = $f; $f = $arg; return oldValue;})()" 638 } 639 "atomicfu\$compareAndSet" -> { 640 val expected = args[0].scopedSource() 641 val updated = args[1].scopedSource() 642 val equals = if (expected == "null") "==" else "===" 643 "(function($SCOPE) {return $f $equals $expected ? function() { $f = $updated; return true }() : false})()" 644 } 645 "atomicfu\$getAndIncrement" -> { 646 "(function($SCOPE) {return $f++;})()" 647 } 648 649 "atomicfu\$getAndIncrement\$long" -> { 650 "(function($SCOPE) {var oldValue = $f; $f = $f.inc(); return oldValue;})()" 651 } 652 653 "atomicfu\$getAndDecrement" -> { 654 "(function($SCOPE) {return $f--;})()" 655 } 656 657 "atomicfu\$getAndDecrement\$long" -> { 658 "(function($SCOPE) {var oldValue = $f; $f = $f.dec(); return oldValue;})()" 659 } 660 661 "atomicfu\$getAndAdd" -> { 662 val arg = args[0].scopedSource() 663 "(function($SCOPE) {var oldValue = $f; $f += $arg; return oldValue;})()" 664 } 665 666 "atomicfu\$getAndAdd\$long" -> { 667 val arg = args[0].scopedSource() 668 "(function($SCOPE) {var oldValue = $f; $f = $f.add($arg); return oldValue;})()" 669 } 670 671 "atomicfu\$addAndGet" -> { 672 val arg = args[0].scopedSource() 673 "(function($SCOPE) {$f += $arg; return $f;})()" 674 } 675 676 "atomicfu\$addAndGet\$long" -> { 677 val arg = args[0].scopedSource() 678 "(function($SCOPE) {$f = $f.add($arg); return $f;})()" 679 } 680 681 "atomicfu\$incrementAndGet" -> { 682 "(function($SCOPE) {return ++$f;})()" 683 } 684 685 "atomicfu\$incrementAndGet\$long" -> { 686 "(function($SCOPE) {return $f = $f.inc();})()" 687 } 688 689 "atomicfu\$decrementAndGet" -> { 690 "(function($SCOPE) {return --$f;})()" 691 } 692 693 "atomicfu\$decrementAndGet\$long" -> { 694 "(function($SCOPE) {return $f = $f.dec();})()" 695 } 696 else -> null 697 } 698 if (code != null) { 699 this.setImpl(code) 700 return true 701 } 702 return false 703 } 704 705 private fun FunctionCall.setImpl(code: String) { 706 val p = Parser(CompilerEnvirons()) 707 val node = p.parse(code, null, 0) 708 if (node.firstChild != null) { 709 val expr = (node.firstChild as ExpressionStatement).expression 710 this.target = (expr as FunctionCall).target 711 val thisNode = Parser(CompilerEnvirons()).parse("this", null, 0) 712 this.arguments = listOf((thisNode.firstChild as ExpressionStatement).expression) 713 } 714 } 715 } 716 717 private class EmptyLine : EmptyExpression() { toSourcenull718 override fun toSource(depth: Int) = "\n" 719 } 720 721 fun main(args: Array<String>) { 722 if (args.size !in 1..2) { 723 println("Usage: AtomicFUTransformerKt <dir> [<output>]") 724 return 725 } 726 val t = AtomicFUTransformerJS(File(args[0]), File(args[1])) 727 t.transform() 728 }