mutator 0.0.3

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 0

mutator

A dart language helper tool for pre-compile/transform time refactoring. A potential alternative for Macro or inline function in desperate times.

##Status: Alpha Type detection currently relies on a solution I improvised without a proper design or abstraction and it is neither fast or exhaustively tested. Skip type detection by passing skip_type_check:true to mutate_t method for safety and speed if possible;see usage example1 for more details.

Usage

A simple usage example: Refactoring math.max(5,9) into (){int t = 5;t = t<9?9:t;return t;}();.

import 'package:mutator/mutator.dart';
String alias = 'math';
String pattern = 'math\\.max\\([0-9,\\w\\s_]+\\)';
//path needs to be set properly if there are relative file imports or part files.
String file_path = '';//leaving empty as neither is the case.

String src = """
      import 'dart:math' as math;
      main() async{
        int m = math.max(5,94,8,3,5,7,4);
      }
      """;

///main  should transform the value of
///src into the code below.
///
///   import 'dart:math' as math;
///   main() async{
///     int m = () {
///       int t = 5;
///       t = t < 94 ? 94 : t;
///       t = t < 8 ? 8 : t;
///       t = t < 3 ? 3 : t;
///       t = t < 5 ? 5 : t;
///       t = t < 7 ? 7 : t;
///       t = t < 4 ? 4 : t;
///       return t;
///     }();
///}
main() async{
  replacer(MethodInvocation e){
    String generate_code_for_getting_larger(
        String variable_name,
        String value1,
        String value2){
      return '${variable_name} = '
          '${value1.trim()}<${value2.trim()}?'
          '${value2.trim()}:${value1.trim()};';
    }
    String s = e.toString();
    s = s.substring(9,s.length-1);//removing `math.max(` and `)`
    var l = s.split(',');

    List f = ['(){int t = ${l[0]};'];

    for(String v in l.sublist(1))
      f.add(generate_code_for_getting_larger('t','t',v));

    f.add('return t;}()');
    return f.join();
  }
  var m = new Mutator<MethodInvocation>(
      '', pattern, replacer,alias_name: alias);
  print(await m.mutate_t(file_path,code:src,skip_type_check:true));
}

Refactoring r.nextInt(5) into a random number. Leaving r2.nextInt(5) unchanged as r2 is not an instance of math.Random.

import 'dart:math' as math;
import 'package:mutator/mutator.dart';

String pattern = '[\\w0-9_]+\\.nextInt\\([0-9]+\\)';
String klass_name = 'Random';
String path = '';//dummy path
String src = """
    import 'dart:math' as math;
    main() async{
    var r = new math.Random(5);
    print(r.nextInt(600));
    var r2 = new Random();
    print(r2.nextInt(600));
    }
    class Random{
        nextInt(int n){
          return n +5;
        }
    }
    """;
/// main should transform the value of src into
/// the code below and print it:
///
///import 'dart:math' as math;
///
///main() async{
///  var r = new math.Random(5);
///  print(88);//Changed
///  var r2 = new Random();
///  print(r2.nextInt(600));//Not changed
///}
///
///class Random {
///  nextInt(int n) {
///    return n + 5;
///  }
///}
///
main() async{
  int random_num;
  var r = new math.Random(5);
  replacer(MethodInvocation e){
    String s = e.toString();
    random_num = r.nextInt( int.parse(new RegExp('[0-9]+')
        .firstMatch(s).group(0)));
    return random_num.toString();
  }
  var m = new Mutator<MethodInvocation>(
      klass_name, pattern, replacer,alias_name: 'math');
  print(await m.mutate_t(path,code:src));
}

Refactoring d.on(o).hi = ()=>print('hi'); into d.on(o).set('hi',()=>print('hi'));

import 'package:mutator/mutator.dart';
String code = """
import 'package:mistletoe';
var d = new Dynamism(expert:true);
main() async{
    var o = new Object();
    d.on(o).hi = ()=>print('hi');
}
""";
const String klass_name = 'Dynamism';
const String pattern =
    '^[a-z.A-Z_0-9]+\\.on\\'
    '([a-z.A-Z_0-9]+\\)\\.[a-z.A-Z_0-9]+';
String file_path = '';
main() async{
    replacer(e){
        String s = e.toString();
        List l  = s.split('=');
        var invocation = l.removeAt(0).split('.');
        String name = invocation.removeLast().trim();
        invocation = invocation.join('.') +
            '.set(\'${name}\', ${l.join('=').trim()})';
        return invocation;
    }
    var m = new Mutator<AssignmentExpression>(
      klass_name, pattern, replacer);
    print(await m.mutate_t(file_path,code:code));
}

Features and bugs

Please file feature requests and bugs at the https://github.com/TastyCatFood/mutator/issues.

