sclass RealmCopy { O dest = mc(); // destination realm new IdentityHashMap seen; new HashMap classMap; new HashMap> fieldsForClass; //IF1 classFinder = toIF1(_defaultClassFinder()); bool fullCopy; // descend into objects even when they have the same type bool nonTransientOnly; // skip transient fields? bool handleEnclosingInstancesProperly; // if false, set enclosing instance to null initially VStack.Computable copyComputation(O o) { ret new CopyObject(o); } O copy(O o) { ret evalInVStack(copyComputation(o)); } noeq record CopyObject(O o) extends VStackComputableWithStep { void step(VStack stack) { // null and simple types if (o == null || o instanceof String || o instanceof Number) { ifdef RealmCopy_debug print("Simple object: " + o); endifdef ret with stack.ret(o); } // seen object O oo = seen.get(o); if (oo != null) stack.ret(oo); dispatchByType(stack, o); } } void dispatchByType(VStack stack, O o) { if (o cast O[]) ret with stack.replace(new CopyObjectArray(o)); if (o cast Cl) if (!overrideCollectionOrMapCopy(o)) ret with stack.replace(new CopyCollection(o)); if (o cast Map) if (!overrideCollectionOrMapCopy(o)) ret with stack.replace(new CopyMap(o)); ret with stack.replace(new CopyObjectFieldByField(o)); } noeq record CopyObjectArray(O[] o) extends VStackComputableWithStep { O[] out; int i; void step(VStack stack) { if (step == 0) { out = newObjectArrayOfSameType(o); seen.put(o, out); ++step; } if (stack.hasSubResult()) out[i++] = stack.subResult(); if (i >= o.length) ret with stack.ret(out); stack.call(new CopyObject(o[i])); } } noeq record CopyCollection(Cl o) extends VStackComputableWithStep { Cl out; Iterator it; void step(VStack stack) { if (step == 0) { out = similarEmptyCollection(o); seen.put(o, out); it = iterator(o); ++step; } if (stack.hasSubResult()) out.add(stack.subResult()); if (!it.hasNext()) ret with stack.ret(out); stack.call(new CopyObject(it.next())); } } noeq record CopyMap(Map o) extends VStackComputableWithStep { Map out; Iterator it; Map.Entry entry; O copiedKey; void step(VStack stack) { if (step == 0) { out = similarEmptyMap(o); seen.put(o, out); it = iterator(o.entrySet()); ++step; } if (stack.hasSubResult()) { if (step == 1) { // got the copied key copiedKey = stack.subResult(); // now copy the value stack.call(new CopyObject(entry.getValue())); ret with ++step; } else { // got the copied value - can put into map out.put(copiedKey, stack.subResult()); step = 1; } } if (!it.hasNext()) ret with stack.ret(out); entry = it.next(); stack.call(new CopyObject(entry.getKey())); } } noeq record CopyObjectFieldByField(O o) extends VStackComputableWithStep { S className; Class c, destClass; O out; O enclosing; L fields; Iterator itField; Field field; Field[] enclosingFields; void step(VStack stack) { if (step == 0) { c = o.getClass(); destClass = classMap.get(c); if (destClass == null) { destClass = findDestClass(c); classMap.put(c, destClass); } if (destClass == c && !fullCopy) ret with stack.ret(o); // short cut unless deep copying // take care of enclosing instance enclosingFields = thisDollarOneFields(c); if (handleEnclosingInstancesProperly && nempty(enclosingFields)) stack.push(new CopyObject(fieldGet(first(enclosingFields), o))); ret with step = 1; } else if (step == 1) { // create new object try { if (stack.hasSubResult()) out = unlisted_gen(destClass, stack.subResult()); else if (nempty(enclosingFields)) out = unlisted_gen(destClass, (O) null); else out = unlisted_gen(destClass); } on fail { printVars_str(+c, +destClass, +enclosingFields); } seen.put(o, out); fields = getOrCreate(fieldsForClass, c, () -> nonTransientOnly ? nonStaticNonTransientFieldObjects(o) : nonStaticFieldObjects(o)); itField = iterator(fields); ret with ++step; } if (stack.hasSubResult()) { ifdef RealmCopy_debug printFunctionCall setOpt(out, field.getName(), stack.subResult()); endifdef setOpt(out, field.getName(), stack.subResult()); } if (!itField.hasNext()) ret with stack.ret(out); field = itField.next(); stack.push(new CopyObject(fieldGet(field, o)); } } Class findDestClass(Class c) { Class destClass = null; S className = c.getName(); className = loadableUtilsClassNameToMain(className); if (!isAnonymousClassName(className)) destClass = getClass_vmName_withLoadableUtils(dest, className); ret or(destClass, c); } // set to true if you want to copy a map or collection field-by-field swappable bool overrideCollectionOrMapCopy(O o) { false; } }