diff --git a/src/kiss/EmbeddedScript.hx b/src/kiss/EmbeddedScript.hx index daa6842..b0655d3 100644 --- a/src/kiss/EmbeddedScript.hx +++ b/src/kiss/EmbeddedScript.hx @@ -7,7 +7,7 @@ import haxe.macro.PositionTools; import sys.io.File; #end import kiss.Kiss; -import cloner.Cloner; +import kiss.cloner.Cloner; typedef Command = () -> Void; @@ -183,7 +183,7 @@ class EmbeddedScript { for (command in commands) { // a fork needs to be a thorough copy that includes all of the EmbeddedScript subclass's // fields, otherwise DSL state will be lost when forking, which is unacceptable - var fork = new cloner.Cloner().clone(this); + var fork = new kiss.cloner.Cloner().clone(this); fork.instructions[instructionPointer] = command; fork.run(); fork; diff --git a/src/kiss/cloner/Cloner.hx b/src/kiss/cloner/Cloner.hx new file mode 100644 index 0000000..41ea91d --- /dev/null +++ b/src/kiss/cloner/Cloner.hx @@ -0,0 +1,115 @@ +package kiss.cloner; +import Array; +import haxe.ds.ObjectMap; +import Type.ValueType; +import haxe.ds.IntMap; +import haxe.ds.StringMap; +class Cloner { + + var cache:ObjectMap; + var classHandles:MapDynamic>; + var stringMapCloner:MapCloner; + var intMapCloner:MapCloner; + + public function new():Void { + stringMapCloner = new MapCloner(this,StringMap); + intMapCloner = new MapCloner(this,IntMap); + classHandles = new MapDynamic>(); + classHandles.set('String',returnString); + classHandles.set('Array',cloneArray); + classHandles.set('haxe.ds.StringMap',stringMapCloner.clone); + classHandles.set('haxe.ds.IntMap',intMapCloner.clone); + } + + function returnString(v:String):String { + return v; + } + + public function clone (v:T):T { + cache = new ObjectMap(); + var outcome:T = _clone(v); + cache = null; + return outcome; + } + + public function _clone (v:T):T { + #if js + if(Std.is(v, String)) + return v; + #end + + #if neko + try { + if(Type.getClassName(cast v) != null) + return v; + }catch(e:Dynamic) {} + #else + if(Type.getClassName(cast v) != null) + return v; + #end + switch(Type.typeof(v)){ + case TNull: + return null; + case TInt: + return v; + case TFloat: + return v; + case TBool: + return v; + case TObject: + return handleAnonymous(v); + case TFunction: + return v; + case TClass(c): + if(!cache.exists(v)) + cache.set(v,handleClass(c, v)); + return cache.get(v); + case TEnum(e): + return v; + case TUnknown: + return null; + } + } + + function handleAnonymous (v:Dynamic):Dynamic { + var properties:Array = Reflect.fields(v); + var anonymous:Dynamic = {}; + for (i in 0...properties.length) { + var property:String = properties[i]; + Reflect.setField(anonymous, property, _clone(Reflect.getProperty(v, property))); + } + return anonymous; + } + + function handleClass (c:Class,inValue:T):T { + var handle:T->T = classHandles.get(Type.getClassName(c)); + if(handle == null) + handle = cloneClass; + return handle(inValue); + } + + function cloneArray (inValue:Array):Array { + var array:Array = inValue.copy(); + for (i in 0...array.length) + array[i] = _clone(array[i]); + return array; + } + + function cloneClass (inValue:T):T { + var classValue = Type.getClass(inValue); + var outValue:T = Type.createEmptyInstance(classValue); + var fields:Array = Type.getInstanceFields(classValue); + for (i in 0...fields.length) { + var field = fields[i]; + var property = Reflect.getProperty(inValue, field); + try { + Reflect.setField(outValue, field, _clone(property)); + } catch (s) { + // There will be errors on C++ when trying to assign to a member function. + // They're not important, because the function will already be on the clone. + } + } + return outValue; + } + +} \ No newline at end of file diff --git a/src/kiss/cloner/LICENSE.md b/src/kiss/cloner/LICENSE.md new file mode 100644 index 0000000..feaaa85 --- /dev/null +++ b/src/kiss/cloner/LICENSE.md @@ -0,0 +1,24 @@ +License +======= + +The MIT License (MIT) + +Copyright (c) 2015 Thomas Uster + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/src/kiss/cloner/MapCloner.hx b/src/kiss/cloner/MapCloner.hx new file mode 100644 index 0000000..59ced48 --- /dev/null +++ b/src/kiss/cloner/MapCloner.hx @@ -0,0 +1,25 @@ +package kiss.cloner; +import haxe.Constraints.IMap; +import kiss.cloner.Cloner; +import Type.ValueType; +class MapCloner{ + + var cloner:Cloner; + var type:Class>; + var noArgs:Array; + + public function new(cloner:Cloner, type:Class>):Void { + this.cloner = cloner; + this.type = type; + noArgs = []; + } + + public function clone (inValue:IMap):IMap { + var inMap:IMap = inValue; + var map:IMap = cast Type.createInstance(type, noArgs); + for (key in inMap.keys()) { + map.set(key, cloner._clone(inMap.get(key))); + } + return map; + } +} \ No newline at end of file diff --git a/src/kiss/cloner/README.md b/src/kiss/cloner/README.md new file mode 100644 index 0000000..4bfbcf5 --- /dev/null +++ b/src/kiss/cloner/README.md @@ -0,0 +1,12 @@ +[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE.md) +[![TravisCI Build Status](https://travis-ci.org/thomasuster/cloner.svg?branch=master)](https://travis-ci.org/thomasuster/cloner) +[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/thomasuster/cloner?branch=master&svg=true)](https://ci.appveyor.com/project/thomasuster/cloner) + +``` +git clone https://github.com/thomasuster/cloner.git +haxelib dev cloner cloner/src +``` +``` +var cloner:Cloner = new Cloner(); +var clone = cloner.clone(mostAnything); +``` \ No newline at end of file