Giter Site home page Giter Site logo

no-spoon's Introduction

no-spoon - bend it like Neo!

This library makes it easy to modify/replace existing classes with init macros. Requires at least Haxe 3.2.1.

Patching fields on existing classes

The simplest possible way is using class reification:

no.Spoon.bend('haxe.Timer', macro class {
  static function repeat(rate:Int, f:Void->Void) {
    var t = new haxe.Timer(rate);
    t.run = f;
    return t;
  }
})

The second argument to bend however is defined to be a Bender which is defined like so:

abstract Bender from BuildFields->ClassType->Void {
  @:from static function fromDefinition(td:TypeDefinition):Bender;
}

@:forward(length)
abstract BuildFields(Array<Field>) from Array<Field> to Array<Field> {
  @:arrayAccess function get(index:Int):Field;
  public function remove(filter:FieldFilter):Void;
  public function find(filter:FieldFilter):Array<Field>;
  public function patch(p:FieldPatch, ?k:PatchKind):Array<Field>;
}

@:enum abstract PatchKind(String) {
  var All = null;
  var OnlyNew = 'OnlyNew';
  var OnlyExisting = 'OnlyExisting';
}

@:forward
abstract FieldPatch(Array<Field>) from Array<Field> to Array<Field> {
  @:from static function fromDefinition(td:TypeDefinition):FieldPatch;
}

@:callable
abstract FieldFilter(Field->Bool) from Field->Bool {
  @:from static function ofName(s:String):FieldFilter;
}

You can provide a function, that operates on BuildFields - an abstract, that has a few helpers to ease field manipulation. Example:

var fields:BuildFields = ...;
fields.remove('foo');//removes field called `foo` (if it exists)
fields.remove(function (f) return f.access.indexOf(AStatic) == -1);//removes all non static fields

fields.patch(macro class {
  public function foo():Void;
});//will add `foo` to the fields (if `foo` existed before, it is replaced)

fields.patch(macro class {
  public function foo():Void;
}, OnlyNew);//will add `foo` to the fields (if `foo` existed before, nothing is changed)

fields.patch(macro class {
  public function foo():Void;
}, OnlyExisting);//will replace `foo` if it exists

Bending caveats

If you call no.Spoon.bend after the target type has already been loaded, it will have no effect. If your patching attempts seem to not be applied, make sure you're performing them "early enough".

Replacing types

Simple example:

no.Spoon.replace('Math', macro : FastMath);

Replacement caveats

If you call no.Spoon.replace after the target type has already been loaded, you will get a compiler error.

When to use this library

If the idea of using this library seems a bit naughty to you, then that's because it is. In the vast majority of cases you should use this as a temporary bandaid and try to fix the problem upstream. Of course the upstream source may be slow to release patches (e.g. the stdlib) or somehow obsolete (e.g. abandoned library or the stdlib of an old Haxe version etc.).

A truly "legitimate" use case is when the changes that you wish to perform are only beneficial in the very narrow use case you're having and outside it would do more harm than good.

To a very large extent, what this library does comes close to monkey patching in JavaScript/Ruby/Python and method swizzling in Objective-C. There's however one crucial benefit of the technique this library allows: the code replacement is performed at compile time and will be processed by static analysis, meaning there is a pretty solid check in place to make sure that the replacement code is not absolute non-sense.

Alternatives

You can always rely on Haxe's class path shadowing. If you're not happy with how some stdlib or 3rd party code works, you can put a module of the same name into your class path and it will take precedence.

This can be problematic in two ways:

  1. You have a whole module to deal with, even though you wanted to alter a single method. You may have to port unrelated upstream patches into your code base.
  2. If it's a haxe core type, then shadowing will also be applied for macros, which rely on the neko/eval implementation. E.g. if you copy the Std for js into your class path to modify it, macros that rely on Std will fail.

Another advantage of no-spoon is, that your changes can be highly granular and context aware:

if (haxe_ver >= 4)
  no.Spoon.bend(/* work around some Haxe 4 regression */);
else
  no.Spoon.bend(/* make super useful method that was added in Haxe 4 available today */)

no-spoon's People

Contributors

back2dos avatar josuigoa avatar sebthom avatar

Stargazers

Barış Yıldırım avatar Rainy avatar Datee avatar  avatar DQ avatar  avatar David Bruce avatar grepsuzette avatar Joohun, Maeng avatar Vadym avatar Sam Morrison avatar Ian Harrigan avatar Dimitri Pomier avatar Giuseppe Di Mauro avatar Piotr Pawełczyk avatar Pieter Vantorre avatar Viachaslau Tratsiak avatar Thomas Fétiveau avatar Rudy Ges avatar Jonas Malaco avatar  avatar  avatar John Doughty avatar pleclech avatar Adrian Veith avatar Vadim Matkarimov avatar  avatar  avatar David Klein avatar Marcelo Serpa avatar Alexander Gordeyko avatar  avatar

Watchers

grepsuzette avatar  avatar  avatar Alexander Gordeyko avatar  avatar

no-spoon's Issues

You cannot use @:build inside a macro : make sure that your type is not used in macro

project.xml

<haxeflag name="--macro" value="openfl.macro.ReplaceURLLoaderMacro.replace()" />

ReplaceURLLoaderMacro.hx

package openfl.macro;

import openfl.net.CoreURLLoader;

class ReplaceURLLoaderMacro {
    macro static function replace():Void {
        no.Spoon.replace('URLLoader', macro : CoreURLLoader);
    }
}

https://github.com/core-haxe/queues-core/blob/b08c6001378292d8f451157a5bc5ed72d936038c/src/queues/QueueFactory.hx#L5

C:\HaxeToolkit\haxe\lib\queues-core\git\src\queues\QueueFactory.hx:5
You cannot use @:build inside a macro : make sure that your type is not used in macro

What is wrong with my setup.

Failed to load library "macro.ndll"

When trying to run a basic no.Spoon.bend() example, I receive the following error when targeting Neko:

> Called from Main::new line 24
> Called from no.Spoon::bend line 36
> Called from haxe.macro.Context::onAfterGenerate line 370
> Called from haxe.macro.Context::load line 629
> Called from neko.Lib::load line 35
> Uncaught exception - load.c(237) : Failed to load library : macro.ndll

When targeting Windows, I instead receive this error:

C:/HaxeToolkit/haxe/lib/no-spoon/0,2,0/src/no/Spoon.hx:36: characters 15-30 : Class<haxe.macro.Context> has no field onAfterGenerate
C:/HaxeToolkit/haxe/lib/no-spoon/0,2,0/src/no/Spoon.hx:39: characters 14-31 : Class<haxe.macro.Compiler> has no field addGlobalMetadata
C:/HaxeToolkit/haxe/lib/no-spoon/0,2,0/src/no/Spoon.hx:13: characters 26-40 : Class<haxe.macro.Context> has no field getBuildFields
C:/HaxeToolkit/haxe/lib/no-spoon/0,2,0/src/no/Spoon.hx:14: characters 23-36 : Class<haxe.macro.Context> has no field getLocalClass
C:/HaxeToolkit/haxe/lib/no-spoon/0,2,0/src/no/Spoon.hx:23: characters 13-23 : Class<haxe.macro.Context> has no field defineType

local monkey patching

this library can be use for a local monkey patching? for example change code only inside namespaces?
imagine for example the refinements in ruby with a local monkey patching

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.