Limitations

  • No type detection available when the type is not statically defined. e.g.

      f(e){ return e.nextInt(4);}
    
  • Function's return type is ignored. e.g.

     math.Random f(){new math.Random(501);}
     main(){
         f().nextInt(7);
     }
    
  • Type information within conditional statement are ignored. e.g.

      f(e){
         if(e is math.Random){
          return e.nextInt(2);
         }
      }
    

Does not detect the type of variables defined in a file that has been imported as a package or a part of dart-sdk.

e.g.

    import 'package:example_code.dart' as eg;
    main(){
    // Mutator does not look into the package to find the type of [a].
        print(eg.a);
    }

The type of [a] is available when example_code.dart is imported relatively; import './example_code.dart'; or as a part file.

Changelog

0.0.1

  • Initial version

0.0.2

  • A bug fixed

0.0.3

  • mutate and mutate_t are now async methods. This change has been made as sometimes barback attempts to transform a part file before library's main file. Now, mutate_t only completes when dependencies are resolved.

example/mutator_example.dart

import 'package:analyzer/analyzer.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:mutator/mutator.dart';
import 'package:path/path.dart' as Path;
import 'dart:io' show Platform;

const String klass_name = 'Dynamism';
const String pattern =
    '^[a-z.A-Z_0-9]+\\.on\\'
    '([a-z.A-Z_0-9]+\\)\\.[a-z.A-Z_0-9]+';
String file_path = to_abs_path('../web/index.dart');
to_abs_path(path,[base_dir = null]){
// Fetching the project home dir
//  var cd = Path.current;

//  Changing the current directory
//  Directory original_dir = Directory.current;
//  Directory.current = dirname.toFilePath();
  Path.Context context;
  if(Platform.isWindows){
    context = new Path.Context(style:Path.Style.windows);
  }else{
    context = new Path.Context(style:Path.Style.posix);
  }
  base_dir ??= Path.dirname(
      Platform.script.toFilePath());
  path = context.join( base_dir,path);
  return context.normalize(path);
}
main() async {

  var m = new Mutator<AssignmentExpression>(
      klass_name, pattern,(e){
    String s = e.toString();
    List l  = s.split('=');
    var invocation = l.removeAt(0).split('.');
    String name = invocation.removeLast().trim();
    invocation = invocation.join('.') +
        '.set(\'${name}\', ${l.join('=').trim()})';
    return invocation;
  });
  String r = await m.mutate_t(file_path);


  m = new Mutator<PropertyAccess>(klass_name,pattern,
        (e){
      List l = e.toString().split('.');
      String property_name = l.removeLast();
      String invocation = l.join('.');
      invocation = invocation +
          '.get(\'${property_name.trim()}\')';
      return invocation;
    });
  r = await m.mutate_t(file_path,code:r);


  m = new Mutator<MethodInvocation>(klass_name,pattern,
      (MethodInvocation e){
          String s = e.toString();
          var m = new RegExp(
              'on\\([\\w_\\.]+\\)\\.').firstMatch(s);
          //splitting d.on(e).hi(e) into `d.on(e)`
          // and `hi(e)`
          String on_call = s.substring(0,m.end-1);
          String method_call = s.substring(m.end,s.length);

          //splitting `hi(e)` into `hi` and `(e)`
          int idx = method_call.indexOf('(');
          String method_name =
          method_call.substring(0,idx).trim();
          String params = method_call.substring(idx+1,
              method_call.length-1);
          //assembling parts into a method call
          return '${on_call}.invoke'
              '(\'${method_name}\',[${params}])';
  });
  r = await m.mutate_t(file_path,code:r);
  print(r);
  return;
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  mutator: ^0.0.3

2. Install it

You can install packages from the command line:

with pub:


$ pub get

Alternatively, your editor might support pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:mutator/mutator.dart';
  
Version Uploaded Documentation Archive
0.0.4 Apr 28, 2016 Go to the documentation of mutator 0.0.4 Download mutator 0.0.4 archive
0.0.3 Apr 26, 2016 Go to the documentation of mutator 0.0.3 Download mutator 0.0.3 archive
0.0.2 Apr 16, 2016 Go to the documentation of mutator 0.0.2 Download mutator 0.0.2 archive
0.0.1 Apr 16, 2016 Go to the documentation of mutator 0.0.1 Download mutator 0.0.1 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
0
Health:
Code health derived from static analysis. [more]
--
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
--
Overall:
Weighted score of the above. [more]
0
Learn more about scoring.

This package version is not analyzed, because it is more than two years old. Check the latest stable version for its analysis.

The package version is not analyzed, because it does not support Dart 2. Until this is resolved, the package will receive a health and maintenance score of 0.

Maintenance issues and suggestions

Running dartdoc failed. (-10 points)

Make sure dartdoc runs without any issues.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.0.0 <2.0.0