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 fieldDelegates = mutableMapOf<String, String>() 56 private val delegatedProperties = mutableMapOf<String, String>() 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(TransformVisitor()) 85 root.visit(AtomicOperationsInliner()) 86 return root.eraseGetValue().toByteArray() 87 } 88 89 // erase getting value of atomic field 90 private fun AstNode.eraseGetValue(): String { 91 var res = this.toSource() 92 val primitiveGetValue = MANGLE_VALUE_REGEX 93 val arrayGetElement = ARRAY_GET_ELEMENT_REGEX 94 while (res.contains(arrayGetElement)) { 95 res = res.replace(arrayGetElement) { matchResult -> 96 val greedyToLastClosingParen = matchResult.groupValues[1] 97 var balance = 1 98 var indexEndPos = 0 99 for (i in 0 until greedyToLastClosingParen.length) { 100 val c = greedyToLastClosingParen[i] 101 if (c == '(') balance++ 102 if (c == ')') balance-- 103 if (balance == 0) { 104 indexEndPos = i 105 break 106 } 107 } 108 val closingParen = indexEndPos == greedyToLastClosingParen.lastIndex 109 if (balance == 1) { 110 "[$greedyToLastClosingParen]" 111 } else { 112 "[${greedyToLastClosingParen.substring(0, indexEndPos)}]${greedyToLastClosingParen.substring(indexEndPos + 1)}${if (!closingParen) ")" else ""}" 113 } 114 } 115 } 116 return res.replace(primitiveGetValue) { "" } 117 } 118 119 inner class DependencyEraser : NodeVisitor { 120 private fun isAtomicfuDependency(node: AstNode) = 121 (node.type == Token.STRING && node.toSource() == KOTLINX_ATOMICFU) 122 123 private fun isAtomicfuModule(node: AstNode) = 124 (node.type == Token.NAME && node.toSource().matches(Regex(MODULE_KOTLINX_ATOMICFU))) 125 126 override fun visit(node: AstNode): Boolean { 127 when (node.type) { 128 Token.ARRAYLIT -> { 129 // erasing 'kotlinx-atomicfu' from the list of defined dependencies 130 val elements = (node as ArrayLiteral).elements as MutableList 131 val it = elements.listIterator() 132 while (it.hasNext()) { 133 val arg = it.next() 134 if (isAtomicfuDependency(arg)) { 135 it.remove() 136 } 137 } 138 } 139 Token.FUNCTION -> { 140 if (node is FunctionNode) { 141 val it = node.params.listIterator() 142 while (it.hasNext()) { 143 // erasing 'kotlinx-atomicfu' module passed as parameter 144 if (isAtomicfuModule(it.next())) { 145 it.remove() 146 } 147 } 148 } 149 } 150 Token.CALL -> { 151 if (node is FunctionCall && node.target.toSource() == FACTORY) { 152 val it = node.arguments.listIterator() 153 while (it.hasNext()) { 154 val arg = it.next() 155 when (arg.type) { 156 Token.GETELEM -> { 157 // erasing 'kotlinx-atomicfu' dependency as factory argument 158 if (isAtomicfuDependency((arg as ElementGet).element)) { 159 it.remove() 160 } 161 } 162 Token.CALL -> { 163 // erasing require of 'kotlinx-atomicfu' dependency 164 if ((arg as FunctionCall).target.toSource() == REQUIRE) { 165 if (isAtomicfuDependency(arg.arguments[0])) { 166 it.remove() 167 } 168 } 169 } 170 } 171 } 172 } 173 } 174 Token.GETELEM -> { 175 if (isAtomicfuDependency((node as ElementGet).element)) { 176 val enclosingNode = node.parent 177 // erasing the check whether 'kotlinx-atomicfu' is defined 178 if (enclosingNode.type == Token.TYPEOF) { 179 if (enclosingNode.parent.parent.type == Token.IF) { 180 val ifStatement = enclosingNode.parent.parent as IfStatement 181 val falseKeyword = KeywordLiteral() 182 falseKeyword.type = Token.FALSE 183 ifStatement.condition = falseKeyword 184 val oneLineBlock = Block() 185 oneLineBlock.addStatement(EmptyLine()) 186 ifStatement.thenPart = oneLineBlock 187 } 188 } 189 190 } 191 } 192 Token.BLOCK -> { 193 // erasing importsForInline for 'kotlinx-atomicfu' 194 for (stmt in node) { 195 if (stmt is ExpressionStatement) { 196 val expr = stmt.expression 197 if (expr is Assignment && expr.left is ElementGet) { 198 if (isAtomicfuDependency((expr.left as ElementGet).element)) { 199 node.replaceChild(stmt, EmptyLine()) 200 } 201 } 202 } 203 } 204 } 205 } 206 return true 207 } 208 } 209 210 inner class AtomicConstructorDetector : NodeVisitor { 211 private fun kotlinxAtomicfuModuleName(name: String) = "$MODULE_KOTLINX_ATOMICFU.$KOTLINX_ATOMICFU_PACKAGE.$name" 212 213 override fun visit(node: AstNode?): Boolean { 214 if (node is Block) { 215 for (stmt in node) { 216 if (stmt is VariableDeclaration) { 217 val varInit = stmt.variables[0] as VariableInitializer 218 if (varInit.initializer is PropertyGet) { 219 val initializer = varInit.initializer.toSource() 220 if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($ATOMIC_CONSTRUCTOR|$ATOMIC_CONSTRUCTOR_BINARY_COMPATIBILITY)""")))) { 221 atomicConstructors.add(varInit.target.toSource()) 222 node.replaceChild(stmt, EmptyLine()) 223 } else if (initializer.matches(Regex(kotlinxAtomicfuModuleName(TRACE_CONSTRUCTOR)))) { 224 traceConstructors.add(varInit.target.toSource()) 225 node.replaceChild(stmt, EmptyLine()) 226 } else if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($LOCKS|$TRACE_FORMAT_CONSTRUCTOR|$TRACE_BASE_CLASS|$TRACE_NAMED)""")))) { 227 node.replaceChild(stmt, EmptyLine()) 228 } 229 } 230 } 231 } 232 } 233 if (node is PropertyGet && node.property.toSource().matches(Regex(TRACE_FORMAT_FORMAT))) { 234 val target = node.target 235 node.property = Name().also { it.identifier = "emptyProperty" } 236 if (target is PropertyGet && target.property.toSource().matches(Regex(PROTOTYPE))) { 237 traceFormatObjects.add(target.target.toSource()) 238 } 239 } 240 if (node is VariableInitializer && node.initializer is PropertyGet) { 241 val initializer = node.initializer.toSource() 242 if (initializer.matches(Regex(REENTRANT_LOCK_ATOMICFU_SINGLETON))) { 243 node.initializer = null 244 } 245 if (initializer.matches(Regex(kotlinxAtomicfuModuleName("""($ATOMIC_CONSTRUCTOR|$ATOMIC_CONSTRUCTOR_BINARY_COMPATIBILITY)""")))) { 246 atomicConstructors.add(node.target.toSource()) 247 node.initializer = null 248 } 249 if (initializer.matches(Regex(kotlinxAtomicfuModuleName(ATOMIC_ARRAY_CONSTRUCTOR)))) { 250 val initialValue = when (initializer.substringAfterLast('$')) { 251 "int" -> "0" 252 "long" -> "0" 253 "boolean" -> "false" 254 else -> null 255 } 256 atomicArrayConstructors[node.target.toSource()] = initialValue 257 node.initializer = null 258 } 259 return false 260 } else if (node is Assignment && node.right is PropertyGet) { 261 val initializer = node.right.toSource() 262 if (initializer.matches(Regex(REENTRANT_LOCK_ATOMICFU_SINGLETON))) { 263 node.right = Name().also { it.identifier = "null" } 264 return false 265 } 266 } 267 return true 268 } 269 } 270 271 inner class FieldDelegatesVisitor : NodeVisitor { 272 override fun visit(node: AstNode?): Boolean { 273 if (node is FunctionCall) { 274 val functionName = node.target.toSource() 275 if (atomicConstructors.contains(functionName)) { 276 if (node.parent is Assignment) { 277 val assignment = node.parent as Assignment 278 val atomicField = assignment.left 279 val constructorBlock = ((node.parent.parent as? ExpressionStatement)?.parent as? Block) 280 ?: abort("Incorrect tree structure of the constructor block initializing ${node.parent.toSource()}") 281 // check if there is a delegate field initialized by the reference to this atomic 282 for (stmt in constructorBlock) { 283 if (stmt is ExpressionStatement) { 284 if (stmt.expression is Assignment) { 285 val delegateAssignment = stmt.expression as Assignment 286 if (delegateAssignment.right is PropertyGet) { 287 val initializer = delegateAssignment.right as PropertyGet 288 if (initializer.toSource() == atomicField.toSource()) { 289 // register field delegate and the original atomic field 290 fieldDelegates[(delegateAssignment.left as PropertyGet).property.toSource()] = 291 (atomicField as PropertyGet).property.toSource() 292 } 293 } 294 } 295 } 296 } 297 } 298 } 299 } 300 return true 301 } 302 } 303 304 inner class DelegatedPropertyAccessorsVisitor : NodeVisitor { 305 override fun visit(node: AstNode?): Boolean { 306 if (node is PropertyGet) { 307 if (node.target is PropertyGet) { 308 if ((node.target as PropertyGet).property.toSource() in fieldDelegates && node.property.toSource() == MANGLED_VALUE_PROP) { 309 if (node.parent is ReturnStatement) { 310 val getter = ((((node.parent.parent as? Block)?.parent as? FunctionNode)?.parent as? ObjectProperty)?.parent as? ObjectLiteral) 311 ?: abort("Incorrect tree structure of the accessor for the property delegated " + 312 "to the atomic field ${fieldDelegates[node.target.toSource()]}") 313 val definePropertyCall = getter.parent as FunctionCall 314 val stringLiteral = definePropertyCall.arguments[1] as? StringLiteral 315 ?: abort ("Object.defineProperty invocation should take a property name as the second argument") 316 val delegatedProperty = stringLiteral.value.toString() 317 delegatedProperties[delegatedProperty] = (node.target as PropertyGet).property.toSource() 318 } 319 } 320 } 321 322 } 323 return true 324 } 325 } 326 327 inner class TransformVisitor : NodeVisitor { 328 override fun visit(node: AstNode): Boolean { 329 // remove atomic constructors from classes fields 330 if (node is FunctionCall) { 331 val functionName = node.target.toSource() 332 if (atomicConstructors.contains(functionName)) { 333 if (node.parent is Assignment) { 334 val valueNode = node.arguments[0] 335 (node.parent as Assignment).right = valueNode 336 } 337 return true 338 } else if (atomicArrayConstructors.contains(functionName)) { 339 val arrayConstructor = Name() 340 arrayConstructor.identifier = ARRAY 341 node.target = arrayConstructor 342 atomicArrayConstructors[functionName]?.let { 343 val arrayConsCall = FunctionCall() 344 arrayConsCall.target = node.target 345 arrayConsCall.arguments = node.arguments 346 val target = PropertyGet() 347 val fill = Name() 348 fill.identifier = FILL 349 target.target = arrayConsCall 350 target.property = fill 351 node.target = target 352 val initialValue = Name() 353 initialValue.identifier = it 354 node.arguments = listOf(initialValue) 355 } 356 return true 357 } else if (node.target is PropertyGet) { 358 if ((node.target as PropertyGet).target is FunctionCall) { 359 val atomicOperationTarget = node.target as PropertyGet 360 val funcCall = atomicOperationTarget.target as FunctionCall 361 if (funcCall.target is PropertyGet) { 362 val getterCall = (funcCall.target as PropertyGet).property 363 if (Regex(GET_ELEMENT).matches(getterCall.toSource())) { 364 val getter = getArrayElement(funcCall) 365 atomicOperationTarget.target = getter 366 } 367 } 368 } 369 } 370 } 371 // remove value property call 372 if (node is PropertyGet) { 373 if (node.property.toSource() == MANGLED_VALUE_PROP) { 374 // check whether atomic operation is performed on the type casted atomic field 375 node.target.eraseAtomicFieldFromUncheckedCast()?.let { node.target = it } 376 // A.a.value 377 if (node.target.type == Token.GETPROP) { 378 val clearField = node.target as PropertyGet 379 val targetNode = clearField.target 380 val clearProperety = clearField.property 381 node.setLeftAndRight(targetNode, clearProperety) 382 } 383 // other cases with $receiver.kotlinx$atomicfu$value in inline functions 384 else if (node.target.toSource().matches(Regex(RECEIVER))) { 385 val receiverName = node.target.toSource() // $receiver_i 386 val rr = ReceiverResolver(receiverName) 387 node.enclosingFunction.visit(rr) 388 rr.receiver?.let { node.target = it } 389 } 390 } 391 if (node.property.toSource() in delegatedProperties) { 392 // replace delegated property name with the name of the original atomic field 393 val fieldDelegate = delegatedProperties[node.property.toSource()] 394 val originalField = fieldDelegates[fieldDelegate]!! 395 node.property = Name().apply { identifier = originalField } 396 } 397 // replace Atomic*Array.size call with `length` property on the pure type js array 398 if (node.property.toSource() == ARRAY_SIZE) { 399 node.property = Name().also { it.identifier = LENGTH } 400 } 401 } 402 if (node is Block) { 403 for (stmt in node) { 404 if (stmt is ExpressionStatement) { 405 if (stmt.expression is Assignment) { 406 // erase field initialisation 407 val assignment = stmt.expression as Assignment 408 if (assignment.right is FunctionCall) { 409 val functionName = (assignment.right as FunctionCall).target.toSource() 410 if (traceConstructors.contains(functionName)) { 411 node.replaceChild(stmt, EmptyLine()) 412 } 413 } 414 } 415 if (stmt.expression is FunctionCall) { 416 // erase append(text) call 417 val funcNode = (stmt.expression as FunctionCall).target 418 if (funcNode is PropertyGet && funcNode.property.toSource().matches(Regex(TRACE_APPEND))) { 419 node.replaceChild(stmt, EmptyLine()) 420 } 421 } 422 } 423 } 424 } 425 if (node is Assignment && node.left is PropertyGet) { 426 val left = node.left as PropertyGet 427 if (traceFormatObjects.contains(left.target.toSource())) { 428 if (node.right is FunctionCall) { 429 // TraceFormatObject initialization 430 (node.right as FunctionCall).arguments = listOf(Name().also { it.identifier = "null" }) 431 } 432 } 433 } 434 // remove TraceFormatObject constructor definition 435 if (node is FunctionNode && traceFormatObjects.contains(node.name)) { 436 val body = node.body 437 for (stmt in body) { body.replaceChild(stmt, EmptyLine()) } 438 } 439 // remove TraceFormat from TraceFormatObject interfaces 440 if (node is Assignment && node.left is PropertyGet && node.right is ObjectLiteral) { 441 val left = node.left as PropertyGet 442 val metadata = node.right as ObjectLiteral 443 if (traceFormatObjects.contains(left.target.toSource())) { 444 for (e in metadata.elements) { 445 if (e.right is ArrayLiteral) { 446 val array = (e.right as ArrayLiteral).toSource() 447 if (array.contains(TRACE_FORMAT)) { 448 (e.right as ArrayLiteral).elements = emptyList() 449 } 450 } 451 } 452 } 453 } 454 return true 455 } 456 457 private fun getArrayElement(getterCall: FunctionCall): AstNode { 458 val index = getterCall.arguments[0] 459 val arrayField = (getterCall.target as PropertyGet).target 460 // whether this field is static or not 461 val isStatic = arrayField !is PropertyGet 462 val arrName = if (isStatic) arrayField else (arrayField as PropertyGet).property 463 val getter = ElementGet(arrName, index) 464 return if (isStatic) { //intArr[index] 465 getter 466 } else { //A.intArr[0] 467 val call = PropertyGet() 468 call.target = (arrayField as PropertyGet).target 469 val name = Name() 470 name.identifier = getter.toSource() 471 call.property = name 472 call 473 } 474 } 475 } 476 477 478 // receiver data flow 479 inner class ReceiverResolver(private val receiverName: String) : NodeVisitor { 480 var receiver: AstNode? = null 481 override fun visit(node: AstNode): Boolean { 482 if (node is VariableInitializer) { 483 if (node.target.toSource() == receiverName) { 484 receiver = node.initializer 485 return false 486 } 487 } 488 return true 489 } 490 } 491 492 inner class AtomicOperationsInliner : NodeVisitor { 493 override fun visit(node: AstNode?): Boolean { 494 // inline atomic operations 495 if (node is FunctionCall) { 496 if (node.target is PropertyGet) { 497 val funcName = (node.target as PropertyGet).property 498 var field = (node.target as PropertyGet).target 499 if (field.toSource().matches(Regex(RECEIVER))) { 500 val receiverName = field.toSource() // $receiver_i 501 val rr = ReceiverResolver(receiverName) 502 node.enclosingFunction.visit(rr) 503 if (rr.receiver != null) { 504 field = rr.receiver 505 } 506 } 507 field.eraseAtomicFieldFromUncheckedCast()?.let { field = it } 508 val args = node.arguments 509 val inlined = node.inlineAtomicOperation(funcName.toSource(), field, args) 510 return !inlined 511 } 512 } 513 return true 514 } 515 } 516 517 private fun AstNode.eraseAtomicFieldFromUncheckedCast(): AstNode? { 518 if (this is ParenthesizedExpression && expression is ConditionalExpression) { 519 val testExpression = (expression as ConditionalExpression).testExpression 520 if (testExpression is FunctionCall && testExpression.target.toSource() == KOTLIN_TYPE_CHECK) { 521 // type check 522 val typeToCast = testExpression.arguments[1] 523 if ((typeToCast as Name).identifier == ATOMIC_REF) { 524 // unchecked type cast -> erase atomic field itself 525 return (testExpression.arguments[0] as Assignment).right 526 } 527 } 528 } 529 return null 530 } 531 532 private fun AstNode.isThisNode(): Boolean { 533 return when(this) { 534 is PropertyGet -> { 535 target.isThisNode() 536 } 537 is FunctionCall -> { 538 target.isThisNode() 539 } 540 else -> { 541 (this.type == Token.THIS) 542 } 543 } 544 } 545 546 private fun PropertyGet.resolvePropName(): String { 547 val target = this.target 548 return if (target is PropertyGet) { 549 "${target.resolvePropName()}.${property.toSource()}" 550 } else { 551 property.toSource() 552 } 553 } 554 555 private fun AstNode.scopedSource(): String { 556 if (this.isThisNode()) { 557 if (this is PropertyGet) { 558 val property = resolvePropName() 559 return "$SCOPE.$property" 560 } else if (this is FunctionCall && this.target is PropertyGet) { 561 // check that this function call is getting array element 562 if (this.target is PropertyGet) { 563 val funcName = (this.target as PropertyGet).property.toSource() 564 if (Regex(GET_ELEMENT).matches(funcName)) { 565 val property = (this.target as PropertyGet).resolvePropName() 566 return "$SCOPE.$property(${this.arguments[0].toSource()})" 567 } 568 } 569 } else if (this.type == Token.THIS) { 570 return SCOPE 571 } 572 } 573 return this.toSource() 574 } 575 576 private fun FunctionCall.inlineAtomicOperation( 577 funcName: String, 578 field: AstNode, 579 args: List<AstNode> 580 ): Boolean { 581 val f = field.scopedSource() 582 val code = when (funcName) { 583 "atomicfu\$getAndSet" -> { 584 val arg = args[0].toSource() 585 "(function($SCOPE) {var oldValue = $f; $f = $arg; return oldValue;})()" 586 } 587 "atomicfu\$compareAndSet" -> { 588 val expected = args[0].scopedSource() 589 val updated = args[1].scopedSource() 590 val equals = if (expected == "null") "==" else "===" 591 "(function($SCOPE) {return $f $equals $expected ? function() { $f = $updated; return true }() : false})()" 592 } 593 "atomicfu\$getAndIncrement" -> { 594 "(function($SCOPE) {return $f++;})()" 595 } 596 597 "atomicfu\$getAndIncrement\$long" -> { 598 "(function($SCOPE) {var oldValue = $f; $f = $f.inc(); return oldValue;})()" 599 } 600 601 "atomicfu\$getAndDecrement" -> { 602 "(function($SCOPE) {return $f--;})()" 603 } 604 605 "atomicfu\$getAndDecrement\$long" -> { 606 "(function($SCOPE) {var oldValue = $f; $f = $f.dec(); return oldValue;})()" 607 } 608 609 "atomicfu\$getAndAdd" -> { 610 val arg = args[0].scopedSource() 611 "(function($SCOPE) {var oldValue = $f; $f += $arg; return oldValue;})()" 612 } 613 614 "atomicfu\$getAndAdd\$long" -> { 615 val arg = args[0].scopedSource() 616 "(function($SCOPE) {var oldValue = $f; $f = $f.add($arg); return oldValue;})()" 617 } 618 619 "atomicfu\$addAndGet" -> { 620 val arg = args[0].scopedSource() 621 "(function($SCOPE) {$f += $arg; return $f;})()" 622 } 623 624 "atomicfu\$addAndGet\$long" -> { 625 val arg = args[0].scopedSource() 626 "(function($SCOPE) {$f = $f.add($arg); return $f;})()" 627 } 628 629 "atomicfu\$incrementAndGet" -> { 630 "(function($SCOPE) {return ++$f;})()" 631 } 632 633 "atomicfu\$incrementAndGet\$long" -> { 634 "(function($SCOPE) {return $f = $f.inc();})()" 635 } 636 637 "atomicfu\$decrementAndGet" -> { 638 "(function($SCOPE) {return --$f;})()" 639 } 640 641 "atomicfu\$decrementAndGet\$long" -> { 642 "(function($SCOPE) {return $f = $f.dec();})()" 643 } 644 else -> null 645 } 646 if (code != null) { 647 this.setImpl(code) 648 return true 649 } 650 return false 651 } 652 653 private fun FunctionCall.setImpl(code: String) { 654 val p = Parser(CompilerEnvirons()) 655 val node = p.parse(code, null, 0) 656 if (node.firstChild != null) { 657 val expr = (node.firstChild as ExpressionStatement).expression 658 this.target = (expr as FunctionCall).target 659 val thisNode = Parser(CompilerEnvirons()).parse("this", null, 0) 660 this.arguments = listOf((thisNode.firstChild as ExpressionStatement).expression) 661 } 662 } 663 } 664 665 private class EmptyLine : EmptyExpression() { toSourcenull666 override fun toSource(depth: Int) = "\n" 667 } 668 669 fun main(args: Array<String>) { 670 if (args.size !in 1..2) { 671 println("Usage: AtomicFUTransformerKt <dir> [<output>]") 672 return 673 } 674 val t = AtomicFUTransformerJS(File(args[0]), File(args[1])) 675 t.transform() 676 }